515
462
"""Iterate over an inclusive range of sorted revisions."""
516
463
rev_iter = iter(merge_sorted_revisions)
517
464
if start_revision_id is not None:
518
for node in rev_iter:
465
for rev_id, depth, revno, end_of_merge in rev_iter:
520
466
if rev_id != start_revision_id:
523
469
# The decision to include the start or not
524
470
# depends on the stop_rule if a stop is provided
525
# so pop this node back into the iterator
526
rev_iter = itertools.chain(iter([node]), rev_iter)
472
iter([(rev_id, depth, revno, end_of_merge)]),
528
475
if stop_revision_id is None:
530
for node in rev_iter:
532
yield (rev_id, node.merge_depth, node.revno,
476
for rev_id, depth, revno, end_of_merge in rev_iter:
477
yield rev_id, depth, revno, end_of_merge
534
478
elif stop_rule == 'exclude':
535
for node in rev_iter:
479
for rev_id, depth, revno, end_of_merge in rev_iter:
537
480
if rev_id == stop_revision_id:
539
yield (rev_id, node.merge_depth, node.revno,
482
yield rev_id, depth, revno, end_of_merge
541
483
elif stop_rule == 'include':
542
for node in rev_iter:
544
yield (rev_id, node.merge_depth, node.revno,
484
for rev_id, depth, revno, end_of_merge in rev_iter:
485
yield rev_id, depth, revno, end_of_merge
546
486
if rev_id == stop_revision_id:
548
elif stop_rule == 'with-merges-without-common-ancestry':
549
# We want to exclude all revisions that are already part of the
550
# stop_revision_id ancestry.
551
graph = self.repository.get_graph()
552
ancestors = graph.find_unique_ancestors(start_revision_id,
554
for node in rev_iter:
556
if rev_id not in ancestors:
558
yield (rev_id, node.merge_depth, node.revno,
560
488
elif stop_rule == 'with-merges':
561
489
stop_rev = self.repository.get_revision(stop_revision_id)
562
490
if stop_rev.parent_ids:
563
491
left_parent = stop_rev.parent_ids[0]
565
493
left_parent = _mod_revision.NULL_REVISION
566
# left_parent is the actual revision we want to stop logging at,
567
# since we want to show the merged revisions after the stop_rev too
568
reached_stop_revision_id = False
569
revision_id_whitelist = []
570
for node in rev_iter:
494
for rev_id, depth, revno, end_of_merge in rev_iter:
572
495
if rev_id == left_parent:
573
# reached the left parent after the stop_revision
575
if (not reached_stop_revision_id or
576
rev_id in revision_id_whitelist):
577
yield (rev_id, node.merge_depth, node.revno,
579
if reached_stop_revision_id or rev_id == stop_revision_id:
580
# only do the merged revs of rev_id from now on
581
rev = self.repository.get_revision(rev_id)
583
reached_stop_revision_id = True
584
revision_id_whitelist.extend(rev.parent_ids)
497
yield rev_id, depth, revno, end_of_merge
586
499
raise ValueError('invalid stop_rule %r' % stop_rule)
588
def _filter_start_non_ancestors(self, rev_iter):
589
# If we started from a dotted revno, we want to consider it as a tip
590
# and don't want to yield revisions that are not part of its
591
# ancestry. Given the order guaranteed by the merge sort, we will see
592
# uninteresting descendants of the first parent of our tip before the
594
first = rev_iter.next()
595
(rev_id, merge_depth, revno, end_of_merge) = first
598
# We start at a mainline revision so by definition, all others
599
# revisions in rev_iter are ancestors
600
for node in rev_iter:
605
pmap = self.repository.get_parent_map([rev_id])
606
parents = pmap.get(rev_id, [])
608
whitelist.update(parents)
610
# If there is no parents, there is nothing of interest left
612
# FIXME: It's hard to test this scenario here as this code is never
613
# called in that case. -- vila 20100322
616
for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
618
if rev_id in whitelist:
619
pmap = self.repository.get_parent_map([rev_id])
620
parents = pmap.get(rev_id, [])
621
whitelist.remove(rev_id)
622
whitelist.update(parents)
624
# We've reached the mainline, there is nothing left to
628
# A revision that is not part of the ancestry of our
631
yield (rev_id, merge_depth, revno, end_of_merge)
633
501
def leave_lock_in_place(self):
634
502
"""Tell this branch object not to release the physical lock when this
635
503
object is unlocked.
873
697
old_repository = self.repository
874
698
if len(old_repository._fallback_repositories) != 1:
875
699
raise AssertionError("can't cope with fallback repositories "
876
"of %r (fallbacks: %r)" % (old_repository,
877
old_repository._fallback_repositories))
878
# Open the new repository object.
879
# Repositories don't offer an interface to remove fallback
880
# repositories today; take the conceptually simpler option and just
881
# reopen it. We reopen it starting from the URL so that we
882
# get a separate connection for RemoteRepositories and can
883
# stream from one of them to the other. This does mean doing
884
# separate SSH connection setup, but unstacking is not a
885
# common operation so it's tolerable.
886
new_bzrdir = controldir.ControlDir.open(
887
self.bzrdir.root_transport.base)
888
new_repository = new_bzrdir.find_repository()
889
if new_repository._fallback_repositories:
890
raise AssertionError("didn't expect %r to have "
891
"fallback_repositories"
892
% (self.repository,))
893
# Replace self.repository with the new repository.
894
# Do our best to transfer the lock state (i.e. lock-tokens and
895
# lock count) of self.repository to the new repository.
896
lock_token = old_repository.lock_write().repository_token
897
self.repository = new_repository
898
if isinstance(self, remote.RemoteBranch):
899
# Remote branches can have a second reference to the old
900
# repository that need to be replaced.
901
if self._real_branch is not None:
902
self._real_branch.repository = new_repository
903
self.repository.lock_write(token=lock_token)
904
if lock_token is not None:
905
old_repository.leave_lock_in_place()
700
"of %r" % (self.repository,))
701
# unlock it, including unlocking the fallback
906
702
old_repository.unlock()
907
if lock_token is not None:
908
# XXX: self.repository.leave_lock_in_place() before this
909
# function will not be preserved. Fortunately that doesn't
910
# affect the current default format (2a), and would be a
911
# corner-case anyway.
912
# - Andrew Bennetts, 2010/06/30
913
self.repository.dont_leave_lock_in_place()
917
old_repository.unlock()
918
except errors.LockNotHeld:
921
if old_lock_count == 0:
922
raise AssertionError(
923
'old_repository should have been locked at least once.')
924
for i in range(old_lock_count-1):
703
old_repository.lock_read()
705
# Repositories don't offer an interface to remove fallback
706
# repositories today; take the conceptually simpler option and just
707
# reopen it. We reopen it starting from the URL so that we
708
# get a separate connection for RemoteRepositories and can
709
# stream from one of them to the other. This does mean doing
710
# separate SSH connection setup, but unstacking is not a
711
# common operation so it's tolerable.
712
new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
713
new_repository = new_bzrdir.find_repository()
714
self.repository = new_repository
715
if self.repository._fallback_repositories:
716
raise AssertionError("didn't expect %r to have "
717
"fallback_repositories"
718
% (self.repository,))
719
# this is not paired with an unlock because it's just restoring
720
# the previous state; the lock's released when set_stacked_on_url
925
722
self.repository.lock_write()
926
# Fetch from the old repository into the new.
927
old_repository.lock_read()
929
723
# XXX: If you unstack a branch while it has a working tree
930
724
# with a pending merge, the pending-merged revisions will no
931
725
# longer be present. You can (probably) revert and remerge.
933
tags_to_fetch = set(self.tags.get_reverse_tag_dict())
934
except errors.TagsNotSupported:
935
tags_to_fetch = set()
936
fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
937
old_repository, required_ids=[self.last_revision()],
938
if_present_ids=tags_to_fetch, find_ghosts=True).execute()
939
self.repository.fetch(old_repository, fetch_spec=fetch_spec)
727
# XXX: This only fetches up to the tip of the repository; it
728
# doesn't bring across any tags. That's fairly consistent
729
# with how branch works, but perhaps not ideal.
730
self.repository.fetch(old_repository,
731
revision_id=self.last_revision(),
941
734
old_repository.unlock()
1050
832
:return: A tuple (revno, revision_id).
1052
834
if self._last_revision_info_cache is None:
1053
self._last_revision_info_cache = self._read_last_revision_info()
835
self._last_revision_info_cache = self._last_revision_info()
1054
836
return self._last_revision_info_cache
1056
def _read_last_revision_info(self):
1057
raise NotImplementedError(self._read_last_revision_info)
1059
@deprecated_method(deprecated_in((2, 4, 0)))
838
def _last_revision_info(self):
839
rh = self.revision_history()
842
return (revno, rh[-1])
844
return (0, _mod_revision.NULL_REVISION)
846
@deprecated_method(deprecated_in((1, 6, 0)))
847
def missing_revisions(self, other, stop_revision=None):
848
"""Return a list of new revisions that would perfectly fit.
850
If self and other have not diverged, return a list of the revisions
851
present in other, but missing from self.
853
self_history = self.revision_history()
854
self_len = len(self_history)
855
other_history = other.revision_history()
856
other_len = len(other_history)
857
common_index = min(self_len, other_len) -1
858
if common_index >= 0 and \
859
self_history[common_index] != other_history[common_index]:
860
raise errors.DivergedBranches(self, other)
862
if stop_revision is None:
863
stop_revision = other_len
865
if stop_revision > other_len:
866
raise errors.NoSuchRevision(self, stop_revision)
867
return other_history[self_len:stop_revision]
870
def update_revisions(self, other, stop_revision=None, overwrite=False,
872
"""Pull in new perfect-fit revisions.
874
:param other: Another Branch to pull from
875
:param stop_revision: Updated until the given revision
876
:param overwrite: Always set the branch pointer, rather than checking
877
to see if it is a proper descendant.
878
:param graph: A Graph object that can be used to query history
879
information. This can be None.
882
return InterBranch.get(other, self).update_revisions(stop_revision,
1060
885
def import_last_revision_info(self, source_repo, revno, revid):
1061
886
"""Set the last revision info, importing from another repo if necessary.
888
This is used by the bound branch code to upload a revision to
889
the master branch first before updating the tip of the local branch.
1063
891
:param source_repo: Source repository to optionally fetch from
1064
892
:param revno: Revision number of the new tip
1065
893
:param revid: Revision id of the new tip
1383
1222
Callers will typically also want to check the repository.
1385
:param refs: Calculated refs for this branch as specified by
1386
branch._get_check_refs()
1387
1224
:return: A BranchCheckResult.
1389
result = BranchCheckResult(self)
1226
ret = BranchCheckResult(self)
1227
mainline_parent_id = None
1390
1228
last_revno, last_revision_id = self.last_revision_info()
1391
actual_revno = refs[('lefthand-distance', last_revision_id)]
1392
if actual_revno != last_revno:
1393
result.errors.append(errors.BzrCheckError(
1394
'revno does not match len(mainline) %s != %s' % (
1395
last_revno, actual_revno)))
1396
# TODO: We should probably also check that self.revision_history
1397
# matches the repository for older branch formats.
1398
# If looking for the code that cross-checks repository parents against
1399
# the Graph.iter_lefthand_ancestry output, that is now a repository
1229
real_rev_history = []
1231
for revid in self.repository.iter_reverse_revision_history(
1233
real_rev_history.append(revid)
1234
except errors.RevisionNotPresent:
1235
ret.ghosts_in_mainline = True
1237
ret.ghosts_in_mainline = False
1238
real_rev_history.reverse()
1239
if len(real_rev_history) != last_revno:
1240
raise errors.BzrCheckError('revno does not match len(mainline)'
1241
' %s != %s' % (last_revno, len(real_rev_history)))
1242
# TODO: We should probably also check that real_rev_history actually
1243
# matches self.revision_history()
1244
for revision_id in real_rev_history:
1246
revision = self.repository.get_revision(revision_id)
1247
except errors.NoSuchRevision, e:
1248
raise errors.BzrCheckError("mainline revision {%s} not in repository"
1250
# In general the first entry on the revision history has no parents.
1251
# But it's not illegal for it to have parents listed; this can happen
1252
# in imports from Arch when the parents weren't reachable.
1253
if mainline_parent_id is not None:
1254
if mainline_parent_id not in revision.parent_ids:
1255
raise errors.BzrCheckError("previous revision {%s} not listed among "
1257
% (mainline_parent_id, revision_id))
1258
mainline_parent_id = revision_id
1403
def _get_checkout_format(self, lightweight=False):
1261
def _get_checkout_format(self):
1404
1262
"""Return the most suitable metadir for a checkout of this branch.
1405
1263
Weaves are used if this branch's repository uses weaves.
1407
format = self.repository.bzrdir.checkout_metadir()
1408
format.set_branch_format(self._format)
1265
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
1266
from bzrlib.repofmt import weaverepo
1267
format = bzrdir.BzrDirMetaFormat1()
1268
format.repository_format = weaverepo.RepositoryFormat7()
1270
format = self.repository.bzrdir.checkout_metadir()
1271
format.set_branch_format(self._format)
1411
1274
def create_clone_on_transport(self, to_transport, revision_id=None,
1412
stacked_on=None, create_prefix=False, use_existing_dir=False,
1275
stacked_on=None, create_prefix=False, use_existing_dir=False):
1414
1276
"""Create a clone of this branch and its bzrdir.
1416
1278
:param to_transport: The transport to clone onto.
1602
1438
return not (self == other)
1605
def find_format(klass, controldir, name=None):
1606
"""Return the format for the branch object in controldir."""
1441
def find_format(klass, a_bzrdir):
1442
"""Return the format for the branch object in a_bzrdir."""
1608
transport = controldir.get_branch_transport(None, name=name)
1609
format_string = transport.get_bytes("format")
1610
return format_registry.get(format_string)
1444
transport = a_bzrdir.get_branch_transport(None)
1445
format_string = transport.get("format").read()
1446
return klass._formats[format_string]
1611
1447
except errors.NoSuchFile:
1612
raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1448
raise errors.NotBranchError(path=transport.base)
1613
1449
except KeyError:
1614
1450
raise errors.UnknownFormatError(format=format_string, kind='branch')
1617
@deprecated_method(deprecated_in((2, 4, 0)))
1618
1453
def get_default_format(klass):
1619
1454
"""Return the current default format."""
1620
return format_registry.get_default()
1623
@deprecated_method(deprecated_in((2, 4, 0)))
1624
def get_formats(klass):
1625
"""Get all the known formats.
1627
Warning: This triggers a load of all lazy registered formats: do not
1628
use except when that is desireed.
1630
return format_registry._get_all()
1632
def get_reference(self, controldir, name=None):
1633
"""Get the target reference of the branch in controldir.
1455
return klass._default_format
1457
def get_reference(self, a_bzrdir):
1458
"""Get the target reference of the branch in a_bzrdir.
1635
1460
format probing must have been completed before calling
1636
1461
this method - it is assumed that the format of the branch
1637
in controldir is correct.
1462
in a_bzrdir is correct.
1639
:param controldir: The controldir to get the branch data from.
1640
:param name: Name of the colocated branch to fetch
1464
:param a_bzrdir: The bzrdir to get the branch data from.
1641
1465
:return: None if the branch is not a reference branch.
1646
def set_reference(self, controldir, name, to_branch):
1647
"""Set the target reference of the branch in controldir.
1470
def set_reference(self, a_bzrdir, to_branch):
1471
"""Set the target reference of the branch in a_bzrdir.
1649
1473
format probing must have been completed before calling
1650
1474
this method - it is assumed that the format of the branch
1651
in controldir is correct.
1475
in a_bzrdir is correct.
1653
:param controldir: The controldir to set the branch reference for.
1654
:param name: Name of colocated branch to set, None for default
1477
:param a_bzrdir: The bzrdir to set the branch reference for.
1655
1478
:param to_branch: branch that the checkout is to reference
1657
1480
raise NotImplementedError(self.set_reference)
1664
1487
"""Return the short format description for this format."""
1665
1488
raise NotImplementedError(self.get_format_description)
1667
def _run_post_branch_init_hooks(self, controldir, name, branch):
1668
hooks = Branch.hooks['post_branch_init']
1671
params = BranchInitHookParams(self, controldir, name, branch)
1675
def initialize(self, controldir, name=None, repository=None,
1676
append_revisions_only=None):
1677
"""Create a branch of this format in controldir.
1679
:param name: Name of the colocated branch to create.
1490
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
1492
"""Initialize a branch in a bzrdir, with specified files
1494
:param a_bzrdir: The bzrdir to initialize the branch in
1495
:param utf8_files: The files to create as a list of
1496
(filename, content) tuples
1497
:param set_format: If True, set the format with
1498
self.get_format_string. (BzrBranch4 has its format set
1500
:return: a branch in this format
1502
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1503
branch_transport = a_bzrdir.get_branch_transport(self)
1505
'metadir': ('lock', lockdir.LockDir),
1506
'branch4': ('branch-lock', lockable_files.TransportLock),
1508
lock_name, lock_class = lock_map[lock_type]
1509
control_files = lockable_files.LockableFiles(branch_transport,
1510
lock_name, lock_class)
1511
control_files.create_lock()
1513
control_files.lock_write()
1514
except errors.LockContention:
1515
if lock_type != 'branch4':
1521
utf8_files += [('format', self.get_format_string())]
1523
for (filename, content) in utf8_files:
1524
branch_transport.put_bytes(
1526
mode=a_bzrdir._get_file_mode())
1529
control_files.unlock()
1530
return self.open(a_bzrdir, _found=True)
1532
def initialize(self, a_bzrdir):
1533
"""Create a branch of this format in a_bzrdir."""
1681
1534
raise NotImplementedError(self.initialize)
1683
1536
def is_supported(self):
1842
1649
"basis revision. hooks MUST NOT modify this delta. "
1843
1650
" future_tree is an in-memory tree obtained from "
1844
1651
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1846
self.add_hook('post_commit',
1652
"tree.", (0,91), None))
1653
self.create_hook(HookPoint('post_commit',
1847
1654
"Called in the bzr client after a commit has completed. "
1848
1655
"post_commit is called with (local, master, old_revno, old_revid, "
1849
1656
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1850
"commit to a branch.", (0, 15))
1851
self.add_hook('post_uncommit',
1657
"commit to a branch.", (0, 15), None))
1658
self.create_hook(HookPoint('post_uncommit',
1852
1659
"Called in the bzr client after an uncommit completes. "
1853
1660
"post_uncommit is called with (local, master, old_revno, "
1854
1661
"old_revid, new_revno, new_revid) where local is the local branch "
1855
1662
"or None, master is the target branch, and an empty branch "
1856
"receives new_revno of 0, new_revid of None.", (0, 15))
1857
self.add_hook('pre_change_branch_tip',
1663
"receives new_revno of 0, new_revid of None.", (0, 15), None))
1664
self.create_hook(HookPoint('pre_change_branch_tip',
1858
1665
"Called in bzr client and server before a change to the tip of a "
1859
1666
"branch is made. pre_change_branch_tip is called with a "
1860
1667
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1861
"commit, uncommit will all trigger this hook.", (1, 6))
1862
self.add_hook('post_change_branch_tip',
1668
"commit, uncommit will all trigger this hook.", (1, 6), None))
1669
self.create_hook(HookPoint('post_change_branch_tip',
1863
1670
"Called in bzr client and server after a change to the tip of a "
1864
1671
"branch is made. post_change_branch_tip is called with a "
1865
1672
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1866
"commit, uncommit will all trigger this hook.", (1, 4))
1867
self.add_hook('transform_fallback_location',
1673
"commit, uncommit will all trigger this hook.", (1, 4), None))
1674
self.create_hook(HookPoint('transform_fallback_location',
1868
1675
"Called when a stacked branch is activating its fallback "
1869
1676
"locations. transform_fallback_location is called with (branch, "
1870
1677
"url), and should return a new url. Returning the same url "
1937
1727
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1940
class BranchInitHookParams(object):
1941
"""Object holding parameters passed to `*_branch_init` hooks.
1943
There are 4 fields that hooks may wish to access:
1945
:ivar format: the branch format
1946
:ivar bzrdir: the ControlDir where the branch will be/has been initialized
1947
:ivar name: name of colocated branch, if any (or None)
1948
:ivar branch: the branch created
1950
Note that for lightweight checkouts, the bzrdir and format fields refer to
1951
the checkout, hence they are different from the corresponding fields in
1952
branch, which refer to the original branch.
1955
def __init__(self, format, controldir, name, branch):
1956
"""Create a group of BranchInitHook parameters.
1958
:param format: the branch format
1959
:param controldir: the ControlDir where the branch will be/has been
1961
:param name: name of colocated branch, if any (or None)
1962
:param branch: the branch created
1964
Note that for lightweight checkouts, the bzrdir and format fields refer
1965
to the checkout, hence they are different from the corresponding fields
1966
in branch, which refer to the original branch.
1968
self.format = format
1969
self.bzrdir = controldir
1971
self.branch = branch
1973
def __eq__(self, other):
1974
return self.__dict__ == other.__dict__
1977
return "<%s of %s>" % (self.__class__.__name__, self.branch)
1980
class SwitchHookParams(object):
1981
"""Object holding parameters passed to `*_switch` hooks.
1983
There are 4 fields that hooks may wish to access:
1985
:ivar control_dir: ControlDir of the checkout to change
1986
:ivar to_branch: branch that the checkout is to reference
1987
:ivar force: skip the check for local commits in a heavy checkout
1988
:ivar revision_id: revision ID to switch to (or None)
1991
def __init__(self, control_dir, to_branch, force, revision_id):
1992
"""Create a group of SwitchHook parameters.
1994
:param control_dir: ControlDir of the checkout to change
1995
:param to_branch: branch that the checkout is to reference
1996
:param force: skip the check for local commits in a heavy checkout
1997
:param revision_id: revision ID to switch to (or None)
1999
self.control_dir = control_dir
2000
self.to_branch = to_branch
2002
self.revision_id = revision_id
2004
def __eq__(self, other):
2005
return self.__dict__ == other.__dict__
2008
return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
2009
self.control_dir, self.to_branch,
1730
class BzrBranchFormat4(BranchFormat):
1731
"""Bzr branch format 4.
1734
- a revision-history file.
1735
- a branch-lock lock file [ to be shared with the bzrdir ]
1738
def get_format_description(self):
1739
"""See BranchFormat.get_format_description()."""
1740
return "Branch format 4"
1742
def initialize(self, a_bzrdir):
1743
"""Create a branch of this format in a_bzrdir."""
1744
utf8_files = [('revision-history', ''),
1745
('branch-name', ''),
1747
return self._initialize_helper(a_bzrdir, utf8_files,
1748
lock_type='branch4', set_format=False)
1751
super(BzrBranchFormat4, self).__init__()
1752
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1754
def network_name(self):
1755
"""The network name for this format is the control dirs disk label."""
1756
return self._matchingbzrdir.get_format_string()
1758
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1759
"""See BranchFormat.open()."""
1761
# we are being called directly and must probe.
1762
raise NotImplementedError
1763
return BzrBranch(_format=self,
1764
_control_files=a_bzrdir._control_files,
1766
_repository=a_bzrdir.open_repository())
1769
return "Bazaar-NG branch format 4"
2013
1772
class BranchFormatMetadir(BranchFormat):
2275
1978
"""See BranchFormat.get_format_description()."""
2276
1979
return "Checkout reference format 1"
2278
def get_reference(self, a_bzrdir, name=None):
1981
def get_reference(self, a_bzrdir):
2279
1982
"""See BranchFormat.get_reference()."""
2280
transport = a_bzrdir.get_branch_transport(None, name=name)
2281
return transport.get_bytes('location')
1983
transport = a_bzrdir.get_branch_transport(None)
1984
return transport.get('location').read()
2283
def set_reference(self, a_bzrdir, name, to_branch):
1986
def set_reference(self, a_bzrdir, to_branch):
2284
1987
"""See BranchFormat.set_reference()."""
2285
transport = a_bzrdir.get_branch_transport(None, name=name)
1988
transport = a_bzrdir.get_branch_transport(None)
2286
1989
location = transport.put_bytes('location', to_branch.base)
2288
def initialize(self, a_bzrdir, name=None, target_branch=None,
2289
repository=None, append_revisions_only=None):
1991
def initialize(self, a_bzrdir, target_branch=None):
2290
1992
"""Create a branch of this format in a_bzrdir."""
2291
1993
if target_branch is None:
2292
1994
# this format does not implement branch itself, thus the implicit
2293
1995
# creation contract must see it as uninitializable
2294
1996
raise errors.UninitializableFormat(self)
2295
mutter('creating branch reference in %s', a_bzrdir.user_url)
2296
if a_bzrdir._format.fixed_components:
2297
raise errors.IncompatibleFormat(self, a_bzrdir._format)
2298
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1997
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1998
branch_transport = a_bzrdir.get_branch_transport(self)
2299
1999
branch_transport.put_bytes('location',
2300
target_branch.bzrdir.user_url)
2000
target_branch.bzrdir.root_transport.base)
2301
2001
branch_transport.put_bytes('format', self.get_format_string())
2303
a_bzrdir, name, _found=True,
2003
a_bzrdir, _found=True,
2304
2004
possible_transports=[target_branch.bzrdir.root_transport])
2305
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2007
super(BranchReferenceFormat, self).__init__()
2008
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2009
self._matchingbzrdir.set_branch_format(self)
2308
2011
def _make_reference_clone_function(format, a_branch):
2309
2012
"""Create a clone() routine for a branch dynamically."""
2310
2013
def clone(to_bzrdir, revision_id=None,
2311
2014
repository_policy=None):
2312
2015
"""See Branch.clone()."""
2313
return format.initialize(to_bzrdir, target_branch=a_branch)
2016
return format.initialize(to_bzrdir, a_branch)
2314
2017
# cannot obey revision_id limits when cloning a reference ...
2315
2018
# FIXME RBC 20060210 either nuke revision_id for clone, or
2316
2019
# emit some sort of warning/error to the caller ?!
2319
def open(self, a_bzrdir, name=None, _found=False, location=None,
2320
possible_transports=None, ignore_fallbacks=False,
2321
found_repository=None):
2022
def open(self, a_bzrdir, _found=False, location=None,
2023
possible_transports=None, ignore_fallbacks=False):
2322
2024
"""Return the branch that the branch reference in a_bzrdir points at.
2324
2026
:param a_bzrdir: A BzrDir that contains a branch.
2325
:param name: Name of colocated branch to open, if any
2326
2027
:param _found: a private parameter, do not use it. It is used to
2327
2028
indicate if format probing has already be done.
2328
2029
:param ignore_fallbacks: when set, no fallback branches will be opened
2545
2190
"""See Branch.print_file."""
2546
2191
return self.repository.print_file(file, revision_id)
2193
def _write_revision_history(self, history):
2194
"""Factored out of set_revision_history.
2196
This performs the actual writing to disk.
2197
It is intended to be called by BzrBranch5.set_revision_history."""
2198
self._transport.put_bytes(
2199
'revision-history', '\n'.join(history),
2200
mode=self.bzrdir._get_file_mode())
2203
def set_revision_history(self, rev_history):
2204
"""See Branch.set_revision_history."""
2205
if 'evil' in debug.debug_flags:
2206
mutter_callsite(3, "set_revision_history scales with history.")
2207
check_not_reserved_id = _mod_revision.check_not_reserved_id
2208
for rev_id in rev_history:
2209
check_not_reserved_id(rev_id)
2210
if Branch.hooks['post_change_branch_tip']:
2211
# Don't calculate the last_revision_info() if there are no hooks
2213
old_revno, old_revid = self.last_revision_info()
2214
if len(rev_history) == 0:
2215
revid = _mod_revision.NULL_REVISION
2217
revid = rev_history[-1]
2218
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2219
self._write_revision_history(rev_history)
2220
self._clear_cached_state()
2221
self._cache_revision_history(rev_history)
2222
for hook in Branch.hooks['set_rh']:
2223
hook(self, rev_history)
2224
if Branch.hooks['post_change_branch_tip']:
2225
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2227
def _synchronize_history(self, destination, revision_id):
2228
"""Synchronize last revision and revision history between branches.
2230
This version is most efficient when the destination is also a
2231
BzrBranch5, but works for BzrBranch6 as long as the revision
2232
history is the true lefthand parent history, and all of the revisions
2233
are in the destination's repository. If not, set_revision_history
2236
:param destination: The branch to copy the history into
2237
:param revision_id: The revision-id to truncate history at. May
2238
be None to copy complete history.
2240
if not isinstance(destination._format, BzrBranchFormat5):
2241
super(BzrBranch, self)._synchronize_history(
2242
destination, revision_id)
2244
if revision_id == _mod_revision.NULL_REVISION:
2247
new_history = self.revision_history()
2248
if revision_id is not None and new_history != []:
2250
new_history = new_history[:new_history.index(revision_id) + 1]
2252
rev = self.repository.get_revision(revision_id)
2253
new_history = rev.get_history(self.repository)[1:]
2254
destination.set_revision_history(new_history)
2548
2256
@needs_write_lock
2549
2257
def set_last_revision_info(self, revno, revision_id):
2550
if not revision_id or not isinstance(revision_id, basestring):
2551
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2258
"""Set the last revision of this branch.
2260
The caller is responsible for checking that the revno is correct
2261
for this revision id.
2263
It may be possible to set the branch last revision to an id not
2264
present in the repository. However, branches can also be
2265
configured to check constraints on history, in which case this may not
2552
2268
revision_id = _mod_revision.ensure_null(revision_id)
2553
old_revno, old_revid = self.last_revision_info()
2554
if self.get_append_revisions_only():
2555
self._check_history_violation(revision_id)
2556
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2557
self._write_last_revision_info(revno, revision_id)
2558
self._clear_cached_state()
2559
self._last_revision_info_cache = revno, revision_id
2560
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2269
# this old format stores the full history, but this api doesn't
2270
# provide it, so we must generate, and might as well check it's
2272
history = self._lefthand_history(revision_id)
2273
if len(history) != revno:
2274
raise AssertionError('%d != %d' % (len(history), revno))
2275
self.set_revision_history(history)
2277
def _gen_revision_history(self):
2278
history = self._transport.get_bytes('revision-history').split('\n')
2279
if history[-1:] == ['']:
2280
# There shouldn't be a trailing newline, but just in case.
2285
def generate_revision_history(self, revision_id, last_rev=None,
2287
"""Create a new revision history that will finish with revision_id.
2289
:param revision_id: the new tip to use.
2290
:param last_rev: The previous last_revision. If not None, then this
2291
must be a ancestory of revision_id, or DivergedBranches is raised.
2292
:param other_branch: The other branch that DivergedBranches should
2293
raise with respect to.
2295
self.set_revision_history(self._lefthand_history(revision_id,
2296
last_rev, other_branch))
2562
2298
def basis_tree(self):
2563
2299
"""See Branch.basis_tree."""
2684
def _read_last_revision_info(self):
2685
revision_string = self._transport.get_bytes('last-revision')
2686
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2687
revision_id = cache_utf8.get_cached_utf8(revision_id)
2689
return revno, revision_id
2691
def _write_last_revision_info(self, revno, revision_id):
2692
"""Simply write out the revision id, with no checks.
2694
Use set_last_revision_info to perform this safely.
2696
Does not update the revision_history cache.
2698
revision_id = _mod_revision.ensure_null(revision_id)
2699
out_string = '%d %s\n' % (revno, revision_id)
2700
self._transport.put_bytes('last-revision', out_string,
2701
mode=self.bzrdir._get_file_mode())
2704
class FullHistoryBzrBranch(BzrBranch):
2705
"""Bzr branch which contains the full revision history."""
2708
def set_last_revision_info(self, revno, revision_id):
2709
if not revision_id or not isinstance(revision_id, basestring):
2710
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2711
revision_id = _mod_revision.ensure_null(revision_id)
2712
# this old format stores the full history, but this api doesn't
2713
# provide it, so we must generate, and might as well check it's
2715
history = self._lefthand_history(revision_id)
2716
if len(history) != revno:
2717
raise AssertionError('%d != %d' % (len(history), revno))
2718
self._set_revision_history(history)
2720
def _read_last_revision_info(self):
2721
rh = self._revision_history()
2724
return (revno, rh[-1])
2726
return (0, _mod_revision.NULL_REVISION)
2728
@deprecated_method(deprecated_in((2, 4, 0)))
2730
def set_revision_history(self, rev_history):
2731
"""See Branch.set_revision_history."""
2732
self._set_revision_history(rev_history)
2734
def _set_revision_history(self, rev_history):
2735
if 'evil' in debug.debug_flags:
2736
mutter_callsite(3, "set_revision_history scales with history.")
2737
check_not_reserved_id = _mod_revision.check_not_reserved_id
2738
for rev_id in rev_history:
2739
check_not_reserved_id(rev_id)
2740
if Branch.hooks['post_change_branch_tip']:
2741
# Don't calculate the last_revision_info() if there are no hooks
2743
old_revno, old_revid = self.last_revision_info()
2744
if len(rev_history) == 0:
2745
revid = _mod_revision.NULL_REVISION
2747
revid = rev_history[-1]
2748
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2749
self._write_revision_history(rev_history)
2750
self._clear_cached_state()
2751
self._cache_revision_history(rev_history)
2752
for hook in Branch.hooks['set_rh']:
2753
hook(self, rev_history)
2754
if Branch.hooks['post_change_branch_tip']:
2755
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2757
def _write_revision_history(self, history):
2758
"""Factored out of set_revision_history.
2760
This performs the actual writing to disk.
2761
It is intended to be called by set_revision_history."""
2762
self._transport.put_bytes(
2763
'revision-history', '\n'.join(history),
2764
mode=self.bzrdir._get_file_mode())
2766
def _gen_revision_history(self):
2767
history = self._transport.get_bytes('revision-history').split('\n')
2768
if history[-1:] == ['']:
2769
# There shouldn't be a trailing newline, but just in case.
2773
def _synchronize_history(self, destination, revision_id):
2774
if not isinstance(destination, FullHistoryBzrBranch):
2775
super(BzrBranch, self)._synchronize_history(
2776
destination, revision_id)
2778
if revision_id == _mod_revision.NULL_REVISION:
2781
new_history = self._revision_history()
2782
if revision_id is not None and new_history != []:
2784
new_history = new_history[:new_history.index(revision_id) + 1]
2786
rev = self.repository.get_revision(revision_id)
2787
new_history = rev.get_history(self.repository)[1:]
2788
destination._set_revision_history(new_history)
2791
def generate_revision_history(self, revision_id, last_rev=None,
2793
"""Create a new revision history that will finish with revision_id.
2795
:param revision_id: the new tip to use.
2796
:param last_rev: The previous last_revision. If not None, then this
2797
must be a ancestory of revision_id, or DivergedBranches is raised.
2798
:param other_branch: The other branch that DivergedBranches should
2799
raise with respect to.
2801
self._set_revision_history(self._lefthand_history(revision_id,
2802
last_rev, other_branch))
2805
class BzrBranch5(FullHistoryBzrBranch):
2806
"""A format 5 branch. This supports new features over plain branches.
2808
It has support for a master_branch which is the data for bound branches.
2812
class BzrBranch8(BzrBranch):
2448
class BzrBranch8(BzrBranch5):
2813
2449
"""A branch that stores tree-reference locations."""
2815
2451
def _open_hook(self):
2841
2477
self._last_revision_info_cache = None
2842
2478
self._reference_info = None
2480
def _last_revision_info(self):
2481
revision_string = self._transport.get_bytes('last-revision')
2482
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2483
revision_id = cache_utf8.get_cached_utf8(revision_id)
2485
return revno, revision_id
2487
def _write_last_revision_info(self, revno, revision_id):
2488
"""Simply write out the revision id, with no checks.
2490
Use set_last_revision_info to perform this safely.
2492
Does not update the revision_history cache.
2493
Intended to be called by set_last_revision_info and
2494
_write_revision_history.
2496
revision_id = _mod_revision.ensure_null(revision_id)
2497
out_string = '%d %s\n' % (revno, revision_id)
2498
self._transport.put_bytes('last-revision', out_string,
2499
mode=self.bzrdir._get_file_mode())
2502
def set_last_revision_info(self, revno, revision_id):
2503
revision_id = _mod_revision.ensure_null(revision_id)
2504
old_revno, old_revid = self.last_revision_info()
2505
if self._get_append_revisions_only():
2506
self._check_history_violation(revision_id)
2507
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2508
self._write_last_revision_info(revno, revision_id)
2509
self._clear_cached_state()
2510
self._last_revision_info_cache = revno, revision_id
2511
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2513
def _synchronize_history(self, destination, revision_id):
2514
"""Synchronize last revision and revision history between branches.
2516
:see: Branch._synchronize_history
2518
# XXX: The base Branch has a fast implementation of this method based
2519
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2520
# that uses set_revision_history. This class inherits from BzrBranch5,
2521
# but wants the fast implementation, so it calls
2522
# Branch._synchronize_history directly.
2523
Branch._synchronize_history(self, destination, revision_id)
2844
2525
def _check_history_violation(self, revision_id):
2845
current_revid = self.last_revision()
2846
last_revision = _mod_revision.ensure_null(current_revid)
2526
last_revision = _mod_revision.ensure_null(self.last_revision())
2847
2527
if _mod_revision.is_null(last_revision):
2849
graph = self.repository.get_graph()
2850
for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2851
if lh_ancestor == current_revid:
2853
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2529
if last_revision not in self._lefthand_history(revision_id):
2530
raise errors.AppendRevisionsOnlyViolation(self.base)
2855
2532
def _gen_revision_history(self):
2856
2533
"""Generate the revision history from last revision
3276
2986
raise NotImplementedError(self.push)
3279
def copy_content_into(self, revision_id=None):
3280
"""Copy the content of source into target
3282
revision_id: if not None, the revision history in the new branch will
3283
be truncated to end with revision_id.
3285
raise NotImplementedError(self.copy_content_into)
3288
def fetch(self, stop_revision=None, limit=None):
3291
:param stop_revision: Last revision to fetch
3292
:param limit: Optional rough limit of revisions to fetch
3294
raise NotImplementedError(self.fetch)
3297
2989
class GenericInterBranch(InterBranch):
3298
"""InterBranch implementation that uses public Branch functions."""
3301
def is_compatible(klass, source, target):
3302
# GenericBranch uses the public API, so always compatible
3306
def _get_branch_formats_to_test(klass):
3307
return [(format_registry.get_default(), format_registry.get_default())]
3310
def unwrap_format(klass, format):
3311
if isinstance(format, remote.RemoteBranchFormat):
3312
format._ensure_real()
3313
return format._custom_format
3317
def copy_content_into(self, revision_id=None):
3318
"""Copy the content of source into target
3320
revision_id: if not None, the revision history in the new branch will
3321
be truncated to end with revision_id.
3323
self.source.update_references(self.target)
3324
self.source._synchronize_history(self.target, revision_id)
3326
parent = self.source.get_parent()
3327
except errors.InaccessibleParent, e:
3328
mutter('parent was not accessible to copy: %s', e)
3331
self.target.set_parent(parent)
3332
if self.source._push_should_merge_tags():
3333
self.source.tags.merge_to(self.target.tags)
3336
def fetch(self, stop_revision=None, limit=None):
3337
if self.target.base == self.source.base:
2990
"""InterBranch implementation that uses public Branch functions.
2994
def _get_branch_formats_to_test():
2995
return BranchFormat._default_format, BranchFormat._default_format
2997
def update_revisions(self, stop_revision=None, overwrite=False,
2999
"""See InterBranch.update_revisions()."""
3339
3000
self.source.lock_read()
3341
fetch_spec_factory = fetch.FetchSpecFactory()
3342
fetch_spec_factory.source_branch = self.source
3343
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3344
fetch_spec_factory.source_repo = self.source.repository
3345
fetch_spec_factory.target_repo = self.target.repository
3346
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3347
fetch_spec_factory.limit = limit
3348
fetch_spec = fetch_spec_factory.make_fetch_spec()
3349
return self.target.repository.fetch(self.source.repository,
3350
fetch_spec=fetch_spec)
3002
other_revno, other_last_revision = self.source.last_revision_info()
3003
stop_revno = None # unknown
3004
if stop_revision is None:
3005
stop_revision = other_last_revision
3006
if _mod_revision.is_null(stop_revision):
3007
# if there are no commits, we're done.
3009
stop_revno = other_revno
3011
# what's the current last revision, before we fetch [and change it
3013
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3014
# we fetch here so that we don't process data twice in the common
3015
# case of having something to pull, and so that the check for
3016
# already merged can operate on the just fetched graph, which will
3017
# be cached in memory.
3018
self.target.fetch(self.source, stop_revision)
3019
# Check to see if one is an ancestor of the other
3022
graph = self.target.repository.get_graph()
3023
if self.target._check_if_descendant_or_diverged(
3024
stop_revision, last_rev, graph, self.source):
3025
# stop_revision is a descendant of last_rev, but we aren't
3026
# overwriting, so we're done.
3028
if stop_revno is None:
3030
graph = self.target.repository.get_graph()
3031
this_revno, this_last_revision = \
3032
self.target.last_revision_info()
3033
stop_revno = graph.find_distance_to_null(stop_revision,
3034
[(other_last_revision, other_revno),
3035
(this_last_revision, this_revno)])
3036
self.target.set_last_revision_info(stop_revno, stop_revision)
3352
3038
self.source.unlock()
3355
def _update_revisions(self, stop_revision=None, overwrite=False,
3357
other_revno, other_last_revision = self.source.last_revision_info()
3358
stop_revno = None # unknown
3359
if stop_revision is None:
3360
stop_revision = other_last_revision
3361
if _mod_revision.is_null(stop_revision):
3362
# if there are no commits, we're done.
3364
stop_revno = other_revno
3366
# what's the current last revision, before we fetch [and change it
3368
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3369
# we fetch here so that we don't process data twice in the common
3370
# case of having something to pull, and so that the check for
3371
# already merged can operate on the just fetched graph, which will
3372
# be cached in memory.
3373
self.fetch(stop_revision=stop_revision)
3374
# Check to see if one is an ancestor of the other
3377
graph = self.target.repository.get_graph()
3378
if self.target._check_if_descendant_or_diverged(
3379
stop_revision, last_rev, graph, self.source):
3380
# stop_revision is a descendant of last_rev, but we aren't
3381
# overwriting, so we're done.
3383
if stop_revno is None:
3385
graph = self.target.repository.get_graph()
3386
this_revno, this_last_revision = \
3387
self.target.last_revision_info()
3388
stop_revno = graph.find_distance_to_null(stop_revision,
3389
[(other_last_revision, other_revno),
3390
(this_last_revision, this_revno)])
3391
self.target.set_last_revision_info(stop_revno, stop_revision)
3394
3040
def pull(self, overwrite=False, stop_revision=None,
3395
possible_transports=None, run_hooks=True,
3396
_override_hook_target=None, local=False):
3397
"""Pull from source into self, updating my master if any.
3399
:param run_hooks: Private parameter - if false, this branch
3400
is being called because it's the master of the primary branch,
3401
so it should not run its hooks.
3403
bound_location = self.target.get_bound_location()
3404
if local and not bound_location:
3405
raise errors.LocalRequiresBoundBranch()
3406
master_branch = None
3407
source_is_master = False
3409
# bound_location comes from a config file, some care has to be
3410
# taken to relate it to source.user_url
3411
normalized = urlutils.normalize_url(bound_location)
3413
relpath = self.source.user_transport.relpath(normalized)
3414
source_is_master = (relpath == '')
3415
except (errors.PathNotChild, errors.InvalidURL):
3416
source_is_master = False
3417
if not local and bound_location and not source_is_master:
3418
# not pulling from master, so we need to update master.
3419
master_branch = self.target.get_master_branch(possible_transports)
3420
master_branch.lock_write()
3423
# pull from source into master.
3424
master_branch.pull(self.source, overwrite, stop_revision,
3426
return self._pull(overwrite,
3427
stop_revision, _hook_master=master_branch,
3428
run_hooks=run_hooks,
3429
_override_hook_target=_override_hook_target,
3430
merge_tags_to_master=not source_is_master)
3433
master_branch.unlock()
3435
def push(self, overwrite=False, stop_revision=None, lossy=False,
3436
_override_hook_source_branch=None):
3437
"""See InterBranch.push.
3439
This is the basic concrete implementation of push()
3441
:param _override_hook_source_branch: If specified, run the hooks
3442
passing this Branch as the source, rather than self. This is for
3443
use of RemoteBranch, where push is delegated to the underlying
3447
raise errors.LossyPushToSameVCS(self.source, self.target)
3448
# TODO: Public option to disable running hooks - should be trivial but
3451
op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3452
op.add_cleanup(self.source.lock_read().unlock)
3453
op.add_cleanup(self.target.lock_write().unlock)
3454
return op.run(overwrite, stop_revision,
3455
_override_hook_source_branch=_override_hook_source_branch)
3457
def _basic_push(self, overwrite, stop_revision):
3458
"""Basic implementation of push without bound branches or hooks.
3460
Must be called with source read locked and target write locked.
3462
result = BranchPushResult()
3463
result.source_branch = self.source
3464
result.target_branch = self.target
3465
result.old_revno, result.old_revid = self.target.last_revision_info()
3466
self.source.update_references(self.target)
3467
if result.old_revid != stop_revision:
3468
# We assume that during 'push' this repository is closer than
3470
graph = self.source.repository.get_graph(self.target.repository)
3471
self._update_revisions(stop_revision, overwrite=overwrite,
3473
if self.source._push_should_merge_tags():
3474
result.tag_updates, result.tag_conflicts = (
3475
self.source.tags.merge_to(self.target.tags, overwrite))
3476
result.new_revno, result.new_revid = self.target.last_revision_info()
3479
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
3480
_override_hook_source_branch=None):
3481
"""Push from source into target, and into target's master if any.
3484
if _override_hook_source_branch:
3485
result.source_branch = _override_hook_source_branch
3486
for hook in Branch.hooks['post_push']:
3489
bound_location = self.target.get_bound_location()
3490
if bound_location and self.target.base != bound_location:
3491
# there is a master branch.
3493
# XXX: Why the second check? Is it even supported for a branch to
3494
# be bound to itself? -- mbp 20070507
3495
master_branch = self.target.get_master_branch()
3496
master_branch.lock_write()
3497
operation.add_cleanup(master_branch.unlock)
3498
# push into the master from the source branch.
3499
master_inter = InterBranch.get(self.source, master_branch)
3500
master_inter._basic_push(overwrite, stop_revision)
3501
# and push into the target branch from the source. Note that
3502
# we push from the source branch again, because it's considered
3503
# the highest bandwidth repository.
3504
result = self._basic_push(overwrite, stop_revision)
3505
result.master_branch = master_branch
3506
result.local_branch = self.target
3508
master_branch = None
3510
result = self._basic_push(overwrite, stop_revision)
3511
# TODO: Why set master_branch and local_branch if there's no
3512
# binding? Maybe cleaner to just leave them unset? -- mbp
3514
result.master_branch = self.target
3515
result.local_branch = None
3519
def _pull(self, overwrite=False, stop_revision=None,
3520
3041
possible_transports=None, _hook_master=None, run_hooks=True,
3521
_override_hook_target=None, local=False,
3522
merge_tags_to_master=True):
3042
_override_hook_target=None, local=False):
3523
3043
"""See Branch.pull.
3525
This function is the core worker, used by GenericInterBranch.pull to
3526
avoid duplication when pulling source->master and source->local.
3528
3045
:param _hook_master: Private parameter - set the branch to
3529
3046
be supplied as the master to pull hooks.
3530
3047
:param run_hooks: Private parameter - if false, this branch
3531
3048
is being called because it's the master of the primary branch,
3532
3049
so it should not run its hooks.
3533
is being called because it's the master of the primary branch,
3534
so it should not run its hooks.
3535
3050
:param _override_hook_target: Private parameter - set the branch to be
3536
3051
supplied as the target_branch to pull hooks.
3537
3052
:param local: Only update the local branch, and not the bound branch.
3578
3092
self.source.unlock()
3095
def push(self, overwrite=False, stop_revision=None,
3096
_override_hook_source_branch=None):
3097
"""See InterBranch.push.
3099
This is the basic concrete implementation of push()
3101
:param _override_hook_source_branch: If specified, run
3102
the hooks passing this Branch as the source, rather than self.
3103
This is for use of RemoteBranch, where push is delegated to the
3104
underlying vfs-based Branch.
3106
# TODO: Public option to disable running hooks - should be trivial but
3108
self.source.lock_read()
3110
return _run_with_write_locked_target(
3111
self.target, self._push_with_bound_branches, overwrite,
3113
_override_hook_source_branch=_override_hook_source_branch)
3115
self.source.unlock()
3117
def _push_with_bound_branches(self, overwrite, stop_revision,
3118
_override_hook_source_branch=None):
3119
"""Push from source into target, and into target's master if any.
3122
if _override_hook_source_branch:
3123
result.source_branch = _override_hook_source_branch
3124
for hook in Branch.hooks['post_push']:
3127
bound_location = self.target.get_bound_location()
3128
if bound_location and self.target.base != bound_location:
3129
# there is a master branch.
3131
# XXX: Why the second check? Is it even supported for a branch to
3132
# be bound to itself? -- mbp 20070507
3133
master_branch = self.target.get_master_branch()
3134
master_branch.lock_write()
3136
# push into the master from the source branch.
3137
self.source._basic_push(master_branch, overwrite, stop_revision)
3138
# and push into the target branch from the source. Note that we
3139
# push from the source branch again, because its considered the
3140
# highest bandwidth repository.
3141
result = self.source._basic_push(self.target, overwrite,
3143
result.master_branch = master_branch
3144
result.local_branch = self.target
3148
master_branch.unlock()
3151
result = self.source._basic_push(self.target, overwrite,
3153
# TODO: Why set master_branch and local_branch if there's no
3154
# binding? Maybe cleaner to just leave them unset? -- mbp
3156
result.master_branch = self.target
3157
result.local_branch = None
3162
def is_compatible(self, source, target):
3163
# GenericBranch uses the public API, so always compatible
3167
class InterToBranch5(GenericInterBranch):
3170
def _get_branch_formats_to_test():
3171
return BranchFormat._default_format, BzrBranchFormat5()
3173
def pull(self, overwrite=False, stop_revision=None,
3174
possible_transports=None, run_hooks=True,
3175
_override_hook_target=None, local=False):
3176
"""Pull from source into self, updating my master if any.
3178
:param run_hooks: Private parameter - if false, this branch
3179
is being called because it's the master of the primary branch,
3180
so it should not run its hooks.
3182
bound_location = self.target.get_bound_location()
3183
if local and not bound_location:
3184
raise errors.LocalRequiresBoundBranch()
3185
master_branch = None
3186
if not local and bound_location and self.source.base != bound_location:
3187
# not pulling from master, so we need to update master.
3188
master_branch = self.target.get_master_branch(possible_transports)
3189
master_branch.lock_write()
3192
# pull from source into master.
3193
master_branch.pull(self.source, overwrite, stop_revision,
3195
return super(InterToBranch5, self).pull(overwrite,
3196
stop_revision, _hook_master=master_branch,
3197
run_hooks=run_hooks,
3198
_override_hook_target=_override_hook_target)
3201
master_branch.unlock()
3582
3204
InterBranch.register_optimiser(GenericInterBranch)
3205
InterBranch.register_optimiser(InterToBranch5)