27
24
from bzrlib import (
30
config as _mod_config,
35
revision as _mod_revision,
41
from bzrlib.config import BranchConfig, TreeConfig
42
from bzrlib.lockable_files import LockableFiles, TransportLock
43
from bzrlib.tag import (
36
from bzrlib.config import TreeConfig
49
37
from bzrlib.decorators import needs_read_lock, needs_write_lock
50
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
51
HistoryMissing, InvalidRevisionId,
52
InvalidRevisionNumber, LockError, NoSuchFile,
38
import bzrlib.errors as errors
39
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
40
HistoryMissing, InvalidRevisionId,
41
InvalidRevisionNumber, LockError, NoSuchFile,
53
42
NoSuchRevision, NoWorkingTree, NotVersionedError,
54
NotBranchError, UninitializableFormat,
55
UnlistableStore, UnlistableBranch,
43
NotBranchError, UninitializableFormat,
44
UnlistableStore, UnlistableBranch,
57
from bzrlib.hooks import Hooks
46
from bzrlib.lockable_files import LockableFiles, TransportLock
58
47
from bzrlib.symbol_versioning import (deprecated_function,
60
49
DEPRECATED_PARAMETER,
62
zero_eight, zero_nine, zero_sixteen,
51
zero_eight, zero_nine,
64
53
from bzrlib.trace import mutter, note
67
56
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
68
57
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
69
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
58
BZR_BRANCH_FORMAT_6 = "Bazaar-NG branch, format 6\n"
72
61
# TODO: Maybe include checks for common corruption of newlines, etc?
333
292
def set_revision_history(self, rev_history):
334
293
raise NotImplementedError(self.set_revision_history)
336
def _cache_revision_history(self, rev_history):
337
"""Set the cached revision history to rev_history.
339
The revision_history method will use this cache to avoid regenerating
340
the revision history.
342
This API is semi-public; it only for use by subclasses, all other code
343
should consider it to be private.
345
self._revision_history_cache = rev_history
347
def _clear_cached_state(self):
348
"""Clear any cached data on this branch, e.g. cached revision history.
350
This means the next call to revision_history will need to call
351
_gen_revision_history.
353
This API is semi-public; it only for use by subclasses, all other code
354
should consider it to be private.
356
self._revision_history_cache = None
358
def _gen_revision_history(self):
359
"""Return sequence of revision hashes on to this branch.
361
Unlike revision_history, this method always regenerates or rereads the
362
revision history, i.e. it does not cache the result, so repeated calls
365
Concrete subclasses should override this instead of revision_history so
366
that subclasses do not need to deal with caching logic.
368
This API is semi-public; it only for use by subclasses, all other code
369
should consider it to be private.
371
raise NotImplementedError(self._gen_revision_history)
374
295
def revision_history(self):
375
"""Return sequence of revision hashes on to this branch.
377
This method will cache the revision history for as long as it is safe to
380
if self._revision_history_cache is not None:
381
history = self._revision_history_cache
383
history = self._gen_revision_history()
384
self._cache_revision_history(history)
296
"""Return sequence of revision hashes on to this branch."""
297
raise NotImplementedError(self.revision_history)
388
300
"""Return current revision number for this branch.
613
461
raise InvalidRevisionNumber(revno)
616
def clone(self, to_bzrdir, revision_id=None):
464
def clone(self, *args, **kwargs):
617
465
"""Clone this branch into to_bzrdir preserving all semantic values.
619
467
revision_id: if not None, the revision history in the new branch will
620
468
be truncated to end with revision_id.
470
# for API compatibility, until 0.8 releases we provide the old api:
471
# def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
472
# after 0.8 releases, the *args and **kwargs should be changed:
473
# def clone(self, to_bzrdir, revision_id=None):
474
if (kwargs.get('to_location', None) or
475
kwargs.get('revision', None) or
476
kwargs.get('basis_branch', None) or
477
(len(args) and isinstance(args[0], basestring))):
478
# backwards compatibility api:
479
warn("Branch.clone() has been deprecated for BzrDir.clone() from"
480
" bzrlib 0.8.", DeprecationWarning, stacklevel=3)
483
basis_branch = args[2]
485
basis_branch = kwargs.get('basis_branch', None)
487
basis = basis_branch.bzrdir
492
revision_id = args[1]
494
revision_id = kwargs.get('revision', None)
499
# no default to raise if not provided.
500
url = kwargs.get('to_location')
501
return self.bzrdir.clone(url,
502
revision_id=revision_id,
503
basis=basis).open_branch()
505
# generate args by hand
507
revision_id = args[1]
509
revision_id = kwargs.get('revision_id', None)
513
# no default to raise if not provided.
514
to_bzrdir = kwargs.get('to_bzrdir')
622
515
result = self._format.initialize(to_bzrdir)
623
516
self.copy_content_into(result, revision_id=revision_id)
635
528
result.set_parent(self.bzrdir.root_transport.base)
638
def _synchronize_history(self, destination, revision_id):
639
"""Synchronize last revision and revision history between branches.
641
This version is most efficient when the destination is also a
642
BzrBranch5, but works for BzrBranch6 as long as the revision
643
history is the true lefthand parent history, and all of the revisions
644
are in the destination's repository. If not, set_revision_history
647
:param destination: The branch to copy the history into
648
:param revision_id: The revision-id to truncate history at. May
649
be None to copy complete history.
532
def copy_content_into(self, destination, revision_id=None):
533
"""Copy the content of self into destination.
535
revision_id: if not None, the revision history in the new branch will
536
be truncated to end with revision_id.
651
538
new_history = self.revision_history()
652
539
if revision_id is not None:
653
revision_id = osutils.safe_revision_id(revision_id)
655
541
new_history = new_history[:new_history.index(revision_id) + 1]
656
542
except ValueError:
657
543
rev = self.repository.get_revision(revision_id)
658
544
new_history = rev.get_history(self.repository)[1:]
659
545
destination.set_revision_history(new_history)
662
def copy_content_into(self, destination, revision_id=None):
663
"""Copy the content of self into destination.
665
revision_id: if not None, the revision history in the new branch will
666
be truncated to end with revision_id.
668
self._synchronize_history(destination, revision_id)
670
547
parent = self.get_parent()
671
548
except errors.InaccessibleParent, e:
820
671
def get_format_description(self):
821
672
"""Return the short format description for this format."""
822
raise NotImplementedError(self.get_format_description)
824
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
826
"""Initialize a branch in a bzrdir, with specified files
828
:param a_bzrdir: The bzrdir to initialize the branch in
829
:param utf8_files: The files to create as a list of
830
(filename, content) tuples
831
:param set_format: If True, set the format with
832
self.get_format_string. (BzrBranch4 has its format set
834
:return: a branch in this format
836
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
837
branch_transport = a_bzrdir.get_branch_transport(self)
839
'metadir': ('lock', lockdir.LockDir),
840
'branch4': ('branch-lock', lockable_files.TransportLock),
842
lock_name, lock_class = lock_map[lock_type]
843
control_files = lockable_files.LockableFiles(branch_transport,
844
lock_name, lock_class)
845
control_files.create_lock()
846
control_files.lock_write()
848
control_files.put_utf8('format', self.get_format_string())
850
for file, content in utf8_files:
851
control_files.put_utf8(file, content)
853
control_files.unlock()
854
return self.open(a_bzrdir, _found=True)
673
raise NotImplementedError(self.get_format_string)
856
675
def initialize(self, a_bzrdir):
857
676
"""Create a branch of this format in a_bzrdir."""
890
709
def __str__(self):
891
710
return self.get_format_string().rstrip()
893
def supports_tags(self):
894
"""True if this format supports tags stored in the branch"""
895
return False # by default
897
# XXX: Probably doesn't really belong here -- mbp 20070212
898
def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
900
branch_transport = a_bzrdir.get_branch_transport(self)
901
control_files = lockable_files.LockableFiles(branch_transport,
902
lock_filename, lock_class)
903
control_files.create_lock()
904
control_files.lock_write()
906
for filename, content in utf8_files:
907
control_files.put_utf8(filename, content)
909
control_files.unlock()
912
class BranchHooks(Hooks):
913
"""A dictionary mapping hook name to a list of callables for branch hooks.
915
e.g. ['set_rh'] Is the list of items to be called when the
916
set_revision_history function is invoked.
920
"""Create the default hooks.
922
These are all empty initially, because by default nothing should get
926
# Introduced in 0.15:
927
# invoked whenever the revision history has been set
928
# with set_revision_history. The api signature is
929
# (branch, revision_history), and the branch will
932
# invoked after a push operation completes.
933
# the api signature is
935
# containing the members
936
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
937
# where local is the local branch or None, master is the target
938
# master branch, and the rest should be self explanatory. The source
939
# is read locked and the target branches write locked. Source will
940
# be the local low-latency branch.
941
self['post_push'] = []
942
# invoked after a pull operation completes.
943
# the api signature is
945
# containing the members
946
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
947
# where local is the local branch or None, master is the target
948
# master branch, and the rest should be self explanatory. The source
949
# is read locked and the target branches write locked. The local
950
# branch is the low-latency branch.
951
self['post_pull'] = []
952
# invoked after a commit operation completes.
953
# the api signature is
954
# (local, master, old_revno, old_revid, new_revno, new_revid)
955
# old_revid is NULL_REVISION for the first commit to a branch.
956
self['post_commit'] = []
957
# invoked after a uncommit operation completes.
958
# the api signature is
959
# (local, master, old_revno, old_revid, new_revno, new_revid) where
960
# local is the local branch or None, master is the target branch,
961
# and an empty branch recieves new_revno of 0, new_revid of None.
962
self['post_uncommit'] = []
965
# install the default hooks into the Branch class.
966
Branch.hooks = BranchHooks()
969
713
class BzrBranchFormat4(BranchFormat):
970
714
"""Bzr branch format 4.
1050
815
format = BranchFormat.find_format(a_bzrdir)
1051
816
assert format.__class__ == self.__class__
1052
817
transport = a_bzrdir.get_branch_transport(None)
1053
control_files = lockable_files.LockableFiles(transport, 'lock',
818
control_files = LockableFiles(transport, 'lock', lockdir.LockDir)
1055
819
return BzrBranch5(_format=self,
1056
820
_control_files=control_files,
1057
821
a_bzrdir=a_bzrdir,
1058
822
_repository=a_bzrdir.find_repository())
1061
class BzrBranchFormat6(BzrBranchFormat5):
1062
"""Branch format with last-revision
1064
Unlike previous formats, this has no explicit revision history. Instead,
1065
this just stores the last-revision, and the left-hand history leading
1066
up to there is the history.
1068
This format was introduced in bzr 0.15
1071
def get_format_string(self):
1072
"""See BranchFormat.get_format_string()."""
1073
return "Bazaar Branch Format 6 (bzr 0.15)\n"
1075
def get_format_description(self):
1076
"""See BranchFormat.get_format_description()."""
1077
return "Branch format 6"
1079
def initialize(self, a_bzrdir):
1080
"""Create a branch of this format in a_bzrdir."""
1081
utf8_files = [('last-revision', '0 null:\n'),
1082
('branch-name', ''),
1083
('branch.conf', ''),
1086
return self._initialize_helper(a_bzrdir, utf8_files)
1088
def open(self, a_bzrdir, _found=False):
1089
"""Return the branch object for a_bzrdir
1091
_found is a private parameter, do not use it. It is used to indicate
1092
if format probing has already be done.
1095
format = BranchFormat.find_format(a_bzrdir)
1096
assert format.__class__ == self.__class__
1097
transport = a_bzrdir.get_branch_transport(None)
1098
control_files = lockable_files.LockableFiles(transport, 'lock',
1100
return BzrBranch6(_format=self,
1101
_control_files=control_files,
1103
_repository=a_bzrdir.find_repository())
1105
def supports_tags(self):
825
return "Bazaar-NG Metadir branch format 5"
1109
828
class BranchReferenceFormat(BranchFormat):
1194
912
it's writable, and can be accessed via the normal filesystem API.
1197
def __init__(self, _format=None,
915
def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
916
relax_version_check=DEPRECATED_PARAMETER, _format=None,
1198
917
_control_files=None, a_bzrdir=None, _repository=None):
1199
"""Create new branch object at a particular location."""
1200
Branch.__init__(self)
918
"""Create new branch object at a particular location.
920
transport -- A Transport object, defining how to access files.
922
init -- If True, create new control files in a previously
923
unversioned directory. If False, the branch must already
926
relax_version_check -- If true, the usual check for the branch
927
version is not applied. This is intended only for
928
upgrade/recovery type use; it's not guaranteed that
929
all operations will work on old format branches.
1201
931
if a_bzrdir is None:
1202
raise ValueError('a_bzrdir must be supplied')
932
self.bzrdir = bzrdir.BzrDir.open(transport.base)
1204
934
self.bzrdir = a_bzrdir
1205
# self._transport used to point to the directory containing the
1206
# control directory, but was not used - now it's just the transport
1207
# for the branch control files. mbp 20070212
1208
self._base = self.bzrdir.transport.clone('..').base
935
self._transport = self.bzrdir.transport.clone('..')
936
self._base = self._transport.base
1209
937
self._format = _format
1210
938
if _control_files is None:
1211
939
raise ValueError('BzrBranch _control_files is None')
1212
940
self.control_files = _control_files
1213
self._transport = _control_files._transport
941
if deprecated_passed(init):
942
warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
943
"deprecated as of bzr 0.8. Please use Branch.create().",
947
# this is slower than before deprecation, oh well never mind.
949
self._initialize(transport.base)
950
self._check_format(_format)
951
if deprecated_passed(relax_version_check):
952
warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
953
"relax_version_check parameter is deprecated as of bzr 0.8. "
954
"Please use BzrDir.open_downlevel, or a BzrBranchFormat's "
958
if (not relax_version_check
959
and not self._format.is_supported()):
960
raise errors.UnsupportedFormatError(format=fmt)
961
if deprecated_passed(transport):
962
warn("BzrBranch.__init__(transport=XXX...): The transport "
963
"parameter is deprecated as of bzr 0.8. "
964
"Please use Branch.open, or bzrdir.open_branch().",
1214
967
self.repository = _repository
1216
969
def __str__(self):
1219
972
__repr__ = __str__
1221
974
def _get_base(self):
1222
"""Returns the directory containing the control directory."""
1223
975
return self._base
1225
977
base = property(_get_base, doc="The URL for the root of this branch.")
979
def _finish_transaction(self):
980
"""Exit the current transaction."""
981
return self.control_files._finish_transaction()
983
def get_transaction(self):
984
"""Return the current active transaction.
986
If no transaction is active, this returns a passthrough object
987
for which all data is immediately flushed and no caching happens.
989
# this is an explicit function so that we can do tricky stuff
990
# when the storage in rev_storage is elsewhere.
991
# we probably need to hook the two 'lock a location' and
992
# 'have a transaction' together more delicately, so that
993
# we can have two locks (branch and storage) and one transaction
994
# ... and finishing the transaction unlocks both, but unlocking
995
# does not. - RBC 20051121
996
return self.control_files.get_transaction()
998
def _set_transaction(self, transaction):
999
"""Set a new active transaction."""
1000
return self.control_files._set_transaction(transaction)
1227
1002
def abspath(self, name):
1228
1003
"""See Branch.abspath."""
1229
1004
return self.control_files._transport.abspath(name)
1232
@deprecated_method(zero_sixteen)
1006
def _check_format(self, format):
1007
"""Identify the branch format if needed.
1009
The format is stored as a reference to the format object in
1010
self._format for code that needs to check it later.
1012
The format parameter is either None or the branch format class
1013
used to open this branch.
1015
FIXME: DELETE THIS METHOD when pre 0.8 support is removed.
1018
format = BranchFormat.find_format(self.bzrdir)
1019
self._format = format
1020
mutter("got branch format %s", self._format)
1233
1022
@needs_read_lock
1234
1023
def get_root_id(self):
1235
1024
"""See Branch.get_root_id."""
1283
1068
@needs_write_lock
1284
1069
def append_revision(self, *revision_ids):
1285
1070
"""See Branch.append_revision."""
1286
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1287
1071
for revision_id in revision_ids:
1288
_mod_revision.check_not_reserved_id(revision_id)
1289
1072
mutter("add {%s} to revision-history" % revision_id)
1290
1073
rev_history = self.revision_history()
1291
1074
rev_history.extend(revision_ids)
1292
1075
self.set_revision_history(rev_history)
1294
def _write_revision_history(self, history):
1295
"""Factored out of set_revision_history.
1297
This performs the actual writing to disk.
1298
It is intended to be called by BzrBranch5.set_revision_history."""
1299
self.control_files.put_bytes(
1300
'revision-history', '\n'.join(history))
1302
1077
@needs_write_lock
1303
1078
def set_revision_history(self, rev_history):
1304
1079
"""See Branch.set_revision_history."""
1305
rev_history = [osutils.safe_revision_id(r) for r in rev_history]
1306
self._write_revision_history(rev_history)
1307
self._cache_revision_history(rev_history)
1308
for hook in Branch.hooks['set_rh']:
1309
hook(self, rev_history)
1080
self.control_files.put_utf8(
1081
'revision-history', '\n'.join(rev_history))
1082
transaction = self.get_transaction()
1083
history = transaction.map.find_revision_history()
1084
if history is not None:
1085
# update the revision history in the identity map.
1086
history[:] = list(rev_history)
1087
# this call is disabled because revision_history is
1088
# not really an object yet, and the transaction is for objects.
1089
# transaction.register_dirty(history)
1091
transaction.map.add_revision_history(rev_history)
1092
# this call is disabled because revision_history is
1093
# not really an object yet, and the transaction is for objects.
1094
# transaction.register_clean(history)
1097
def revision_history(self):
1098
"""See Branch.revision_history."""
1099
transaction = self.get_transaction()
1100
history = transaction.map.find_revision_history()
1101
if history is not None:
1102
# mutter("cache hit for revision-history in %s", self)
1103
return list(history)
1104
decode_utf8 = cache_utf8.decode
1105
history = [decode_utf8(l.rstrip('\r\n')) for l in
1106
self.control_files.get('revision-history').readlines()]
1107
transaction.map.add_revision_history(history)
1108
# this call is disabled because revision_history is
1109
# not really an object yet, and the transaction is for objects.
1110
# transaction.register_clean(history, precious=True)
1111
return list(history)
1311
1113
@needs_write_lock
1312
def set_last_revision_info(self, revno, revision_id):
1313
revision_id = osutils.safe_revision_id(revision_id)
1314
history = self._lefthand_history(revision_id)
1315
assert len(history) == revno, '%d != %d' % (len(history), revno)
1316
self.set_revision_history(history)
1318
def _gen_revision_history(self):
1319
history = self.control_files.get('revision-history').read().split('\n')
1320
if history[-1:] == ['']:
1321
# There shouldn't be a trailing newline, but just in case.
1325
def _lefthand_history(self, revision_id, last_rev=None,
1114
def generate_revision_history(self, revision_id, last_rev=None,
1116
"""Create a new revision history that will finish with revision_id.
1118
:param revision_id: the new tip to use.
1119
:param last_rev: The previous last_revision. If not None, then this
1120
must be a ancestory of revision_id, or DivergedBranches is raised.
1121
:param other_branch: The other branch that DivergedBranches should
1122
raise with respect to.
1327
1124
# stop_revision must be a descendant of last_revision
1328
1125
stop_graph = self.repository.get_revision_graph(revision_id)
1329
1126
if last_rev is not None and last_rev not in stop_graph:
1399
1179
return self.bzrdir.open_workingtree()
1401
1181
@needs_write_lock
1402
def pull(self, source, overwrite=False, stop_revision=None,
1403
_hook_master=None, _run_hooks=True):
1406
:param _hook_master: Private parameter - set the branch to
1407
be supplied as the master to push hooks.
1408
:param _run_hooks: Private parameter - allow disabling of
1409
hooks, used when pushing to a master branch.
1411
result = PullResult()
1412
result.source_branch = source
1413
result.target_branch = self
1182
def pull(self, source, overwrite=False, stop_revision=None):
1183
"""See Branch.pull."""
1414
1184
source.lock_read()
1416
result.old_revno, result.old_revid = self.last_revision_info()
1186
old_count = len(self.revision_history())
1418
1188
self.update_revisions(source, stop_revision)
1419
1189
except DivergedBranches:
1420
1190
if not overwrite:
1423
if stop_revision is None:
1424
stop_revision = source.last_revision()
1425
self.generate_revision_history(stop_revision)
1426
result.tag_conflicts = source.tags.merge_to(self.tags)
1427
result.new_revno, result.new_revid = self.last_revision_info()
1429
result.master_branch = _hook_master
1430
result.local_branch = self
1432
result.master_branch = self
1433
result.local_branch = None
1435
for hook in Branch.hooks['post_pull']:
1193
self.set_revision_history(source.revision_history())
1194
new_count = len(self.revision_history())
1195
return new_count - old_count
1438
1197
source.unlock()
1441
def _get_parent_location(self):
1199
def get_parent(self):
1200
"""See Branch.get_parent."""
1442
1202
_locs = ['parent', 'pull', 'x-pull']
1203
assert self.base[-1] == '/'
1443
1204
for l in _locs:
1445
return self.control_files.get(l).read().strip('\n')
1206
parent = self.control_files.get(l).read().strip('\n')
1446
1207
except NoSuchFile:
1209
# This is an old-format absolute path to a local branch
1210
# turn it into a url
1211
if parent.startswith('/'):
1212
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1214
return urlutils.join(self.base[:-1], parent)
1215
except errors.InvalidURLJoin, e:
1216
raise errors.InaccessibleParent(parent, self.base)
1451
def push(self, target, overwrite=False, stop_revision=None,
1452
_hook_master=None, _run_hooks=True):
1455
:param _hook_master: Private parameter - set the branch to
1456
be supplied as the master to push hooks.
1457
:param _run_hooks: Private parameter - allow disabling of
1458
hooks, used when pushing to a master branch.
1460
result = PushResult()
1461
result.source_branch = self
1462
result.target_branch = target
1465
result.old_revno, result.old_revid = target.last_revision_info()
1467
target.update_revisions(self, stop_revision)
1468
except DivergedBranches:
1472
target.set_revision_history(self.revision_history())
1473
result.tag_conflicts = self.tags.merge_to(target.tags)
1474
result.new_revno, result.new_revid = target.last_revision_info()
1476
result.master_branch = _hook_master
1477
result.local_branch = target
1479
result.master_branch = target
1480
result.local_branch = None
1482
for hook in Branch.hooks['post_push']:
1488
def get_parent(self):
1489
"""See Branch.get_parent."""
1491
assert self.base[-1] == '/'
1492
parent = self._get_parent_location()
1495
# This is an old-format absolute path to a local branch
1496
# turn it into a url
1497
if parent.startswith('/'):
1498
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1500
return urlutils.join(self.base[:-1], parent)
1501
except errors.InvalidURLJoin, e:
1502
raise errors.InaccessibleParent(parent, self.base)
1504
1219
def get_push_location(self):
1505
1220
"""See Branch.get_push_location."""
1506
1221
push_loc = self.get_config().get_user_option('push_location')
1562
1272
_repository=_repository)
1564
1274
@needs_write_lock
1565
def pull(self, source, overwrite=False, stop_revision=None,
1567
"""Extends branch.pull to be bound branch aware.
1569
:param _run_hooks: Private parameter used to force hook running
1570
off during bound branch double-pushing.
1275
def pull(self, source, overwrite=False, stop_revision=None):
1276
"""Updates branch.pull to be bound branch aware."""
1572
1277
bound_location = self.get_bound_location()
1573
master_branch = None
1574
if bound_location and source.base != bound_location:
1278
if source.base != bound_location:
1575
1279
# not pulling from master, so we need to update master.
1576
1280
master_branch = self.get_master_branch()
1577
master_branch.lock_write()
1580
# pull from source into master.
1581
master_branch.pull(source, overwrite, stop_revision,
1583
return super(BzrBranch5, self).pull(source, overwrite,
1584
stop_revision, _hook_master=master_branch,
1585
_run_hooks=_run_hooks)
1588
master_branch.unlock()
1591
def push(self, target, overwrite=False, stop_revision=None):
1592
"""Updates branch.push to be bound branch aware."""
1593
bound_location = target.get_bound_location()
1594
master_branch = None
1595
if bound_location and target.base != bound_location:
1596
# not pushing to master, so we need to update master.
1597
master_branch = target.get_master_branch()
1598
master_branch.lock_write()
1601
# push into the master from this branch.
1602
super(BzrBranch5, self).push(master_branch, overwrite,
1603
stop_revision, _run_hooks=False)
1604
# and push into the target branch from this. Note that we push from
1605
# this branch again, because its considered the highest bandwidth
1607
return super(BzrBranch5, self).push(target, overwrite,
1608
stop_revision, _hook_master=master_branch)
1611
master_branch.unlock()
1282
master_branch.pull(source)
1283
source = master_branch
1284
return super(BzrBranch5, self).pull(source, overwrite, stop_revision)
1613
1286
def get_bound_location(self):
1718
class BzrBranchExperimental(BzrBranch5):
1719
"""Bzr experimental branch format
1722
- a revision-history file.
1724
- a lock dir guarding the branch itself
1725
- all of this stored in a branch/ subdirectory
1726
- works with shared repositories.
1727
- a tag dictionary in the branch
1729
This format is new in bzr 0.15, but shouldn't be used for real data,
1732
This class acts as it's own BranchFormat.
1735
_matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1738
def get_format_string(cls):
1739
"""See BranchFormat.get_format_string()."""
1740
return "Bazaar-NG branch format experimental\n"
1743
def get_format_description(cls):
1744
"""See BranchFormat.get_format_description()."""
1745
return "Experimental branch format"
1748
def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1750
branch_transport = a_bzrdir.get_branch_transport(cls)
1751
control_files = lockable_files.LockableFiles(branch_transport,
1752
lock_filename, lock_class)
1753
control_files.create_lock()
1754
control_files.lock_write()
1756
for filename, content in utf8_files:
1757
control_files.put_utf8(filename, content)
1759
control_files.unlock()
1762
def initialize(cls, a_bzrdir):
1763
"""Create a branch of this format in a_bzrdir."""
1764
utf8_files = [('format', cls.get_format_string()),
1765
('revision-history', ''),
1766
('branch-name', ''),
1769
cls._initialize_control_files(a_bzrdir, utf8_files,
1770
'lock', lockdir.LockDir)
1771
return cls.open(a_bzrdir, _found=True)
1774
def open(cls, a_bzrdir, _found=False):
1775
"""Return the branch object for a_bzrdir
1777
_found is a private parameter, do not use it. It is used to indicate
1778
if format probing has already be done.
1781
format = BranchFormat.find_format(a_bzrdir)
1782
assert format.__class__ == cls
1783
transport = a_bzrdir.get_branch_transport(None)
1784
control_files = lockable_files.LockableFiles(transport, 'lock',
1786
return cls(_format=cls,
1787
_control_files=control_files,
1789
_repository=a_bzrdir.find_repository())
1792
def is_supported(cls):
1795
def _make_tags(self):
1796
return BasicTags(self)
1799
def supports_tags(cls):
1803
BranchFormat.register_format(BzrBranchExperimental)
1806
class BzrBranch6(BzrBranch5):
1809
def last_revision_info(self):
1810
revision_string = self.control_files.get('last-revision').read()
1811
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
1812
revision_id = cache_utf8.get_cached_utf8(revision_id)
1814
return revno, revision_id
1816
def last_revision(self):
1817
"""Return last revision id, or None"""
1818
revision_id = self.last_revision_info()[1]
1819
if revision_id == _mod_revision.NULL_REVISION:
1823
def _write_last_revision_info(self, revno, revision_id):
1824
"""Simply write out the revision id, with no checks.
1826
Use set_last_revision_info to perform this safely.
1828
Does not update the revision_history cache.
1829
Intended to be called by set_last_revision_info and
1830
_write_revision_history.
1832
if revision_id is None:
1833
revision_id = 'null:'
1834
out_string = '%d %s\n' % (revno, revision_id)
1835
self.control_files.put_bytes('last-revision', out_string)
1838
def set_last_revision_info(self, revno, revision_id):
1839
revision_id = osutils.safe_revision_id(revision_id)
1840
if self._get_append_revisions_only():
1841
self._check_history_violation(revision_id)
1842
self._write_last_revision_info(revno, revision_id)
1843
self._clear_cached_state()
1845
def _check_history_violation(self, revision_id):
1846
last_revision = self.last_revision()
1847
if last_revision is None:
1849
if last_revision not in self._lefthand_history(revision_id):
1850
raise errors.AppendRevisionsOnlyViolation(self.base)
1852
def _gen_revision_history(self):
1853
"""Generate the revision history from last revision
1855
history = list(self.repository.iter_reverse_revision_history(
1856
self.last_revision()))
1860
def _write_revision_history(self, history):
1861
"""Factored out of set_revision_history.
1863
This performs the actual writing to disk, with format-specific checks.
1864
It is intended to be called by BzrBranch5.set_revision_history.
1866
if len(history) == 0:
1867
last_revision = 'null:'
1869
if history != self._lefthand_history(history[-1]):
1870
raise errors.NotLefthandHistory(history)
1871
last_revision = history[-1]
1872
if self._get_append_revisions_only():
1873
self._check_history_violation(last_revision)
1874
self._write_last_revision_info(len(history), last_revision)
1877
def append_revision(self, *revision_ids):
1878
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1879
if len(revision_ids) == 0:
1881
prev_revno, prev_revision = self.last_revision_info()
1882
for revision in self.repository.get_revisions(revision_ids):
1883
if prev_revision == _mod_revision.NULL_REVISION:
1884
if revision.parent_ids != []:
1885
raise errors.NotLeftParentDescendant(self, prev_revision,
1886
revision.revision_id)
1888
if revision.parent_ids[0] != prev_revision:
1889
raise errors.NotLeftParentDescendant(self, prev_revision,
1890
revision.revision_id)
1891
prev_revision = revision.revision_id
1892
self.set_last_revision_info(prev_revno + len(revision_ids),
1896
def _set_parent_location(self, url):
1897
"""Set the parent branch"""
1898
self._set_config_location('parent_location', url, make_relative=True)
1901
def _get_parent_location(self):
1902
"""Set the parent branch"""
1903
return self._get_config_location('parent_location')
1905
def set_push_location(self, location):
1906
"""See Branch.set_push_location."""
1907
self._set_config_location('push_location', location)
1909
def set_bound_location(self, location):
1910
"""See Branch.set_push_location."""
1912
config = self.get_config()
1913
if location is None:
1914
if config.get_user_option('bound') != 'True':
1917
config.set_user_option('bound', 'False')
1920
self._set_config_location('bound_location', location,
1922
config.set_user_option('bound', 'True')
1925
def _get_bound_location(self, bound):
1926
"""Return the bound location in the config file.
1928
Return None if the bound parameter does not match"""
1929
config = self.get_config()
1930
config_bound = (config.get_user_option('bound') == 'True')
1931
if config_bound != bound:
1933
return self._get_config_location('bound_location', config=config)
1935
def get_bound_location(self):
1936
"""See Branch.set_push_location."""
1937
return self._get_bound_location(True)
1939
def get_old_bound_location(self):
1940
"""See Branch.get_old_bound_location"""
1941
return self._get_bound_location(False)
1943
def set_append_revisions_only(self, enabled):
1948
self.get_config().set_user_option('append_revisions_only', value)
1950
def _get_append_revisions_only(self):
1951
value = self.get_config().get_user_option('append_revisions_only')
1952
return value == 'True'
1954
def _synchronize_history(self, destination, revision_id):
1955
"""Synchronize last revision and revision history between branches.
1957
This version is most efficient when the destination is also a
1958
BzrBranch6, but works for BzrBranch5, as long as the destination's
1959
repository contains all the lefthand ancestors of the intended
1960
last_revision. If not, set_last_revision_info will fail.
1962
:param destination: The branch to copy the history into
1963
:param revision_id: The revision-id to truncate history at. May
1964
be None to copy complete history.
1966
if revision_id is None:
1967
revno, revision_id = self.last_revision_info()
1969
revno = self.revision_id_to_revno(revision_id)
1970
destination.set_last_revision_info(revno, revision_id)
1972
def _make_tags(self):
1973
return BasicTags(self)
1976
1391
class BranchTestProviderAdapter(object):
1977
1392
"""A tool to generate a suite testing multiple branch formats at once.
1996
1411
new_test.bzrdir_format = bzrdir_format
1997
1412
new_test.branch_format = branch_format
1998
1413
def make_new_test_id():
1999
# the format can be either a class or an instance
2000
name = getattr(branch_format, '__name__',
2001
branch_format.__class__.__name__)
2002
new_id = "%s(%s)" % (new_test.id(), name)
1414
new_id = "%s(%s)" % (new_test.id(), branch_format.__class__.__name__)
2003
1415
return lambda: new_id
2004
1416
new_test.id = make_new_test_id()
2005
1417
result.addTest(new_test)
2009
######################################################################
2010
# results of operations
2013
class _Result(object):
2015
def _show_tag_conficts(self, to_file):
2016
if not getattr(self, 'tag_conflicts', None):
2018
to_file.write('Conflicting tags:\n')
2019
for name, value1, value2 in self.tag_conflicts:
2020
to_file.write(' %s\n' % (name, ))
2023
class PullResult(_Result):
2024
"""Result of a Branch.pull operation.
2026
:ivar old_revno: Revision number before pull.
2027
:ivar new_revno: Revision number after pull.
2028
:ivar old_revid: Tip revision id before pull.
2029
:ivar new_revid: Tip revision id after pull.
2030
:ivar source_branch: Source (local) branch object.
2031
:ivar master_branch: Master branch of the target, or None.
2032
:ivar target_branch: Target/destination branch object.
2036
# DEPRECATED: pull used to return the change in revno
2037
return self.new_revno - self.old_revno
2039
def report(self, to_file):
2040
if self.old_revid == self.new_revid:
2041
to_file.write('No revisions to pull.\n')
2043
to_file.write('Now on revision %d.\n' % self.new_revno)
2044
self._show_tag_conficts(to_file)
2047
class PushResult(_Result):
2048
"""Result of a Branch.push operation.
2050
:ivar old_revno: Revision number before push.
2051
:ivar new_revno: Revision number after push.
2052
:ivar old_revid: Tip revision id before push.
2053
:ivar new_revid: Tip revision id after push.
2054
:ivar source_branch: Source branch object.
2055
:ivar master_branch: Master branch of the target, or None.
2056
:ivar target_branch: Target/destination branch object.
2060
# DEPRECATED: push used to return the change in revno
2061
return self.new_revno - self.old_revno
2063
def report(self, to_file):
2064
"""Write a human-readable description of the result."""
2065
if self.old_revid == self.new_revid:
2066
to_file.write('No new revisions to push.\n')
2068
to_file.write('Pushed up to revision %d.\n' % self.new_revno)
2069
self._show_tag_conficts(to_file)
2072
1421
class BranchCheckResult(object):
2073
1422
"""Results of checking branch consistency.