133
146
possible_transports)
134
147
return control.open_branch(), relpath
149
def _push_should_merge_tags(self):
150
"""Should _basic_push merge this branch's tags into the target?
152
The default implementation returns False if this branch has no tags,
153
and True the rest of the time. Subclasses may override this.
155
return self.supports_tags() and self.tags.get_tag_dict()
136
157
def get_config(self):
137
158
return BranchConfig(self)
140
return self.get_config().get_nickname()
160
def _get_tags_bytes(self):
161
"""Get the bytes of a serialised tags dict.
163
Note that not all branches support tags, nor do all use the same tags
164
logic: this method is specific to BasicTags. Other tag implementations
165
may use the same method name and behave differently, safely, because
166
of the double-dispatch via
167
format.make_tags->tags_instance->get_tags_dict.
169
:return: The bytes of the tags file.
170
:seealso: Branch._set_tags_bytes.
172
return self._transport.get_bytes('tags')
174
def _get_nick(self, local=False, possible_transports=None):
175
config = self.get_config()
176
# explicit overrides master, but don't look for master if local is True
177
if not local and not config.has_explicit_nickname():
179
master = self.get_master_branch(possible_transports)
180
if master is not None:
181
# return the master branch value
183
except errors.BzrError, e:
184
# Silently fall back to local implicit nick if the master is
186
mutter("Could not connect to bound branch, "
187
"falling back to local nick.\n " + str(e))
188
return config.get_nickname()
142
190
def _set_nick(self, nick):
143
191
self.get_config().set_user_option('nickname', nick, warn_masked=True)
164
239
raise NotImplementedError(self.get_physical_lock_status)
242
def dotted_revno_to_revision_id(self, revno, _cache_reverse=False):
243
"""Return the revision_id for a dotted revno.
245
:param revno: a tuple like (1,) or (1,1,2)
246
:param _cache_reverse: a private parameter enabling storage
247
of the reverse mapping in a top level cache. (This should
248
only be done in selective circumstances as we want to
249
avoid having the mapping cached multiple times.)
250
:return: the revision_id
251
:raises errors.NoSuchRevision: if the revno doesn't exist
253
rev_id = self._do_dotted_revno_to_revision_id(revno)
255
self._partial_revision_id_to_revno_cache[rev_id] = revno
258
def _do_dotted_revno_to_revision_id(self, revno):
259
"""Worker function for dotted_revno_to_revision_id.
261
Subclasses should override this if they wish to
262
provide a more efficient implementation.
265
return self.get_rev_id(revno[0])
266
revision_id_to_revno = self.get_revision_id_to_revno_map()
267
revision_ids = [revision_id for revision_id, this_revno
268
in revision_id_to_revno.iteritems()
269
if revno == this_revno]
270
if len(revision_ids) == 1:
271
return revision_ids[0]
273
revno_str = '.'.join(map(str, revno))
274
raise errors.NoSuchRevision(self, revno_str)
277
def revision_id_to_dotted_revno(self, revision_id):
278
"""Given a revision id, return its dotted revno.
280
:return: a tuple like (1,) or (400,1,3).
282
return self._do_revision_id_to_dotted_revno(revision_id)
284
def _do_revision_id_to_dotted_revno(self, revision_id):
285
"""Worker function for revision_id_to_revno."""
286
# Try the caches if they are loaded
287
result = self._partial_revision_id_to_revno_cache.get(revision_id)
288
if result is not None:
290
if self._revision_id_to_revno_cache:
291
result = self._revision_id_to_revno_cache.get(revision_id)
293
raise errors.NoSuchRevision(self, revision_id)
294
# Try the mainline as it's optimised
296
revno = self.revision_id_to_revno(revision_id)
298
except errors.NoSuchRevision:
299
# We need to load and use the full revno map after all
300
result = self.get_revision_id_to_revno_map().get(revision_id)
302
raise errors.NoSuchRevision(self, revision_id)
167
306
def get_revision_id_to_revno_map(self):
168
307
"""Return the revision_id => dotted revno map.
194
333
:return: A dictionary mapping revision_id => dotted revno.
196
last_revision = self.last_revision()
197
revision_graph = repository._old_get_graph(self.repository,
199
merge_sorted_revisions = tsort.merge_sort(
204
335
revision_id_to_revno = dict((rev_id, revno)
205
for seq_num, rev_id, depth, revno, end_of_merge
206
in merge_sorted_revisions)
336
for rev_id, depth, revno, end_of_merge
337
in self.iter_merge_sorted_revisions())
207
338
return revision_id_to_revno
341
def iter_merge_sorted_revisions(self, start_revision_id=None,
342
stop_revision_id=None, stop_rule='exclude', direction='reverse'):
343
"""Walk the revisions for a branch in merge sorted order.
345
Merge sorted order is the output from a merge-aware,
346
topological sort, i.e. all parents come before their
347
children going forward; the opposite for reverse.
349
:param start_revision_id: the revision_id to begin walking from.
350
If None, the branch tip is used.
351
:param stop_revision_id: the revision_id to terminate the walk
352
after. If None, the rest of history is included.
353
:param stop_rule: if stop_revision_id is not None, the precise rule
354
to use for termination:
355
* 'exclude' - leave the stop revision out of the result (default)
356
* 'include' - the stop revision is the last item in the result
357
* 'with-merges' - include the stop revision and all of its
358
merged revisions in the result
359
:param direction: either 'reverse' or 'forward':
360
* reverse means return the start_revision_id first, i.e.
361
start at the most recent revision and go backwards in history
362
* forward returns tuples in the opposite order to reverse.
363
Note in particular that forward does *not* do any intelligent
364
ordering w.r.t. depth as some clients of this API may like.
365
(If required, that ought to be done at higher layers.)
367
:return: an iterator over (revision_id, depth, revno, end_of_merge)
370
* revision_id: the unique id of the revision
371
* depth: How many levels of merging deep this node has been
373
* revno_sequence: This field provides a sequence of
374
revision numbers for all revisions. The format is:
375
(REVNO, BRANCHNUM, BRANCHREVNO). BRANCHNUM is the number of the
376
branch that the revno is on. From left to right the REVNO numbers
377
are the sequence numbers within that branch of the revision.
378
* end_of_merge: When True the next node (earlier in history) is
379
part of a different merge.
381
# Note: depth and revno values are in the context of the branch so
382
# we need the full graph to get stable numbers, regardless of the
384
if self._merge_sorted_revisions_cache is None:
385
last_revision = self.last_revision()
386
graph = self.repository.get_graph()
387
parent_map = dict(((key, value) for key, value in
388
graph.iter_ancestry([last_revision]) if value is not None))
389
revision_graph = repository._strip_NULL_ghosts(parent_map)
390
revs = tsort.merge_sort(revision_graph, last_revision, None,
392
# Drop the sequence # before caching
393
self._merge_sorted_revisions_cache = [r[1:] for r in revs]
395
filtered = self._filter_merge_sorted_revisions(
396
self._merge_sorted_revisions_cache, start_revision_id,
397
stop_revision_id, stop_rule)
398
if direction == 'reverse':
400
if direction == 'forward':
401
return reversed(list(filtered))
403
raise ValueError('invalid direction %r' % direction)
405
def _filter_merge_sorted_revisions(self, merge_sorted_revisions,
406
start_revision_id, stop_revision_id, stop_rule):
407
"""Iterate over an inclusive range of sorted revisions."""
408
rev_iter = iter(merge_sorted_revisions)
409
if start_revision_id is not None:
410
for rev_id, depth, revno, end_of_merge in rev_iter:
411
if rev_id != start_revision_id:
414
# The decision to include the start or not
415
# depends on the stop_rule if a stop is provided
417
iter([(rev_id, depth, revno, end_of_merge)]),
420
if stop_revision_id is None:
421
for rev_id, depth, revno, end_of_merge in rev_iter:
422
yield rev_id, depth, revno, end_of_merge
423
elif stop_rule == 'exclude':
424
for rev_id, depth, revno, end_of_merge in rev_iter:
425
if rev_id == stop_revision_id:
427
yield rev_id, depth, revno, end_of_merge
428
elif stop_rule == 'include':
429
for rev_id, depth, revno, end_of_merge in rev_iter:
430
yield rev_id, depth, revno, end_of_merge
431
if rev_id == stop_revision_id:
433
elif stop_rule == 'with-merges':
434
stop_rev = self.repository.get_revision(stop_revision_id)
435
if stop_rev.parent_ids:
436
left_parent = stop_rev.parent_ids[0]
438
left_parent = _mod_revision.NULL_REVISION
439
for rev_id, depth, revno, end_of_merge in rev_iter:
440
if rev_id == left_parent:
442
yield rev_id, depth, revno, end_of_merge
444
raise ValueError('invalid stop_rule %r' % stop_rule)
209
446
def leave_lock_in_place(self):
210
447
"""Tell this branch object not to release the physical lock when this
211
448
object is unlocked.
213
450
If lock_write doesn't return a token, then this method is not supported.
215
452
self.control_files.leave_in_place()
503
775
"""Return `Tree` object for last revision."""
504
776
return self.repository.revision_tree(self.last_revision())
506
def rename_one(self, from_rel, to_rel):
509
This can change the directory or the filename or both.
511
raise NotImplementedError(self.rename_one)
513
def move(self, from_paths, to_name):
516
to_name must exist as a versioned directory.
518
If to_name exists and is a directory, the files are moved into
519
it, keeping their old names. If it is a directory,
521
Note that to_name is only the last component of the new name;
522
this doesn't change the directory.
524
This returns a list of (from_path, to_path) pairs for each
527
raise NotImplementedError(self.move)
529
778
def get_parent(self):
530
779
"""Return the parent location of the branch.
532
This is the default location for push/pull/missing. The usual
781
This is the default location for pull/missing. The usual
533
782
pattern is that the user can override it by specifying a
536
raise NotImplementedError(self.get_parent)
785
parent = self._get_parent_location()
788
# This is an old-format absolute path to a local branch
790
if parent.startswith('/'):
791
parent = urlutils.local_path_to_url(parent.decode('utf8'))
793
return urlutils.join(self.base[:-1], parent)
794
except errors.InvalidURLJoin, e:
795
raise errors.InaccessibleParent(parent, self.base)
797
def _get_parent_location(self):
798
raise NotImplementedError(self._get_parent_location)
538
800
def _set_config_location(self, name, url, config=None,
539
801
make_relative=False):
597
859
"""Set a new push location for this branch."""
598
860
raise NotImplementedError(self.set_push_location)
862
def _run_post_change_branch_tip_hooks(self, old_revno, old_revid):
863
"""Run the post_change_branch_tip hooks."""
864
hooks = Branch.hooks['post_change_branch_tip']
867
new_revno, new_revid = self.last_revision_info()
868
params = ChangeBranchTipParams(
869
self, old_revno, new_revno, old_revid, new_revid)
873
def _run_pre_change_branch_tip_hooks(self, new_revno, new_revid):
874
"""Run the pre_change_branch_tip hooks."""
875
hooks = Branch.hooks['pre_change_branch_tip']
878
old_revno, old_revid = self.last_revision_info()
879
params = ChangeBranchTipParams(
880
self, old_revno, new_revno, old_revid, new_revid)
884
except errors.TipChangeRejected:
887
exc_info = sys.exc_info()
888
hook_name = Branch.hooks.get_hook_name(hook)
889
raise errors.HookFailed(
890
'pre_change_branch_tip', hook_name, exc_info)
600
892
def set_parent(self, url):
601
893
raise NotImplementedError(self.set_parent)
603
895
@needs_write_lock
604
896
def update(self):
605
"""Synchronise this branch with the master branch if any.
897
"""Synchronise this branch with the master branch if any.
607
899
:return: None or the last_revision pivoted out during the update.
625
917
raise errors.InvalidRevisionNumber(revno)
628
def clone(self, to_bzrdir, revision_id=None):
920
def clone(self, to_bzrdir, revision_id=None, repository_policy=None):
629
921
"""Clone this branch into to_bzrdir preserving all semantic values.
923
Most API users will want 'create_clone_on_transport', which creates a
924
new bzrdir and branch on the fly.
631
926
revision_id: if not None, the revision history in the new branch will
632
927
be truncated to end with revision_id.
634
result = self._format.initialize(to_bzrdir)
929
result = to_bzrdir.create_branch()
930
if repository_policy is not None:
931
repository_policy.configure_branch(result)
635
932
self.copy_content_into(result, revision_id=revision_id)
639
def sprout(self, to_bzrdir, revision_id=None):
936
def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
640
937
"""Create a new line of development from the branch, into to_bzrdir.
939
to_bzrdir controls the branch format.
642
941
revision_id: if not None, the revision history in the new branch will
643
942
be truncated to end with revision_id.
645
result = self._format.initialize(to_bzrdir)
944
result = to_bzrdir.create_branch()
945
if repository_policy is not None:
946
repository_policy.configure_branch(result)
646
947
self.copy_content_into(result, revision_id=revision_id)
647
948
result.set_parent(self.bzrdir.root_transport.base)
651
952
"""Synchronize last revision and revision history between branches.
653
954
This version is most efficient when the destination is also a
654
BzrBranch5, but works for BzrBranch6 as long as the revision
655
history is the true lefthand parent history, and all of the revisions
656
are in the destination's repository. If not, set_revision_history
955
BzrBranch6, but works for BzrBranch5, as long as the destination's
956
repository contains all the lefthand ancestors of the intended
957
last_revision. If not, set_last_revision_info will fail.
659
959
:param destination: The branch to copy the history into
660
960
:param revision_id: The revision-id to truncate history at. May
661
961
be None to copy complete history.
663
if revision_id == _mod_revision.NULL_REVISION:
665
new_history = self.revision_history()
666
if revision_id is not None and new_history != []:
668
new_history = new_history[:new_history.index(revision_id) + 1]
670
rev = self.repository.get_revision(revision_id)
671
new_history = rev.get_history(self.repository)[1:]
672
destination.set_revision_history(new_history)
963
source_revno, source_revision_id = self.last_revision_info()
964
if revision_id is None:
965
revno, revision_id = source_revno, source_revision_id
966
elif source_revision_id == revision_id:
967
# we know the revno without needing to walk all of history
970
# To figure out the revno for a random revision, we need to build
971
# the revision history, and count its length.
972
# We don't care about the order, just how long it is.
973
# Alternatively, we could start at the current location, and count
974
# backwards. But there is no guarantee that we will find it since
975
# it may be a merged revision.
976
revno = len(list(self.repository.iter_reverse_revision_history(
978
destination.set_last_revision_info(revno, revision_id)
675
981
def copy_content_into(self, destination, revision_id=None):
809
1131
def supports_tags(self):
810
1132
return self._format.supports_tags()
1134
def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1136
"""Ensure that revision_b is a descendant of revision_a.
1138
This is a helper function for update_revisions.
1140
:raises: DivergedBranches if revision_b has diverged from revision_a.
1141
:returns: True if revision_b is a descendant of revision_a.
1143
relation = self._revision_relations(revision_a, revision_b, graph)
1144
if relation == 'b_descends_from_a':
1146
elif relation == 'diverged':
1147
raise errors.DivergedBranches(self, other_branch)
1148
elif relation == 'a_descends_from_b':
1151
raise AssertionError("invalid relation: %r" % (relation,))
1153
def _revision_relations(self, revision_a, revision_b, graph):
1154
"""Determine the relationship between two revisions.
1156
:returns: One of: 'a_descends_from_b', 'b_descends_from_a', 'diverged'
1158
heads = graph.heads([revision_a, revision_b])
1159
if heads == set([revision_b]):
1160
return 'b_descends_from_a'
1161
elif heads == set([revision_a, revision_b]):
1162
# These branches have diverged
1164
elif heads == set([revision_a]):
1165
return 'a_descends_from_b'
1167
raise AssertionError("invalid heads: %r" % (heads,))
813
1170
class BranchFormat(object):
814
1171
"""An encapsulation of the initialization and open routines for a format.
981
1370
Hooks.__init__(self)
982
# Introduced in 0.15:
983
# invoked whenever the revision history has been set
984
# with set_revision_history. The api signature is
985
# (branch, revision_history), and the branch will
988
# invoked after a push operation completes.
989
# the api signature is
991
# containing the members
992
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
993
# where local is the local target branch or None, master is the target
994
# master branch, and the rest should be self explanatory. The source
995
# is read locked and the target branches write locked. Source will
996
# be the local low-latency branch.
997
self['post_push'] = []
998
# invoked after a pull operation completes.
999
# the api signature is
1001
# containing the members
1002
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
1003
# where local is the local branch or None, master is the target
1004
# master branch, and the rest should be self explanatory. The source
1005
# is read locked and the target branches write locked. The local
1006
# branch is the low-latency branch.
1007
self['post_pull'] = []
1008
# invoked before a commit operation takes place.
1009
# the api signature is
1010
# (local, master, old_revno, old_revid, future_revno, future_revid,
1011
# tree_delta, future_tree).
1012
# old_revid is NULL_REVISION for the first commit to a branch
1013
# tree_delta is a TreeDelta object describing changes from the basis
1014
# revision, hooks MUST NOT modify this delta
1015
# future_tree is an in-memory tree obtained from
1016
# CommitBuilder.revision_tree() and hooks MUST NOT modify this tree
1017
self['pre_commit'] = []
1018
# invoked after a commit operation completes.
1019
# the api signature is
1020
# (local, master, old_revno, old_revid, new_revno, new_revid)
1021
# old_revid is NULL_REVISION for the first commit to a branch.
1022
self['post_commit'] = []
1023
# invoked after a uncommit operation completes.
1024
# the api signature is
1025
# (local, master, old_revno, old_revid, new_revno, new_revid) where
1026
# local is the local branch or None, master is the target branch,
1027
# and an empty branch recieves new_revno of 0, new_revid of None.
1028
self['post_uncommit'] = []
1030
# Invoked after the tip of a branch changes.
1031
# the api signature is
1032
# (params) where params is a ChangeBranchTipParams with the members
1033
# (branch, old_revno, new_revno, old_revid, new_revid)
1034
self['post_change_branch_tip'] = []
1371
self.create_hook(HookPoint('set_rh',
1372
"Invoked whenever the revision history has been set via "
1373
"set_revision_history. The api signature is (branch, "
1374
"revision_history), and the branch will be write-locked. "
1375
"The set_rh hook can be expensive for bzr to trigger, a better "
1376
"hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1377
self.create_hook(HookPoint('open',
1378
"Called with the Branch object that has been opened after a "
1379
"branch is opened.", (1, 8), None))
1380
self.create_hook(HookPoint('post_push',
1381
"Called after a push operation completes. post_push is called "
1382
"with a bzrlib.branch.BranchPushResult object and only runs in the "
1383
"bzr client.", (0, 15), None))
1384
self.create_hook(HookPoint('post_pull',
1385
"Called after a pull operation completes. post_pull is called "
1386
"with a bzrlib.branch.PullResult object and only runs in the "
1387
"bzr client.", (0, 15), None))
1388
self.create_hook(HookPoint('pre_commit',
1389
"Called after a commit is calculated but before it is is "
1390
"completed. pre_commit is called with (local, master, old_revno, "
1391
"old_revid, future_revno, future_revid, tree_delta, future_tree"
1392
"). old_revid is NULL_REVISION for the first commit to a branch, "
1393
"tree_delta is a TreeDelta object describing changes from the "
1394
"basis revision. hooks MUST NOT modify this delta. "
1395
" future_tree is an in-memory tree obtained from "
1396
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1397
"tree.", (0,91), None))
1398
self.create_hook(HookPoint('post_commit',
1399
"Called in the bzr client after a commit has completed. "
1400
"post_commit is called with (local, master, old_revno, old_revid, "
1401
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1402
"commit to a branch.", (0, 15), None))
1403
self.create_hook(HookPoint('post_uncommit',
1404
"Called in the bzr client after an uncommit completes. "
1405
"post_uncommit is called with (local, master, old_revno, "
1406
"old_revid, new_revno, new_revid) where local is the local branch "
1407
"or None, master is the target branch, and an empty branch "
1408
"recieves new_revno of 0, new_revid of None.", (0, 15), None))
1409
self.create_hook(HookPoint('pre_change_branch_tip',
1410
"Called in bzr client and server before a change to the tip of a "
1411
"branch is made. pre_change_branch_tip is called with a "
1412
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1413
"commit, uncommit will all trigger this hook.", (1, 6), None))
1414
self.create_hook(HookPoint('post_change_branch_tip',
1415
"Called in bzr client and server after a change to the tip of a "
1416
"branch is made. post_change_branch_tip is called with a "
1417
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1418
"commit, uncommit will all trigger this hook.", (1, 4), None))
1419
self.create_hook(HookPoint('transform_fallback_location',
1420
"Called when a stacked branch is activating its fallback "
1421
"locations. transform_fallback_location is called with (branch, "
1422
"url), and should return a new url. Returning the same url "
1423
"allows it to be used as-is, returning a different one can be "
1424
"used to cause the branch to stack on a closer copy of that "
1425
"fallback_location. Note that the branch cannot have history "
1426
"accessing methods called on it during this hook because the "
1427
"fallback locations have not been activated. When there are "
1428
"multiple hooks installed for transform_fallback_location, "
1429
"all are called with the url returned from the previous hook."
1430
"The order is however undefined.", (1, 9), None))
1037
1433
# install the default hooks into the Branch class.
1110
1518
return "Bazaar-NG branch format 4"
1113
class BzrBranchFormat5(BranchFormat):
1521
class BranchFormatMetadir(BranchFormat):
1522
"""Common logic for meta-dir based branch formats."""
1524
def _branch_class(self):
1525
"""What class to instantiate on open calls."""
1526
raise NotImplementedError(self._branch_class)
1528
def network_name(self):
1529
"""A simple byte string uniquely identifying this format for RPC calls.
1531
Metadir branch formats use their format string.
1533
return self.get_format_string()
1535
def open(self, a_bzrdir, _found=False):
1536
"""Return the branch object for a_bzrdir.
1538
_found is a private parameter, do not use it. It is used to indicate
1539
if format probing has already be done.
1542
format = BranchFormat.find_format(a_bzrdir)
1543
if format.__class__ != self.__class__:
1544
raise AssertionError("wrong format %r found for %r" %
1547
transport = a_bzrdir.get_branch_transport(None)
1548
control_files = lockable_files.LockableFiles(transport, 'lock',
1550
return self._branch_class()(_format=self,
1551
_control_files=control_files,
1553
_repository=a_bzrdir.find_repository())
1554
except errors.NoSuchFile:
1555
raise errors.NotBranchError(path=transport.base)
1558
super(BranchFormatMetadir, self).__init__()
1559
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1560
self._matchingbzrdir.set_branch_format(self)
1562
def supports_tags(self):
1566
class BzrBranchFormat5(BranchFormatMetadir):
1114
1567
"""Bzr branch format 5.
1116
1569
This format has:
1193
1629
return self._initialize_helper(a_bzrdir, utf8_files)
1195
def open(self, a_bzrdir, _found=False):
1196
"""Return the branch object for a_bzrdir
1198
_found is a private parameter, do not use it. It is used to indicate
1199
if format probing has already be done.
1202
format = BranchFormat.find_format(a_bzrdir)
1203
if format.__class__ != self.__class__:
1204
raise AssertionError("wrong format %r found for %r" %
1206
transport = a_bzrdir.get_branch_transport(None)
1207
control_files = lockable_files.LockableFiles(transport, 'lock',
1209
return BzrBranch6(_format=self,
1210
_control_files=control_files,
1212
_repository=a_bzrdir.find_repository())
1214
def supports_tags(self):
1631
def make_tags(self, branch):
1632
"""See bzrlib.branch.BranchFormat.make_tags()."""
1633
return BasicTags(branch)
1637
class BzrBranchFormat7(BranchFormatMetadir):
1638
"""Branch format with last-revision, tags, and a stacked location pointer.
1640
The stacked location pointer is passed down to the repository and requires
1641
a repository format with supports_external_lookups = True.
1643
This format was introduced in bzr 1.6.
1646
def _branch_class(self):
1649
def get_format_string(self):
1650
"""See BranchFormat.get_format_string()."""
1651
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
1653
def get_format_description(self):
1654
"""See BranchFormat.get_format_description()."""
1655
return "Branch format 7"
1657
def initialize(self, a_bzrdir):
1658
"""Create a branch of this format in a_bzrdir."""
1659
utf8_files = [('last-revision', '0 null:\n'),
1660
('branch.conf', ''),
1663
return self._initialize_helper(a_bzrdir, utf8_files)
1666
super(BzrBranchFormat7, self).__init__()
1667
self._matchingbzrdir.repository_format = \
1668
RepositoryFormatKnitPack5RichRoot()
1670
def make_tags(self, branch):
1671
"""See bzrlib.branch.BranchFormat.make_tags()."""
1672
return BasicTags(branch)
1674
def supports_stacking(self):
1420
1891
"""See Branch.set_revision_history."""
1421
1892
if 'evil' in debug.debug_flags:
1422
1893
mutter_callsite(3, "set_revision_history scales with history.")
1894
check_not_reserved_id = _mod_revision.check_not_reserved_id
1895
for rev_id in rev_history:
1896
check_not_reserved_id(rev_id)
1897
if Branch.hooks['post_change_branch_tip']:
1898
# Don't calculate the last_revision_info() if there are no hooks
1900
old_revno, old_revid = self.last_revision_info()
1901
if len(rev_history) == 0:
1902
revid = _mod_revision.NULL_REVISION
1904
revid = rev_history[-1]
1905
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
1423
1906
self._write_revision_history(rev_history)
1424
1907
self._clear_cached_state()
1425
1908
self._cache_revision_history(rev_history)
1426
1909
for hook in Branch.hooks['set_rh']:
1427
1910
hook(self, rev_history)
1429
def _run_post_change_branch_tip_hooks(self, old_revno, old_revid):
1430
"""Run the post_change_branch_tip hooks."""
1431
hooks = Branch.hooks['post_change_branch_tip']
1911
if Branch.hooks['post_change_branch_tip']:
1912
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1914
def _synchronize_history(self, destination, revision_id):
1915
"""Synchronize last revision and revision history between branches.
1917
This version is most efficient when the destination is also a
1918
BzrBranch5, but works for BzrBranch6 as long as the revision
1919
history is the true lefthand parent history, and all of the revisions
1920
are in the destination's repository. If not, set_revision_history
1923
:param destination: The branch to copy the history into
1924
:param revision_id: The revision-id to truncate history at. May
1925
be None to copy complete history.
1927
if not isinstance(destination._format, BzrBranchFormat5):
1928
super(BzrBranch, self)._synchronize_history(
1929
destination, revision_id)
1434
new_revno, new_revid = self.last_revision_info()
1435
params = ChangeBranchTipParams(
1436
self, old_revno, new_revno, old_revid, new_revid)
1931
if revision_id == _mod_revision.NULL_REVISION:
1934
new_history = self.revision_history()
1935
if revision_id is not None and new_history != []:
1937
new_history = new_history[:new_history.index(revision_id) + 1]
1939
rev = self.repository.get_revision(revision_id)
1940
new_history = rev.get_history(self.repository)[1:]
1941
destination.set_revision_history(new_history)
1440
1943
@needs_write_lock
1441
1944
def set_last_revision_info(self, revno, revision_id):
1442
1945
"""Set the last revision of this branch.
1506
1982
self.set_revision_history(self._lefthand_history(revision_id,
1507
1983
last_rev, other_branch))
1510
def update_revisions(self, other, stop_revision=None, overwrite=False,
1512
"""See Branch.update_revisions."""
1515
other_revno, other_last_revision = other.last_revision_info()
1516
stop_revno = None # unknown
1517
if stop_revision is None:
1518
stop_revision = other_last_revision
1519
if _mod_revision.is_null(stop_revision):
1520
# if there are no commits, we're done.
1522
stop_revno = other_revno
1524
# what's the current last revision, before we fetch [and change it
1526
last_rev = _mod_revision.ensure_null(self.last_revision())
1527
# we fetch here so that we don't process data twice in the common
1528
# case of having something to pull, and so that the check for
1529
# already merged can operate on the just fetched graph, which will
1530
# be cached in memory.
1531
self.fetch(other, stop_revision)
1532
# Check to see if one is an ancestor of the other
1535
graph = self.repository.get_graph()
1536
heads = graph.heads([stop_revision, last_rev])
1537
if heads == set([last_rev]):
1538
# The current revision is a decendent of the target,
1541
elif heads == set([stop_revision, last_rev]):
1542
# These branches have diverged
1543
raise errors.DivergedBranches(self, other)
1544
elif heads != set([stop_revision]):
1545
raise AssertionError("invalid heads: %r" % heads)
1546
if stop_revno is None:
1548
graph = self.repository.get_graph()
1549
this_revno, this_last_revision = self.last_revision_info()
1550
stop_revno = graph.find_distance_to_null(stop_revision,
1551
[(other_last_revision, other_revno),
1552
(this_last_revision, this_revno)])
1553
self.set_last_revision_info(stop_revno, stop_revision)
1557
1985
def basis_tree(self):
1558
1986
"""See Branch.basis_tree."""
1559
1987
return self.repository.revision_tree(self.last_revision())
1561
1989
@needs_write_lock
1562
1990
def pull(self, source, overwrite=False, stop_revision=None,
1563
_hook_master=None, run_hooks=True, possible_transports=None):
1991
_hook_master=None, run_hooks=True, possible_transports=None,
1992
_override_hook_target=None):
1564
1993
"""See Branch.pull.
1566
:param _hook_master: Private parameter - set the branch to
1567
be supplied as the master to push hooks.
1995
:param _hook_master: Private parameter - set the branch to
1996
be supplied as the master to pull hooks.
1568
1997
:param run_hooks: Private parameter - if false, this branch
1569
1998
is being called because it's the master of the primary branch,
1570
1999
so it should not run its hooks.
2000
:param _override_hook_target: Private parameter - set the branch to be
2001
supplied as the target_branch to pull hooks.
1572
2003
result = PullResult()
1573
2004
result.source_branch = source
1574
result.target_branch = self
2005
if _override_hook_target is None:
2006
result.target_branch = self
2008
result.target_branch = _override_hook_target
1575
2009
source.lock_read()
1577
2011
# We assume that during 'pull' the local repository is closer than
1679
2109
Must be called with self read locked and target write locked.
1681
result = PushResult()
2111
result = BranchPushResult()
1682
2112
result.source_branch = self
1683
2113
result.target_branch = target
1684
2114
result.old_revno, result.old_revid = target.last_revision_info()
1686
# We assume that during 'push' this repository is closer than
1688
graph = self.repository.get_graph(target.repository)
1689
target.update_revisions(self, stop_revision, overwrite=overwrite,
1691
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
2115
if result.old_revid != self.last_revision():
2116
# We assume that during 'push' this repository is closer than
2118
graph = self.repository.get_graph(target.repository)
2119
target.update_revisions(self, stop_revision, overwrite=overwrite,
2121
if self._push_should_merge_tags():
2122
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
1692
2123
result.new_revno, result.new_revid = target.last_revision_info()
1695
def get_parent(self):
1696
"""See Branch.get_parent."""
1697
parent = self._get_parent_location()
1700
# This is an old-format absolute path to a local branch
1701
# turn it into a url
1702
if parent.startswith('/'):
1703
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1705
return urlutils.join(self.base[:-1], parent)
1706
except errors.InvalidURLJoin, e:
1707
raise errors.InaccessibleParent(parent, self.base)
2126
def get_stacked_on_url(self):
2127
raise errors.UnstackableBranchFormat(self._format, self.base)
1709
2129
def set_push_location(self, location):
1710
2130
"""See Branch.set_push_location."""
1876
class BzrBranch6(BzrBranch5):
2291
class BzrBranch7(BzrBranch5):
2292
"""A branch with support for a fallback repository."""
2294
def _get_fallback_repository(self, url):
2295
"""Get the repository we fallback to at url."""
2296
url = urlutils.join(self.base, url)
2297
a_bzrdir = bzrdir.BzrDir.open(url,
2298
possible_transports=[self._transport])
2299
return a_bzrdir.open_branch().repository
2301
def _activate_fallback_location(self, url):
2302
"""Activate the branch/repository from url as a fallback repository."""
2303
self.repository.add_fallback_repository(
2304
self._get_fallback_repository(url))
2306
def _open_hook(self):
2308
url = self.get_stacked_on_url()
2309
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2310
errors.UnstackableBranchFormat):
2313
for hook in Branch.hooks['transform_fallback_location']:
2314
url = hook(self, url)
2316
hook_name = Branch.hooks.get_hook_name(hook)
2317
raise AssertionError(
2318
"'transform_fallback_location' hook %s returned "
2319
"None, not a URL." % hook_name)
2320
self._activate_fallback_location(url)
2322
def _check_stackable_repo(self):
2323
if not self.repository._format.supports_external_lookups:
2324
raise errors.UnstackableRepositoryFormat(self.repository._format,
2325
self.repository.base)
1878
2327
def __init__(self, *args, **kwargs):
1879
super(BzrBranch6, self).__init__(*args, **kwargs)
2328
super(BzrBranch7, self).__init__(*args, **kwargs)
1880
2329
self._last_revision_info_cache = None
1881
2330
self._partial_revision_history_cache = []
1883
2332
def _clear_cached_state(self):
1884
super(BzrBranch6, self)._clear_cached_state()
2333
super(BzrBranch7, self)._clear_cached_state()
1885
2334
self._last_revision_info_cache = None
1886
2335
self._partial_revision_history_cache = []
1889
def last_revision_info(self):
1890
"""Return information about the last revision.
1892
:return: A tuple (revno, revision_id).
1894
if self._last_revision_info_cache is None:
1895
self._last_revision_info_cache = self._last_revision_info()
1896
return self._last_revision_info_cache
1898
2337
def _last_revision_info(self):
1899
2338
revision_string = self._transport.get_bytes('last-revision')
1900
2339
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
1922
2361
old_revno, old_revid = self.last_revision_info()
1923
2362
if self._get_append_revisions_only():
1924
2363
self._check_history_violation(revision_id)
2364
self._run_pre_change_branch_tip_hooks(revno, revision_id)
1925
2365
self._write_last_revision_info(revno, revision_id)
1926
2366
self._clear_cached_state()
1927
2367
self._last_revision_info_cache = revno, revision_id
1928
2368
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2370
def _synchronize_history(self, destination, revision_id):
2371
"""Synchronize last revision and revision history between branches.
2373
:see: Branch._synchronize_history
2375
# XXX: The base Branch has a fast implementation of this method based
2376
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2377
# that uses set_revision_history. This class inherits from BzrBranch5,
2378
# but wants the fast implementation, so it calls
2379
# Branch._synchronize_history directly.
2380
Branch._synchronize_history(self, destination, revision_id)
1930
2382
def _check_history_violation(self, revision_id):
1931
2383
last_revision = _mod_revision.ensure_null(self.last_revision())
1932
2384
if _mod_revision.is_null(last_revision):
2041
2503
self.get_config().set_user_option('append_revisions_only', value,
2042
2504
warn_masked=True)
2506
def set_stacked_on_url(self, url):
2507
self._check_stackable_repo()
2510
old_url = self.get_stacked_on_url()
2511
except (errors.NotStacked, errors.UnstackableBranchFormat,
2512
errors.UnstackableRepositoryFormat):
2515
# repositories don't offer an interface to remove fallback
2516
# repositories today; take the conceptually simpler option and just
2518
self.repository = self.bzrdir.find_repository()
2519
# for every revision reference the branch has, ensure it is pulled
2521
source_repository = self._get_fallback_repository(old_url)
2522
for revision_id in chain([self.last_revision()],
2523
self.tags.get_reverse_tag_dict()):
2524
self.repository.fetch(source_repository, revision_id,
2527
self._activate_fallback_location(url)
2528
# write this out after the repository is stacked to avoid setting a
2529
# stacked config that doesn't work.
2530
self._set_config_location('stacked_on_location', url)
2044
2532
def _get_append_revisions_only(self):
2045
2533
value = self.get_config().get_user_option('append_revisions_only')
2046
2534
return value == 'True'
2048
def _synchronize_history(self, destination, revision_id):
2049
"""Synchronize last revision and revision history between branches.
2051
This version is most efficient when the destination is also a
2052
BzrBranch6, but works for BzrBranch5, as long as the destination's
2053
repository contains all the lefthand ancestors of the intended
2054
last_revision. If not, set_last_revision_info will fail.
2056
:param destination: The branch to copy the history into
2057
:param revision_id: The revision-id to truncate history at. May
2058
be None to copy complete history.
2060
source_revno, source_revision_id = self.last_revision_info()
2061
if revision_id is None:
2062
revno, revision_id = source_revno, source_revision_id
2063
elif source_revision_id == revision_id:
2064
# we know the revno without needing to walk all of history
2065
revno = source_revno
2067
# To figure out the revno for a random revision, we need to build
2068
# the revision history, and count its length.
2069
# We don't care about the order, just how long it is.
2070
# Alternatively, we could start at the current location, and count
2071
# backwards. But there is no guarantee that we will find it since
2072
# it may be a merged revision.
2073
revno = len(list(self.repository.iter_reverse_revision_history(
2075
destination.set_last_revision_info(revno, revision_id)
2077
def _make_tags(self):
2078
return BasicTags(self)
2080
2536
@needs_write_lock
2081
2537
def generate_revision_history(self, revision_id, last_rev=None,
2082
2538
other_branch=None):
2234
2714
except errors.NoSuchFile:
2236
2716
branch.set_bound_location(None)
2719
class Converter6to7(object):
2720
"""Perform an in-place upgrade of format 6 to format 7"""
2722
def convert(self, branch):
2723
format = BzrBranchFormat7()
2724
branch._set_config_location('stacked_on_location', '')
2725
# update target format
2726
branch._transport.put_bytes('format', format.get_format_string())
2730
def _run_with_write_locked_target(target, callable, *args, **kwargs):
2731
"""Run ``callable(*args, **kwargs)``, write-locking target for the
2734
_run_with_write_locked_target will attempt to release the lock it acquires.
2736
If an exception is raised by callable, then that exception *will* be
2737
propagated, even if the unlock attempt raises its own error. Thus
2738
_run_with_write_locked_target should be preferred to simply doing::
2742
return callable(*args, **kwargs)
2747
# This is very similar to bzrlib.decorators.needs_write_lock. Perhaps they
2748
# should share code?
2751
result = callable(*args, **kwargs)
2753
exc_info = sys.exc_info()
2757
raise exc_info[0], exc_info[1], exc_info[2]
2763
class InterBranch(InterObject):
2764
"""This class represents operations taking place between two branches.
2766
Its instances have methods like pull() and push() and contain
2767
references to the source and target repositories these operations
2768
can be carried out on.
2772
"""The available optimised InterBranch types."""
2775
def _get_branch_formats_to_test():
2776
"""Return a tuple with the Branch formats to use when testing."""
2777
raise NotImplementedError(self._get_branch_formats_to_test)
2779
def update_revisions(self, stop_revision=None, overwrite=False,
2781
"""Pull in new perfect-fit revisions.
2783
:param stop_revision: Updated until the given revision
2784
:param overwrite: Always set the branch pointer, rather than checking
2785
to see if it is a proper descendant.
2786
:param graph: A Graph object that can be used to query history
2787
information. This can be None.
2790
raise NotImplementedError(self.update_revisions)
2793
class GenericInterBranch(InterBranch):
2794
"""InterBranch implementation that uses public Branch functions.
2798
def _get_branch_formats_to_test():
2799
return BranchFormat._default_format, BranchFormat._default_format
2801
def update_revisions(self, stop_revision=None, overwrite=False,
2803
"""See InterBranch.update_revisions()."""
2804
self.source.lock_read()
2806
other_revno, other_last_revision = self.source.last_revision_info()
2807
stop_revno = None # unknown
2808
if stop_revision is None:
2809
stop_revision = other_last_revision
2810
if _mod_revision.is_null(stop_revision):
2811
# if there are no commits, we're done.
2813
stop_revno = other_revno
2815
# what's the current last revision, before we fetch [and change it
2817
last_rev = _mod_revision.ensure_null(self.target.last_revision())
2818
# we fetch here so that we don't process data twice in the common
2819
# case of having something to pull, and so that the check for
2820
# already merged can operate on the just fetched graph, which will
2821
# be cached in memory.
2822
self.target.fetch(self.source, stop_revision)
2823
# Check to see if one is an ancestor of the other
2826
graph = self.target.repository.get_graph()
2827
if self.target._check_if_descendant_or_diverged(
2828
stop_revision, last_rev, graph, self.source):
2829
# stop_revision is a descendant of last_rev, but we aren't
2830
# overwriting, so we're done.
2832
if stop_revno is None:
2834
graph = self.target.repository.get_graph()
2835
this_revno, this_last_revision = \
2836
self.target.last_revision_info()
2837
stop_revno = graph.find_distance_to_null(stop_revision,
2838
[(other_last_revision, other_revno),
2839
(this_last_revision, this_revno)])
2840
self.target.set_last_revision_info(stop_revno, stop_revision)
2842
self.source.unlock()
2845
def is_compatible(self, source, target):
2846
# GenericBranch uses the public API, so always compatible
2850
InterBranch.register_optimiser(GenericInterBranch)