182
197
def get_physical_lock_status(self):
183
198
raise NotImplementedError(self.get_physical_lock_status)
201
def get_revision_id_to_revno_map(self):
202
"""Return the revision_id => dotted revno map.
204
This will be regenerated on demand, but will be cached.
206
:return: A dictionary mapping revision_id => dotted revno.
207
This dictionary should not be modified by the caller.
209
if self._revision_id_to_revno_cache is not None:
210
mapping = self._revision_id_to_revno_cache
212
mapping = self._gen_revno_map()
213
self._cache_revision_id_to_revno(mapping)
214
# TODO: jam 20070417 Since this is being cached, should we be returning
216
# I would rather not, and instead just declare that users should not
217
# modify the return value.
220
def _gen_revno_map(self):
221
"""Create a new mapping from revision ids to dotted revnos.
223
Dotted revnos are generated based on the current tip in the revision
225
This is the worker function for get_revision_id_to_revno_map, which
226
just caches the return value.
228
:return: A dictionary mapping revision_id => dotted revno.
230
last_revision = self.last_revision()
231
revision_graph = self.repository.get_revision_graph(last_revision)
232
merge_sorted_revisions = tsort.merge_sort(
237
revision_id_to_revno = dict((rev_id, revno)
238
for seq_num, rev_id, depth, revno, end_of_merge
239
in merge_sorted_revisions)
240
return revision_id_to_revno
242
def leave_lock_in_place(self):
243
"""Tell this branch object not to release the physical lock when this
246
If lock_write doesn't return a token, then this method is not supported.
248
self.control_files.leave_in_place()
250
def dont_leave_lock_in_place(self):
251
"""Tell this branch object to release the physical lock when this
252
object is unlocked, even if it didn't originally acquire it.
254
If lock_write doesn't return a token, then this method is not supported.
256
self.control_files.dont_leave_in_place()
185
258
def abspath(self, name):
186
259
"""Return absolute filename for something in the branch
297
377
def set_revision_history(self, rev_history):
298
378
raise NotImplementedError(self.set_revision_history)
380
def _cache_revision_history(self, rev_history):
381
"""Set the cached revision history to rev_history.
383
The revision_history method will use this cache to avoid regenerating
384
the revision history.
386
This API is semi-public; it only for use by subclasses, all other code
387
should consider it to be private.
389
self._revision_history_cache = rev_history
391
def _cache_revision_id_to_revno(self, revision_id_to_revno):
392
"""Set the cached revision_id => revno map to revision_id_to_revno.
394
This API is semi-public; it only for use by subclasses, all other code
395
should consider it to be private.
397
self._revision_id_to_revno_cache = revision_id_to_revno
399
def _clear_cached_state(self):
400
"""Clear any cached data on this branch, e.g. cached revision history.
402
This means the next call to revision_history will need to call
403
_gen_revision_history.
405
This API is semi-public; it only for use by subclasses, all other code
406
should consider it to be private.
408
self._revision_history_cache = None
409
self._revision_id_to_revno_cache = None
411
def _gen_revision_history(self):
412
"""Return sequence of revision hashes on to this branch.
414
Unlike revision_history, this method always regenerates or rereads the
415
revision history, i.e. it does not cache the result, so repeated calls
418
Concrete subclasses should override this instead of revision_history so
419
that subclasses do not need to deal with caching logic.
421
This API is semi-public; it only for use by subclasses, all other code
422
should consider it to be private.
424
raise NotImplementedError(self._gen_revision_history)
300
427
def revision_history(self):
301
"""Return sequence of revision hashes on to this branch."""
302
raise NotImplementedError(self.revision_history)
428
"""Return sequence of revision hashes on to this branch.
430
This method will cache the revision history for as long as it is safe to
433
if self._revision_history_cache is not None:
434
history = self._revision_history_cache
436
history = self._gen_revision_history()
437
self._cache_revision_history(history)
305
441
"""Return current revision number for this branch.
466
667
raise InvalidRevisionNumber(revno)
469
def clone(self, *args, **kwargs):
670
def clone(self, to_bzrdir, revision_id=None):
470
671
"""Clone this branch into to_bzrdir preserving all semantic values.
472
673
revision_id: if not None, the revision history in the new branch will
473
674
be truncated to end with revision_id.
475
# for API compatibility, until 0.8 releases we provide the old api:
476
# def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
477
# after 0.8 releases, the *args and **kwargs should be changed:
478
# def clone(self, to_bzrdir, revision_id=None):
479
if (kwargs.get('to_location', None) or
480
kwargs.get('revision', None) or
481
kwargs.get('basis_branch', None) or
482
(len(args) and isinstance(args[0], basestring))):
483
# backwards compatibility api:
484
warn("Branch.clone() has been deprecated for BzrDir.clone() from"
485
" bzrlib 0.8.", DeprecationWarning, stacklevel=3)
488
basis_branch = args[2]
490
basis_branch = kwargs.get('basis_branch', None)
492
basis = basis_branch.bzrdir
497
revision_id = args[1]
499
revision_id = kwargs.get('revision', None)
504
# no default to raise if not provided.
505
url = kwargs.get('to_location')
506
return self.bzrdir.clone(url,
507
revision_id=revision_id,
508
basis=basis).open_branch()
510
# generate args by hand
512
revision_id = args[1]
514
revision_id = kwargs.get('revision_id', None)
518
# no default to raise if not provided.
519
to_bzrdir = kwargs.get('to_bzrdir')
520
676
result = self._format.initialize(to_bzrdir)
521
677
self.copy_content_into(result, revision_id=revision_id)
533
689
result.set_parent(self.bzrdir.root_transport.base)
537
def copy_content_into(self, destination, revision_id=None):
538
"""Copy the content of self into destination.
540
revision_id: if not None, the revision history in the new branch will
541
be truncated to end with revision_id.
692
def _synchronize_history(self, destination, revision_id):
693
"""Synchronize last revision and revision history between branches.
695
This version is most efficient when the destination is also a
696
BzrBranch5, but works for BzrBranch6 as long as the revision
697
history is the true lefthand parent history, and all of the revisions
698
are in the destination's repository. If not, set_revision_history
701
:param destination: The branch to copy the history into
702
:param revision_id: The revision-id to truncate history at. May
703
be None to copy complete history.
543
705
new_history = self.revision_history()
544
706
if revision_id is not None:
707
revision_id = osutils.safe_revision_id(revision_id)
546
709
new_history = new_history[:new_history.index(revision_id) + 1]
547
710
except ValueError:
548
711
rev = self.repository.get_revision(revision_id)
549
712
new_history = rev.get_history(self.repository)[1:]
550
713
destination.set_revision_history(new_history)
716
def copy_content_into(self, destination, revision_id=None):
717
"""Copy the content of self into destination.
719
revision_id: if not None, the revision history in the new branch will
720
be truncated to end with revision_id.
722
self._synchronize_history(destination, revision_id)
552
724
parent = self.get_parent()
553
725
except errors.InaccessibleParent, e:
669
864
"""Return the current default format."""
670
865
return klass._default_format
867
def get_reference(self, a_bzrdir):
868
"""Get the target reference of the branch in a_bzrdir.
870
format probing must have been completed before calling
871
this method - it is assumed that the format of the branch
872
in a_bzrdir is correct.
874
:param a_bzrdir: The bzrdir to get the branch data from.
875
:return: None if the branch is not a reference branch.
672
879
def get_format_string(self):
673
880
"""Return the ASCII format string that identifies this format."""
674
881
raise NotImplementedError(self.get_format_string)
676
883
def get_format_description(self):
677
884
"""Return the short format description for this format."""
678
raise NotImplementedError(self.get_format_string)
885
raise NotImplementedError(self.get_format_description)
887
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
889
"""Initialize a branch in a bzrdir, with specified files
891
:param a_bzrdir: The bzrdir to initialize the branch in
892
:param utf8_files: The files to create as a list of
893
(filename, content) tuples
894
:param set_format: If True, set the format with
895
self.get_format_string. (BzrBranch4 has its format set
897
:return: a branch in this format
899
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
900
branch_transport = a_bzrdir.get_branch_transport(self)
902
'metadir': ('lock', lockdir.LockDir),
903
'branch4': ('branch-lock', lockable_files.TransportLock),
905
lock_name, lock_class = lock_map[lock_type]
906
control_files = lockable_files.LockableFiles(branch_transport,
907
lock_name, lock_class)
908
control_files.create_lock()
909
control_files.lock_write()
911
control_files.put_utf8('format', self.get_format_string())
913
for file, content in utf8_files:
914
control_files.put_utf8(file, content)
916
control_files.unlock()
917
return self.open(a_bzrdir, _found=True)
680
919
def initialize(self, a_bzrdir):
681
920
"""Create a branch of this format in a_bzrdir."""
714
953
def __str__(self):
715
954
return self.get_format_string().rstrip()
956
def supports_tags(self):
957
"""True if this format supports tags stored in the branch"""
958
return False # by default
960
# XXX: Probably doesn't really belong here -- mbp 20070212
961
def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
963
branch_transport = a_bzrdir.get_branch_transport(self)
964
control_files = lockable_files.LockableFiles(branch_transport,
965
lock_filename, lock_class)
966
control_files.create_lock()
967
control_files.lock_write()
969
for filename, content in utf8_files:
970
control_files.put_utf8(filename, content)
972
control_files.unlock()
975
class BranchHooks(Hooks):
976
"""A dictionary mapping hook name to a list of callables for branch hooks.
978
e.g. ['set_rh'] Is the list of items to be called when the
979
set_revision_history function is invoked.
983
"""Create the default hooks.
985
These are all empty initially, because by default nothing should get
989
# Introduced in 0.15:
990
# invoked whenever the revision history has been set
991
# with set_revision_history. The api signature is
992
# (branch, revision_history), and the branch will
995
# invoked after a push operation completes.
996
# the api signature is
998
# containing the members
999
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
1000
# where local is the local branch or None, master is the target
1001
# master branch, and the rest should be self explanatory. The source
1002
# is read locked and the target branches write locked. Source will
1003
# be the local low-latency branch.
1004
self['post_push'] = []
1005
# invoked after a pull operation completes.
1006
# the api signature is
1008
# containing the members
1009
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
1010
# where local is the local branch or None, master is the target
1011
# master branch, and the rest should be self explanatory. The source
1012
# is read locked and the target branches write locked. The local
1013
# branch is the low-latency branch.
1014
self['post_pull'] = []
1015
# invoked after a commit operation completes.
1016
# the api signature is
1017
# (local, master, old_revno, old_revid, new_revno, new_revid)
1018
# old_revid is NULL_REVISION for the first commit to a branch.
1019
self['post_commit'] = []
1020
# invoked after a uncommit operation completes.
1021
# the api signature is
1022
# (local, master, old_revno, old_revid, new_revno, new_revid) where
1023
# local is the local branch or None, master is the target branch,
1024
# and an empty branch recieves new_revno of 0, new_revid of None.
1025
self['post_uncommit'] = []
1028
# install the default hooks into the Branch class.
1029
Branch.hooks = BranchHooks()
718
1032
class BzrBranchFormat4(BranchFormat):
719
1033
"""Bzr branch format 4.
821
1113
format = BranchFormat.find_format(a_bzrdir)
822
1114
assert format.__class__ == self.__class__
1116
transport = a_bzrdir.get_branch_transport(None)
1117
control_files = lockable_files.LockableFiles(transport, 'lock',
1119
return BzrBranch5(_format=self,
1120
_control_files=control_files,
1122
_repository=a_bzrdir.find_repository())
1124
raise NotBranchError(path=transport.base)
1127
class BzrBranchFormat6(BzrBranchFormat5):
1128
"""Branch format with last-revision
1130
Unlike previous formats, this has no explicit revision history. Instead,
1131
this just stores the last-revision, and the left-hand history leading
1132
up to there is the history.
1134
This format was introduced in bzr 0.15
1137
def get_format_string(self):
1138
"""See BranchFormat.get_format_string()."""
1139
return "Bazaar Branch Format 6 (bzr 0.15)\n"
1141
def get_format_description(self):
1142
"""See BranchFormat.get_format_description()."""
1143
return "Branch format 6"
1145
def initialize(self, a_bzrdir):
1146
"""Create a branch of this format in a_bzrdir."""
1147
utf8_files = [('last-revision', '0 null:\n'),
1148
('branch-name', ''),
1149
('branch.conf', ''),
1152
return self._initialize_helper(a_bzrdir, utf8_files)
1154
def open(self, a_bzrdir, _found=False):
1155
"""Return the branch object for a_bzrdir
1157
_found is a private parameter, do not use it. It is used to indicate
1158
if format probing has already be done.
1161
format = BranchFormat.find_format(a_bzrdir)
1162
assert format.__class__ == self.__class__
823
1163
transport = a_bzrdir.get_branch_transport(None)
824
1164
control_files = lockable_files.LockableFiles(transport, 'lock',
825
1165
lockdir.LockDir)
826
return BzrBranch5(_format=self,
1166
return BzrBranch6(_format=self,
827
1167
_control_files=control_files,
828
1168
a_bzrdir=a_bzrdir,
829
1169
_repository=a_bzrdir.find_repository())
832
return "Bazaar-NG Metadir branch format 5"
1171
def supports_tags(self):
835
1175
class BranchReferenceFormat(BranchFormat):
919
1266
it's writable, and can be accessed via the normal filesystem API.
922
def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
923
relax_version_check=DEPRECATED_PARAMETER, _format=None,
1269
def __init__(self, _format=None,
924
1270
_control_files=None, a_bzrdir=None, _repository=None):
925
"""Create new branch object at a particular location.
927
transport -- A Transport object, defining how to access files.
929
init -- If True, create new control files in a previously
930
unversioned directory. If False, the branch must already
933
relax_version_check -- If true, the usual check for the branch
934
version is not applied. This is intended only for
935
upgrade/recovery type use; it's not guaranteed that
936
all operations will work on old format branches.
1271
"""Create new branch object at a particular location."""
1272
Branch.__init__(self)
938
1273
if a_bzrdir is None:
939
self.bzrdir = bzrdir.BzrDir.open(transport.base)
1274
raise ValueError('a_bzrdir must be supplied')
941
1276
self.bzrdir = a_bzrdir
942
self._transport = self.bzrdir.transport.clone('..')
943
self._base = self._transport.base
1277
# self._transport used to point to the directory containing the
1278
# control directory, but was not used - now it's just the transport
1279
# for the branch control files. mbp 20070212
1280
self._base = self.bzrdir.transport.clone('..').base
944
1281
self._format = _format
945
1282
if _control_files is None:
946
1283
raise ValueError('BzrBranch _control_files is None')
947
1284
self.control_files = _control_files
948
if deprecated_passed(init):
949
warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
950
"deprecated as of bzr 0.8. Please use Branch.create().",
954
# this is slower than before deprecation, oh well never mind.
956
self._initialize(transport.base)
957
self._check_format(_format)
958
if deprecated_passed(relax_version_check):
959
warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
960
"relax_version_check parameter is deprecated as of bzr 0.8. "
961
"Please use BzrDir.open_downlevel, or a BzrBranchFormat's "
965
if (not relax_version_check
966
and not self._format.is_supported()):
967
raise errors.UnsupportedFormatError(format=fmt)
968
if deprecated_passed(transport):
969
warn("BzrBranch.__init__(transport=XXX...): The transport "
970
"parameter is deprecated as of bzr 0.8. "
971
"Please use Branch.open, or bzrdir.open_branch().",
1285
self._transport = _control_files._transport
974
1286
self.repository = _repository
976
1288
def __str__(self):
979
1291
__repr__ = __str__
981
1293
def _get_base(self):
1294
"""Returns the directory containing the control directory."""
982
1295
return self._base
984
1297
base = property(_get_base, doc="The URL for the root of this branch.")
986
def _finish_transaction(self):
987
"""Exit the current transaction."""
988
return self.control_files._finish_transaction()
990
def get_transaction(self):
991
"""Return the current active transaction.
993
If no transaction is active, this returns a passthrough object
994
for which all data is immediately flushed and no caching happens.
996
# this is an explicit function so that we can do tricky stuff
997
# when the storage in rev_storage is elsewhere.
998
# we probably need to hook the two 'lock a location' and
999
# 'have a transaction' together more delicately, so that
1000
# we can have two locks (branch and storage) and one transaction
1001
# ... and finishing the transaction unlocks both, but unlocking
1002
# does not. - RBC 20051121
1003
return self.control_files.get_transaction()
1005
def _set_transaction(self, transaction):
1006
"""Set a new active transaction."""
1007
return self.control_files._set_transaction(transaction)
1009
1299
def abspath(self, name):
1010
1300
"""See Branch.abspath."""
1011
1301
return self.control_files._transport.abspath(name)
1013
def _check_format(self, format):
1014
"""Identify the branch format if needed.
1016
The format is stored as a reference to the format object in
1017
self._format for code that needs to check it later.
1019
The format parameter is either None or the branch format class
1020
used to open this branch.
1022
FIXME: DELETE THIS METHOD when pre 0.8 support is removed.
1025
format = BranchFormat.find_format(self.bzrdir)
1026
self._format = format
1027
mutter("got branch format %s", self._format)
1304
@deprecated_method(zero_sixteen)
1029
1305
@needs_read_lock
1030
1306
def get_root_id(self):
1031
1307
"""See Branch.get_root_id."""
1075
1355
@needs_write_lock
1076
1356
def append_revision(self, *revision_ids):
1077
1357
"""See Branch.append_revision."""
1358
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1078
1359
for revision_id in revision_ids:
1360
_mod_revision.check_not_reserved_id(revision_id)
1079
1361
mutter("add {%s} to revision-history" % revision_id)
1080
1362
rev_history = self.revision_history()
1081
1363
rev_history.extend(revision_ids)
1082
1364
self.set_revision_history(rev_history)
1366
def _write_revision_history(self, history):
1367
"""Factored out of set_revision_history.
1369
This performs the actual writing to disk.
1370
It is intended to be called by BzrBranch5.set_revision_history."""
1371
self.control_files.put_bytes(
1372
'revision-history', '\n'.join(history))
1084
1374
@needs_write_lock
1085
1375
def set_revision_history(self, rev_history):
1086
1376
"""See Branch.set_revision_history."""
1087
self.control_files.put_utf8(
1088
'revision-history', '\n'.join(rev_history))
1089
transaction = self.get_transaction()
1090
history = transaction.map.find_revision_history()
1091
if history is not None:
1092
# update the revision history in the identity map.
1093
history[:] = list(rev_history)
1094
# this call is disabled because revision_history is
1095
# not really an object yet, and the transaction is for objects.
1096
# transaction.register_dirty(history)
1098
transaction.map.add_revision_history(rev_history)
1099
# this call is disabled because revision_history is
1100
# not really an object yet, and the transaction is for objects.
1101
# transaction.register_clean(history)
1104
def revision_history(self):
1105
"""See Branch.revision_history."""
1106
transaction = self.get_transaction()
1107
history = transaction.map.find_revision_history()
1108
if history is not None:
1109
# mutter("cache hit for revision-history in %s", self)
1110
return list(history)
1111
decode_utf8 = cache_utf8.decode
1112
history = [decode_utf8(l.rstrip('\r\n')) for l in
1113
self.control_files.get('revision-history').readlines()]
1114
transaction.map.add_revision_history(history)
1115
# this call is disabled because revision_history is
1116
# not really an object yet, and the transaction is for objects.
1117
# transaction.register_clean(history, precious=True)
1118
return list(history)
1377
rev_history = [osutils.safe_revision_id(r) for r in rev_history]
1378
self._clear_cached_state()
1379
self._write_revision_history(rev_history)
1380
self._cache_revision_history(rev_history)
1381
for hook in Branch.hooks['set_rh']:
1382
hook(self, rev_history)
1120
1384
@needs_write_lock
1121
def generate_revision_history(self, revision_id, last_rev=None,
1123
"""Create a new revision history that will finish with revision_id.
1125
:param revision_id: the new tip to use.
1126
:param last_rev: The previous last_revision. If not None, then this
1127
must be a ancestory of revision_id, or DivergedBranches is raised.
1128
:param other_branch: The other branch that DivergedBranches should
1129
raise with respect to.
1385
def set_last_revision_info(self, revno, revision_id):
1386
revision_id = osutils.safe_revision_id(revision_id)
1387
history = self._lefthand_history(revision_id)
1388
assert len(history) == revno, '%d != %d' % (len(history), revno)
1389
self.set_revision_history(history)
1391
def _gen_revision_history(self):
1392
history = self.control_files.get('revision-history').read().split('\n')
1393
if history[-1:] == ['']:
1394
# There shouldn't be a trailing newline, but just in case.
1398
def _lefthand_history(self, revision_id, last_rev=None,
1131
1400
# stop_revision must be a descendant of last_revision
1132
1401
stop_graph = self.repository.get_revision_graph(revision_id)
1133
1402
if last_rev is not None and last_rev not in stop_graph:
1186
1472
return self.bzrdir.open_workingtree()
1188
1474
@needs_write_lock
1189
def pull(self, source, overwrite=False, stop_revision=None):
1190
"""See Branch.pull."""
1475
def pull(self, source, overwrite=False, stop_revision=None,
1476
_hook_master=None, _run_hooks=True):
1479
:param _hook_master: Private parameter - set the branch to
1480
be supplied as the master to push hooks.
1481
:param _run_hooks: Private parameter - allow disabling of
1482
hooks, used when pushing to a master branch.
1484
result = PullResult()
1485
result.source_branch = source
1486
result.target_branch = self
1191
1487
source.lock_read()
1193
old_count = len(self.revision_history())
1489
result.old_revno, result.old_revid = self.last_revision_info()
1195
1491
self.update_revisions(source, stop_revision)
1196
1492
except DivergedBranches:
1197
1493
if not overwrite:
1200
self.set_revision_history(source.revision_history())
1201
new_count = len(self.revision_history())
1202
return new_count - old_count
1496
if stop_revision is None:
1497
stop_revision = source.last_revision()
1498
self.generate_revision_history(stop_revision)
1499
result.tag_conflicts = source.tags.merge_to(self.tags)
1500
result.new_revno, result.new_revid = self.last_revision_info()
1502
result.master_branch = _hook_master
1503
result.local_branch = self
1505
result.master_branch = self
1506
result.local_branch = None
1508
for hook in Branch.hooks['post_pull']:
1204
1511
source.unlock()
1514
def _get_parent_location(self):
1515
_locs = ['parent', 'pull', 'x-pull']
1518
return self.control_files.get(l).read().strip('\n')
1524
def push(self, target, overwrite=False, stop_revision=None,
1525
_hook_master=None, _run_hooks=True):
1528
:param _hook_master: Private parameter - set the branch to
1529
be supplied as the master to push hooks.
1530
:param _run_hooks: Private parameter - allow disabling of
1531
hooks, used when pushing to a master branch.
1533
result = PushResult()
1534
result.source_branch = self
1535
result.target_branch = target
1538
result.old_revno, result.old_revid = target.last_revision_info()
1540
target.update_revisions(self, stop_revision)
1541
except DivergedBranches:
1545
target.set_revision_history(self.revision_history())
1546
result.tag_conflicts = self.tags.merge_to(target.tags)
1547
result.new_revno, result.new_revid = target.last_revision_info()
1549
result.master_branch = _hook_master
1550
result.local_branch = target
1552
result.master_branch = target
1553
result.local_branch = None
1555
for hook in Branch.hooks['post_push']:
1206
1561
def get_parent(self):
1207
1562
"""See Branch.get_parent."""
1209
_locs = ['parent', 'pull', 'x-pull']
1210
1564
assert self.base[-1] == '/'
1213
parent = self.control_files.get(l).read().strip('\n')
1216
# This is an old-format absolute path to a local branch
1217
# turn it into a url
1218
if parent.startswith('/'):
1219
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1221
return urlutils.join(self.base[:-1], parent)
1222
except errors.InvalidURLJoin, e:
1223
raise errors.InaccessibleParent(parent, self.base)
1226
def get_push_location(self):
1227
"""See Branch.get_push_location."""
1228
push_loc = self.get_config().get_user_option('push_location')
1565
parent = self._get_parent_location()
1568
# This is an old-format absolute path to a local branch
1569
# turn it into a url
1570
if parent.startswith('/'):
1571
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1573
return urlutils.join(self.base[:-1], parent)
1574
except errors.InvalidURLJoin, e:
1575
raise errors.InaccessibleParent(parent, self.base)
1231
1577
def set_push_location(self, location):
1232
1578
"""See Branch.set_push_location."""
1233
self.get_config().set_user_option('push_location', location,
1579
self.get_config().set_user_option(
1580
'push_location', location,
1581
store=_mod_config.STORE_LOCATION_NORECURSE)
1236
1583
@needs_write_lock
1237
1584
def set_parent(self, url):
1279
1630
_repository=_repository)
1281
1632
@needs_write_lock
1282
def pull(self, source, overwrite=False, stop_revision=None):
1283
"""Updates branch.pull to be bound branch aware."""
1633
def pull(self, source, overwrite=False, stop_revision=None,
1635
"""Extends branch.pull to be bound branch aware.
1637
:param _run_hooks: Private parameter used to force hook running
1638
off during bound branch double-pushing.
1284
1640
bound_location = self.get_bound_location()
1285
if source.base != bound_location:
1641
master_branch = None
1642
if bound_location and source.base != bound_location:
1286
1643
# not pulling from master, so we need to update master.
1287
1644
master_branch = self.get_master_branch()
1289
master_branch.pull(source)
1290
source = master_branch
1291
return super(BzrBranch5, self).pull(source, overwrite, stop_revision)
1645
master_branch.lock_write()
1648
# pull from source into master.
1649
master_branch.pull(source, overwrite, stop_revision,
1651
return super(BzrBranch5, self).pull(source, overwrite,
1652
stop_revision, _hook_master=master_branch,
1653
_run_hooks=_run_hooks)
1656
master_branch.unlock()
1659
def push(self, target, overwrite=False, stop_revision=None):
1660
"""Updates branch.push to be bound branch aware."""
1661
bound_location = target.get_bound_location()
1662
master_branch = None
1663
if bound_location and target.base != bound_location:
1664
# not pushing to master, so we need to update master.
1665
master_branch = target.get_master_branch()
1666
master_branch.lock_write()
1669
# push into the master from this branch.
1670
super(BzrBranch5, self).push(master_branch, overwrite,
1671
stop_revision, _run_hooks=False)
1672
# and push into the target branch from this. Note that we push from
1673
# this branch again, because its considered the highest bandwidth
1675
return super(BzrBranch5, self).push(target, overwrite,
1676
stop_revision, _hook_master=master_branch)
1679
master_branch.unlock()
1293
1681
def get_bound_location(self):
1786
class BzrBranchExperimental(BzrBranch5):
1787
"""Bzr experimental branch format
1790
- a revision-history file.
1792
- a lock dir guarding the branch itself
1793
- all of this stored in a branch/ subdirectory
1794
- works with shared repositories.
1795
- a tag dictionary in the branch
1797
This format is new in bzr 0.15, but shouldn't be used for real data,
1800
This class acts as it's own BranchFormat.
1803
_matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1806
def get_format_string(cls):
1807
"""See BranchFormat.get_format_string()."""
1808
return "Bazaar-NG branch format experimental\n"
1811
def get_format_description(cls):
1812
"""See BranchFormat.get_format_description()."""
1813
return "Experimental branch format"
1816
def get_reference(cls, a_bzrdir):
1817
"""Get the target reference of the branch in a_bzrdir.
1819
format probing must have been completed before calling
1820
this method - it is assumed that the format of the branch
1821
in a_bzrdir is correct.
1823
:param a_bzrdir: The bzrdir to get the branch data from.
1824
:return: None if the branch is not a reference branch.
1829
def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1831
branch_transport = a_bzrdir.get_branch_transport(cls)
1832
control_files = lockable_files.LockableFiles(branch_transport,
1833
lock_filename, lock_class)
1834
control_files.create_lock()
1835
control_files.lock_write()
1837
for filename, content in utf8_files:
1838
control_files.put_utf8(filename, content)
1840
control_files.unlock()
1843
def initialize(cls, a_bzrdir):
1844
"""Create a branch of this format in a_bzrdir."""
1845
utf8_files = [('format', cls.get_format_string()),
1846
('revision-history', ''),
1847
('branch-name', ''),
1850
cls._initialize_control_files(a_bzrdir, utf8_files,
1851
'lock', lockdir.LockDir)
1852
return cls.open(a_bzrdir, _found=True)
1855
def open(cls, a_bzrdir, _found=False):
1856
"""Return the branch object for a_bzrdir
1858
_found is a private parameter, do not use it. It is used to indicate
1859
if format probing has already be done.
1862
format = BranchFormat.find_format(a_bzrdir)
1863
assert format.__class__ == cls
1864
transport = a_bzrdir.get_branch_transport(None)
1865
control_files = lockable_files.LockableFiles(transport, 'lock',
1867
return cls(_format=cls,
1868
_control_files=control_files,
1870
_repository=a_bzrdir.find_repository())
1873
def is_supported(cls):
1876
def _make_tags(self):
1877
return BasicTags(self)
1880
def supports_tags(cls):
1884
BranchFormat.register_format(BzrBranchExperimental)
1887
class BzrBranch6(BzrBranch5):
1890
def last_revision_info(self):
1891
revision_string = self.control_files.get('last-revision').read()
1892
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
1893
revision_id = cache_utf8.get_cached_utf8(revision_id)
1895
return revno, revision_id
1897
def last_revision(self):
1898
"""Return last revision id, or None"""
1899
revision_id = self.last_revision_info()[1]
1900
if revision_id == _mod_revision.NULL_REVISION:
1904
def _write_last_revision_info(self, revno, revision_id):
1905
"""Simply write out the revision id, with no checks.
1907
Use set_last_revision_info to perform this safely.
1909
Does not update the revision_history cache.
1910
Intended to be called by set_last_revision_info and
1911
_write_revision_history.
1913
if revision_id is None:
1914
revision_id = 'null:'
1915
out_string = '%d %s\n' % (revno, revision_id)
1916
self.control_files.put_bytes('last-revision', out_string)
1919
def set_last_revision_info(self, revno, revision_id):
1920
revision_id = osutils.safe_revision_id(revision_id)
1921
if self._get_append_revisions_only():
1922
self._check_history_violation(revision_id)
1923
self._write_last_revision_info(revno, revision_id)
1924
self._clear_cached_state()
1926
def _check_history_violation(self, revision_id):
1927
last_revision = self.last_revision()
1928
if last_revision is None:
1930
if last_revision not in self._lefthand_history(revision_id):
1931
raise errors.AppendRevisionsOnlyViolation(self.base)
1933
def _gen_revision_history(self):
1934
"""Generate the revision history from last revision
1936
history = list(self.repository.iter_reverse_revision_history(
1937
self.last_revision()))
1941
def _write_revision_history(self, history):
1942
"""Factored out of set_revision_history.
1944
This performs the actual writing to disk, with format-specific checks.
1945
It is intended to be called by BzrBranch5.set_revision_history.
1947
if len(history) == 0:
1948
last_revision = 'null:'
1950
if history != self._lefthand_history(history[-1]):
1951
raise errors.NotLefthandHistory(history)
1952
last_revision = history[-1]
1953
if self._get_append_revisions_only():
1954
self._check_history_violation(last_revision)
1955
self._write_last_revision_info(len(history), last_revision)
1958
def append_revision(self, *revision_ids):
1959
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1960
if len(revision_ids) == 0:
1962
prev_revno, prev_revision = self.last_revision_info()
1963
for revision in self.repository.get_revisions(revision_ids):
1964
if prev_revision == _mod_revision.NULL_REVISION:
1965
if revision.parent_ids != []:
1966
raise errors.NotLeftParentDescendant(self, prev_revision,
1967
revision.revision_id)
1969
if revision.parent_ids[0] != prev_revision:
1970
raise errors.NotLeftParentDescendant(self, prev_revision,
1971
revision.revision_id)
1972
prev_revision = revision.revision_id
1973
self.set_last_revision_info(prev_revno + len(revision_ids),
1977
def _set_parent_location(self, url):
1978
"""Set the parent branch"""
1979
self._set_config_location('parent_location', url, make_relative=True)
1982
def _get_parent_location(self):
1983
"""Set the parent branch"""
1984
return self._get_config_location('parent_location')
1986
def set_push_location(self, location):
1987
"""See Branch.set_push_location."""
1988
self._set_config_location('push_location', location)
1990
def set_bound_location(self, location):
1991
"""See Branch.set_push_location."""
1993
config = self.get_config()
1994
if location is None:
1995
if config.get_user_option('bound') != 'True':
1998
config.set_user_option('bound', 'False')
2001
self._set_config_location('bound_location', location,
2003
config.set_user_option('bound', 'True')
2006
def _get_bound_location(self, bound):
2007
"""Return the bound location in the config file.
2009
Return None if the bound parameter does not match"""
2010
config = self.get_config()
2011
config_bound = (config.get_user_option('bound') == 'True')
2012
if config_bound != bound:
2014
return self._get_config_location('bound_location', config=config)
2016
def get_bound_location(self):
2017
"""See Branch.set_push_location."""
2018
return self._get_bound_location(True)
2020
def get_old_bound_location(self):
2021
"""See Branch.get_old_bound_location"""
2022
return self._get_bound_location(False)
2024
def set_append_revisions_only(self, enabled):
2029
self.get_config().set_user_option('append_revisions_only', value)
2031
def _get_append_revisions_only(self):
2032
value = self.get_config().get_user_option('append_revisions_only')
2033
return value == 'True'
2035
def _synchronize_history(self, destination, revision_id):
2036
"""Synchronize last revision and revision history between branches.
2038
This version is most efficient when the destination is also a
2039
BzrBranch6, but works for BzrBranch5, as long as the destination's
2040
repository contains all the lefthand ancestors of the intended
2041
last_revision. If not, set_last_revision_info will fail.
2043
:param destination: The branch to copy the history into
2044
:param revision_id: The revision-id to truncate history at. May
2045
be None to copy complete history.
2047
if revision_id is None:
2048
revno, revision_id = self.last_revision_info()
2050
revno = self.revision_id_to_revno(revision_id)
2051
destination.set_last_revision_info(revno, revision_id)
2053
def _make_tags(self):
2054
return BasicTags(self)
1398
2057
class BranchTestProviderAdapter(object):
1399
2058
"""A tool to generate a suite testing multiple branch formats at once.
1418
2078
new_test.bzrdir_format = bzrdir_format
1419
2079
new_test.branch_format = branch_format
1420
2080
def make_new_test_id():
1421
new_id = "%s(%s)" % (new_test.id(), branch_format.__class__.__name__)
2081
# the format can be either a class or an instance
2082
name = getattr(branch_format, '__name__',
2083
branch_format.__class__.__name__)
2084
new_id = "%s(%s)" % (new_test.id(), name)
1422
2085
return lambda: new_id
1423
2086
new_test.id = make_new_test_id()
1424
2087
result.addTest(new_test)
2091
######################################################################
2092
# results of operations
2095
class _Result(object):
2097
def _show_tag_conficts(self, to_file):
2098
if not getattr(self, 'tag_conflicts', None):
2100
to_file.write('Conflicting tags:\n')
2101
for name, value1, value2 in self.tag_conflicts:
2102
to_file.write(' %s\n' % (name, ))
2105
class PullResult(_Result):
2106
"""Result of a Branch.pull operation.
2108
:ivar old_revno: Revision number before pull.
2109
:ivar new_revno: Revision number after pull.
2110
:ivar old_revid: Tip revision id before pull.
2111
:ivar new_revid: Tip revision id after pull.
2112
:ivar source_branch: Source (local) branch object.
2113
:ivar master_branch: Master branch of the target, or None.
2114
:ivar target_branch: Target/destination branch object.
2118
# DEPRECATED: pull used to return the change in revno
2119
return self.new_revno - self.old_revno
2121
def report(self, to_file):
2122
if self.old_revid == self.new_revid:
2123
to_file.write('No revisions to pull.\n')
2125
to_file.write('Now on revision %d.\n' % self.new_revno)
2126
self._show_tag_conficts(to_file)
2129
class PushResult(_Result):
2130
"""Result of a Branch.push operation.
2132
:ivar old_revno: Revision number before push.
2133
:ivar new_revno: Revision number after push.
2134
:ivar old_revid: Tip revision id before push.
2135
:ivar new_revid: Tip revision id after push.
2136
:ivar source_branch: Source branch object.
2137
:ivar master_branch: Master branch of the target, or None.
2138
:ivar target_branch: Target/destination branch object.
2142
# DEPRECATED: push used to return the change in revno
2143
return self.new_revno - self.old_revno
2145
def report(self, to_file):
2146
"""Write a human-readable description of the result."""
2147
if self.old_revid == self.new_revid:
2148
to_file.write('No new revisions to push.\n')
2150
to_file.write('Pushed up to revision %d.\n' % self.new_revno)
2151
self._show_tag_conficts(to_file)
1428
2154
class BranchCheckResult(object):
1429
2155
"""Results of checking branch consistency.