14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19
from cStringIO import StringIO
21
21
from bzrlib.lazy_import import lazy_import
22
22
lazy_import(globals(), """
23
from itertools import chain
24
24
from bzrlib import (
27
config as _mod_config,
35
revision as _mod_revision,
43
from bzrlib.config import BranchConfig, TransportConfig
44
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
45
from bzrlib.tag import (
29
config as _mod_config,
38
revision as _mod_revision,
46
from bzrlib.i18n import gettext, ngettext
51
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
52
from bzrlib.hooks import HookPoint, Hooks
52
from bzrlib.decorators import (
57
from bzrlib.hooks import Hooks
53
58
from bzrlib.inter import InterObject
54
59
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
55
60
from bzrlib import registry
86
87
def user_transport(self):
87
88
return self.bzrdir.user_transport
89
def __init__(self, *ignored, **ignored_too):
90
def __init__(self, possible_transports=None):
90
91
self.tags = self._format.make_tags(self)
91
92
self._revision_history_cache = None
92
93
self._revision_id_to_revno_cache = None
93
94
self._partial_revision_id_to_revno_cache = {}
94
95
self._partial_revision_history_cache = []
96
self._tags_bytes = None
95
97
self._last_revision_info_cache = None
98
self._master_branch_cache = None
96
99
self._merge_sorted_revisions_cache = None
100
self._open_hook(possible_transports)
98
101
hooks = Branch.hooks['open']
99
102
for hook in hooks:
102
def _open_hook(self):
105
def _open_hook(self, possible_transports):
103
106
"""Called by init to allow simpler extension of the base class."""
105
def _activate_fallback_location(self, url):
108
def _activate_fallback_location(self, url, possible_transports):
106
109
"""Activate the branch/repository from url as a fallback repository."""
107
repo = self._get_fallback_repository(url)
110
for existing_fallback_repo in self.repository._fallback_repositories:
111
if existing_fallback_repo.user_url == url:
112
# This fallback is already configured. This probably only
113
# happens because ControlDir.sprout is a horrible mess. To avoid
114
# confusing _unstack we don't add this a second time.
115
mutter('duplicate activation of fallback %r on %r', url, self)
117
repo = self._get_fallback_repository(url, possible_transports)
108
118
if repo.has_same_location(self.repository):
109
119
raise errors.UnstackableLocationError(self.user_url, url)
110
120
self.repository.add_fallback_repository(repo)
164
174
For instance, if the branch is at URL/.bzr/branch,
165
175
Branch.open(URL) -> a Branch instance.
167
control = bzrdir.BzrDir.open(base, _unsupported,
177
control = controldir.ControlDir.open(base, _unsupported,
168
178
possible_transports=possible_transports)
169
return control.open_branch(unsupported=_unsupported)
179
return control.open_branch(unsupported=_unsupported,
180
possible_transports=possible_transports)
172
def open_from_transport(transport, name=None, _unsupported=False):
183
def open_from_transport(transport, name=None, _unsupported=False,
184
possible_transports=None):
173
185
"""Open the branch rooted at transport"""
174
control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
175
return control.open_branch(name=name, unsupported=_unsupported)
186
control = controldir.ControlDir.open_from_transport(transport, _unsupported)
187
return control.open_branch(name=name, unsupported=_unsupported,
188
possible_transports=possible_transports)
178
191
def open_containing(url, possible_transports=None):
492
520
rev_iter = iter(merge_sorted_revisions)
493
521
if start_revision_id is not None:
494
522
for node in rev_iter:
495
rev_id = node.key[-1]
496
524
if rev_id != start_revision_id:
499
527
# The decision to include the start or not
500
528
# depends on the stop_rule if a stop is provided
501
529
# so pop this node back into the iterator
502
rev_iter = chain(iter([node]), rev_iter)
530
rev_iter = itertools.chain(iter([node]), rev_iter)
504
532
if stop_revision_id is None:
505
533
# Yield everything
506
534
for node in rev_iter:
507
rev_id = node.key[-1]
508
536
yield (rev_id, node.merge_depth, node.revno,
509
537
node.end_of_merge)
510
538
elif stop_rule == 'exclude':
511
539
for node in rev_iter:
512
rev_id = node.key[-1]
513
541
if rev_id == stop_revision_id:
515
543
yield (rev_id, node.merge_depth, node.revno,
516
544
node.end_of_merge)
517
545
elif stop_rule == 'include':
518
546
for node in rev_iter:
519
rev_id = node.key[-1]
520
548
yield (rev_id, node.merge_depth, node.revno,
521
549
node.end_of_merge)
522
550
if rev_id == stop_revision_id:
649
679
raise errors.UnsupportedOperation(self.get_reference_info, self)
651
681
@needs_write_lock
652
def fetch(self, from_branch, last_revision=None, pb=None):
682
def fetch(self, from_branch, last_revision=None, limit=None):
653
683
"""Copy revisions from from_branch into this branch.
655
685
:param from_branch: Where to copy from.
656
686
:param last_revision: What revision to stop at (None for at the end
658
:param pb: An optional progress bar to use.
688
:param limit: Optional rough limit of revisions to fetch
661
if self.base == from_branch.base:
664
symbol_versioning.warn(
665
symbol_versioning.deprecated_in((1, 14, 0))
666
% "pb parameter to fetch()")
667
from_branch.lock_read()
669
if last_revision is None:
670
last_revision = from_branch.last_revision()
671
last_revision = _mod_revision.ensure_null(last_revision)
672
return self.repository.fetch(from_branch.repository,
673
revision_id=last_revision,
691
return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
678
693
def get_bound_location(self):
679
694
"""Return the URL of the branch we are bound to.
700
715
:param committer: Optional committer to set for commit.
701
716
:param revprops: Optional dictionary of revision properties.
702
717
:param revision_id: Optional revision id.
718
:param lossy: Whether to discard data that can not be natively
719
represented, when pushing to a foreign VCS
706
config = self.get_config()
722
if config_stack is None:
723
config_stack = self.get_config_stack()
708
return self.repository.get_commit_builder(self, parents, config,
709
timestamp, timezone, committer, revprops, revision_id)
725
return self.repository.get_commit_builder(self, parents, config_stack,
726
timestamp, timezone, committer, revprops, revision_id,
711
729
def get_master_branch(self, possible_transports=None):
712
730
"""Return the branch we are bound to.
739
759
"""Print `file` to stdout."""
740
760
raise NotImplementedError(self.print_file)
762
@deprecated_method(deprecated_in((2, 4, 0)))
742
763
def set_revision_history(self, rev_history):
743
raise NotImplementedError(self.set_revision_history)
764
"""See Branch.set_revision_history."""
765
self._set_revision_history(rev_history)
768
def _set_revision_history(self, rev_history):
769
if len(rev_history) == 0:
770
revid = _mod_revision.NULL_REVISION
772
revid = rev_history[-1]
773
if rev_history != self._lefthand_history(revid):
774
raise errors.NotLefthandHistory(rev_history)
775
self.set_last_revision_info(len(rev_history), revid)
776
self._cache_revision_history(rev_history)
777
for hook in Branch.hooks['set_rh']:
778
hook(self, rev_history)
781
def set_last_revision_info(self, revno, revision_id):
782
"""Set the last revision of this branch.
784
The caller is responsible for checking that the revno is correct
785
for this revision id.
787
It may be possible to set the branch last revision to an id not
788
present in the repository. However, branches can also be
789
configured to check constraints on history, in which case this may not
792
raise NotImplementedError(self.set_last_revision_info)
795
def generate_revision_history(self, revision_id, last_rev=None,
797
"""See Branch.generate_revision_history"""
798
graph = self.repository.get_graph()
799
(last_revno, last_revid) = self.last_revision_info()
800
known_revision_ids = [
801
(last_revid, last_revno),
802
(_mod_revision.NULL_REVISION, 0),
804
if last_rev is not None:
805
if not graph.is_ancestor(last_rev, revision_id):
806
# our previous tip is not merged into stop_revision
807
raise errors.DivergedBranches(self, other_branch)
808
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
809
self.set_last_revision_info(revno, revision_id)
745
811
@needs_write_lock
746
812
def set_parent(self, url):
968
1049
:return: A tuple (revno, revision_id).
970
1051
if self._last_revision_info_cache is None:
971
self._last_revision_info_cache = self._last_revision_info()
1052
self._last_revision_info_cache = self._read_last_revision_info()
972
1053
return self._last_revision_info_cache
974
def _last_revision_info(self):
975
rh = self.revision_history()
978
return (revno, rh[-1])
980
return (0, _mod_revision.NULL_REVISION)
982
@deprecated_method(deprecated_in((1, 6, 0)))
983
def missing_revisions(self, other, stop_revision=None):
984
"""Return a list of new revisions that would perfectly fit.
986
If self and other have not diverged, return a list of the revisions
987
present in other, but missing from self.
989
self_history = self.revision_history()
990
self_len = len(self_history)
991
other_history = other.revision_history()
992
other_len = len(other_history)
993
common_index = min(self_len, other_len) -1
994
if common_index >= 0 and \
995
self_history[common_index] != other_history[common_index]:
996
raise errors.DivergedBranches(self, other)
998
if stop_revision is None:
999
stop_revision = other_len
1001
if stop_revision > other_len:
1002
raise errors.NoSuchRevision(self, stop_revision)
1003
return other_history[self_len:stop_revision]
1005
def update_revisions(self, other, stop_revision=None, overwrite=False,
1007
"""Pull in new perfect-fit revisions.
1009
:param other: Another Branch to pull from
1010
:param stop_revision: Updated until the given revision
1011
:param overwrite: Always set the branch pointer, rather than checking
1012
to see if it is a proper descendant.
1013
:param graph: A Graph object that can be used to query history
1014
information. This can be None.
1017
return InterBranch.get(other, self).update_revisions(stop_revision,
1055
def _read_last_revision_info(self):
1056
raise NotImplementedError(self._read_last_revision_info)
1058
@deprecated_method(deprecated_in((2, 4, 0)))
1020
1059
def import_last_revision_info(self, source_repo, revno, revid):
1021
1060
"""Set the last revision info, importing from another repo if necessary.
1023
This is used by the bound branch code to upload a revision to
1024
the master branch first before updating the tip of the local branch.
1026
1062
:param source_repo: Source repository to optionally fetch from
1027
1063
:param revno: Revision number of the new tip
1028
1064
:param revid: Revision id of the new tip
1031
1067
self.repository.fetch(source_repo, revision_id=revid)
1032
1068
self.set_last_revision_info(revno, revid)
1070
def import_last_revision_info_and_tags(self, source, revno, revid,
1072
"""Set the last revision info, importing from another repo if necessary.
1074
This is used by the bound branch code to upload a revision to
1075
the master branch first before updating the tip of the local branch.
1076
Revisions referenced by source's tags are also transferred.
1078
:param source: Source branch to optionally fetch from
1079
:param revno: Revision number of the new tip
1080
:param revid: Revision id of the new tip
1081
:param lossy: Whether to discard metadata that can not be
1082
natively represented
1083
:return: Tuple with the new revision number and revision id
1084
(should only be different from the arguments when lossy=True)
1086
if not self.repository.has_same_location(source.repository):
1087
self.fetch(source, revid)
1088
self.set_last_revision_info(revno, revid)
1089
return (revno, revid)
1034
1091
def revision_id_to_revno(self, revision_id):
1035
1092
"""Given a revision id, return its revno"""
1036
1093
if _mod_revision.is_null(revision_id):
1038
history = self.revision_history()
1095
history = self._revision_history()
1040
1097
return history.index(revision_id) + 1
1041
1098
except ValueError:
1068
1125
stop_revision=stop_revision,
1069
1126
possible_transports=possible_transports, *args, **kwargs)
1071
def push(self, target, overwrite=False, stop_revision=None, *args,
1128
def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1073
1130
"""Mirror this branch into target.
1075
1132
This branch is considered to be 'local', having low latency.
1077
1134
return InterBranch.get(self, target).push(overwrite, stop_revision,
1080
def lossy_push(self, target, stop_revision=None):
1081
"""Push deltas into another branch.
1083
:note: This does not, like push, retain the revision ids from
1084
the source branch and will, rather than adding bzr-specific
1085
metadata, push only those semantics of the revision that can be
1086
natively represented by this branch' VCS.
1088
:param target: Target branch
1089
:param stop_revision: Revision to push, defaults to last revision.
1090
:return: BranchPushResult with an extra member revidmap:
1091
A dictionary mapping revision ids from the target branch
1092
to new revision ids in the target branch, for each
1093
revision that was pushed.
1095
inter = InterBranch.get(self, target)
1096
lossy_push = getattr(inter, "lossy_push", None)
1097
if lossy_push is None:
1098
raise errors.LossyPushToSameVCS(self, target)
1099
return lossy_push(stop_revision)
1135
lossy, *args, **kwargs)
1101
1137
def basis_tree(self):
1102
1138
"""Return `Tree` object for last revision."""
1354
1395
# TODO: We should probably also check that self.revision_history
1355
1396
# matches the repository for older branch formats.
1356
1397
# If looking for the code that cross-checks repository parents against
1357
# the iter_reverse_revision_history output, that is now a repository
1398
# the Graph.iter_lefthand_ancestry output, that is now a repository
1358
1399
# specific check.
1361
def _get_checkout_format(self):
1402
def _get_checkout_format(self, lightweight=False):
1362
1403
"""Return the most suitable metadir for a checkout of this branch.
1363
1404
Weaves are used if this branch's repository uses weaves.
1365
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
1366
from bzrlib.repofmt import weaverepo
1367
format = bzrdir.BzrDirMetaFormat1()
1368
format.repository_format = weaverepo.RepositoryFormat7()
1370
format = self.repository.bzrdir.checkout_metadir()
1371
format.set_branch_format(self._format)
1406
format = self.repository.bzrdir.checkout_metadir()
1407
format.set_branch_format(self._format)
1374
1410
def create_clone_on_transport(self, to_transport, revision_id=None,
1515
1551
raise AssertionError("invalid heads: %r" % (heads,))
1518
class BranchFormat(object):
1553
def heads_to_fetch(self):
1554
"""Return the heads that must and that should be fetched to copy this
1555
branch into another repo.
1557
:returns: a 2-tuple of (must_fetch, if_present_fetch). must_fetch is a
1558
set of heads that must be fetched. if_present_fetch is a set of
1559
heads that must be fetched if present, but no error is necessary if
1560
they are not present.
1562
# For bzr native formats must_fetch is just the tip, and if_present_fetch
1564
must_fetch = set([self.last_revision()])
1565
if_present_fetch = set()
1566
c = self.get_config()
1567
include_tags = c.get_user_option_as_bool('branch.fetch_tags',
1571
if_present_fetch = set(self.tags.get_reverse_tag_dict())
1572
except errors.TagsNotSupported:
1574
must_fetch.discard(_mod_revision.NULL_REVISION)
1575
if_present_fetch.discard(_mod_revision.NULL_REVISION)
1576
return must_fetch, if_present_fetch
1579
class BranchFormat(controldir.ControlComponentFormat):
1519
1580
"""An encapsulation of the initialization and open routines for a format.
1521
1582
Formats provide three things:
1548
1601
return not (self == other)
1551
def find_format(klass, a_bzrdir, name=None):
1552
"""Return the format for the branch object in a_bzrdir."""
1554
transport = a_bzrdir.get_branch_transport(None, name=name)
1555
format_string = transport.get_bytes("format")
1556
format = klass._formats[format_string]
1557
if isinstance(format, MetaDirBranchFormatFactory):
1560
except errors.NoSuchFile:
1561
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1563
raise errors.UnknownFormatError(format=format_string, kind='branch')
1604
@deprecated_method(deprecated_in((2, 4, 0)))
1566
1605
def get_default_format(klass):
1567
1606
"""Return the current default format."""
1568
return klass._default_format
1607
return format_registry.get_default()
1610
@deprecated_method(deprecated_in((2, 4, 0)))
1571
1611
def get_formats(klass):
1572
1612
"""Get all the known formats.
1574
1614
Warning: This triggers a load of all lazy registered formats: do not
1575
1615
use except when that is desireed.
1578
for fmt in klass._formats.values():
1579
if isinstance(fmt, MetaDirBranchFormatFactory):
1617
return format_registry._get_all()
1584
def get_reference(self, a_bzrdir, name=None):
1585
"""Get the target reference of the branch in a_bzrdir.
1619
def get_reference(self, controldir, name=None):
1620
"""Get the target reference of the branch in controldir.
1587
1622
format probing must have been completed before calling
1588
1623
this method - it is assumed that the format of the branch
1589
in a_bzrdir is correct.
1624
in controldir is correct.
1591
:param a_bzrdir: The bzrdir to get the branch data from.
1626
:param controldir: The controldir to get the branch data from.
1592
1627
:param name: Name of the colocated branch to fetch
1593
1628
:return: None if the branch is not a reference branch.
1598
def set_reference(self, a_bzrdir, name, to_branch):
1599
"""Set the target reference of the branch in a_bzrdir.
1633
def set_reference(self, controldir, name, to_branch):
1634
"""Set the target reference of the branch in controldir.
1601
1636
format probing must have been completed before calling
1602
1637
this method - it is assumed that the format of the branch
1603
in a_bzrdir is correct.
1638
in controldir is correct.
1605
:param a_bzrdir: The bzrdir to set the branch reference for.
1640
:param controldir: The controldir to set the branch reference for.
1606
1641
:param name: Name of colocated branch to set, None for default
1607
1642
:param to_branch: branch that the checkout is to reference
1609
1644
raise NotImplementedError(self.set_reference)
1611
def get_format_string(self):
1612
"""Return the ASCII format string that identifies this format."""
1613
raise NotImplementedError(self.get_format_string)
1615
1646
def get_format_description(self):
1616
1647
"""Return the short format description for this format."""
1617
1648
raise NotImplementedError(self.get_format_description)
1619
def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
1650
def _run_post_branch_init_hooks(self, controldir, name, branch):
1620
1651
hooks = Branch.hooks['post_branch_init']
1623
params = BranchInitHookParams(self, a_bzrdir, name, branch)
1654
params = BranchInitHookParams(self, controldir, name, branch)
1624
1655
for hook in hooks:
1627
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1628
lock_type='metadir', set_format=True):
1629
"""Initialize a branch in a bzrdir, with specified files
1631
:param a_bzrdir: The bzrdir to initialize the branch in
1632
:param utf8_files: The files to create as a list of
1633
(filename, content) tuples
1634
:param name: Name of colocated branch to create, if any
1635
:param set_format: If True, set the format with
1636
self.get_format_string. (BzrBranch4 has its format set
1638
:return: a branch in this format
1640
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1641
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1643
'metadir': ('lock', lockdir.LockDir),
1644
'branch4': ('branch-lock', lockable_files.TransportLock),
1646
lock_name, lock_class = lock_map[lock_type]
1647
control_files = lockable_files.LockableFiles(branch_transport,
1648
lock_name, lock_class)
1649
control_files.create_lock()
1651
control_files.lock_write()
1652
except errors.LockContention:
1653
if lock_type != 'branch4':
1659
utf8_files += [('format', self.get_format_string())]
1661
for (filename, content) in utf8_files:
1662
branch_transport.put_bytes(
1664
mode=a_bzrdir._get_file_mode())
1667
control_files.unlock()
1668
branch = self.open(a_bzrdir, name, _found=True)
1669
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1672
def initialize(self, a_bzrdir, name=None):
1673
"""Create a branch of this format in a_bzrdir.
1658
def initialize(self, controldir, name=None, repository=None,
1659
append_revisions_only=None):
1660
"""Create a branch of this format in controldir.
1675
1662
:param name: Name of the colocated branch to create.
1677
1664
raise NotImplementedError(self.initialize)
1722
1710
raise NotImplementedError(self.open)
1713
@deprecated_method(deprecated_in((2, 4, 0)))
1725
1714
def register_format(klass, format):
1726
1715
"""Register a metadir format.
1728
1717
See MetaDirBranchFormatFactory for the ability to register a format
1729
1718
without loading the code the format needs until it is actually used.
1731
klass._formats[format.get_format_string()] = format
1732
# Metadir formats have a network name of their format string, and get
1733
# registered as factories.
1734
if isinstance(format, MetaDirBranchFormatFactory):
1735
network_format_registry.register(format.get_format_string(), format)
1737
network_format_registry.register(format.get_format_string(),
1720
format_registry.register(format)
1723
@deprecated_method(deprecated_in((2, 4, 0)))
1741
1724
def set_default_format(klass, format):
1742
klass._default_format = format
1725
format_registry.set_default(format)
1744
1727
def supports_set_append_revisions_only(self):
1745
1728
"""True if this format supports set_append_revisions_only."""
1802
1798
These are all empty initially, because by default nothing should get
1805
Hooks.__init__(self)
1806
self.create_hook(HookPoint('set_rh',
1801
Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1802
self.add_hook('set_rh',
1807
1803
"Invoked whenever the revision history has been set via "
1808
1804
"set_revision_history. The api signature is (branch, "
1809
1805
"revision_history), and the branch will be write-locked. "
1810
1806
"The set_rh hook can be expensive for bzr to trigger, a better "
1811
"hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1812
self.create_hook(HookPoint('open',
1807
"hook to use is Branch.post_change_branch_tip.", (0, 15))
1808
self.add_hook('open',
1813
1809
"Called with the Branch object that has been opened after a "
1814
"branch is opened.", (1, 8), None))
1815
self.create_hook(HookPoint('post_push',
1810
"branch is opened.", (1, 8))
1811
self.add_hook('post_push',
1816
1812
"Called after a push operation completes. post_push is called "
1817
1813
"with a bzrlib.branch.BranchPushResult object and only runs in the "
1818
"bzr client.", (0, 15), None))
1819
self.create_hook(HookPoint('post_pull',
1814
"bzr client.", (0, 15))
1815
self.add_hook('post_pull',
1820
1816
"Called after a pull operation completes. post_pull is called "
1821
1817
"with a bzrlib.branch.PullResult object and only runs in the "
1822
"bzr client.", (0, 15), None))
1823
self.create_hook(HookPoint('pre_commit',
1818
"bzr client.", (0, 15))
1819
self.add_hook('pre_commit',
1824
1820
"Called after a commit is calculated but before it is "
1825
1821
"completed. pre_commit is called with (local, master, old_revno, "
1826
1822
"old_revid, future_revno, future_revid, tree_delta, future_tree"
1829
1825
"basis revision. hooks MUST NOT modify this delta. "
1830
1826
" future_tree is an in-memory tree obtained from "
1831
1827
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1832
"tree.", (0,91), None))
1833
self.create_hook(HookPoint('post_commit',
1829
self.add_hook('post_commit',
1834
1830
"Called in the bzr client after a commit has completed. "
1835
1831
"post_commit is called with (local, master, old_revno, old_revid, "
1836
1832
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1837
"commit to a branch.", (0, 15), None))
1838
self.create_hook(HookPoint('post_uncommit',
1833
"commit to a branch.", (0, 15))
1834
self.add_hook('post_uncommit',
1839
1835
"Called in the bzr client after an uncommit completes. "
1840
1836
"post_uncommit is called with (local, master, old_revno, "
1841
1837
"old_revid, new_revno, new_revid) where local is the local branch "
1842
1838
"or None, master is the target branch, and an empty branch "
1843
"receives new_revno of 0, new_revid of None.", (0, 15), None))
1844
self.create_hook(HookPoint('pre_change_branch_tip',
1839
"receives new_revno of 0, new_revid of None.", (0, 15))
1840
self.add_hook('pre_change_branch_tip',
1845
1841
"Called in bzr client and server before a change to the tip of a "
1846
1842
"branch is made. pre_change_branch_tip is called with a "
1847
1843
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1848
"commit, uncommit will all trigger this hook.", (1, 6), None))
1849
self.create_hook(HookPoint('post_change_branch_tip',
1844
"commit, uncommit will all trigger this hook.", (1, 6))
1845
self.add_hook('post_change_branch_tip',
1850
1846
"Called in bzr client and server after a change to the tip of a "
1851
1847
"branch is made. post_change_branch_tip is called with a "
1852
1848
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1853
"commit, uncommit will all trigger this hook.", (1, 4), None))
1854
self.create_hook(HookPoint('transform_fallback_location',
1849
"commit, uncommit will all trigger this hook.", (1, 4))
1850
self.add_hook('transform_fallback_location',
1855
1851
"Called when a stacked branch is activating its fallback "
1856
1852
"locations. transform_fallback_location is called with (branch, "
1857
1853
"url), and should return a new url. Returning the same url "
1862
1858
"fallback locations have not been activated. When there are "
1863
1859
"multiple hooks installed for transform_fallback_location, "
1864
1860
"all are called with the url returned from the previous hook."
1865
"The order is however undefined.", (1, 9), None))
1866
self.create_hook(HookPoint('automatic_tag_name',
1861
"The order is however undefined.", (1, 9))
1862
self.add_hook('automatic_tag_name',
1867
1863
"Called to determine an automatic tag name for a revision. "
1868
1864
"automatic_tag_name is called with (branch, revision_id) and "
1869
1865
"should return a tag name or None if no tag name could be "
1870
1866
"determined. The first non-None tag name returned will be used.",
1872
self.create_hook(HookPoint('post_branch_init',
1868
self.add_hook('post_branch_init',
1873
1869
"Called after new branch initialization completes. "
1874
1870
"post_branch_init is called with a "
1875
1871
"bzrlib.branch.BranchInitHookParams. "
1876
1872
"Note that init, branch and checkout (both heavyweight and "
1877
"lightweight) will all trigger this hook.", (2, 2), None))
1878
self.create_hook(HookPoint('post_switch',
1873
"lightweight) will all trigger this hook.", (2, 2))
1874
self.add_hook('post_switch',
1879
1875
"Called after a checkout switches branch. "
1880
1876
"post_switch is called with a "
1881
"bzrlib.branch.SwitchHookParams.", (2, 2), None))
1877
"bzrlib.branch.SwitchHookParams.", (2, 2))
1997
1993
self.revision_id)
2000
class BzrBranchFormat4(BranchFormat):
2001
"""Bzr branch format 4.
2004
- a revision-history file.
2005
- a branch-lock lock file [ to be shared with the bzrdir ]
1996
class BranchFormatMetadir(bzrdir.BzrDirMetaComponentFormat, BranchFormat):
1997
"""Base class for branch formats that live in meta directories.
2008
def get_format_description(self):
2009
"""See BranchFormat.get_format_description()."""
2010
return "Branch format 4"
2012
def initialize(self, a_bzrdir, name=None):
2013
"""Create a branch of this format in a_bzrdir."""
2014
utf8_files = [('revision-history', ''),
2015
('branch-name', ''),
2017
return self._initialize_helper(a_bzrdir, utf8_files, name=name,
2018
lock_type='branch4', set_format=False)
2020
2000
def __init__(self):
2021
super(BzrBranchFormat4, self).__init__()
2022
self._matchingbzrdir = bzrdir.BzrDirFormat6()
2024
def network_name(self):
2025
"""The network name for this format is the control dirs disk label."""
2026
return self._matchingbzrdir.get_format_string()
2028
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2029
"""See BranchFormat.open()."""
2031
# we are being called directly and must probe.
2032
raise NotImplementedError
2033
return BzrBranch(_format=self,
2034
_control_files=a_bzrdir._control_files,
2037
_repository=a_bzrdir.open_repository())
2040
return "Bazaar-NG branch format 4"
2043
class BranchFormatMetadir(BranchFormat):
2044
"""Common logic for meta-dir based branch formats."""
2001
BranchFormat.__init__(self)
2002
bzrdir.BzrDirMetaComponentFormat.__init__(self)
2005
def find_format(klass, controldir, name=None):
2006
"""Return the format for the branch object in controldir."""
2008
transport = controldir.get_branch_transport(None, name=name)
2009
except errors.NoSuchFile:
2010
raise errors.NotBranchError(path=name, bzrdir=controldir)
2012
format_string = transport.get_bytes("format")
2013
except errors.NoSuchFile:
2014
raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
2015
return klass._find_format(format_registry, 'branch', format_string)
2046
2017
def _branch_class(self):
2047
2018
"""What class to instantiate on open calls."""
2048
2019
raise NotImplementedError(self._branch_class)
2050
def network_name(self):
2051
"""A simple byte string uniquely identifying this format for RPC calls.
2053
Metadir branch formats use their format string.
2021
def _get_initial_config(self, append_revisions_only=None):
2022
if append_revisions_only:
2023
return "append_revisions_only = True\n"
2025
# Avoid writing anything if append_revisions_only is disabled,
2026
# as that is the default.
2029
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
2031
"""Initialize a branch in a bzrdir, with specified files
2033
:param a_bzrdir: The bzrdir to initialize the branch in
2034
:param utf8_files: The files to create as a list of
2035
(filename, content) tuples
2036
:param name: Name of colocated branch to create, if any
2037
:return: a branch in this format
2055
return self.get_format_string()
2039
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
2040
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2041
control_files = lockable_files.LockableFiles(branch_transport,
2042
'lock', lockdir.LockDir)
2043
control_files.create_lock()
2044
control_files.lock_write()
2046
utf8_files += [('format', self.get_format_string())]
2047
for (filename, content) in utf8_files:
2048
branch_transport.put_bytes(
2050
mode=a_bzrdir._get_file_mode())
2052
control_files.unlock()
2053
branch = self.open(a_bzrdir, name, _found=True,
2054
found_repository=repository)
2055
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2057
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2058
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2059
found_repository=None, possible_transports=None):
2058
2060
"""See BranchFormat.open()."""
2060
format = BranchFormat.find_format(a_bzrdir, name=name)
2062
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2061
2063
if format.__class__ != self.__class__:
2062
2064
raise AssertionError("wrong format %r found for %r" %
2063
2065
(format, self))
2066
2068
control_files = lockable_files.LockableFiles(transport, 'lock',
2067
2069
lockdir.LockDir)
2070
if found_repository is None:
2071
found_repository = a_bzrdir.find_repository()
2068
2072
return self._branch_class()(_format=self,
2069
2073
_control_files=control_files,
2071
2075
a_bzrdir=a_bzrdir,
2072
_repository=a_bzrdir.find_repository(),
2073
ignore_fallbacks=ignore_fallbacks)
2076
_repository=found_repository,
2077
ignore_fallbacks=ignore_fallbacks,
2078
possible_transports=possible_transports)
2074
2079
except errors.NoSuchFile:
2075
2080
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2078
super(BranchFormatMetadir, self).__init__()
2079
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2080
self._matchingbzrdir.set_branch_format(self)
2083
def _matchingbzrdir(self):
2084
ret = bzrdir.BzrDirMetaFormat1()
2085
ret.set_branch_format(self)
2082
2088
def supports_tags(self):
2091
def supports_leaving_lock(self):
2086
2095
class BzrBranchFormat5(BranchFormatMetadir):
2087
2096
"""Bzr branch format 5.
2170
2187
"""See BranchFormat.get_format_description()."""
2171
2188
return "Branch format 8"
2173
def initialize(self, a_bzrdir, name=None):
2190
def initialize(self, a_bzrdir, name=None, repository=None,
2191
append_revisions_only=None):
2174
2192
"""Create a branch of this format in a_bzrdir."""
2175
2193
utf8_files = [('last-revision', '0 null:\n'),
2176
('branch.conf', ''),
2195
self._get_initial_config(append_revisions_only)),
2178
2197
('references', '')
2180
return self._initialize_helper(a_bzrdir, utf8_files, name)
2183
super(BzrBranchFormat8, self).__init__()
2184
self._matchingbzrdir.repository_format = \
2185
RepositoryFormatKnitPack5RichRoot()
2199
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2187
2201
def make_tags(self, branch):
2188
2202
"""See bzrlib.branch.BranchFormat.make_tags()."""
2189
return BasicTags(branch)
2203
return _mod_tag.BasicTags(branch)
2191
2205
def supports_set_append_revisions_only(self):
2260
2285
transport = a_bzrdir.get_branch_transport(None, name=name)
2261
2286
location = transport.put_bytes('location', to_branch.base)
2263
def initialize(self, a_bzrdir, name=None, target_branch=None):
2288
def initialize(self, a_bzrdir, name=None, target_branch=None,
2289
repository=None, append_revisions_only=None):
2264
2290
"""Create a branch of this format in a_bzrdir."""
2265
2291
if target_branch is None:
2266
2292
# this format does not implement branch itself, thus the implicit
2267
2293
# creation contract must see it as uninitializable
2268
2294
raise errors.UninitializableFormat(self)
2269
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)
2270
2298
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2271
2299
branch_transport.put_bytes('location',
2272
target_branch.bzrdir.user_url)
2300
target_branch.user_url)
2273
2301
branch_transport.put_bytes('format', self.get_format_string())
2274
2302
branch = self.open(
2275
2303
a_bzrdir, name, _found=True,
2394
2431
def __init__(self, _format=None,
2395
2432
_control_files=None, a_bzrdir=None, name=None,
2396
_repository=None, ignore_fallbacks=False):
2433
_repository=None, ignore_fallbacks=False,
2434
possible_transports=None):
2397
2435
"""Create new branch object at a particular location."""
2398
2436
if a_bzrdir is None:
2399
2437
raise ValueError('a_bzrdir must be supplied')
2401
2439
self.bzrdir = a_bzrdir
2402
self._base = self.bzrdir.transport.clone('..').base
2440
self._user_transport = self.bzrdir.transport.clone('..')
2441
if name is not None:
2442
self._user_transport.set_segment_parameter(
2443
"branch", urlutils.escape(name))
2444
self._base = self._user_transport.base
2403
2445
self.name = name
2404
# XXX: We should be able to just do
2405
# self.base = self.bzrdir.root_transport.base
2406
# but this does not quite work yet -- mbp 20080522
2407
2446
self._format = _format
2408
2447
if _control_files is None:
2409
2448
raise ValueError('BzrBranch _control_files is None')
2410
2449
self.control_files = _control_files
2411
2450
self._transport = _control_files._transport
2412
2451
self.repository = _repository
2413
Branch.__init__(self)
2452
Branch.__init__(self, possible_transports)
2415
2454
def __str__(self):
2416
if self.name is None:
2417
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2419
return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2455
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2422
2457
__repr__ = __str__
2509
2551
"""See Branch.print_file."""
2510
2552
return self.repository.print_file(file, revision_id)
2512
def _write_revision_history(self, history):
2513
"""Factored out of set_revision_history.
2515
This performs the actual writing to disk.
2516
It is intended to be called by BzrBranch5.set_revision_history."""
2517
self._transport.put_bytes(
2518
'revision-history', '\n'.join(history),
2519
mode=self.bzrdir._get_file_mode())
2522
def set_revision_history(self, rev_history):
2523
"""See Branch.set_revision_history."""
2524
if 'evil' in debug.debug_flags:
2525
mutter_callsite(3, "set_revision_history scales with history.")
2526
check_not_reserved_id = _mod_revision.check_not_reserved_id
2527
for rev_id in rev_history:
2528
check_not_reserved_id(rev_id)
2529
if Branch.hooks['post_change_branch_tip']:
2530
# Don't calculate the last_revision_info() if there are no hooks
2532
old_revno, old_revid = self.last_revision_info()
2533
if len(rev_history) == 0:
2534
revid = _mod_revision.NULL_REVISION
2536
revid = rev_history[-1]
2537
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2538
self._write_revision_history(rev_history)
2539
self._clear_cached_state()
2540
self._cache_revision_history(rev_history)
2541
for hook in Branch.hooks['set_rh']:
2542
hook(self, rev_history)
2543
if Branch.hooks['post_change_branch_tip']:
2544
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2546
def _synchronize_history(self, destination, revision_id):
2547
"""Synchronize last revision and revision history between branches.
2549
This version is most efficient when the destination is also a
2550
BzrBranch5, but works for BzrBranch6 as long as the revision
2551
history is the true lefthand parent history, and all of the revisions
2552
are in the destination's repository. If not, set_revision_history
2555
:param destination: The branch to copy the history into
2556
:param revision_id: The revision-id to truncate history at. May
2557
be None to copy complete history.
2559
if not isinstance(destination._format, BzrBranchFormat5):
2560
super(BzrBranch, self)._synchronize_history(
2561
destination, revision_id)
2563
if revision_id == _mod_revision.NULL_REVISION:
2566
new_history = self.revision_history()
2567
if revision_id is not None and new_history != []:
2569
new_history = new_history[:new_history.index(revision_id) + 1]
2571
rev = self.repository.get_revision(revision_id)
2572
new_history = rev.get_history(self.repository)[1:]
2573
destination.set_revision_history(new_history)
2575
2554
@needs_write_lock
2576
2555
def set_last_revision_info(self, revno, revision_id):
2577
"""Set the last revision of this branch.
2579
The caller is responsible for checking that the revno is correct
2580
for this revision id.
2582
It may be possible to set the branch last revision to an id not
2583
present in the repository. However, branches can also be
2584
configured to check constraints on history, in which case this may not
2556
if not revision_id or not isinstance(revision_id, basestring):
2557
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2587
2558
revision_id = _mod_revision.ensure_null(revision_id)
2588
# this old format stores the full history, but this api doesn't
2589
# provide it, so we must generate, and might as well check it's
2591
history = self._lefthand_history(revision_id)
2592
if len(history) != revno:
2593
raise AssertionError('%d != %d' % (len(history), revno))
2594
self.set_revision_history(history)
2596
def _gen_revision_history(self):
2597
history = self._transport.get_bytes('revision-history').split('\n')
2598
if history[-1:] == ['']:
2599
# There shouldn't be a trailing newline, but just in case.
2604
def generate_revision_history(self, revision_id, last_rev=None,
2606
"""Create a new revision history that will finish with revision_id.
2608
:param revision_id: the new tip to use.
2609
:param last_rev: The previous last_revision. If not None, then this
2610
must be a ancestory of revision_id, or DivergedBranches is raised.
2611
:param other_branch: The other branch that DivergedBranches should
2612
raise with respect to.
2614
self.set_revision_history(self._lefthand_history(revision_id,
2615
last_rev, other_branch))
2559
old_revno, old_revid = self.last_revision_info()
2560
if self.get_append_revisions_only():
2561
self._check_history_violation(revision_id)
2562
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2563
self._write_last_revision_info(revno, revision_id)
2564
self._clear_cached_state()
2565
self._last_revision_info_cache = revno, revision_id
2566
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2617
2568
def basis_tree(self):
2618
2569
"""See Branch.basis_tree."""
2630
def _basic_push(self, target, overwrite, stop_revision):
2631
"""Basic implementation of push without bound branches or hooks.
2633
Must be called with source read locked and target write locked.
2635
result = BranchPushResult()
2636
result.source_branch = self
2637
result.target_branch = target
2638
result.old_revno, result.old_revid = target.last_revision_info()
2639
self.update_references(target)
2640
if result.old_revid != self.last_revision():
2641
# We assume that during 'push' this repository is closer than
2643
graph = self.repository.get_graph(target.repository)
2644
target.update_revisions(self, stop_revision,
2645
overwrite=overwrite, graph=graph)
2646
if self._push_should_merge_tags():
2647
result.tag_conflicts = self.tags.merge_to(target.tags,
2649
result.new_revno, result.new_revid = target.last_revision_info()
2652
2581
def get_stacked_on_url(self):
2653
2582
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2665
2594
self._transport.put_bytes('parent', url + '\n',
2666
2595
mode=self.bzrdir._get_file_mode())
2669
class BzrBranch5(BzrBranch):
2670
"""A format 5 branch. This supports new features over plain branches.
2672
It has support for a master_branch which is the data for bound branches.
2675
def get_bound_location(self):
2677
return self._transport.get_bytes('bound')[:-1]
2678
except errors.NoSuchFile:
2682
def get_master_branch(self, possible_transports=None):
2683
"""Return the branch we are bound to.
2685
:return: Either a Branch, or None
2687
This could memoise the branch, but if thats done
2688
it must be revalidated on each new lock.
2689
So for now we just don't memoise it.
2690
# RBC 20060304 review this decision.
2692
bound_loc = self.get_bound_location()
2696
return Branch.open(bound_loc,
2697
possible_transports=possible_transports)
2698
except (errors.NotBranchError, errors.ConnectionError), e:
2699
raise errors.BoundBranchConnectionFailure(
2702
2597
@needs_write_lock
2703
def set_bound_location(self, location):
2704
"""Set the target where this branch is bound to.
2706
:param location: URL to the target branch
2709
self._transport.put_bytes('bound', location+'\n',
2710
mode=self.bzrdir._get_file_mode())
2713
self._transport.delete('bound')
2714
except errors.NoSuchFile:
2599
"""If bound, unbind"""
2600
return self.set_bound_location(None)
2718
2602
@needs_write_lock
2719
2603
def bind(self, other):
2741
2625
# history around
2742
2626
self.set_bound_location(other.base)
2628
def get_bound_location(self):
2630
return self._transport.get_bytes('bound')[:-1]
2631
except errors.NoSuchFile:
2635
def get_master_branch(self, possible_transports=None):
2636
"""Return the branch we are bound to.
2638
:return: Either a Branch, or None
2640
if self._master_branch_cache is None:
2641
self._master_branch_cache = self._get_master_branch(
2642
possible_transports)
2643
return self._master_branch_cache
2645
def _get_master_branch(self, possible_transports):
2646
bound_loc = self.get_bound_location()
2650
return Branch.open(bound_loc,
2651
possible_transports=possible_transports)
2652
except (errors.NotBranchError, errors.ConnectionError), e:
2653
raise errors.BoundBranchConnectionFailure(
2744
2656
@needs_write_lock
2746
"""If bound, unbind"""
2747
return self.set_bound_location(None)
2657
def set_bound_location(self, location):
2658
"""Set the target where this branch is bound to.
2660
:param location: URL to the target branch
2662
self._master_branch_cache = None
2664
self._transport.put_bytes('bound', location+'\n',
2665
mode=self.bzrdir._get_file_mode())
2668
self._transport.delete('bound')
2669
except errors.NoSuchFile:
2749
2673
@needs_write_lock
2750
2674
def update(self, possible_transports=None):
2767
class BzrBranch8(BzrBranch5):
2690
def _read_last_revision_info(self):
2691
revision_string = self._transport.get_bytes('last-revision')
2692
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2693
revision_id = cache_utf8.get_cached_utf8(revision_id)
2695
return revno, revision_id
2697
def _write_last_revision_info(self, revno, revision_id):
2698
"""Simply write out the revision id, with no checks.
2700
Use set_last_revision_info to perform this safely.
2702
Does not update the revision_history cache.
2704
revision_id = _mod_revision.ensure_null(revision_id)
2705
out_string = '%d %s\n' % (revno, revision_id)
2706
self._transport.put_bytes('last-revision', out_string,
2707
mode=self.bzrdir._get_file_mode())
2710
class FullHistoryBzrBranch(BzrBranch):
2711
"""Bzr branch which contains the full revision history."""
2714
def set_last_revision_info(self, revno, revision_id):
2715
if not revision_id or not isinstance(revision_id, basestring):
2716
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2717
revision_id = _mod_revision.ensure_null(revision_id)
2718
# this old format stores the full history, but this api doesn't
2719
# provide it, so we must generate, and might as well check it's
2721
history = self._lefthand_history(revision_id)
2722
if len(history) != revno:
2723
raise AssertionError('%d != %d' % (len(history), revno))
2724
self._set_revision_history(history)
2726
def _read_last_revision_info(self):
2727
rh = self._revision_history()
2730
return (revno, rh[-1])
2732
return (0, _mod_revision.NULL_REVISION)
2734
@deprecated_method(deprecated_in((2, 4, 0)))
2736
def set_revision_history(self, rev_history):
2737
"""See Branch.set_revision_history."""
2738
self._set_revision_history(rev_history)
2740
def _set_revision_history(self, rev_history):
2741
if 'evil' in debug.debug_flags:
2742
mutter_callsite(3, "set_revision_history scales with history.")
2743
check_not_reserved_id = _mod_revision.check_not_reserved_id
2744
for rev_id in rev_history:
2745
check_not_reserved_id(rev_id)
2746
if Branch.hooks['post_change_branch_tip']:
2747
# Don't calculate the last_revision_info() if there are no hooks
2749
old_revno, old_revid = self.last_revision_info()
2750
if len(rev_history) == 0:
2751
revid = _mod_revision.NULL_REVISION
2753
revid = rev_history[-1]
2754
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2755
self._write_revision_history(rev_history)
2756
self._clear_cached_state()
2757
self._cache_revision_history(rev_history)
2758
for hook in Branch.hooks['set_rh']:
2759
hook(self, rev_history)
2760
if Branch.hooks['post_change_branch_tip']:
2761
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2763
def _write_revision_history(self, history):
2764
"""Factored out of set_revision_history.
2766
This performs the actual writing to disk.
2767
It is intended to be called by set_revision_history."""
2768
self._transport.put_bytes(
2769
'revision-history', '\n'.join(history),
2770
mode=self.bzrdir._get_file_mode())
2772
def _gen_revision_history(self):
2773
history = self._transport.get_bytes('revision-history').split('\n')
2774
if history[-1:] == ['']:
2775
# There shouldn't be a trailing newline, but just in case.
2779
def _synchronize_history(self, destination, revision_id):
2780
if not isinstance(destination, FullHistoryBzrBranch):
2781
super(BzrBranch, self)._synchronize_history(
2782
destination, revision_id)
2784
if revision_id == _mod_revision.NULL_REVISION:
2787
new_history = self._revision_history()
2788
if revision_id is not None and new_history != []:
2790
new_history = new_history[:new_history.index(revision_id) + 1]
2792
rev = self.repository.get_revision(revision_id)
2793
new_history = rev.get_history(self.repository)[1:]
2794
destination._set_revision_history(new_history)
2797
def generate_revision_history(self, revision_id, last_rev=None,
2799
"""Create a new revision history that will finish with revision_id.
2801
:param revision_id: the new tip to use.
2802
:param last_rev: The previous last_revision. If not None, then this
2803
must be a ancestory of revision_id, or DivergedBranches is raised.
2804
:param other_branch: The other branch that DivergedBranches should
2805
raise with respect to.
2807
self._set_revision_history(self._lefthand_history(revision_id,
2808
last_rev, other_branch))
2811
class BzrBranch5(FullHistoryBzrBranch):
2812
"""A format 5 branch. This supports new features over plain branches.
2814
It has support for a master_branch which is the data for bound branches.
2818
class BzrBranch8(BzrBranch):
2768
2819
"""A branch that stores tree-reference locations."""
2770
def _open_hook(self):
2821
def _open_hook(self, possible_transports=None):
2771
2822
if self._ignore_fallbacks:
2824
if possible_transports is None:
2825
possible_transports = [self.bzrdir.root_transport]
2774
2827
url = self.get_stacked_on_url()
2775
2828
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2796
2850
self._last_revision_info_cache = None
2797
2851
self._reference_info = None
2799
def _last_revision_info(self):
2800
revision_string = self._transport.get_bytes('last-revision')
2801
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2802
revision_id = cache_utf8.get_cached_utf8(revision_id)
2804
return revno, revision_id
2806
def _write_last_revision_info(self, revno, revision_id):
2807
"""Simply write out the revision id, with no checks.
2809
Use set_last_revision_info to perform this safely.
2811
Does not update the revision_history cache.
2812
Intended to be called by set_last_revision_info and
2813
_write_revision_history.
2815
revision_id = _mod_revision.ensure_null(revision_id)
2816
out_string = '%d %s\n' % (revno, revision_id)
2817
self._transport.put_bytes('last-revision', out_string,
2818
mode=self.bzrdir._get_file_mode())
2821
def set_last_revision_info(self, revno, revision_id):
2822
revision_id = _mod_revision.ensure_null(revision_id)
2823
old_revno, old_revid = self.last_revision_info()
2824
if self._get_append_revisions_only():
2825
self._check_history_violation(revision_id)
2826
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2827
self._write_last_revision_info(revno, revision_id)
2828
self._clear_cached_state()
2829
self._last_revision_info_cache = revno, revision_id
2830
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2832
def _synchronize_history(self, destination, revision_id):
2833
"""Synchronize last revision and revision history between branches.
2835
:see: Branch._synchronize_history
2837
# XXX: The base Branch has a fast implementation of this method based
2838
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2839
# that uses set_revision_history. This class inherits from BzrBranch5,
2840
# but wants the fast implementation, so it calls
2841
# Branch._synchronize_history directly.
2842
Branch._synchronize_history(self, destination, revision_id)
2844
2853
def _check_history_violation(self, revision_id):
2845
last_revision = _mod_revision.ensure_null(self.last_revision())
2854
current_revid = self.last_revision()
2855
last_revision = _mod_revision.ensure_null(current_revid)
2846
2856
if _mod_revision.is_null(last_revision):
2848
if last_revision not in self._lefthand_history(revision_id):
2849
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2858
graph = self.repository.get_graph()
2859
for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2860
if lh_ancestor == current_revid:
2862
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2851
2864
def _gen_revision_history(self):
2852
2865
"""Generate the revision history from last revision
2998
2996
# you can always ask for the URL; but you might not be able to use it
2999
2997
# if the repo can't support stacking.
3000
2998
## self._check_stackable_repo()
3001
stacked_url = self._get_config_location('stacked_on_location')
2999
# stacked_on_location is only ever defined in branch.conf, so don't
3000
# waste effort reading the whole stack of config files.
3001
config = self.get_config()._get_branch_data_config()
3002
stacked_url = self._get_config_location('stacked_on_location',
3002
3004
if stacked_url is None:
3003
3005
raise errors.NotStacked(self)
3004
3006
return stacked_url
3006
def _get_append_revisions_only(self):
3007
return self.get_config(
3008
).get_user_option_as_bool('append_revisions_only')
3011
def generate_revision_history(self, revision_id, last_rev=None,
3013
"""See BzrBranch5.generate_revision_history"""
3014
history = self._lefthand_history(revision_id, last_rev, other_branch)
3015
revno = len(history)
3016
self.set_last_revision_info(revno, revision_id)
3018
3008
@needs_read_lock
3019
3009
def get_rev_id(self, revno, history=None):
3020
3010
"""Find the revision id of the specified revno."""
3150
3153
return self.new_revno - self.old_revno
3152
3155
def report(self, to_file):
3153
"""Write a human-readable description of the result."""
3154
if self.old_revid == self.new_revid:
3155
note('No new revisions to push.')
3157
note('Pushed up to revision %d.' % self.new_revno)
3156
# TODO: This function gets passed a to_file, but then
3157
# ignores it and calls note() instead. This is also
3158
# inconsistent with PullResult(), which writes to stdout.
3159
# -- JRV20110901, bug #838853
3160
tag_conflicts = getattr(self, "tag_conflicts", None)
3161
tag_updates = getattr(self, "tag_updates", None)
3163
if self.old_revid != self.new_revid:
3164
note(gettext('Pushed up to revision %d.') % self.new_revno)
3166
note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
3167
if self.old_revid == self.new_revid and not tag_updates:
3168
if not tag_conflicts:
3169
note(gettext('No new revisions or tags to push.'))
3171
note(gettext('No new revisions to push.'))
3158
3172
self._show_tag_conficts(to_file)
3368
3344
self.source.tags.merge_to(self.target.tags)
3370
3346
@needs_write_lock
3371
def update_revisions(self, stop_revision=None, overwrite=False,
3373
"""See InterBranch.update_revisions()."""
3347
def fetch(self, stop_revision=None, limit=None):
3348
if self.target.base == self.source.base:
3350
self.source.lock_read()
3352
fetch_spec_factory = fetch.FetchSpecFactory()
3353
fetch_spec_factory.source_branch = self.source
3354
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3355
fetch_spec_factory.source_repo = self.source.repository
3356
fetch_spec_factory.target_repo = self.target.repository
3357
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3358
fetch_spec_factory.limit = limit
3359
fetch_spec = fetch_spec_factory.make_fetch_spec()
3360
return self.target.repository.fetch(self.source.repository,
3361
fetch_spec=fetch_spec)
3363
self.source.unlock()
3366
def _update_revisions(self, stop_revision=None, overwrite=False,
3374
3368
other_revno, other_last_revision = self.source.last_revision_info()
3375
3369
stop_revno = None # unknown
3376
3370
if stop_revision is None:
3433
3437
return self._pull(overwrite,
3434
3438
stop_revision, _hook_master=master_branch,
3435
3439
run_hooks=run_hooks,
3436
_override_hook_target=_override_hook_target)
3440
_override_hook_target=_override_hook_target,
3441
merge_tags_to_master=not source_is_master)
3438
3443
if master_branch:
3439
3444
master_branch.unlock()
3441
def push(self, overwrite=False, stop_revision=None,
3446
def push(self, overwrite=False, stop_revision=None, lossy=False,
3442
3447
_override_hook_source_branch=None):
3443
3448
"""See InterBranch.push.
3445
3450
This is the basic concrete implementation of push()
3447
:param _override_hook_source_branch: If specified, run
3448
the hooks passing this Branch as the source, rather than self.
3449
This is for use of RemoteBranch, where push is delegated to the
3450
underlying vfs-based Branch.
3452
:param _override_hook_source_branch: If specified, run the hooks
3453
passing this Branch as the source, rather than self. This is for
3454
use of RemoteBranch, where push is delegated to the underlying
3458
raise errors.LossyPushToSameVCS(self.source, self.target)
3452
3459
# TODO: Public option to disable running hooks - should be trivial but
3454
self.source.lock_read()
3456
return _run_with_write_locked_target(
3457
self.target, self._push_with_bound_branches, overwrite,
3459
_override_hook_source_branch=_override_hook_source_branch)
3461
self.source.unlock()
3463
def _push_with_bound_branches(self, overwrite, stop_revision,
3462
op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3463
op.add_cleanup(self.source.lock_read().unlock)
3464
op.add_cleanup(self.target.lock_write().unlock)
3465
return op.run(overwrite, stop_revision,
3466
_override_hook_source_branch=_override_hook_source_branch)
3468
def _basic_push(self, overwrite, stop_revision):
3469
"""Basic implementation of push without bound branches or hooks.
3471
Must be called with source read locked and target write locked.
3473
result = BranchPushResult()
3474
result.source_branch = self.source
3475
result.target_branch = self.target
3476
result.old_revno, result.old_revid = self.target.last_revision_info()
3477
self.source.update_references(self.target)
3478
if result.old_revid != stop_revision:
3479
# We assume that during 'push' this repository is closer than
3481
graph = self.source.repository.get_graph(self.target.repository)
3482
self._update_revisions(stop_revision, overwrite=overwrite,
3484
if self.source._push_should_merge_tags():
3485
result.tag_updates, result.tag_conflicts = (
3486
self.source.tags.merge_to(self.target.tags, overwrite))
3487
result.new_revno, result.new_revid = self.target.last_revision_info()
3490
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
3464
3491
_override_hook_source_branch=None):
3465
3492
"""Push from source into target, and into target's master if any.
3478
3505
# be bound to itself? -- mbp 20070507
3479
3506
master_branch = self.target.get_master_branch()
3480
3507
master_branch.lock_write()
3482
# push into the master from the source branch.
3483
self.source._basic_push(master_branch, overwrite, stop_revision)
3484
# and push into the target branch from the source. Note that we
3485
# push from the source branch again, because it's considered the
3486
# highest bandwidth repository.
3487
result = self.source._basic_push(self.target, overwrite,
3489
result.master_branch = master_branch
3490
result.local_branch = self.target
3494
master_branch.unlock()
3508
operation.add_cleanup(master_branch.unlock)
3509
# push into the master from the source branch.
3510
master_inter = InterBranch.get(self.source, master_branch)
3511
master_inter._basic_push(overwrite, stop_revision)
3512
# and push into the target branch from the source. Note that
3513
# we push from the source branch again, because it's considered
3514
# the highest bandwidth repository.
3515
result = self._basic_push(overwrite, stop_revision)
3516
result.master_branch = master_branch
3517
result.local_branch = self.target
3519
master_branch = None
3496
3520
# no master branch
3497
result = self.source._basic_push(self.target, overwrite,
3521
result = self._basic_push(overwrite, stop_revision)
3499
3522
# TODO: Why set master_branch and local_branch if there's no
3500
3523
# binding? Maybe cleaner to just leave them unset? -- mbp
3502
3525
result.master_branch = self.target
3503
3526
result.local_branch = None
3507
3530
def _pull(self, overwrite=False, stop_revision=None,
3508
3531
possible_transports=None, _hook_master=None, run_hooks=True,
3509
_override_hook_target=None, local=False):
3532
_override_hook_target=None, local=False,
3533
merge_tags_to_master=True):
3510
3534
"""See Branch.pull.
3512
3536
This function is the core worker, used by GenericInterBranch.pull to