462
515
"""Iterate over an inclusive range of sorted revisions."""
463
516
rev_iter = iter(merge_sorted_revisions)
464
517
if start_revision_id is not None:
465
for rev_id, depth, revno, end_of_merge in rev_iter:
518
for node in rev_iter:
466
520
if rev_id != start_revision_id:
469
523
# The decision to include the start or not
470
524
# depends on the stop_rule if a stop is provided
472
iter([(rev_id, depth, revno, end_of_merge)]),
525
# so pop this node back into the iterator
526
rev_iter = itertools.chain(iter([node]), rev_iter)
475
528
if stop_revision_id is None:
476
for rev_id, depth, revno, end_of_merge in rev_iter:
477
yield rev_id, depth, revno, end_of_merge
530
for node in rev_iter:
532
yield (rev_id, node.merge_depth, node.revno,
478
534
elif stop_rule == 'exclude':
479
for rev_id, depth, revno, end_of_merge in rev_iter:
535
for node in rev_iter:
480
537
if rev_id == stop_revision_id:
482
yield rev_id, depth, revno, end_of_merge
539
yield (rev_id, node.merge_depth, node.revno,
483
541
elif stop_rule == 'include':
484
for rev_id, depth, revno, end_of_merge in rev_iter:
485
yield rev_id, depth, revno, end_of_merge
542
for node in rev_iter:
544
yield (rev_id, node.merge_depth, node.revno,
486
546
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,
488
560
elif stop_rule == 'with-merges':
489
561
stop_rev = self.repository.get_revision(stop_revision_id)
490
562
if stop_rev.parent_ids:
491
563
left_parent = stop_rev.parent_ids[0]
493
565
left_parent = _mod_revision.NULL_REVISION
494
for rev_id, depth, revno, end_of_merge in rev_iter:
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:
495
572
if rev_id == left_parent:
573
# reached the left parent after the stop_revision
497
yield rev_id, depth, revno, end_of_merge
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)
499
586
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)
501
633
def leave_lock_in_place(self):
502
634
"""Tell this branch object not to release the physical lock when this
503
635
object is unlocked.
697
873
old_repository = self.repository
698
874
if len(old_repository._fallback_repositories) != 1:
699
875
raise AssertionError("can't cope with fallback repositories "
700
"of %r" % (self.repository,))
701
# unlock it, including unlocking the fallback
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()
702
906
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):
925
self.repository.lock_write()
926
# Fetch from the old repository into the new.
703
927
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
722
self.repository.lock_write()
723
929
# XXX: If you unstack a branch while it has a working tree
724
930
# with a pending merge, the pending-merged revisions will no
725
931
# longer be present. You can (probably) revert and remerge.
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(),
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)
734
941
old_repository.unlock()
832
1050
:return: A tuple (revno, revision_id).
834
1052
if self._last_revision_info_cache is None:
835
self._last_revision_info_cache = self._last_revision_info()
1053
self._last_revision_info_cache = self._read_last_revision_info()
836
1054
return self._last_revision_info_cache
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,
1056
def _read_last_revision_info(self):
1057
raise NotImplementedError(self._read_last_revision_info)
1059
@deprecated_method(deprecated_in((2, 4, 0)))
885
1060
def import_last_revision_info(self, source_repo, revno, revid):
886
1061
"""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.
891
1063
:param source_repo: Source repository to optionally fetch from
892
1064
:param revno: Revision number of the new tip
893
1065
:param revid: Revision id of the new tip
1222
1383
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()
1224
1387
:return: A BranchCheckResult.
1226
ret = BranchCheckResult(self)
1227
mainline_parent_id = None
1389
result = BranchCheckResult(self)
1228
1390
last_revno, last_revision_id = self.last_revision_info()
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
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
1261
def _get_checkout_format(self):
1403
def _get_checkout_format(self, lightweight=False):
1262
1404
"""Return the most suitable metadir for a checkout of this branch.
1263
1405
Weaves are used if this branch's repository uses weaves.
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)
1407
format = self.repository.bzrdir.checkout_metadir()
1408
format.set_branch_format(self._format)
1274
1411
def create_clone_on_transport(self, to_transport, revision_id=None,
1275
stacked_on=None, create_prefix=False, use_existing_dir=False):
1412
stacked_on=None, create_prefix=False, use_existing_dir=False,
1276
1414
"""Create a clone of this branch and its bzrdir.
1278
1416
:param to_transport: The transport to clone onto.
1438
1602
return not (self == other)
1441
def find_format(klass, a_bzrdir):
1442
"""Return the format for the branch object in a_bzrdir."""
1605
def find_format(klass, controldir, name=None):
1606
"""Return the format for the branch object in controldir."""
1444
transport = a_bzrdir.get_branch_transport(None)
1445
format_string = transport.get("format").read()
1446
return klass._formats[format_string]
1608
transport = controldir.get_branch_transport(None, name=name)
1609
format_string = transport.get_bytes("format")
1610
return format_registry.get(format_string)
1447
1611
except errors.NoSuchFile:
1448
raise errors.NotBranchError(path=transport.base)
1612
raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1449
1613
except KeyError:
1450
1614
raise errors.UnknownFormatError(format=format_string, kind='branch')
1617
@deprecated_method(deprecated_in((2, 4, 0)))
1453
1618
def get_default_format(klass):
1454
1619
"""Return the current default format."""
1455
return klass._default_format
1457
def get_reference(self, a_bzrdir):
1458
"""Get the target reference of the branch in a_bzrdir.
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.
1460
1635
format probing must have been completed before calling
1461
1636
this method - it is assumed that the format of the branch
1462
in a_bzrdir is correct.
1637
in controldir is correct.
1464
:param a_bzrdir: The bzrdir to get the branch data from.
1639
:param controldir: The controldir to get the branch data from.
1640
:param name: Name of the colocated branch to fetch
1465
1641
:return: None if the branch is not a reference branch.
1470
def set_reference(self, a_bzrdir, to_branch):
1471
"""Set the target reference of the branch in a_bzrdir.
1646
def set_reference(self, controldir, name, to_branch):
1647
"""Set the target reference of the branch in controldir.
1473
1649
format probing must have been completed before calling
1474
1650
this method - it is assumed that the format of the branch
1475
in a_bzrdir is correct.
1651
in controldir is correct.
1477
:param a_bzrdir: The bzrdir to set the branch reference for.
1653
:param controldir: The controldir to set the branch reference for.
1654
:param name: Name of colocated branch to set, None for default
1478
1655
:param to_branch: branch that the checkout is to reference
1480
1657
raise NotImplementedError(self.set_reference)
1487
1664
"""Return the short format description for this format."""
1488
1665
raise NotImplementedError(self.get_format_description)
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
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.
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."""
1534
1681
raise NotImplementedError(self.initialize)
1536
1683
def is_supported(self):
1649
1842
"basis revision. hooks MUST NOT modify this delta. "
1650
1843
" future_tree is an in-memory tree obtained from "
1651
1844
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1652
"tree.", (0,91), None))
1653
self.create_hook(HookPoint('post_commit',
1846
self.add_hook('post_commit',
1654
1847
"Called in the bzr client after a commit has completed. "
1655
1848
"post_commit is called with (local, master, old_revno, old_revid, "
1656
1849
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1657
"commit to a branch.", (0, 15), None))
1658
self.create_hook(HookPoint('post_uncommit',
1850
"commit to a branch.", (0, 15))
1851
self.add_hook('post_uncommit',
1659
1852
"Called in the bzr client after an uncommit completes. "
1660
1853
"post_uncommit is called with (local, master, old_revno, "
1661
1854
"old_revid, new_revno, new_revid) where local is the local branch "
1662
1855
"or None, master is the target branch, and an empty branch "
1663
"receives new_revno of 0, new_revid of None.", (0, 15), None))
1664
self.create_hook(HookPoint('pre_change_branch_tip',
1856
"receives new_revno of 0, new_revid of None.", (0, 15))
1857
self.add_hook('pre_change_branch_tip',
1665
1858
"Called in bzr client and server before a change to the tip of a "
1666
1859
"branch is made. pre_change_branch_tip is called with a "
1667
1860
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1668
"commit, uncommit will all trigger this hook.", (1, 6), None))
1669
self.create_hook(HookPoint('post_change_branch_tip',
1861
"commit, uncommit will all trigger this hook.", (1, 6))
1862
self.add_hook('post_change_branch_tip',
1670
1863
"Called in bzr client and server after a change to the tip of a "
1671
1864
"branch is made. post_change_branch_tip is called with a "
1672
1865
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1673
"commit, uncommit will all trigger this hook.", (1, 4), None))
1674
self.create_hook(HookPoint('transform_fallback_location',
1866
"commit, uncommit will all trigger this hook.", (1, 4))
1867
self.add_hook('transform_fallback_location',
1675
1868
"Called when a stacked branch is activating its fallback "
1676
1869
"locations. transform_fallback_location is called with (branch, "
1677
1870
"url), and should return a new url. Returning the same url "
1727
1937
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
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"
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,
1772
2013
class BranchFormatMetadir(BranchFormat):
1978
2275
"""See BranchFormat.get_format_description()."""
1979
2276
return "Checkout reference format 1"
1981
def get_reference(self, a_bzrdir):
2278
def get_reference(self, a_bzrdir, name=None):
1982
2279
"""See BranchFormat.get_reference()."""
1983
transport = a_bzrdir.get_branch_transport(None)
1984
return transport.get('location').read()
2280
transport = a_bzrdir.get_branch_transport(None, name=name)
2281
return transport.get_bytes('location')
1986
def set_reference(self, a_bzrdir, to_branch):
2283
def set_reference(self, a_bzrdir, name, to_branch):
1987
2284
"""See BranchFormat.set_reference()."""
1988
transport = a_bzrdir.get_branch_transport(None)
2285
transport = a_bzrdir.get_branch_transport(None, name=name)
1989
2286
location = transport.put_bytes('location', to_branch.base)
1991
def initialize(self, a_bzrdir, target_branch=None):
2288
def initialize(self, a_bzrdir, name=None, target_branch=None,
2289
repository=None, append_revisions_only=None):
1992
2290
"""Create a branch of this format in a_bzrdir."""
1993
2291
if target_branch is None:
1994
2292
# this format does not implement branch itself, thus the implicit
1995
2293
# creation contract must see it as uninitializable
1996
2294
raise errors.UninitializableFormat(self)
1997
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1998
branch_transport = a_bzrdir.get_branch_transport(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)
1999
2299
branch_transport.put_bytes('location',
2000
target_branch.bzrdir.root_transport.base)
2300
target_branch.bzrdir.user_url)
2001
2301
branch_transport.put_bytes('format', self.get_format_string())
2003
a_bzrdir, _found=True,
2303
a_bzrdir, name, _found=True,
2004
2304
possible_transports=[target_branch.bzrdir.root_transport])
2007
super(BranchReferenceFormat, self).__init__()
2008
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2009
self._matchingbzrdir.set_branch_format(self)
2305
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2011
2308
def _make_reference_clone_function(format, a_branch):
2012
2309
"""Create a clone() routine for a branch dynamically."""
2013
2310
def clone(to_bzrdir, revision_id=None,
2014
2311
repository_policy=None):
2015
2312
"""See Branch.clone()."""
2016
return format.initialize(to_bzrdir, a_branch)
2313
return format.initialize(to_bzrdir, target_branch=a_branch)
2017
2314
# cannot obey revision_id limits when cloning a reference ...
2018
2315
# FIXME RBC 20060210 either nuke revision_id for clone, or
2019
2316
# emit some sort of warning/error to the caller ?!
2022
def open(self, a_bzrdir, _found=False, location=None,
2023
possible_transports=None, ignore_fallbacks=False):
2319
def open(self, a_bzrdir, name=None, _found=False, location=None,
2320
possible_transports=None, ignore_fallbacks=False,
2321
found_repository=None):
2024
2322
"""Return the branch that the branch reference in a_bzrdir points at.
2026
2324
:param a_bzrdir: A BzrDir that contains a branch.
2325
:param name: Name of colocated branch to open, if any
2027
2326
:param _found: a private parameter, do not use it. It is used to
2028
2327
indicate if format probing has already be done.
2029
2328
:param ignore_fallbacks: when set, no fallback branches will be opened
2190
2545
"""See Branch.print_file."""
2191
2546
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)
2256
2548
@needs_write_lock
2257
2549
def set_last_revision_info(self, revno, revision_id):
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
2550
if not revision_id or not isinstance(revision_id, basestring):
2551
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2268
2552
revision_id = _mod_revision.ensure_null(revision_id)
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))
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)
2298
2562
def basis_tree(self):
2299
2563
"""See Branch.basis_tree."""
2448
class BzrBranch8(BzrBranch5):
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):
2449
2813
"""A branch that stores tree-reference locations."""
2451
2815
def _open_hook(self):
2477
2841
self._last_revision_info_cache = None
2478
2842
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)
2525
2844
def _check_history_violation(self, revision_id):
2526
last_revision = _mod_revision.ensure_null(self.last_revision())
2845
current_revid = self.last_revision()
2846
last_revision = _mod_revision.ensure_null(current_revid)
2527
2847
if _mod_revision.is_null(last_revision):
2529
if last_revision not in self._lefthand_history(revision_id):
2530
raise errors.AppendRevisionsOnlyViolation(self.base)
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)
2532
2855
def _gen_revision_history(self):
2533
2856
"""Generate the revision history from last revision
2986
3276
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)
2989
3297
class GenericInterBranch(InterBranch):
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()."""
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:
3000
3339
self.source.lock_read()
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)
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)
3038
3352
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)
3040
3394
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,
3041
3520
possible_transports=None, _hook_master=None, run_hooks=True,
3042
_override_hook_target=None, local=False):
3521
_override_hook_target=None, local=False,
3522
merge_tags_to_master=True):
3043
3523
"""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.
3045
3528
:param _hook_master: Private parameter - set the branch to
3046
3529
be supplied as the master to pull hooks.
3047
3530
:param run_hooks: Private parameter - if false, this branch
3048
3531
is being called because it's the master of the primary branch,
3049
3532
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.
3050
3535
:param _override_hook_target: Private parameter - set the branch to be
3051
3536
supplied as the target_branch to pull hooks.
3052
3537
:param local: Only update the local branch, and not the bound branch.
3092
3578
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()
3204
3582
InterBranch.register_optimiser(GenericInterBranch)
3205
InterBranch.register_optimiser(InterToBranch5)