121
113
if master is not None:
122
114
master.break_lock()
124
def _check_stackable_repo(self):
125
if not self.repository._format.supports_external_lookups:
126
raise errors.UnstackableRepositoryFormat(self.repository._format,
127
self.repository.base)
129
def _extend_partial_history(self, stop_index=None, stop_revision=None):
130
"""Extend the partial history to include a given index
132
If a stop_index is supplied, stop when that index has been reached.
133
If a stop_revision is supplied, stop when that revision is
134
encountered. Otherwise, stop when the beginning of history is
137
:param stop_index: The index which should be present. When it is
138
present, history extension will stop.
139
:param stop_revision: The revision id which should be present. When
140
it is encountered, history extension will stop.
142
if len(self._partial_revision_history_cache) == 0:
143
self._partial_revision_history_cache = [self.last_revision()]
144
repository._iter_for_revno(
145
self.repository, self._partial_revision_history_cache,
146
stop_index=stop_index, stop_revision=stop_revision)
147
if self._partial_revision_history_cache[-1] == _mod_revision.NULL_REVISION:
148
self._partial_revision_history_cache.pop()
150
def _get_check_refs(self):
151
"""Get the references needed for check().
155
revid = self.last_revision()
156
return [('revision-existence', revid), ('lefthand-distance', revid)]
159
117
def open(base, _unsupported=False, possible_transports=None):
160
118
"""Open the branch rooted at base.
456
383
# start_revision_id.
457
384
if self._merge_sorted_revisions_cache is None:
458
385
last_revision = self.last_revision()
459
known_graph = self.repository.get_known_graph_ancestry(
461
self._merge_sorted_revisions_cache = known_graph.merge_sort(
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]
463
395
filtered = self._filter_merge_sorted_revisions(
464
396
self._merge_sorted_revisions_cache, start_revision_id,
465
397
stop_revision_id, stop_rule)
466
# Make sure we don't return revisions that are not part of the
467
# start_revision_id ancestry.
468
filtered = self._filter_start_non_ancestors(filtered)
469
398
if direction == 'reverse':
471
400
if direction == 'forward':
478
407
"""Iterate over an inclusive range of sorted revisions."""
479
408
rev_iter = iter(merge_sorted_revisions)
480
409
if start_revision_id is not None:
481
for node in rev_iter:
482
rev_id = node.key[-1]
410
for rev_id, depth, revno, end_of_merge in rev_iter:
483
411
if rev_id != start_revision_id:
486
414
# The decision to include the start or not
487
415
# depends on the stop_rule if a stop is provided
488
# so pop this node back into the iterator
489
rev_iter = chain(iter([node]), rev_iter)
417
iter([(rev_id, depth, revno, end_of_merge)]),
491
420
if stop_revision_id is None:
493
for node in rev_iter:
494
rev_id = node.key[-1]
495
yield (rev_id, node.merge_depth, node.revno,
421
for rev_id, depth, revno, end_of_merge in rev_iter:
422
yield rev_id, depth, revno, end_of_merge
497
423
elif stop_rule == 'exclude':
498
for node in rev_iter:
499
rev_id = node.key[-1]
424
for rev_id, depth, revno, end_of_merge in rev_iter:
500
425
if rev_id == stop_revision_id:
502
yield (rev_id, node.merge_depth, node.revno,
427
yield rev_id, depth, revno, end_of_merge
504
428
elif stop_rule == 'include':
505
for node in rev_iter:
506
rev_id = node.key[-1]
507
yield (rev_id, node.merge_depth, node.revno,
429
for rev_id, depth, revno, end_of_merge in rev_iter:
430
yield rev_id, depth, revno, end_of_merge
509
431
if rev_id == stop_revision_id:
511
elif stop_rule == 'with-merges-without-common-ancestry':
512
# We want to exclude all revisions that are already part of the
513
# stop_revision_id ancestry.
514
graph = self.repository.get_graph()
515
ancestors = graph.find_unique_ancestors(start_revision_id,
517
for node in rev_iter:
518
rev_id = node.key[-1]
519
if rev_id not in ancestors:
521
yield (rev_id, node.merge_depth, node.revno,
523
433
elif stop_rule == 'with-merges':
524
434
stop_rev = self.repository.get_revision(stop_revision_id)
525
435
if stop_rev.parent_ids:
526
436
left_parent = stop_rev.parent_ids[0]
528
438
left_parent = _mod_revision.NULL_REVISION
529
# left_parent is the actual revision we want to stop logging at,
530
# since we want to show the merged revisions after the stop_rev too
531
reached_stop_revision_id = False
532
revision_id_whitelist = []
533
for node in rev_iter:
534
rev_id = node.key[-1]
439
for rev_id, depth, revno, end_of_merge in rev_iter:
535
440
if rev_id == left_parent:
536
# reached the left parent after the stop_revision
538
if (not reached_stop_revision_id or
539
rev_id in revision_id_whitelist):
540
yield (rev_id, node.merge_depth, node.revno,
542
if reached_stop_revision_id or rev_id == stop_revision_id:
543
# only do the merged revs of rev_id from now on
544
rev = self.repository.get_revision(rev_id)
546
reached_stop_revision_id = True
547
revision_id_whitelist.extend(rev.parent_ids)
442
yield rev_id, depth, revno, end_of_merge
549
444
raise ValueError('invalid stop_rule %r' % stop_rule)
551
def _filter_start_non_ancestors(self, rev_iter):
552
# If we started from a dotted revno, we want to consider it as a tip
553
# and don't want to yield revisions that are not part of its
554
# ancestry. Given the order guaranteed by the merge sort, we will see
555
# uninteresting descendants of the first parent of our tip before the
557
first = rev_iter.next()
558
(rev_id, merge_depth, revno, end_of_merge) = first
561
# We start at a mainline revision so by definition, all others
562
# revisions in rev_iter are ancestors
563
for node in rev_iter:
568
pmap = self.repository.get_parent_map([rev_id])
569
parents = pmap.get(rev_id, [])
571
whitelist.update(parents)
573
# If there is no parents, there is nothing of interest left
575
# FIXME: It's hard to test this scenario here as this code is never
576
# called in that case. -- vila 20100322
579
for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
581
if rev_id in whitelist:
582
pmap = self.repository.get_parent_map([rev_id])
583
parents = pmap.get(rev_id, [])
584
whitelist.remove(rev_id)
585
whitelist.update(parents)
587
# We've reached the mainline, there is nothing left to
591
# A revision that is not part of the ancestry of our
594
yield (rev_id, merge_depth, revno, end_of_merge)
596
446
def leave_lock_in_place(self):
597
447
"""Tell this branch object not to release the physical lock when this
598
448
object is unlocked.
756
569
:raises UnstackableRepositoryFormat: If the repository does not support
759
if not self._format.supports_stacking():
760
raise errors.UnstackableBranchFormat(self._format, self.user_url)
761
# XXX: Changing from one fallback repository to another does not check
762
# that all the data you need is present in the new fallback.
763
# Possibly it should.
764
self._check_stackable_repo()
767
old_url = self.get_stacked_on_url()
768
except (errors.NotStacked, errors.UnstackableBranchFormat,
769
errors.UnstackableRepositoryFormat):
773
self._activate_fallback_location(url)
774
# write this out after the repository is stacked to avoid setting a
775
# stacked config that doesn't work.
776
self._set_config_location('stacked_on_location', url)
779
"""Change a branch to be unstacked, copying data as needed.
781
Don't call this directly, use set_stacked_on_url(None).
783
pb = ui.ui_factory.nested_progress_bar()
785
pb.update("Unstacking")
786
# The basic approach here is to fetch the tip of the branch,
787
# including all available ghosts, from the existing stacked
788
# repository into a new repository object without the fallbacks.
790
# XXX: See <https://launchpad.net/bugs/397286> - this may not be
791
# correct for CHKMap repostiories
792
old_repository = self.repository
793
if len(old_repository._fallback_repositories) != 1:
794
raise AssertionError("can't cope with fallback repositories "
795
"of %r" % (self.repository,))
796
# unlock it, including unlocking the fallback
797
old_repository.unlock()
798
old_repository.lock_read()
800
# Repositories don't offer an interface to remove fallback
801
# repositories today; take the conceptually simpler option and just
802
# reopen it. We reopen it starting from the URL so that we
803
# get a separate connection for RemoteRepositories and can
804
# stream from one of them to the other. This does mean doing
805
# separate SSH connection setup, but unstacking is not a
806
# common operation so it's tolerable.
807
new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
808
new_repository = new_bzrdir.find_repository()
809
self.repository = new_repository
810
if self.repository._fallback_repositories:
811
raise AssertionError("didn't expect %r to have "
812
"fallback_repositories"
813
% (self.repository,))
814
# this is not paired with an unlock because it's just restoring
815
# the previous state; the lock's released when set_stacked_on_url
817
self.repository.lock_write()
818
# XXX: If you unstack a branch while it has a working tree
819
# with a pending merge, the pending-merged revisions will no
820
# longer be present. You can (probably) revert and remerge.
822
# XXX: This only fetches up to the tip of the repository; it
823
# doesn't bring across any tags. That's fairly consistent
824
# with how branch works, but perhaps not ideal.
825
self.repository.fetch(old_repository,
826
revision_id=self.last_revision(),
829
old_repository.unlock()
572
raise NotImplementedError(self.set_stacked_on_url)
833
574
def _set_tags_bytes(self, bytes):
834
575
"""Mirror method for _get_tags_bytes.
1001
744
except ValueError:
1002
745
raise errors.NoSuchRevision(self, revision_id)
1005
747
def get_rev_id(self, revno, history=None):
1006
748
"""Find the revision id of the specified revno."""
1008
750
return _mod_revision.NULL_REVISION
1009
last_revno, last_revid = self.last_revision_info()
1010
if revno == last_revno:
1012
if revno <= 0 or revno > last_revno:
752
history = self.revision_history()
753
if revno <= 0 or revno > len(history):
1013
754
raise errors.NoSuchRevision(self, revno)
1014
distance_from_last = last_revno - revno
1015
if len(self._partial_revision_history_cache) <= distance_from_last:
1016
self._extend_partial_history(distance_from_last)
1017
return self._partial_revision_history_cache[distance_from_last]
755
return history[revno - 1]
1020
757
def pull(self, source, overwrite=False, stop_revision=None,
1021
possible_transports=None, *args, **kwargs):
758
possible_transports=None, _override_hook_target=None):
1022
759
"""Mirror source into this branch.
1024
761
This branch is considered to be 'local', having low latency.
1026
763
:returns: PullResult instance
1028
return InterBranch.get(source, self).pull(overwrite=overwrite,
1029
stop_revision=stop_revision,
1030
possible_transports=possible_transports, *args, **kwargs)
765
raise NotImplementedError(self.pull)
1032
def push(self, target, overwrite=False, stop_revision=None, *args,
767
def push(self, target, overwrite=False, stop_revision=None):
1034
768
"""Mirror this branch into target.
1036
770
This branch is considered to be 'local', having low latency.
1038
return InterBranch.get(self, target).push(overwrite, stop_revision,
1041
def lossy_push(self, target, stop_revision=None):
1042
"""Push deltas into another branch.
1044
:note: This does not, like push, retain the revision ids from
1045
the source branch and will, rather than adding bzr-specific
1046
metadata, push only those semantics of the revision that can be
1047
natively represented by this branch' VCS.
1049
:param target: Target branch
1050
:param stop_revision: Revision to push, defaults to last revision.
1051
:return: BranchPushResult with an extra member revidmap:
1052
A dictionary mapping revision ids from the target branch
1053
to new revision ids in the target branch, for each
1054
revision that was pushed.
1056
inter = InterBranch.get(self, target)
1057
lossy_push = getattr(inter, "lossy_push", None)
1058
if lossy_push is None:
1059
raise errors.LossyPushToSameVCS(self, target)
1060
return lossy_push(stop_revision)
772
raise NotImplementedError(self.push)
1062
774
def basis_tree(self):
1063
775
"""Return `Tree` object for last revision."""
1255
963
source_revno, source_revision_id = self.last_revision_info()
1256
964
if revision_id is None:
1257
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
1259
graph = self.repository.get_graph()
1261
revno = graph.find_distance_to_null(revision_id,
1262
[(source_revision_id, source_revno)])
1263
except errors.GhostRevisionsHaveNoRevno:
1264
# Default to 1, if we can't find anything else
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(
1266
978
destination.set_last_revision_info(revno, revision_id)
1268
980
@needs_read_lock
1312
1006
Callers will typically also want to check the repository.
1314
:param refs: Calculated refs for this branch as specified by
1315
branch._get_check_refs()
1316
1008
:return: A BranchCheckResult.
1318
result = BranchCheckResult(self)
1010
mainline_parent_id = None
1319
1011
last_revno, last_revision_id = self.last_revision_info()
1320
actual_revno = refs[('lefthand-distance', last_revision_id)]
1321
if actual_revno != last_revno:
1322
result.errors.append(errors.BzrCheckError(
1323
'revno does not match len(mainline) %s != %s' % (
1324
last_revno, actual_revno)))
1325
# TODO: We should probably also check that self.revision_history
1326
# matches the repository for older branch formats.
1327
# If looking for the code that cross-checks repository parents against
1328
# the iter_reverse_revision_history output, that is now a repository
1012
real_rev_history = list(self.repository.iter_reverse_revision_history(
1014
real_rev_history.reverse()
1015
if len(real_rev_history) != last_revno:
1016
raise errors.BzrCheckError('revno does not match len(mainline)'
1017
' %s != %s' % (last_revno, len(real_rev_history)))
1018
# TODO: We should probably also check that real_rev_history actually
1019
# matches self.revision_history()
1020
for revision_id in real_rev_history:
1022
revision = self.repository.get_revision(revision_id)
1023
except errors.NoSuchRevision, e:
1024
raise errors.BzrCheckError("mainline revision {%s} not in repository"
1026
# In general the first entry on the revision history has no parents.
1027
# But it's not illegal for it to have parents listed; this can happen
1028
# in imports from Arch when the parents weren't reachable.
1029
if mainline_parent_id is not None:
1030
if mainline_parent_id not in revision.parent_ids:
1031
raise errors.BzrCheckError("previous revision {%s} not listed among "
1033
% (mainline_parent_id, revision_id))
1034
mainline_parent_id = revision_id
1035
return BranchCheckResult(self)
1332
1037
def _get_checkout_format(self):
1333
1038
"""Return the most suitable metadir for a checkout of this branch.
1345
1050
def create_clone_on_transport(self, to_transport, revision_id=None,
1346
stacked_on=None, create_prefix=False, use_existing_dir=False):
1347
1052
"""Create a clone of this branch and its bzrdir.
1349
1054
:param to_transport: The transport to clone onto.
1350
1055
:param revision_id: The revision id to use as tip in the new branch.
1351
1056
If None the tip is obtained from this branch.
1352
1057
:param stacked_on: An optional URL to stack the clone on.
1353
:param create_prefix: Create any missing directories leading up to
1355
:param use_existing_dir: Use an existing directory if one exists.
1357
1059
# XXX: Fix the bzrdir API to allow getting the branch back from the
1358
1060
# clone call. Or something. 20090224 RBC/spiv.
1359
if revision_id is None:
1360
revision_id = self.last_revision()
1361
1061
dir_to = self.bzrdir.clone_on_transport(to_transport,
1362
revision_id=revision_id, stacked_on=stacked_on,
1363
create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1062
revision_id=revision_id, stacked_on=stacked_on)
1364
1063
return dir_to.open_branch()
1366
1065
def create_checkout(self, to_location, revision_id=None,
1421
1119
reconciler.reconcile()
1422
1120
return reconciler
1424
def reference_parent(self, file_id, path, possible_transports=None):
1122
def reference_parent(self, file_id, path):
1425
1123
"""Return the parent branch for a tree-reference file_id
1426
1124
:param file_id: The file_id of the tree reference
1427
1125
:param path: The path of the file_id in the tree
1428
1126
:return: A branch associated with the file_id
1430
1128
# FIXME should provide multiple branches, based on config
1431
return Branch.open(self.bzrdir.root_transport.clone(path).base,
1432
possible_transports=possible_transports)
1129
return Branch.open(self.bzrdir.root_transport.clone(path).base)
1434
1131
def supports_tags(self):
1435
1132
return self._format.supports_tags()
1437
def automatic_tag_name(self, revision_id):
1438
"""Try to automatically find the tag name for a revision.
1440
:param revision_id: Revision id of the revision.
1441
:return: A tag name or None if no tag name could be determined.
1443
for hook in Branch.hooks['automatic_tag_name']:
1444
ret = hook(self, revision_id)
1449
1134
def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1451
1136
"""Ensure that revision_b is a descendant of revision_a.
1564
1247
"""Return the short format description for this format."""
1565
1248
raise NotImplementedError(self.get_format_description)
1567
def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
1568
hooks = Branch.hooks['post_branch_init']
1571
params = BranchInitHookParams(self, a_bzrdir, name, branch)
1575
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1576
lock_type='metadir', set_format=True):
1250
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
1577
1252
"""Initialize a branch in a bzrdir, with specified files
1579
1254
:param a_bzrdir: The bzrdir to initialize the branch in
1580
1255
:param utf8_files: The files to create as a list of
1581
1256
(filename, content) tuples
1582
:param name: Name of colocated branch to create, if any
1583
1257
:param set_format: If True, set the format with
1584
1258
self.get_format_string. (BzrBranch4 has its format set
1586
1260
:return: a branch in this format
1588
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1589
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1262
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1263
branch_transport = a_bzrdir.get_branch_transport(self)
1591
1265
'metadir': ('lock', lockdir.LockDir),
1592
1266
'branch4': ('branch-lock', lockable_files.TransportLock),
1775
1428
"multiple hooks installed for transform_fallback_location, "
1776
1429
"all are called with the url returned from the previous hook."
1777
1430
"The order is however undefined.", (1, 9), None))
1778
self.create_hook(HookPoint('automatic_tag_name',
1779
"Called to determine an automatic tag name for a revision."
1780
"automatic_tag_name is called with (branch, revision_id) and "
1781
"should return a tag name or None if no tag name could be "
1782
"determined. The first non-None tag name returned will be used.",
1784
self.create_hook(HookPoint('post_branch_init',
1785
"Called after new branch initialization completes. "
1786
"post_branch_init is called with a "
1787
"bzrlib.branch.BranchInitHookParams. "
1788
"Note that init, branch and checkout (both heavyweight and "
1789
"lightweight) will all trigger this hook.", (2, 2), None))
1790
self.create_hook(HookPoint('post_switch',
1791
"Called after a checkout switches branch. "
1792
"post_switch is called with a "
1793
"bzrlib.branch.SwitchHookParams.", (2, 2), None))
1797
1433
# install the default hooks into the Branch class.
1836
1472
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1839
class BranchInitHookParams(object):
1840
"""Object holding parameters passed to *_branch_init hooks.
1842
There are 4 fields that hooks may wish to access:
1844
:ivar format: the branch format
1845
:ivar bzrdir: the BzrDir where the branch will be/has been initialized
1846
:ivar name: name of colocated branch, if any (or None)
1847
:ivar branch: the branch created
1849
Note that for lightweight checkouts, the bzrdir and format fields refer to
1850
the checkout, hence they are different from the corresponding fields in
1851
branch, which refer to the original branch.
1854
def __init__(self, format, a_bzrdir, name, branch):
1855
"""Create a group of BranchInitHook parameters.
1857
:param format: the branch format
1858
:param a_bzrdir: the BzrDir where the branch will be/has been
1860
:param name: name of colocated branch, if any (or None)
1861
:param branch: the branch created
1863
Note that for lightweight checkouts, the bzrdir and format fields refer
1864
to the checkout, hence they are different from the corresponding fields
1865
in branch, which refer to the original branch.
1867
self.format = format
1868
self.bzrdir = a_bzrdir
1870
self.branch = branch
1872
def __eq__(self, other):
1873
return self.__dict__ == other.__dict__
1877
return "<%s of %s>" % (self.__class__.__name__, self.branch)
1879
return "<%s of format:%s bzrdir:%s>" % (
1880
self.__class__.__name__, self.branch,
1881
self.format, self.bzrdir)
1884
class SwitchHookParams(object):
1885
"""Object holding parameters passed to *_switch hooks.
1887
There are 4 fields that hooks may wish to access:
1889
:ivar control_dir: BzrDir of the checkout to change
1890
:ivar to_branch: branch that the checkout is to reference
1891
:ivar force: skip the check for local commits in a heavy checkout
1892
:ivar revision_id: revision ID to switch to (or None)
1895
def __init__(self, control_dir, to_branch, force, revision_id):
1896
"""Create a group of SwitchHook parameters.
1898
:param control_dir: BzrDir of the checkout to change
1899
:param to_branch: branch that the checkout is to reference
1900
:param force: skip the check for local commits in a heavy checkout
1901
:param revision_id: revision ID to switch to (or None)
1903
self.control_dir = control_dir
1904
self.to_branch = to_branch
1906
self.revision_id = revision_id
1908
def __eq__(self, other):
1909
return self.__dict__ == other.__dict__
1912
return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
1913
self.control_dir, self.to_branch,
1917
1475
class BzrBranchFormat4(BranchFormat):
1918
1476
"""Bzr branch format 4.
1972
1533
return self.get_format_string()
1974
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1975
"""See BranchFormat.open()."""
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.
1977
format = BranchFormat.find_format(a_bzrdir, name=name)
1542
format = BranchFormat.find_format(a_bzrdir)
1978
1543
if format.__class__ != self.__class__:
1979
1544
raise AssertionError("wrong format %r found for %r" %
1980
1545
(format, self))
1981
transport = a_bzrdir.get_branch_transport(None, name=name)
1547
transport = a_bzrdir.get_branch_transport(None)
1983
1548
control_files = lockable_files.LockableFiles(transport, 'lock',
1984
1549
lockdir.LockDir)
1985
1550
return self._branch_class()(_format=self,
1986
1551
_control_files=control_files,
1988
1552
a_bzrdir=a_bzrdir,
1989
_repository=a_bzrdir.find_repository(),
1990
ignore_fallbacks=ignore_fallbacks)
1553
_repository=a_bzrdir.find_repository())
1991
1554
except errors.NoSuchFile:
1992
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1555
raise errors.NotBranchError(path=transport.base)
1994
1557
def __init__(self):
1995
1558
super(BranchFormatMetadir, self).__init__()
2057
1620
"""See BranchFormat.get_format_description()."""
2058
1621
return "Branch format 6"
2060
def initialize(self, a_bzrdir, name=None):
2061
"""Create a branch of this format in a_bzrdir."""
2062
utf8_files = [('last-revision', '0 null:\n'),
2063
('branch.conf', ''),
2066
return self._initialize_helper(a_bzrdir, utf8_files, name)
2068
def make_tags(self, branch):
2069
"""See bzrlib.branch.BranchFormat.make_tags()."""
2070
return BasicTags(branch)
2072
def supports_set_append_revisions_only(self):
2076
class BzrBranchFormat8(BranchFormatMetadir):
2077
"""Metadir format supporting storing locations of subtree branches."""
2079
def _branch_class(self):
2082
def get_format_string(self):
2083
"""See BranchFormat.get_format_string()."""
2084
return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2086
def get_format_description(self):
2087
"""See BranchFormat.get_format_description()."""
2088
return "Branch format 8"
2090
def initialize(self, a_bzrdir, name=None):
2091
"""Create a branch of this format in a_bzrdir."""
2092
utf8_files = [('last-revision', '0 null:\n'),
2093
('branch.conf', ''),
2097
return self._initialize_helper(a_bzrdir, utf8_files, name)
2100
super(BzrBranchFormat8, self).__init__()
2101
self._matchingbzrdir.repository_format = \
2102
RepositoryFormatKnitPack5RichRoot()
2104
def make_tags(self, branch):
2105
"""See bzrlib.branch.BranchFormat.make_tags()."""
2106
return BasicTags(branch)
2108
def supports_set_append_revisions_only(self):
2111
def supports_stacking(self):
2114
supports_reference_locations = True
2117
class BzrBranchFormat7(BzrBranchFormat8):
1623
def initialize(self, a_bzrdir):
1624
"""Create a branch of this format in a_bzrdir."""
1625
utf8_files = [('last-revision', '0 null:\n'),
1626
('branch.conf', ''),
1629
return self._initialize_helper(a_bzrdir, utf8_files)
1631
def make_tags(self, branch):
1632
"""See bzrlib.branch.BranchFormat.make_tags()."""
1633
return BasicTags(branch)
1637
class BzrBranchFormat7(BranchFormatMetadir):
2118
1638
"""Branch format with last-revision, tags, and a stacked location pointer.
2120
1640
The stacked location pointer is passed down to the repository and requires
2170
1697
def get_reference(self, a_bzrdir):
2171
1698
"""See BranchFormat.get_reference()."""
2172
1699
transport = a_bzrdir.get_branch_transport(None)
2173
return transport.get_bytes('location')
1700
return transport.get('location').read()
2175
1702
def set_reference(self, a_bzrdir, to_branch):
2176
1703
"""See BranchFormat.set_reference()."""
2177
1704
transport = a_bzrdir.get_branch_transport(None)
2178
1705
location = transport.put_bytes('location', to_branch.base)
2180
def initialize(self, a_bzrdir, name=None, target_branch=None):
1707
def initialize(self, a_bzrdir, target_branch=None):
2181
1708
"""Create a branch of this format in a_bzrdir."""
2182
1709
if target_branch is None:
2183
1710
# this format does not implement branch itself, thus the implicit
2184
1711
# creation contract must see it as uninitializable
2185
1712
raise errors.UninitializableFormat(self)
2186
mutter('creating branch reference in %s', a_bzrdir.user_url)
2187
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1713
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1714
branch_transport = a_bzrdir.get_branch_transport(self)
2188
1715
branch_transport.put_bytes('location',
2189
target_branch.bzrdir.user_url)
1716
target_branch.bzrdir.root_transport.base)
2190
1717
branch_transport.put_bytes('format', self.get_format_string())
2192
a_bzrdir, name, _found=True,
1719
a_bzrdir, _found=True,
2193
1720
possible_transports=[target_branch.bzrdir.root_transport])
2194
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2197
1722
def __init__(self):
2198
1723
super(BranchReferenceFormat, self).__init__()
2204
1729
def clone(to_bzrdir, revision_id=None,
2205
1730
repository_policy=None):
2206
1731
"""See Branch.clone()."""
2207
return format.initialize(to_bzrdir, target_branch=a_branch)
1732
return format.initialize(to_bzrdir, a_branch)
2208
1733
# cannot obey revision_id limits when cloning a reference ...
2209
1734
# FIXME RBC 20060210 either nuke revision_id for clone, or
2210
1735
# emit some sort of warning/error to the caller ?!
2213
def open(self, a_bzrdir, name=None, _found=False, location=None,
2214
possible_transports=None, ignore_fallbacks=False):
1738
def open(self, a_bzrdir, _found=False, location=None,
1739
possible_transports=None):
2215
1740
"""Return the branch that the branch reference in a_bzrdir points at.
2217
:param a_bzrdir: A BzrDir that contains a branch.
2218
:param name: Name of colocated branch to open, if any
2219
:param _found: a private parameter, do not use it. It is used to
2220
indicate if format probing has already be done.
2221
:param ignore_fallbacks: when set, no fallback branches will be opened
2222
(if there are any). Default is to open fallbacks.
2223
:param location: The location of the referenced branch. If
2224
unspecified, this will be determined from the branch reference in
2226
:param possible_transports: An optional reusable transports list.
1742
_found is a private parameter, do not use it. It is used to indicate
1743
if format probing has already be done.
2229
format = BranchFormat.find_format(a_bzrdir, name=name)
1746
format = BranchFormat.find_format(a_bzrdir)
2230
1747
if format.__class__ != self.__class__:
2231
1748
raise AssertionError("wrong format %r found for %r" %
2232
1749
(format, self))
2262
1778
__format5 = BzrBranchFormat5()
2263
1779
__format6 = BzrBranchFormat6()
2264
1780
__format7 = BzrBranchFormat7()
2265
__format8 = BzrBranchFormat8()
2266
1781
BranchFormat.register_format(__format5)
2267
1782
BranchFormat.register_format(BranchReferenceFormat())
2268
1783
BranchFormat.register_format(__format6)
2269
1784
BranchFormat.register_format(__format7)
2270
BranchFormat.register_format(__format8)
2271
BranchFormat.set_default_format(__format7)
1785
BranchFormat.set_default_format(__format6)
2272
1786
_legacy_formats = [BzrBranchFormat4(),
2274
1788
network_format_registry.register(
2275
1789
_legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2278
class BranchWriteLockResult(LogicalLockResult):
2279
"""The result of write locking a branch.
2281
:ivar branch_token: The token obtained from the underlying branch lock, or
2283
:ivar unlock: A callable which will unlock the lock.
2286
def __init__(self, unlock, branch_token):
2287
LogicalLockResult.__init__(self, unlock)
2288
self.branch_token = branch_token
2291
return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
2295
class BzrBranch(Branch, _RelockDebugMixin):
1792
class BzrBranch(Branch):
2296
1793
"""A branch stored in the actual filesystem.
2298
1795
Note that it's "local" in the context of the filesystem; it doesn't
2345
1834
base = property(_get_base, doc="The URL for the root of this branch.")
2347
def _get_config(self):
2348
return TransportConfig(self._transport, 'branch.conf')
2350
1836
def is_locked(self):
2351
1837
return self.control_files.is_locked()
2353
1839
def lock_write(self, token=None):
2354
"""Lock the branch for write operations.
2356
:param token: A token to permit reacquiring a previously held and
2358
:return: A BranchWriteLockResult.
2360
if not self.is_locked():
2361
self._note_lock('w')
2362
# All-in-one needs to always unlock/lock.
2363
repo_control = getattr(self.repository, 'control_files', None)
2364
if self.control_files == repo_control or not self.is_locked():
2365
self.repository._warn_if_deprecated(self)
2366
self.repository.lock_write()
1840
repo_token = self.repository.lock_write()
2371
return BranchWriteLockResult(self.unlock,
2372
self.control_files.lock_write(token=token))
1842
token = self.control_files.lock_write(token=token)
2375
self.repository.unlock()
1844
self.repository.unlock()
2378
1848
def lock_read(self):
2379
"""Lock the branch for read operations.
2381
:return: A bzrlib.lock.LogicalLockResult.
2383
if not self.is_locked():
2384
self._note_lock('r')
2385
# All-in-one needs to always unlock/lock.
2386
repo_control = getattr(self.repository, 'control_files', None)
2387
if self.control_files == repo_control or not self.is_locked():
2388
self.repository._warn_if_deprecated(self)
2389
self.repository.lock_read()
1849
self.repository.lock_read()
2394
1851
self.control_files.lock_read()
2395
return LogicalLockResult(self.unlock)
2398
self.repository.unlock()
1853
self.repository.unlock()
2401
@only_raises(errors.LockNotHeld, errors.LockBroken)
2402
1856
def unlock(self):
1857
# TODO: test for failed two phase locks. This is known broken.
2404
1859
self.control_files.unlock()
2406
# All-in-one needs to always unlock/lock.
2407
repo_control = getattr(self.repository, 'control_files', None)
2408
if (self.control_files == repo_control or
2409
not self.control_files.is_locked()):
2410
self.repository.unlock()
2411
if not self.control_files.is_locked():
2412
# we just released the lock
2413
self._clear_cached_state()
1861
self.repository.unlock()
1862
if not self.control_files.is_locked():
1863
# we just released the lock
1864
self._clear_cached_state()
2415
1866
def peek_lock_mode(self):
2416
1867
if self.control_files._lock_count == 0:
2535
1986
"""See Branch.basis_tree."""
2536
1987
return self.repository.revision_tree(self.last_revision())
1990
def pull(self, source, overwrite=False, stop_revision=None,
1991
_hook_master=None, run_hooks=True, possible_transports=None,
1992
_override_hook_target=None):
1995
:param _hook_master: Private parameter - set the branch to
1996
be supplied as the master to pull hooks.
1997
:param run_hooks: Private parameter - if false, this branch
1998
is being called because it's the master of the primary branch,
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.
2003
result = PullResult()
2004
result.source_branch = source
2005
if _override_hook_target is None:
2006
result.target_branch = self
2008
result.target_branch = _override_hook_target
2011
# We assume that during 'pull' the local repository is closer than
2013
graph = self.repository.get_graph(source.repository)
2014
result.old_revno, result.old_revid = self.last_revision_info()
2015
self.update_revisions(source, stop_revision, overwrite=overwrite,
2017
result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
2018
result.new_revno, result.new_revid = self.last_revision_info()
2020
result.master_branch = _hook_master
2021
result.local_branch = result.target_branch
2023
result.master_branch = result.target_branch
2024
result.local_branch = None
2026
for hook in Branch.hooks['post_pull']:
2538
2032
def _get_parent_location(self):
2539
2033
_locs = ['parent', 'pull', 'x-pull']
2540
2034
for l in _locs:
2042
def push(self, target, overwrite=False, stop_revision=None,
2043
_override_hook_source_branch=None):
2046
This is the basic concrete implementation of push()
2048
:param _override_hook_source_branch: If specified, run
2049
the hooks passing this Branch as the source, rather than self.
2050
This is for use of RemoteBranch, where push is delegated to the
2051
underlying vfs-based Branch.
2053
# TODO: Public option to disable running hooks - should be trivial but
2055
return _run_with_write_locked_target(
2056
target, self._push_with_bound_branches, target, overwrite,
2058
_override_hook_source_branch=_override_hook_source_branch)
2060
def _push_with_bound_branches(self, target, overwrite,
2062
_override_hook_source_branch=None):
2063
"""Push from self into target, and into target's master if any.
2065
This is on the base BzrBranch class even though it doesn't support
2066
bound branches because the *target* might be bound.
2069
if _override_hook_source_branch:
2070
result.source_branch = _override_hook_source_branch
2071
for hook in Branch.hooks['post_push']:
2074
bound_location = target.get_bound_location()
2075
if bound_location and target.base != bound_location:
2076
# there is a master branch.
2078
# XXX: Why the second check? Is it even supported for a branch to
2079
# be bound to itself? -- mbp 20070507
2080
master_branch = target.get_master_branch()
2081
master_branch.lock_write()
2083
# push into the master from this branch.
2084
self._basic_push(master_branch, overwrite, stop_revision)
2085
# and push into the target branch from this. Note that we push from
2086
# this branch again, because its considered the highest bandwidth
2088
result = self._basic_push(target, overwrite, stop_revision)
2089
result.master_branch = master_branch
2090
result.local_branch = target
2094
master_branch.unlock()
2097
result = self._basic_push(target, overwrite, stop_revision)
2098
# TODO: Why set master_branch and local_branch if there's no
2099
# binding? Maybe cleaner to just leave them unset? -- mbp
2101
result.master_branch = target
2102
result.local_branch = None
2547
2106
def _basic_push(self, target, overwrite, stop_revision):
2548
2107
"""Basic implementation of push without bound branches or hooks.
2550
Must be called with source read locked and target write locked.
2109
Must be called with self read locked and target write locked.
2552
2111
result = BranchPushResult()
2553
2112
result.source_branch = self
2554
2113
result.target_branch = target
2555
2114
result.old_revno, result.old_revid = target.last_revision_info()
2556
self.update_references(target)
2557
2115
if result.old_revid != self.last_revision():
2558
2116
# We assume that during 'push' this repository is closer than
2560
2118
graph = self.repository.get_graph(target.repository)
2561
target.update_revisions(self, stop_revision,
2562
overwrite=overwrite, graph=graph)
2119
target.update_revisions(self, stop_revision, overwrite=overwrite,
2563
2121
if self._push_should_merge_tags():
2564
result.tag_conflicts = self.tags.merge_to(target.tags,
2122
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
2566
2123
result.new_revno, result.new_revid = target.last_revision_info()
2569
2126
def get_stacked_on_url(self):
2570
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2127
raise errors.UnstackableBranchFormat(self._format, self.base)
2572
2129
def set_push_location(self, location):
2573
2130
"""See Branch.set_push_location."""
2589
2167
It has support for a master_branch which is the data for bound branches.
2171
def pull(self, source, overwrite=False, stop_revision=None,
2172
run_hooks=True, possible_transports=None,
2173
_override_hook_target=None):
2174
"""Pull from source into self, updating my master if any.
2176
:param run_hooks: Private parameter - if false, this branch
2177
is being called because it's the master of the primary branch,
2178
so it should not run its hooks.
2180
bound_location = self.get_bound_location()
2181
master_branch = None
2182
if bound_location and source.base != bound_location:
2183
# not pulling from master, so we need to update master.
2184
master_branch = self.get_master_branch(possible_transports)
2185
master_branch.lock_write()
2188
# pull from source into master.
2189
master_branch.pull(source, overwrite, stop_revision,
2191
return super(BzrBranch5, self).pull(source, overwrite,
2192
stop_revision, _hook_master=master_branch,
2193
run_hooks=run_hooks,
2194
_override_hook_target=_override_hook_target)
2197
master_branch.unlock()
2592
2199
def get_bound_location(self):
2594
2201
return self._transport.get_bytes('bound')[:-1]
2702
2319
"None, not a URL." % hook_name)
2703
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)
2705
2327
def __init__(self, *args, **kwargs):
2706
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2707
super(BzrBranch8, self).__init__(*args, **kwargs)
2328
super(BzrBranch7, self).__init__(*args, **kwargs)
2708
2329
self._last_revision_info_cache = None
2709
self._reference_info = None
2330
self._partial_revision_history_cache = []
2711
2332
def _clear_cached_state(self):
2712
super(BzrBranch8, self)._clear_cached_state()
2333
super(BzrBranch7, self)._clear_cached_state()
2713
2334
self._last_revision_info_cache = None
2714
self._reference_info = None
2335
self._partial_revision_history_cache = []
2716
2337
def _last_revision_info(self):
2717
2338
revision_string = self._transport.get_bytes('last-revision')
2772
2393
self._extend_partial_history(stop_index=last_revno-1)
2773
2394
return list(reversed(self._partial_revision_history_cache))
2396
def _extend_partial_history(self, stop_index=None, stop_revision=None):
2397
"""Extend the partial history to include a given index
2399
If a stop_index is supplied, stop when that index has been reached.
2400
If a stop_revision is supplied, stop when that revision is
2401
encountered. Otherwise, stop when the beginning of history is
2404
:param stop_index: The index which should be present. When it is
2405
present, history extension will stop.
2406
:param revision_id: The revision id which should be present. When
2407
it is encountered, history extension will stop.
2409
repo = self.repository
2410
if len(self._partial_revision_history_cache) == 0:
2411
iterator = repo.iter_reverse_revision_history(self.last_revision())
2413
start_revision = self._partial_revision_history_cache[-1]
2414
iterator = repo.iter_reverse_revision_history(start_revision)
2415
#skip the last revision in the list
2416
next_revision = iterator.next()
2417
for revision_id in iterator:
2418
self._partial_revision_history_cache.append(revision_id)
2419
if (stop_index is not None and
2420
len(self._partial_revision_history_cache) > stop_index):
2422
if revision_id == stop_revision:
2775
2425
def _write_revision_history(self, history):
2776
2426
"""Factored out of set_revision_history.
2798
2448
"""Set the parent branch"""
2799
2449
return self._get_config_location('parent_location')
2802
def _set_all_reference_info(self, info_dict):
2803
"""Replace all reference info stored in a branch.
2805
:param info_dict: A dict of {file_id: (tree_path, branch_location)}
2808
writer = rio.RioWriter(s)
2809
for key, (tree_path, branch_location) in info_dict.iteritems():
2810
stanza = rio.Stanza(file_id=key, tree_path=tree_path,
2811
branch_location=branch_location)
2812
writer.write_stanza(stanza)
2813
self._transport.put_bytes('references', s.getvalue())
2814
self._reference_info = info_dict
2817
def _get_all_reference_info(self):
2818
"""Return all the reference info stored in a branch.
2820
:return: A dict of {file_id: (tree_path, branch_location)}
2822
if self._reference_info is not None:
2823
return self._reference_info
2824
rio_file = self._transport.get('references')
2826
stanzas = rio.read_stanzas(rio_file)
2827
info_dict = dict((s['file_id'], (s['tree_path'],
2828
s['branch_location'])) for s in stanzas)
2831
self._reference_info = info_dict
2834
def set_reference_info(self, file_id, tree_path, branch_location):
2835
"""Set the branch location to use for a tree reference.
2837
:param file_id: The file-id of the tree reference.
2838
:param tree_path: The path of the tree reference in the tree.
2839
:param branch_location: The location of the branch to retrieve tree
2842
info_dict = self._get_all_reference_info()
2843
info_dict[file_id] = (tree_path, branch_location)
2844
if None in (tree_path, branch_location):
2845
if tree_path is not None:
2846
raise ValueError('tree_path must be None when branch_location'
2848
if branch_location is not None:
2849
raise ValueError('branch_location must be None when tree_path'
2851
del info_dict[file_id]
2852
self._set_all_reference_info(info_dict)
2854
def get_reference_info(self, file_id):
2855
"""Get the tree_path and branch_location for a tree reference.
2857
:return: a tuple of (tree_path, branch_location)
2859
return self._get_all_reference_info().get(file_id, (None, None))
2861
def reference_parent(self, file_id, path, possible_transports=None):
2862
"""Return the parent branch for a tree-reference file_id.
2864
:param file_id: The file_id of the tree reference
2865
:param path: The path of the file_id in the tree
2866
:return: A branch associated with the file_id
2868
branch_location = self.get_reference_info(file_id)[1]
2869
if branch_location is None:
2870
return Branch.reference_parent(self, file_id, path,
2871
possible_transports)
2872
branch_location = urlutils.join(self.user_url, branch_location)
2873
return Branch.open(branch_location,
2874
possible_transports=possible_transports)
2876
2451
def set_push_location(self, location):
2877
2452
"""See Branch.set_push_location."""
2878
2453
self._set_config_location('push_location', location)
2920
2495
raise errors.NotStacked(self)
2921
2496
return stacked_url
2498
def set_append_revisions_only(self, enabled):
2503
self.get_config().set_user_option('append_revisions_only', value,
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)
2923
2532
def _get_append_revisions_only(self):
2924
return self.get_config(
2925
).get_user_option_as_bool('append_revisions_only')
2533
value = self.get_config().get_user_option('append_revisions_only')
2534
return value == 'True'
2927
2536
@needs_write_lock
2928
2537
def generate_revision_history(self, revision_id, last_rev=None,
3273
2842
self.source.unlock()
3275
def pull(self, overwrite=False, stop_revision=None,
3276
possible_transports=None, _hook_master=None, run_hooks=True,
3277
_override_hook_target=None, local=False):
3280
:param _hook_master: Private parameter - set the branch to
3281
be supplied as the master to pull hooks.
3282
:param run_hooks: Private parameter - if false, this branch
3283
is being called because it's the master of the primary branch,
3284
so it should not run its hooks.
3285
:param _override_hook_target: Private parameter - set the branch to be
3286
supplied as the target_branch to pull hooks.
3287
:param local: Only update the local branch, and not the bound branch.
3289
# This type of branch can't be bound.
3291
raise errors.LocalRequiresBoundBranch()
3292
result = PullResult()
3293
result.source_branch = self.source
3294
if _override_hook_target is None:
3295
result.target_branch = self.target
3297
result.target_branch = _override_hook_target
3298
self.source.lock_read()
3300
# We assume that during 'pull' the target repository is closer than
3302
self.source.update_references(self.target)
3303
graph = self.target.repository.get_graph(self.source.repository)
3304
# TODO: Branch formats should have a flag that indicates
3305
# that revno's are expensive, and pull() should honor that flag.
3307
result.old_revno, result.old_revid = \
3308
self.target.last_revision_info()
3309
self.target.update_revisions(self.source, stop_revision,
3310
overwrite=overwrite, graph=graph)
3311
# TODO: The old revid should be specified when merging tags,
3312
# so a tags implementation that versions tags can only
3313
# pull in the most recent changes. -- JRV20090506
3314
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3316
result.new_revno, result.new_revid = self.target.last_revision_info()
3318
result.master_branch = _hook_master
3319
result.local_branch = result.target_branch
3321
result.master_branch = result.target_branch
3322
result.local_branch = None
3324
for hook in Branch.hooks['post_pull']:
3327
self.source.unlock()
3330
def push(self, overwrite=False, stop_revision=None,
3331
_override_hook_source_branch=None):
3332
"""See InterBranch.push.
3334
This is the basic concrete implementation of push()
3336
:param _override_hook_source_branch: If specified, run
3337
the hooks passing this Branch as the source, rather than self.
3338
This is for use of RemoteBranch, where push is delegated to the
3339
underlying vfs-based Branch.
3341
# TODO: Public option to disable running hooks - should be trivial but
3343
self.source.lock_read()
3345
return _run_with_write_locked_target(
3346
self.target, self._push_with_bound_branches, overwrite,
3348
_override_hook_source_branch=_override_hook_source_branch)
3350
self.source.unlock()
3352
def _push_with_bound_branches(self, overwrite, stop_revision,
3353
_override_hook_source_branch=None):
3354
"""Push from source into target, and into target's master if any.
3357
if _override_hook_source_branch:
3358
result.source_branch = _override_hook_source_branch
3359
for hook in Branch.hooks['post_push']:
3362
bound_location = self.target.get_bound_location()
3363
if bound_location and self.target.base != bound_location:
3364
# there is a master branch.
3366
# XXX: Why the second check? Is it even supported for a branch to
3367
# be bound to itself? -- mbp 20070507
3368
master_branch = self.target.get_master_branch()
3369
master_branch.lock_write()
3371
# push into the master from the source branch.
3372
self.source._basic_push(master_branch, overwrite, stop_revision)
3373
# and push into the target branch from the source. Note that we
3374
# push from the source branch again, because its considered the
3375
# highest bandwidth repository.
3376
result = self.source._basic_push(self.target, overwrite,
3378
result.master_branch = master_branch
3379
result.local_branch = self.target
3383
master_branch.unlock()
3386
result = self.source._basic_push(self.target, overwrite,
3388
# TODO: Why set master_branch and local_branch if there's no
3389
# binding? Maybe cleaner to just leave them unset? -- mbp
3391
result.master_branch = self.target
3392
result.local_branch = None
3397
2845
def is_compatible(self, source, target):
3398
2846
# GenericBranch uses the public API, so always compatible
3402
class InterToBranch5(GenericInterBranch):
3405
def _get_branch_formats_to_test():
3406
return BranchFormat._default_format, BzrBranchFormat5()
3408
def pull(self, overwrite=False, stop_revision=None,
3409
possible_transports=None, run_hooks=True,
3410
_override_hook_target=None, local=False):
3411
"""Pull from source into self, updating my master if any.
3413
:param run_hooks: Private parameter - if false, this branch
3414
is being called because it's the master of the primary branch,
3415
so it should not run its hooks.
3417
bound_location = self.target.get_bound_location()
3418
if local and not bound_location:
3419
raise errors.LocalRequiresBoundBranch()
3420
master_branch = None
3421
if not local and bound_location and self.source.user_url != bound_location:
3422
# not pulling from master, so we need to update master.
3423
master_branch = self.target.get_master_branch(possible_transports)
3424
master_branch.lock_write()
3427
# pull from source into master.
3428
master_branch.pull(self.source, overwrite, stop_revision,
3430
return super(InterToBranch5, self).pull(overwrite,
3431
stop_revision, _hook_master=master_branch,
3432
run_hooks=run_hooks,
3433
_override_hook_target=_override_hook_target)
3436
master_branch.unlock()
3439
2850
InterBranch.register_optimiser(GenericInterBranch)
3440
InterBranch.register_optimiser(InterToBranch5)