72
58
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
75
class Branch(controldir.ControlComponent):
61
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
62
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
63
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
66
# TODO: Maybe include checks for common corruption of newlines, etc?
68
# TODO: Some operations like log might retrieve the same revisions
69
# repeatedly to calculate deltas. We could perhaps have a weakref
70
# cache in memory to make this faster. In general anything can be
71
# cached in memory between lock and unlock operations. .. nb thats
72
# what the transaction identity map provides
75
######################################################################
76
79
"""Branch holding a history of revisions.
79
Base directory/url of the branch; using control_url and
80
control_transport is more standardized.
81
:ivar hooks: An instance of BranchHooks.
82
:ivar _master_branch_cache: cached result of get_master_branch, see
82
Base directory/url of the branch.
84
hooks: An instance of BranchHooks.
85
86
# this is really an instance variable - FIXME move it there
90
def control_transport(self):
91
return self._transport
94
def user_transport(self):
95
return self.bzrdir.user_transport
97
def __init__(self, possible_transports=None):
90
def __init__(self, *ignored, **ignored_too):
98
91
self.tags = self._format.make_tags(self)
99
92
self._revision_history_cache = None
100
93
self._revision_id_to_revno_cache = None
101
94
self._partial_revision_id_to_revno_cache = {}
102
95
self._partial_revision_history_cache = []
103
self._tags_bytes = None
104
96
self._last_revision_info_cache = None
105
self._master_branch_cache = None
106
97
self._merge_sorted_revisions_cache = None
107
self._open_hook(possible_transports)
108
99
hooks = Branch.hooks['open']
109
100
for hook in hooks:
112
def _open_hook(self, possible_transports):
103
def _open_hook(self):
113
104
"""Called by init to allow simpler extension of the base class."""
115
def _activate_fallback_location(self, url, possible_transports):
106
def _activate_fallback_location(self, url):
116
107
"""Activate the branch/repository from url as a fallback repository."""
117
for existing_fallback_repo in self.repository._fallback_repositories:
118
if existing_fallback_repo.user_url == url:
119
# This fallback is already configured. This probably only
120
# happens because ControlDir.sprout is a horrible mess. To avoid
121
# confusing _unstack we don't add this a second time.
122
mutter('duplicate activation of fallback %r on %r', url, self)
124
repo = self._get_fallback_repository(url, possible_transports)
108
repo = self._get_fallback_repository(url)
125
109
if repo.has_same_location(self.repository):
126
raise errors.UnstackableLocationError(self.user_url, url)
110
raise errors.UnstackableLocationError(self.base, url)
127
111
self.repository.add_fallback_repository(repo)
129
113
def break_lock(self):
527
468
rev_iter = iter(merge_sorted_revisions)
528
469
if start_revision_id is not None:
529
470
for node in rev_iter:
471
rev_id = node.key[-1]
531
472
if rev_id != start_revision_id:
534
475
# The decision to include the start or not
535
476
# depends on the stop_rule if a stop is provided
536
477
# so pop this node back into the iterator
537
rev_iter = itertools.chain(iter([node]), rev_iter)
478
rev_iter = chain(iter([node]), rev_iter)
539
480
if stop_revision_id is None:
540
481
# Yield everything
541
482
for node in rev_iter:
483
rev_id = node.key[-1]
543
484
yield (rev_id, node.merge_depth, node.revno,
544
485
node.end_of_merge)
545
486
elif stop_rule == 'exclude':
546
487
for node in rev_iter:
488
rev_id = node.key[-1]
548
489
if rev_id == stop_revision_id:
550
491
yield (rev_id, node.merge_depth, node.revno,
551
492
node.end_of_merge)
552
493
elif stop_rule == 'include':
553
494
for node in rev_iter:
495
rev_id = node.key[-1]
555
496
yield (rev_id, node.merge_depth, node.revno,
556
497
node.end_of_merge)
557
498
if rev_id == stop_revision_id:
559
elif stop_rule == 'with-merges-without-common-ancestry':
560
# We want to exclude all revisions that are already part of the
561
# stop_revision_id ancestry.
562
graph = self.repository.get_graph()
563
ancestors = graph.find_unique_ancestors(start_revision_id,
565
for node in rev_iter:
567
if rev_id not in ancestors:
569
yield (rev_id, node.merge_depth, node.revno,
571
500
elif stop_rule == 'with-merges':
572
501
stop_rev = self.repository.get_revision(stop_revision_id)
573
502
if stop_rev.parent_ids:
574
503
left_parent = stop_rev.parent_ids[0]
576
505
left_parent = _mod_revision.NULL_REVISION
577
# left_parent is the actual revision we want to stop logging at,
578
# since we want to show the merged revisions after the stop_rev too
579
reached_stop_revision_id = False
580
revision_id_whitelist = []
581
506
for node in rev_iter:
507
rev_id = node.key[-1]
583
508
if rev_id == left_parent:
584
# reached the left parent after the stop_revision
586
if (not reached_stop_revision_id or
587
rev_id in revision_id_whitelist):
588
yield (rev_id, node.merge_depth, node.revno,
510
yield (rev_id, node.merge_depth, node.revno,
589
511
node.end_of_merge)
590
if reached_stop_revision_id or rev_id == stop_revision_id:
591
# only do the merged revs of rev_id from now on
592
rev = self.repository.get_revision(rev_id)
594
reached_stop_revision_id = True
595
revision_id_whitelist.extend(rev.parent_ids)
597
513
raise ValueError('invalid stop_rule %r' % stop_rule)
599
def _filter_start_non_ancestors(self, rev_iter):
600
# If we started from a dotted revno, we want to consider it as a tip
601
# and don't want to yield revisions that are not part of its
602
# ancestry. Given the order guaranteed by the merge sort, we will see
603
# uninteresting descendants of the first parent of our tip before the
605
first = rev_iter.next()
606
(rev_id, merge_depth, revno, end_of_merge) = first
609
# We start at a mainline revision so by definition, all others
610
# revisions in rev_iter are ancestors
611
for node in rev_iter:
616
pmap = self.repository.get_parent_map([rev_id])
617
parents = pmap.get(rev_id, [])
619
whitelist.update(parents)
621
# If there is no parents, there is nothing of interest left
623
# FIXME: It's hard to test this scenario here as this code is never
624
# called in that case. -- vila 20100322
627
for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
629
if rev_id in whitelist:
630
pmap = self.repository.get_parent_map([rev_id])
631
parents = pmap.get(rev_id, [])
632
whitelist.remove(rev_id)
633
whitelist.update(parents)
635
# We've reached the mainline, there is nothing left to
639
# A revision that is not part of the ancestry of our
642
yield (rev_id, merge_depth, revno, end_of_merge)
644
515
def leave_lock_in_place(self):
645
516
"""Tell this branch object not to release the physical lock when this
646
517
object is unlocked.
861
711
old_repository = self.repository
862
712
if len(old_repository._fallback_repositories) != 1:
863
713
raise AssertionError("can't cope with fallback repositories "
864
"of %r (fallbacks: %r)" % (old_repository,
865
old_repository._fallback_repositories))
866
# Open the new repository object.
867
# Repositories don't offer an interface to remove fallback
868
# repositories today; take the conceptually simpler option and just
869
# reopen it. We reopen it starting from the URL so that we
870
# get a separate connection for RemoteRepositories and can
871
# stream from one of them to the other. This does mean doing
872
# separate SSH connection setup, but unstacking is not a
873
# common operation so it's tolerable.
874
new_bzrdir = controldir.ControlDir.open(
875
self.bzrdir.root_transport.base)
876
new_repository = new_bzrdir.find_repository()
877
if new_repository._fallback_repositories:
878
raise AssertionError("didn't expect %r to have "
879
"fallback_repositories"
880
% (self.repository,))
881
# Replace self.repository with the new repository.
882
# Do our best to transfer the lock state (i.e. lock-tokens and
883
# lock count) of self.repository to the new repository.
884
lock_token = old_repository.lock_write().repository_token
885
self.repository = new_repository
886
if isinstance(self, remote.RemoteBranch):
887
# Remote branches can have a second reference to the old
888
# repository that need to be replaced.
889
if self._real_branch is not None:
890
self._real_branch.repository = new_repository
891
self.repository.lock_write(token=lock_token)
892
if lock_token is not None:
893
old_repository.leave_lock_in_place()
714
"of %r" % (self.repository,))
715
# unlock it, including unlocking the fallback
894
716
old_repository.unlock()
895
if lock_token is not None:
896
# XXX: self.repository.leave_lock_in_place() before this
897
# function will not be preserved. Fortunately that doesn't
898
# affect the current default format (2a), and would be a
899
# corner-case anyway.
900
# - Andrew Bennetts, 2010/06/30
901
self.repository.dont_leave_lock_in_place()
905
old_repository.unlock()
906
except errors.LockNotHeld:
909
if old_lock_count == 0:
910
raise AssertionError(
911
'old_repository should have been locked at least once.')
912
for i in range(old_lock_count-1):
717
old_repository.lock_read()
719
# Repositories don't offer an interface to remove fallback
720
# repositories today; take the conceptually simpler option and just
721
# reopen it. We reopen it starting from the URL so that we
722
# get a separate connection for RemoteRepositories and can
723
# stream from one of them to the other. This does mean doing
724
# separate SSH connection setup, but unstacking is not a
725
# common operation so it's tolerable.
726
new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
727
new_repository = new_bzrdir.find_repository()
728
self.repository = new_repository
729
if self.repository._fallback_repositories:
730
raise AssertionError("didn't expect %r to have "
731
"fallback_repositories"
732
% (self.repository,))
733
# this is not paired with an unlock because it's just restoring
734
# the previous state; the lock's released when set_stacked_on_url
913
736
self.repository.lock_write()
914
# Fetch from the old repository into the new.
915
old_repository.lock_read()
917
737
# XXX: If you unstack a branch while it has a working tree
918
738
# with a pending merge, the pending-merged revisions will no
919
739
# longer be present. You can (probably) revert and remerge.
921
tags_to_fetch = set(self.tags.get_reverse_tag_dict())
922
except errors.TagsNotSupported:
923
tags_to_fetch = set()
924
fetch_spec = vf_search.NotInOtherForRevs(self.repository,
925
old_repository, required_ids=[self.last_revision()],
926
if_present_ids=tags_to_fetch, find_ghosts=True).execute()
927
self.repository.fetch(old_repository, fetch_spec=fetch_spec)
741
# XXX: This only fetches up to the tip of the repository; it
742
# doesn't bring across any tags. That's fairly consistent
743
# with how branch works, but perhaps not ideal.
744
self.repository.fetch(old_repository,
745
revision_id=self.last_revision(),
929
748
old_repository.unlock()
1028
846
:return: A tuple (revno, revision_id).
1030
848
if self._last_revision_info_cache is None:
1031
self._last_revision_info_cache = self._read_last_revision_info()
849
self._last_revision_info_cache = self._last_revision_info()
1032
850
return self._last_revision_info_cache
1034
def _read_last_revision_info(self):
1035
raise NotImplementedError(self._read_last_revision_info)
1037
def import_last_revision_info_and_tags(self, source, revno, revid,
852
def _last_revision_info(self):
853
rh = self.revision_history()
856
return (revno, rh[-1])
858
return (0, _mod_revision.NULL_REVISION)
860
@deprecated_method(deprecated_in((1, 6, 0)))
861
def missing_revisions(self, other, stop_revision=None):
862
"""Return a list of new revisions that would perfectly fit.
864
If self and other have not diverged, return a list of the revisions
865
present in other, but missing from self.
867
self_history = self.revision_history()
868
self_len = len(self_history)
869
other_history = other.revision_history()
870
other_len = len(other_history)
871
common_index = min(self_len, other_len) -1
872
if common_index >= 0 and \
873
self_history[common_index] != other_history[common_index]:
874
raise errors.DivergedBranches(self, other)
876
if stop_revision is None:
877
stop_revision = other_len
879
if stop_revision > other_len:
880
raise errors.NoSuchRevision(self, stop_revision)
881
return other_history[self_len:stop_revision]
884
def update_revisions(self, other, stop_revision=None, overwrite=False,
886
"""Pull in new perfect-fit revisions.
888
:param other: Another Branch to pull from
889
:param stop_revision: Updated until the given revision
890
:param overwrite: Always set the branch pointer, rather than checking
891
to see if it is a proper descendant.
892
:param graph: A Graph object that can be used to query history
893
information. This can be None.
896
return InterBranch.get(other, self).update_revisions(stop_revision,
899
def import_last_revision_info(self, source_repo, revno, revid):
1039
900
"""Set the last revision info, importing from another repo if necessary.
1041
902
This is used by the bound branch code to upload a revision to
1042
903
the master branch first before updating the tip of the local branch.
1043
Revisions referenced by source's tags are also transferred.
1045
:param source: Source branch to optionally fetch from
905
:param source_repo: Source repository to optionally fetch from
1046
906
:param revno: Revision number of the new tip
1047
907
:param revid: Revision id of the new tip
1048
:param lossy: Whether to discard metadata that can not be
1049
natively represented
1050
:return: Tuple with the new revision number and revision id
1051
(should only be different from the arguments when lossy=True)
1053
if not self.repository.has_same_location(source.repository):
1054
self.fetch(source, revid)
909
if not self.repository.has_same_location(source_repo):
910
self.repository.fetch(source_repo, revision_id=revid)
1055
911
self.set_last_revision_info(revno, revid)
1056
return (revno, revid)
1058
913
def revision_id_to_revno(self, revision_id):
1059
914
"""Given a revision id, return its revno"""
1060
915
if _mod_revision.is_null(revision_id):
1062
history = self._revision_history()
917
history = self.revision_history()
1064
919
return history.index(revision_id) + 1
1065
920
except ValueError:
1571
1414
object will be created every time regardless.
1417
_default_format = None
1418
"""The default format used for new branches."""
1421
"""The known formats."""
1423
can_set_append_revisions_only = True
1574
1425
def __eq__(self, other):
1575
1426
return self.__class__ is other.__class__
1577
1428
def __ne__(self, other):
1578
1429
return not (self == other)
1580
def get_reference(self, controldir, name=None):
1581
"""Get the target reference of the branch in controldir.
1432
def find_format(klass, a_bzrdir):
1433
"""Return the format for the branch object in a_bzrdir."""
1435
transport = a_bzrdir.get_branch_transport(None)
1436
format_string = transport.get_bytes("format")
1437
return klass._formats[format_string]
1438
except errors.NoSuchFile:
1439
raise errors.NotBranchError(path=transport.base)
1441
raise errors.UnknownFormatError(format=format_string, kind='branch')
1444
def get_default_format(klass):
1445
"""Return the current default format."""
1446
return klass._default_format
1448
def get_reference(self, a_bzrdir):
1449
"""Get the target reference of the branch in a_bzrdir.
1583
1451
format probing must have been completed before calling
1584
1452
this method - it is assumed that the format of the branch
1585
in controldir is correct.
1453
in a_bzrdir is correct.
1587
:param controldir: The controldir to get the branch data from.
1588
:param name: Name of the colocated branch to fetch
1455
:param a_bzrdir: The bzrdir to get the branch data from.
1589
1456
:return: None if the branch is not a reference branch.
1594
def set_reference(self, controldir, name, to_branch):
1595
"""Set the target reference of the branch in controldir.
1461
def set_reference(self, a_bzrdir, to_branch):
1462
"""Set the target reference of the branch in a_bzrdir.
1597
1464
format probing must have been completed before calling
1598
1465
this method - it is assumed that the format of the branch
1599
in controldir is correct.
1466
in a_bzrdir is correct.
1601
:param controldir: The controldir to set the branch reference for.
1602
:param name: Name of colocated branch to set, None for default
1468
:param a_bzrdir: The bzrdir to set the branch reference for.
1603
1469
:param to_branch: branch that the checkout is to reference
1605
1471
raise NotImplementedError(self.set_reference)
1473
def get_format_string(self):
1474
"""Return the ASCII format string that identifies this format."""
1475
raise NotImplementedError(self.get_format_string)
1607
1477
def get_format_description(self):
1608
1478
"""Return the short format description for this format."""
1609
1479
raise NotImplementedError(self.get_format_description)
1611
def _run_post_branch_init_hooks(self, controldir, name, branch):
1612
hooks = Branch.hooks['post_branch_init']
1615
params = BranchInitHookParams(self, controldir, name, branch)
1619
def initialize(self, controldir, name=None, repository=None,
1620
append_revisions_only=None):
1621
"""Create a branch of this format in controldir.
1623
:param name: Name of the colocated branch to create.
1481
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
1483
"""Initialize a branch in a bzrdir, with specified files
1485
:param a_bzrdir: The bzrdir to initialize the branch in
1486
:param utf8_files: The files to create as a list of
1487
(filename, content) tuples
1488
:param set_format: If True, set the format with
1489
self.get_format_string. (BzrBranch4 has its format set
1491
:return: a branch in this format
1493
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1494
branch_transport = a_bzrdir.get_branch_transport(self)
1496
'metadir': ('lock', lockdir.LockDir),
1497
'branch4': ('branch-lock', lockable_files.TransportLock),
1499
lock_name, lock_class = lock_map[lock_type]
1500
control_files = lockable_files.LockableFiles(branch_transport,
1501
lock_name, lock_class)
1502
control_files.create_lock()
1504
control_files.lock_write()
1505
except errors.LockContention:
1506
if lock_type != 'branch4':
1512
utf8_files += [('format', self.get_format_string())]
1514
for (filename, content) in utf8_files:
1515
branch_transport.put_bytes(
1517
mode=a_bzrdir._get_file_mode())
1520
control_files.unlock()
1521
return self.open(a_bzrdir, _found=True)
1523
def initialize(self, a_bzrdir):
1524
"""Create a branch of this format in a_bzrdir."""
1625
1525
raise NotImplementedError(self.initialize)
1627
1527
def is_supported(self):
1760
1640
"basis revision. hooks MUST NOT modify this delta. "
1761
1641
" future_tree is an in-memory tree obtained from "
1762
1642
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1764
self.add_hook('post_commit',
1643
"tree.", (0,91), None))
1644
self.create_hook(HookPoint('post_commit',
1765
1645
"Called in the bzr client after a commit has completed. "
1766
1646
"post_commit is called with (local, master, old_revno, old_revid, "
1767
1647
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1768
"commit to a branch.", (0, 15))
1769
self.add_hook('post_uncommit',
1648
"commit to a branch.", (0, 15), None))
1649
self.create_hook(HookPoint('post_uncommit',
1770
1650
"Called in the bzr client after an uncommit completes. "
1771
1651
"post_uncommit is called with (local, master, old_revno, "
1772
1652
"old_revid, new_revno, new_revid) where local is the local branch "
1773
1653
"or None, master is the target branch, and an empty branch "
1774
"receives new_revno of 0, new_revid of None.", (0, 15))
1775
self.add_hook('pre_change_branch_tip',
1654
"receives new_revno of 0, new_revid of None.", (0, 15), None))
1655
self.create_hook(HookPoint('pre_change_branch_tip',
1776
1656
"Called in bzr client and server before a change to the tip of a "
1777
1657
"branch is made. pre_change_branch_tip is called with a "
1778
1658
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1779
"commit, uncommit will all trigger this hook.", (1, 6))
1780
self.add_hook('post_change_branch_tip',
1659
"commit, uncommit will all trigger this hook.", (1, 6), None))
1660
self.create_hook(HookPoint('post_change_branch_tip',
1781
1661
"Called in bzr client and server after a change to the tip of a "
1782
1662
"branch is made. post_change_branch_tip is called with a "
1783
1663
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1784
"commit, uncommit will all trigger this hook.", (1, 4))
1785
self.add_hook('transform_fallback_location',
1664
"commit, uncommit will all trigger this hook.", (1, 4), None))
1665
self.create_hook(HookPoint('transform_fallback_location',
1786
1666
"Called when a stacked branch is activating its fallback "
1787
1667
"locations. transform_fallback_location is called with (branch, "
1788
1668
"url), and should return a new url. Returning the same url "
1855
1718
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1858
class BranchInitHookParams(object):
1859
"""Object holding parameters passed to `*_branch_init` hooks.
1861
There are 4 fields that hooks may wish to access:
1863
:ivar format: the branch format
1864
:ivar bzrdir: the ControlDir where the branch will be/has been initialized
1865
:ivar name: name of colocated branch, if any (or None)
1866
:ivar branch: the branch created
1868
Note that for lightweight checkouts, the bzrdir and format fields refer to
1869
the checkout, hence they are different from the corresponding fields in
1870
branch, which refer to the original branch.
1873
def __init__(self, format, controldir, name, branch):
1874
"""Create a group of BranchInitHook parameters.
1876
:param format: the branch format
1877
:param controldir: the ControlDir where the branch will be/has been
1879
:param name: name of colocated branch, if any (or None)
1880
:param branch: the branch created
1882
Note that for lightweight checkouts, the bzrdir and format fields refer
1883
to the checkout, hence they are different from the corresponding fields
1884
in branch, which refer to the original branch.
1886
self.format = format
1887
self.bzrdir = controldir
1889
self.branch = branch
1891
def __eq__(self, other):
1892
return self.__dict__ == other.__dict__
1895
return "<%s of %s>" % (self.__class__.__name__, self.branch)
1898
class SwitchHookParams(object):
1899
"""Object holding parameters passed to `*_switch` hooks.
1901
There are 4 fields that hooks may wish to access:
1903
:ivar control_dir: ControlDir of the checkout to change
1904
:ivar to_branch: branch that the checkout is to reference
1905
:ivar force: skip the check for local commits in a heavy checkout
1906
:ivar revision_id: revision ID to switch to (or None)
1909
def __init__(self, control_dir, to_branch, force, revision_id):
1910
"""Create a group of SwitchHook parameters.
1912
:param control_dir: ControlDir of the checkout to change
1913
:param to_branch: branch that the checkout is to reference
1914
:param force: skip the check for local commits in a heavy checkout
1915
:param revision_id: revision ID to switch to (or None)
1917
self.control_dir = control_dir
1918
self.to_branch = to_branch
1920
self.revision_id = revision_id
1922
def __eq__(self, other):
1923
return self.__dict__ == other.__dict__
1926
return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
1927
self.control_dir, self.to_branch,
1931
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
1932
"""Base class for branch formats that live in meta directories.
1721
class BzrBranchFormat4(BranchFormat):
1722
"""Bzr branch format 4.
1725
- a revision-history file.
1726
- a branch-lock lock file [ to be shared with the bzrdir ]
1729
def get_format_description(self):
1730
"""See BranchFormat.get_format_description()."""
1731
return "Branch format 4"
1733
def initialize(self, a_bzrdir):
1734
"""Create a branch of this format in a_bzrdir."""
1735
utf8_files = [('revision-history', ''),
1736
('branch-name', ''),
1738
return self._initialize_helper(a_bzrdir, utf8_files,
1739
lock_type='branch4', set_format=False)
1935
1741
def __init__(self):
1936
BranchFormat.__init__(self)
1937
bzrdir.BzrFormat.__init__(self)
1940
def find_format(klass, controldir, name=None):
1941
"""Return the format for the branch object in controldir."""
1943
transport = controldir.get_branch_transport(None, name=name)
1944
except errors.NoSuchFile:
1945
raise errors.NotBranchError(path=name, bzrdir=controldir)
1947
format_string = transport.get_bytes("format")
1948
except errors.NoSuchFile:
1949
raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1950
return klass._find_format(format_registry, 'branch', format_string)
1742
super(BzrBranchFormat4, self).__init__()
1743
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1745
def network_name(self):
1746
"""The network name for this format is the control dirs disk label."""
1747
return self._matchingbzrdir.get_format_string()
1749
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1750
"""See BranchFormat.open()."""
1752
# we are being called directly and must probe.
1753
raise NotImplementedError
1754
return BzrBranch(_format=self,
1755
_control_files=a_bzrdir._control_files,
1757
_repository=a_bzrdir.open_repository())
1760
return "Bazaar-NG branch format 4"
1763
class BranchFormatMetadir(BranchFormat):
1764
"""Common logic for meta-dir based branch formats."""
1952
1766
def _branch_class(self):
1953
1767
"""What class to instantiate on open calls."""
1954
1768
raise NotImplementedError(self._branch_class)
1956
def _get_initial_config(self, append_revisions_only=None):
1957
if append_revisions_only:
1958
return "append_revisions_only = True\n"
1960
# Avoid writing anything if append_revisions_only is disabled,
1961
# as that is the default.
1964
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1966
"""Initialize a branch in a control dir, with specified files
1968
:param a_bzrdir: The bzrdir to initialize the branch in
1969
:param utf8_files: The files to create as a list of
1970
(filename, content) tuples
1971
:param name: Name of colocated branch to create, if any
1972
:return: a branch in this format
1770
def network_name(self):
1771
"""A simple byte string uniquely identifying this format for RPC calls.
1773
Metadir branch formats use their format string.
1975
name = a_bzrdir._get_selected_branch()
1976
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1977
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1978
control_files = lockable_files.LockableFiles(branch_transport,
1979
'lock', lockdir.LockDir)
1980
control_files.create_lock()
1981
control_files.lock_write()
1983
utf8_files += [('format', self.as_string())]
1984
for (filename, content) in utf8_files:
1985
branch_transport.put_bytes(
1987
mode=a_bzrdir._get_file_mode())
1989
control_files.unlock()
1990
branch = self.open(a_bzrdir, name, _found=True,
1991
found_repository=repository)
1992
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1775
return self.get_format_string()
1995
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
1996
found_repository=None, possible_transports=None):
1777
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1997
1778
"""See BranchFormat.open()."""
1999
name = a_bzrdir._get_selected_branch()
2001
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
1780
format = BranchFormat.find_format(a_bzrdir)
2002
1781
if format.__class__ != self.__class__:
2003
1782
raise AssertionError("wrong format %r found for %r" %
2004
1783
(format, self))
2005
transport = a_bzrdir.get_branch_transport(None, name=name)
1785
transport = a_bzrdir.get_branch_transport(None)
2007
1786
control_files = lockable_files.LockableFiles(transport, 'lock',
2008
1787
lockdir.LockDir)
2009
if found_repository is None:
2010
found_repository = a_bzrdir.find_repository()
2011
1788
return self._branch_class()(_format=self,
2012
1789
_control_files=control_files,
2014
1790
a_bzrdir=a_bzrdir,
2015
_repository=found_repository,
2016
ignore_fallbacks=ignore_fallbacks,
2017
possible_transports=possible_transports)
1791
_repository=a_bzrdir.find_repository(),
1792
ignore_fallbacks=ignore_fallbacks)
2018
1793
except errors.NoSuchFile:
2019
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2022
def _matchingbzrdir(self):
2023
ret = bzrdir.BzrDirMetaFormat1()
2024
ret.set_branch_format(self)
2027
def supports_tags(self):
2030
def supports_leaving_lock(self):
2033
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
2035
BranchFormat.check_support_status(self,
2036
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
2038
bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
2039
recommend_upgrade=recommend_upgrade, basedir=basedir)
1794
raise errors.NotBranchError(path=transport.base)
1797
super(BranchFormatMetadir, self).__init__()
1798
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1799
self._matchingbzrdir.set_branch_format(self)
1801
def supports_tags(self):
1805
class BzrBranchFormat5(BranchFormatMetadir):
1806
"""Bzr branch format 5.
1809
- a revision-history file.
1811
- a lock dir guarding the branch itself
1812
- all of this stored in a branch/ subdirectory
1813
- works with shared repositories.
1815
This format is new in bzr 0.8.
1818
def _branch_class(self):
1821
def get_format_string(self):
1822
"""See BranchFormat.get_format_string()."""
1823
return "Bazaar-NG branch format 5\n"
1825
def get_format_description(self):
1826
"""See BranchFormat.get_format_description()."""
1827
return "Branch format 5"
1829
def initialize(self, a_bzrdir):
1830
"""Create a branch of this format in a_bzrdir."""
1831
utf8_files = [('revision-history', ''),
1832
('branch-name', ''),
1834
return self._initialize_helper(a_bzrdir, utf8_files)
1836
def supports_tags(self):
2042
1840
class BzrBranchFormat6(BranchFormatMetadir):
2183
1969
"""See BranchFormat.get_format_description()."""
2184
1970
return "Checkout reference format 1"
2186
def get_reference(self, a_bzrdir, name=None):
1972
def get_reference(self, a_bzrdir):
2187
1973
"""See BranchFormat.get_reference()."""
2188
transport = a_bzrdir.get_branch_transport(None, name=name)
1974
transport = a_bzrdir.get_branch_transport(None)
2189
1975
return transport.get_bytes('location')
2191
def set_reference(self, a_bzrdir, name, to_branch):
1977
def set_reference(self, a_bzrdir, to_branch):
2192
1978
"""See BranchFormat.set_reference()."""
2193
transport = a_bzrdir.get_branch_transport(None, name=name)
1979
transport = a_bzrdir.get_branch_transport(None)
2194
1980
location = transport.put_bytes('location', to_branch.base)
2196
def initialize(self, a_bzrdir, name=None, target_branch=None,
2197
repository=None, append_revisions_only=None):
1982
def initialize(self, a_bzrdir, target_branch=None):
2198
1983
"""Create a branch of this format in a_bzrdir."""
2199
1984
if target_branch is None:
2200
1985
# this format does not implement branch itself, thus the implicit
2201
1986
# creation contract must see it as uninitializable
2202
1987
raise errors.UninitializableFormat(self)
2203
mutter('creating branch reference in %s', a_bzrdir.user_url)
2204
if a_bzrdir._format.fixed_components:
2205
raise errors.IncompatibleFormat(self, a_bzrdir._format)
2207
name = a_bzrdir._get_selected_branch()
2208
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1988
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1989
branch_transport = a_bzrdir.get_branch_transport(self)
2209
1990
branch_transport.put_bytes('location',
2210
target_branch.user_url)
2211
branch_transport.put_bytes('format', self.as_string())
2212
branch = self.open(a_bzrdir, name, _found=True,
1991
target_branch.bzrdir.root_transport.base)
1992
branch_transport.put_bytes('format', self.get_format_string())
1994
a_bzrdir, _found=True,
2213
1995
possible_transports=[target_branch.bzrdir.root_transport])
2214
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1998
super(BranchReferenceFormat, self).__init__()
1999
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2000
self._matchingbzrdir.set_branch_format(self)
2217
2002
def _make_reference_clone_function(format, a_branch):
2218
2003
"""Create a clone() routine for a branch dynamically."""
2219
2004
def clone(to_bzrdir, revision_id=None,
2220
2005
repository_policy=None):
2221
2006
"""See Branch.clone()."""
2222
return format.initialize(to_bzrdir, target_branch=a_branch)
2007
return format.initialize(to_bzrdir, a_branch)
2223
2008
# cannot obey revision_id limits when cloning a reference ...
2224
2009
# FIXME RBC 20060210 either nuke revision_id for clone, or
2225
2010
# emit some sort of warning/error to the caller ?!
2228
def open(self, a_bzrdir, name=None, _found=False, location=None,
2229
possible_transports=None, ignore_fallbacks=False,
2230
found_repository=None):
2013
def open(self, a_bzrdir, _found=False, location=None,
2014
possible_transports=None, ignore_fallbacks=False):
2231
2015
"""Return the branch that the branch reference in a_bzrdir points at.
2233
2017
:param a_bzrdir: A BzrDir that contains a branch.
2234
:param name: Name of colocated branch to open, if any
2235
2018
:param _found: a private parameter, do not use it. It is used to
2236
2019
indicate if format probing has already be done.
2237
2020
:param ignore_fallbacks: when set, no fallback branches will be opened
2457
2186
"""See Branch.print_file."""
2458
2187
return self.repository.print_file(file, revision_id)
2189
def _write_revision_history(self, history):
2190
"""Factored out of set_revision_history.
2192
This performs the actual writing to disk.
2193
It is intended to be called by BzrBranch5.set_revision_history."""
2194
self._transport.put_bytes(
2195
'revision-history', '\n'.join(history),
2196
mode=self.bzrdir._get_file_mode())
2199
def set_revision_history(self, rev_history):
2200
"""See Branch.set_revision_history."""
2201
if 'evil' in debug.debug_flags:
2202
mutter_callsite(3, "set_revision_history scales with history.")
2203
check_not_reserved_id = _mod_revision.check_not_reserved_id
2204
for rev_id in rev_history:
2205
check_not_reserved_id(rev_id)
2206
if Branch.hooks['post_change_branch_tip']:
2207
# Don't calculate the last_revision_info() if there are no hooks
2209
old_revno, old_revid = self.last_revision_info()
2210
if len(rev_history) == 0:
2211
revid = _mod_revision.NULL_REVISION
2213
revid = rev_history[-1]
2214
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2215
self._write_revision_history(rev_history)
2216
self._clear_cached_state()
2217
self._cache_revision_history(rev_history)
2218
for hook in Branch.hooks['set_rh']:
2219
hook(self, rev_history)
2220
if Branch.hooks['post_change_branch_tip']:
2221
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2223
def _synchronize_history(self, destination, revision_id):
2224
"""Synchronize last revision and revision history between branches.
2226
This version is most efficient when the destination is also a
2227
BzrBranch5, but works for BzrBranch6 as long as the revision
2228
history is the true lefthand parent history, and all of the revisions
2229
are in the destination's repository. If not, set_revision_history
2232
:param destination: The branch to copy the history into
2233
:param revision_id: The revision-id to truncate history at. May
2234
be None to copy complete history.
2236
if not isinstance(destination._format, BzrBranchFormat5):
2237
super(BzrBranch, self)._synchronize_history(
2238
destination, revision_id)
2240
if revision_id == _mod_revision.NULL_REVISION:
2243
new_history = self.revision_history()
2244
if revision_id is not None and new_history != []:
2246
new_history = new_history[:new_history.index(revision_id) + 1]
2248
rev = self.repository.get_revision(revision_id)
2249
new_history = rev.get_history(self.repository)[1:]
2250
destination.set_revision_history(new_history)
2460
2252
@needs_write_lock
2461
2253
def set_last_revision_info(self, revno, revision_id):
2462
if not revision_id or not isinstance(revision_id, basestring):
2463
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2254
"""Set the last revision of this branch.
2256
The caller is responsible for checking that the revno is correct
2257
for this revision id.
2259
It may be possible to set the branch last revision to an id not
2260
present in the repository. However, branches can also be
2261
configured to check constraints on history, in which case this may not
2464
2264
revision_id = _mod_revision.ensure_null(revision_id)
2465
old_revno, old_revid = self.last_revision_info()
2466
if self.get_append_revisions_only():
2467
self._check_history_violation(revision_id)
2468
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2469
self._write_last_revision_info(revno, revision_id)
2470
self._clear_cached_state()
2471
self._last_revision_info_cache = revno, revision_id
2472
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2265
# this old format stores the full history, but this api doesn't
2266
# provide it, so we must generate, and might as well check it's
2268
history = self._lefthand_history(revision_id)
2269
if len(history) != revno:
2270
raise AssertionError('%d != %d' % (len(history), revno))
2271
self.set_revision_history(history)
2273
def _gen_revision_history(self):
2274
history = self._transport.get_bytes('revision-history').split('\n')
2275
if history[-1:] == ['']:
2276
# There shouldn't be a trailing newline, but just in case.
2281
def generate_revision_history(self, revision_id, last_rev=None,
2283
"""Create a new revision history that will finish with revision_id.
2285
:param revision_id: the new tip to use.
2286
:param last_rev: The previous last_revision. If not None, then this
2287
must be a ancestory of revision_id, or DivergedBranches is raised.
2288
:param other_branch: The other branch that DivergedBranches should
2289
raise with respect to.
2291
self.set_revision_history(self._lefthand_history(revision_id,
2292
last_rev, other_branch))
2474
2294
def basis_tree(self):
2475
2295
"""See Branch.basis_tree."""
2658
2473
self._last_revision_info_cache = None
2659
2474
self._reference_info = None
2476
def _last_revision_info(self):
2477
revision_string = self._transport.get_bytes('last-revision')
2478
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2479
revision_id = cache_utf8.get_cached_utf8(revision_id)
2481
return revno, revision_id
2483
def _write_last_revision_info(self, revno, revision_id):
2484
"""Simply write out the revision id, with no checks.
2486
Use set_last_revision_info to perform this safely.
2488
Does not update the revision_history cache.
2489
Intended to be called by set_last_revision_info and
2490
_write_revision_history.
2492
revision_id = _mod_revision.ensure_null(revision_id)
2493
out_string = '%d %s\n' % (revno, revision_id)
2494
self._transport.put_bytes('last-revision', out_string,
2495
mode=self.bzrdir._get_file_mode())
2498
def set_last_revision_info(self, revno, revision_id):
2499
revision_id = _mod_revision.ensure_null(revision_id)
2500
old_revno, old_revid = self.last_revision_info()
2501
if self._get_append_revisions_only():
2502
self._check_history_violation(revision_id)
2503
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2504
self._write_last_revision_info(revno, revision_id)
2505
self._clear_cached_state()
2506
self._last_revision_info_cache = revno, revision_id
2507
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2509
def _synchronize_history(self, destination, revision_id):
2510
"""Synchronize last revision and revision history between branches.
2512
:see: Branch._synchronize_history
2514
# XXX: The base Branch has a fast implementation of this method based
2515
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2516
# that uses set_revision_history. This class inherits from BzrBranch5,
2517
# but wants the fast implementation, so it calls
2518
# Branch._synchronize_history directly.
2519
Branch._synchronize_history(self, destination, revision_id)
2661
2521
def _check_history_violation(self, revision_id):
2662
current_revid = self.last_revision()
2663
last_revision = _mod_revision.ensure_null(current_revid)
2522
last_revision = _mod_revision.ensure_null(self.last_revision())
2664
2523
if _mod_revision.is_null(last_revision):
2666
graph = self.repository.get_graph()
2667
for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2668
if lh_ancestor == current_revid:
2670
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2525
if last_revision not in self._lefthand_history(revision_id):
2526
raise errors.AppendRevisionsOnlyViolation(self.base)
2672
2528
def _gen_revision_history(self):
2673
2529
"""Generate the revision history from last revision
3086
2981
raise NotImplementedError(self.push)
3089
def copy_content_into(self, revision_id=None):
3090
"""Copy the content of source into target
3092
revision_id: if not None, the revision history in the new branch will
3093
be truncated to end with revision_id.
3095
raise NotImplementedError(self.copy_content_into)
3098
def fetch(self, stop_revision=None, limit=None):
3101
:param stop_revision: Last revision to fetch
3102
:param limit: Optional rough limit of revisions to fetch
3104
raise NotImplementedError(self.fetch)
3107
def _fix_overwrite_type(overwrite):
3108
if isinstance(overwrite, bool):
3110
return ["history", "tags"]
3116
2984
class GenericInterBranch(InterBranch):
3117
"""InterBranch implementation that uses public Branch functions."""
3120
def is_compatible(klass, source, target):
3121
# GenericBranch uses the public API, so always compatible
3125
def _get_branch_formats_to_test(klass):
3126
return [(format_registry.get_default(), format_registry.get_default())]
3129
def unwrap_format(klass, format):
3130
if isinstance(format, remote.RemoteBranchFormat):
3131
format._ensure_real()
3132
return format._custom_format
3136
def copy_content_into(self, revision_id=None):
3137
"""Copy the content of source into target
3139
revision_id: if not None, the revision history in the new branch will
3140
be truncated to end with revision_id.
3142
self.source.update_references(self.target)
3143
self.source._synchronize_history(self.target, revision_id)
3145
parent = self.source.get_parent()
3146
except errors.InaccessibleParent, e:
3147
mutter('parent was not accessible to copy: %s', e)
3150
self.target.set_parent(parent)
3151
if self.source._push_should_merge_tags():
3152
self.source.tags.merge_to(self.target.tags)
3155
def fetch(self, stop_revision=None, limit=None):
3156
if self.target.base == self.source.base:
2985
"""InterBranch implementation that uses public Branch functions.
2989
def _get_branch_formats_to_test():
2990
return BranchFormat._default_format, BranchFormat._default_format
2992
def update_revisions(self, stop_revision=None, overwrite=False,
2994
"""See InterBranch.update_revisions()."""
3158
2995
self.source.lock_read()
3160
fetch_spec_factory = fetch.FetchSpecFactory()
3161
fetch_spec_factory.source_branch = self.source
3162
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3163
fetch_spec_factory.source_repo = self.source.repository
3164
fetch_spec_factory.target_repo = self.target.repository
3165
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3166
fetch_spec_factory.limit = limit
3167
fetch_spec = fetch_spec_factory.make_fetch_spec()
3168
return self.target.repository.fetch(self.source.repository,
3169
fetch_spec=fetch_spec)
2997
other_revno, other_last_revision = self.source.last_revision_info()
2998
stop_revno = None # unknown
2999
if stop_revision is None:
3000
stop_revision = other_last_revision
3001
if _mod_revision.is_null(stop_revision):
3002
# if there are no commits, we're done.
3004
stop_revno = other_revno
3006
# what's the current last revision, before we fetch [and change it
3008
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3009
# we fetch here so that we don't process data twice in the common
3010
# case of having something to pull, and so that the check for
3011
# already merged can operate on the just fetched graph, which will
3012
# be cached in memory.
3013
self.target.fetch(self.source, stop_revision)
3014
# Check to see if one is an ancestor of the other
3017
graph = self.target.repository.get_graph()
3018
if self.target._check_if_descendant_or_diverged(
3019
stop_revision, last_rev, graph, self.source):
3020
# stop_revision is a descendant of last_rev, but we aren't
3021
# overwriting, so we're done.
3023
if stop_revno is None:
3025
graph = self.target.repository.get_graph()
3026
this_revno, this_last_revision = \
3027
self.target.last_revision_info()
3028
stop_revno = graph.find_distance_to_null(stop_revision,
3029
[(other_last_revision, other_revno),
3030
(this_last_revision, this_revno)])
3031
self.target.set_last_revision_info(stop_revno, stop_revision)
3171
3033
self.source.unlock()
3174
def _update_revisions(self, stop_revision=None, overwrite=False,
3176
other_revno, other_last_revision = self.source.last_revision_info()
3177
stop_revno = None # unknown
3178
if stop_revision is None:
3179
stop_revision = other_last_revision
3180
if _mod_revision.is_null(stop_revision):
3181
# if there are no commits, we're done.
3183
stop_revno = other_revno
3185
# what's the current last revision, before we fetch [and change it
3187
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3188
# we fetch here so that we don't process data twice in the common
3189
# case of having something to pull, and so that the check for
3190
# already merged can operate on the just fetched graph, which will
3191
# be cached in memory.
3192
self.fetch(stop_revision=stop_revision)
3193
# Check to see if one is an ancestor of the other
3196
graph = self.target.repository.get_graph()
3197
if self.target._check_if_descendant_or_diverged(
3198
stop_revision, last_rev, graph, self.source):
3199
# stop_revision is a descendant of last_rev, but we aren't
3200
# overwriting, so we're done.
3202
if stop_revno is None:
3204
graph = self.target.repository.get_graph()
3205
this_revno, this_last_revision = \
3206
self.target.last_revision_info()
3207
stop_revno = graph.find_distance_to_null(stop_revision,
3208
[(other_last_revision, other_revno),
3209
(this_last_revision, this_revno)])
3210
self.target.set_last_revision_info(stop_revno, stop_revision)
3213
3035
def pull(self, overwrite=False, stop_revision=None,
3214
possible_transports=None, run_hooks=True,
3215
_override_hook_target=None, local=False):
3216
"""Pull from source into self, updating my master if any.
3218
:param run_hooks: Private parameter - if false, this branch
3219
is being called because it's the master of the primary branch,
3220
so it should not run its hooks.
3222
bound_location = self.target.get_bound_location()
3223
if local and not bound_location:
3224
raise errors.LocalRequiresBoundBranch()
3225
master_branch = None
3226
source_is_master = False
3228
# bound_location comes from a config file, some care has to be
3229
# taken to relate it to source.user_url
3230
normalized = urlutils.normalize_url(bound_location)
3232
relpath = self.source.user_transport.relpath(normalized)
3233
source_is_master = (relpath == '')
3234
except (errors.PathNotChild, errors.InvalidURL):
3235
source_is_master = False
3236
if not local and bound_location and not source_is_master:
3237
# not pulling from master, so we need to update master.
3238
master_branch = self.target.get_master_branch(possible_transports)
3239
master_branch.lock_write()
3242
# pull from source into master.
3243
master_branch.pull(self.source, overwrite, stop_revision,
3245
return self._pull(overwrite,
3246
stop_revision, _hook_master=master_branch,
3247
run_hooks=run_hooks,
3248
_override_hook_target=_override_hook_target,
3249
merge_tags_to_master=not source_is_master)
3252
master_branch.unlock()
3254
def push(self, overwrite=False, stop_revision=None, lossy=False,
3255
_override_hook_source_branch=None):
3256
"""See InterBranch.push.
3258
This is the basic concrete implementation of push()
3260
:param _override_hook_source_branch: If specified, run the hooks
3261
passing this Branch as the source, rather than self. This is for
3262
use of RemoteBranch, where push is delegated to the underlying
3266
raise errors.LossyPushToSameVCS(self.source, self.target)
3267
# TODO: Public option to disable running hooks - should be trivial but
3270
op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3271
op.add_cleanup(self.source.lock_read().unlock)
3272
op.add_cleanup(self.target.lock_write().unlock)
3273
return op.run(overwrite, stop_revision,
3274
_override_hook_source_branch=_override_hook_source_branch)
3276
def _basic_push(self, overwrite, stop_revision):
3277
"""Basic implementation of push without bound branches or hooks.
3279
Must be called with source read locked and target write locked.
3281
result = BranchPushResult()
3282
result.source_branch = self.source
3283
result.target_branch = self.target
3284
result.old_revno, result.old_revid = self.target.last_revision_info()
3285
self.source.update_references(self.target)
3286
overwrite = _fix_overwrite_type(overwrite)
3287
if result.old_revid != stop_revision:
3288
# We assume that during 'push' this repository is closer than
3290
graph = self.source.repository.get_graph(self.target.repository)
3291
self._update_revisions(stop_revision,
3292
overwrite=("history" in overwrite),
3294
if self.source._push_should_merge_tags():
3295
result.tag_updates, result.tag_conflicts = (
3296
self.source.tags.merge_to(
3297
self.target.tags, "tags" in overwrite))
3298
result.new_revno, result.new_revid = self.target.last_revision_info()
3301
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
3302
_override_hook_source_branch=None):
3303
"""Push from source into target, and into target's master if any.
3306
if _override_hook_source_branch:
3307
result.source_branch = _override_hook_source_branch
3308
for hook in Branch.hooks['post_push']:
3311
bound_location = self.target.get_bound_location()
3312
if bound_location and self.target.base != bound_location:
3313
# there is a master branch.
3315
# XXX: Why the second check? Is it even supported for a branch to
3316
# be bound to itself? -- mbp 20070507
3317
master_branch = self.target.get_master_branch()
3318
master_branch.lock_write()
3319
operation.add_cleanup(master_branch.unlock)
3320
# push into the master from the source branch.
3321
master_inter = InterBranch.get(self.source, master_branch)
3322
master_inter._basic_push(overwrite, stop_revision)
3323
# and push into the target branch from the source. Note that
3324
# we push from the source branch again, because it's considered
3325
# the highest bandwidth repository.
3326
result = self._basic_push(overwrite, stop_revision)
3327
result.master_branch = master_branch
3328
result.local_branch = self.target
3330
master_branch = None
3332
result = self._basic_push(overwrite, stop_revision)
3333
# TODO: Why set master_branch and local_branch if there's no
3334
# binding? Maybe cleaner to just leave them unset? -- mbp
3336
result.master_branch = self.target
3337
result.local_branch = None
3341
def _pull(self, overwrite=False, stop_revision=None,
3342
3036
possible_transports=None, _hook_master=None, run_hooks=True,
3343
_override_hook_target=None, local=False,
3344
merge_tags_to_master=True):
3037
_override_hook_target=None, local=False):
3345
3038
"""See Branch.pull.
3347
This function is the core worker, used by GenericInterBranch.pull to
3348
avoid duplication when pulling source->master and source->local.
3350
3040
:param _hook_master: Private parameter - set the branch to
3351
3041
be supplied as the master to pull hooks.
3352
3042
:param run_hooks: Private parameter - if false, this branch
3353
3043
is being called because it's the master of the primary branch,
3354
3044
so it should not run its hooks.
3355
is being called because it's the master of the primary branch,
3356
so it should not run its hooks.
3357
3045
:param _override_hook_target: Private parameter - set the branch to be
3358
3046
supplied as the target_branch to pull hooks.
3359
3047
:param local: Only update the local branch, and not the bound branch.
3403
3087
self.source.unlock()
3090
def push(self, overwrite=False, stop_revision=None,
3091
_override_hook_source_branch=None):
3092
"""See InterBranch.push.
3094
This is the basic concrete implementation of push()
3096
:param _override_hook_source_branch: If specified, run
3097
the hooks passing this Branch as the source, rather than self.
3098
This is for use of RemoteBranch, where push is delegated to the
3099
underlying vfs-based Branch.
3101
# TODO: Public option to disable running hooks - should be trivial but
3103
self.source.lock_read()
3105
return _run_with_write_locked_target(
3106
self.target, self._push_with_bound_branches, overwrite,
3108
_override_hook_source_branch=_override_hook_source_branch)
3110
self.source.unlock()
3112
def _push_with_bound_branches(self, overwrite, stop_revision,
3113
_override_hook_source_branch=None):
3114
"""Push from source into target, and into target's master if any.
3117
if _override_hook_source_branch:
3118
result.source_branch = _override_hook_source_branch
3119
for hook in Branch.hooks['post_push']:
3122
bound_location = self.target.get_bound_location()
3123
if bound_location and self.target.base != bound_location:
3124
# there is a master branch.
3126
# XXX: Why the second check? Is it even supported for a branch to
3127
# be bound to itself? -- mbp 20070507
3128
master_branch = self.target.get_master_branch()
3129
master_branch.lock_write()
3131
# push into the master from the source branch.
3132
self.source._basic_push(master_branch, overwrite, stop_revision)
3133
# and push into the target branch from the source. Note that we
3134
# push from the source branch again, because its considered the
3135
# highest bandwidth repository.
3136
result = self.source._basic_push(self.target, overwrite,
3138
result.master_branch = master_branch
3139
result.local_branch = self.target
3143
master_branch.unlock()
3146
result = self.source._basic_push(self.target, overwrite,
3148
# TODO: Why set master_branch and local_branch if there's no
3149
# binding? Maybe cleaner to just leave them unset? -- mbp
3151
result.master_branch = self.target
3152
result.local_branch = None
3157
def is_compatible(self, source, target):
3158
# GenericBranch uses the public API, so always compatible
3162
class InterToBranch5(GenericInterBranch):
3165
def _get_branch_formats_to_test():
3166
return BranchFormat._default_format, BzrBranchFormat5()
3168
def pull(self, overwrite=False, stop_revision=None,
3169
possible_transports=None, run_hooks=True,
3170
_override_hook_target=None, local=False):
3171
"""Pull from source into self, updating my master if any.
3173
:param run_hooks: Private parameter - if false, this branch
3174
is being called because it's the master of the primary branch,
3175
so it should not run its hooks.
3177
bound_location = self.target.get_bound_location()
3178
if local and not bound_location:
3179
raise errors.LocalRequiresBoundBranch()
3180
master_branch = None
3181
if not local and bound_location and self.source.base != bound_location:
3182
# not pulling from master, so we need to update master.
3183
master_branch = self.target.get_master_branch(possible_transports)
3184
master_branch.lock_write()
3187
# pull from source into master.
3188
master_branch.pull(self.source, overwrite, stop_revision,
3190
return super(InterToBranch5, self).pull(overwrite,
3191
stop_revision, _hook_master=master_branch,
3192
run_hooks=run_hooks,
3193
_override_hook_target=_override_hook_target)
3196
master_branch.unlock()
3407
3199
InterBranch.register_optimiser(GenericInterBranch)
3200
InterBranch.register_optimiser(InterToBranch5)