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
17
from __future__ import absolute_import
21
18
from cStringIO import StringIO
23
21
from bzrlib.lazy_import import lazy_import
24
22
lazy_import(globals(), """
23
from itertools import chain
26
24
from bzrlib import (
31
config as _mod_config,
40
revision as _mod_revision,
27
config as _mod_config,
37
revision as _mod_revision,
45
from bzrlib.config import BranchConfig, TransportConfig
46
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
47
from bzrlib.tag import (
48
from bzrlib.i18n import gettext, ngettext
51
# Explicitly import bzrlib.bzrdir so that the BzrProber
52
# is guaranteed to be registered.
59
from bzrlib.decorators import (
64
from bzrlib.hooks import Hooks
53
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
54
from bzrlib.hooks import HookPoint, Hooks
65
55
from bzrlib.inter import InterObject
66
56
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
67
57
from bzrlib import registry
102
96
self._partial_revision_history_cache = []
103
97
self._tags_bytes = None
104
98
self._last_revision_info_cache = None
105
self._master_branch_cache = None
106
99
self._merge_sorted_revisions_cache = None
107
self._open_hook(possible_transports)
108
101
hooks = Branch.hooks['open']
109
102
for hook in hooks:
112
def _open_hook(self, possible_transports):
105
def _open_hook(self):
113
106
"""Called by init to allow simpler extension of the base class."""
115
def _activate_fallback_location(self, url, possible_transports):
108
def _activate_fallback_location(self, url):
116
109
"""Activate the branch/repository from url as a fallback repository."""
117
110
for existing_fallback_repo in self.repository._fallback_repositories:
118
111
if existing_fallback_repo.user_url == url:
119
112
# This fallback is already configured. This probably only
120
# happens because ControlDir.sprout is a horrible mess. To avoid
113
# happens because BzrDir.sprout is a horrible mess. To avoid
121
114
# confusing _unstack we don't add this a second time.
122
115
mutter('duplicate activation of fallback %r on %r', url, self)
124
repo = self._get_fallback_repository(url, possible_transports)
117
repo = self._get_fallback_repository(url)
125
118
if repo.has_same_location(self.repository):
126
119
raise errors.UnstackableLocationError(self.user_url, url)
127
120
self.repository.add_fallback_repository(repo)
181
174
For instance, if the branch is at URL/.bzr/branch,
182
175
Branch.open(URL) -> a Branch instance.
184
control = controldir.ControlDir.open(base,
185
possible_transports=possible_transports, _unsupported=_unsupported)
186
return control.open_branch(unsupported=_unsupported,
187
possible_transports=possible_transports)
177
control = bzrdir.BzrDir.open(base, _unsupported,
178
possible_transports=possible_transports)
179
return control.open_branch(unsupported=_unsupported)
190
def open_from_transport(transport, name=None, _unsupported=False,
191
possible_transports=None):
182
def open_from_transport(transport, name=None, _unsupported=False):
192
183
"""Open the branch rooted at transport"""
193
control = controldir.ControlDir.open_from_transport(transport, _unsupported)
194
return control.open_branch(name=name, unsupported=_unsupported,
195
possible_transports=possible_transports)
184
control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
185
return control.open_branch(name=name, unsupported=_unsupported)
198
188
def open_containing(url, possible_transports=None):
527
505
rev_iter = iter(merge_sorted_revisions)
528
506
if start_revision_id is not None:
529
507
for node in rev_iter:
508
rev_id = node.key[-1]
531
509
if rev_id != start_revision_id:
534
512
# The decision to include the start or not
535
513
# depends on the stop_rule if a stop is provided
536
514
# so pop this node back into the iterator
537
rev_iter = itertools.chain(iter([node]), rev_iter)
515
rev_iter = chain(iter([node]), rev_iter)
539
517
if stop_revision_id is None:
540
518
# Yield everything
541
519
for node in rev_iter:
520
rev_id = node.key[-1]
543
521
yield (rev_id, node.merge_depth, node.revno,
544
522
node.end_of_merge)
545
523
elif stop_rule == 'exclude':
546
524
for node in rev_iter:
525
rev_id = node.key[-1]
548
526
if rev_id == stop_revision_id:
550
528
yield (rev_id, node.merge_depth, node.revno,
551
529
node.end_of_merge)
552
530
elif stop_rule == 'include':
553
531
for node in rev_iter:
532
rev_id = node.key[-1]
555
533
yield (rev_id, node.merge_depth, node.revno,
556
534
node.end_of_merge)
557
535
if rev_id == stop_revision_id:
686
662
raise errors.UnsupportedOperation(self.get_reference_info, self)
688
664
@needs_write_lock
689
def fetch(self, from_branch, last_revision=None, limit=None):
665
def fetch(self, from_branch, last_revision=None, pb=None, fetch_spec=None):
690
666
"""Copy revisions from from_branch into this branch.
692
668
:param from_branch: Where to copy from.
693
669
:param last_revision: What revision to stop at (None for at the end
695
:param limit: Optional rough limit of revisions to fetch
671
:param pb: An optional progress bar to use.
672
:param fetch_spec: If specified, a SearchResult or
673
PendingAncestryResult that describes which revisions to copy. This
674
allows copying multiple heads at once. Mutually exclusive with
698
return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
678
if fetch_spec is not None and last_revision is not None:
679
raise AssertionError(
680
"fetch_spec and last_revision are mutually exclusive.")
681
if self.base == from_branch.base:
684
symbol_versioning.warn(
685
symbol_versioning.deprecated_in((1, 14, 0))
686
% "pb parameter to fetch()")
687
from_branch.lock_read()
689
if last_revision is None and fetch_spec is None:
690
last_revision = from_branch.last_revision()
691
last_revision = _mod_revision.ensure_null(last_revision)
692
return self.repository.fetch(from_branch.repository,
693
revision_id=last_revision,
694
pb=pb, fetch_spec=fetch_spec)
700
698
def get_bound_location(self):
701
699
"""Return the URL of the branch we are bound to.
722
720
:param committer: Optional committer to set for commit.
723
721
:param revprops: Optional dictionary of revision properties.
724
722
:param revision_id: Optional revision id.
725
:param lossy: Whether to discard data that can not be natively
726
represented, when pushing to a foreign VCS
729
if config_stack is None:
730
config_stack = self.get_config_stack()
726
config = self.get_config()
732
return self.repository.get_commit_builder(self, parents, config_stack,
733
timestamp, timezone, committer, revprops, revision_id,
728
return self.repository.get_commit_builder(self, parents, config,
729
timestamp, timezone, committer, revprops, revision_id)
736
731
def get_master_branch(self, possible_transports=None):
737
732
"""Return the branch we are bound to.
766
759
"""Print `file` to stdout."""
767
760
raise NotImplementedError(self.print_file)
769
@deprecated_method(deprecated_in((2, 4, 0)))
770
762
def set_revision_history(self, rev_history):
771
"""See Branch.set_revision_history."""
772
self._set_revision_history(rev_history)
775
def _set_revision_history(self, rev_history):
776
if len(rev_history) == 0:
777
revid = _mod_revision.NULL_REVISION
779
revid = rev_history[-1]
780
if rev_history != self._lefthand_history(revid):
781
raise errors.NotLefthandHistory(rev_history)
782
self.set_last_revision_info(len(rev_history), revid)
783
self._cache_revision_history(rev_history)
784
for hook in Branch.hooks['set_rh']:
785
hook(self, rev_history)
788
def set_last_revision_info(self, revno, revision_id):
789
"""Set the last revision of this branch.
791
The caller is responsible for checking that the revno is correct
792
for this revision id.
794
It may be possible to set the branch last revision to an id not
795
present in the repository. However, branches can also be
796
configured to check constraints on history, in which case this may not
799
raise NotImplementedError(self.set_last_revision_info)
802
def generate_revision_history(self, revision_id, last_rev=None,
804
"""See Branch.generate_revision_history"""
805
graph = self.repository.get_graph()
806
(last_revno, last_revid) = self.last_revision_info()
807
known_revision_ids = [
808
(last_revid, last_revno),
809
(_mod_revision.NULL_REVISION, 0),
811
if last_rev is not None:
812
if not graph.is_ancestor(last_rev, revision_id):
813
# our previous tip is not merged into stop_revision
814
raise errors.DivergedBranches(self, other_branch)
815
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
816
self.set_last_revision_info(revno, revision_id)
763
raise NotImplementedError(self.set_revision_history)
818
765
@needs_write_lock
819
766
def set_parent(self, url):
1056
995
:return: A tuple (revno, revision_id).
1058
997
if self._last_revision_info_cache is None:
1059
self._last_revision_info_cache = self._read_last_revision_info()
998
self._last_revision_info_cache = self._last_revision_info()
1060
999
return self._last_revision_info_cache
1062
def _read_last_revision_info(self):
1063
raise NotImplementedError(self._read_last_revision_info)
1001
def _last_revision_info(self):
1002
rh = self.revision_history()
1005
return (revno, rh[-1])
1007
return (0, _mod_revision.NULL_REVISION)
1009
@deprecated_method(deprecated_in((1, 6, 0)))
1010
def missing_revisions(self, other, stop_revision=None):
1011
"""Return a list of new revisions that would perfectly fit.
1013
If self and other have not diverged, return a list of the revisions
1014
present in other, but missing from self.
1016
self_history = self.revision_history()
1017
self_len = len(self_history)
1018
other_history = other.revision_history()
1019
other_len = len(other_history)
1020
common_index = min(self_len, other_len) -1
1021
if common_index >= 0 and \
1022
self_history[common_index] != other_history[common_index]:
1023
raise errors.DivergedBranches(self, other)
1025
if stop_revision is None:
1026
stop_revision = other_len
1028
if stop_revision > other_len:
1029
raise errors.NoSuchRevision(self, stop_revision)
1030
return other_history[self_len:stop_revision]
1032
def update_revisions(self, other, stop_revision=None, overwrite=False,
1033
graph=None, fetch_tags=True):
1034
"""Pull in new perfect-fit revisions.
1036
:param other: Another Branch to pull from
1037
:param stop_revision: Updated until the given revision
1038
:param overwrite: Always set the branch pointer, rather than checking
1039
to see if it is a proper descendant.
1040
:param graph: A Graph object that can be used to query history
1041
information. This can be None.
1042
:param fetch_tags: Flag that specifies if tags from other should be
1046
return InterBranch.get(other, self).update_revisions(stop_revision,
1047
overwrite, graph, fetch_tags=fetch_tags)
1065
1049
@deprecated_method(deprecated_in((2, 4, 0)))
1066
1050
def import_last_revision_info(self, source_repo, revno, revid):
1085
1068
:param source: Source branch to optionally fetch from
1086
1069
:param revno: Revision number of the new tip
1087
1070
:param revid: Revision id of the new tip
1088
:param lossy: Whether to discard metadata that can not be
1089
natively represented
1090
:return: Tuple with the new revision number and revision id
1091
(should only be different from the arguments when lossy=True)
1093
1072
if not self.repository.has_same_location(source.repository):
1094
self.fetch(source, revid)
1074
tags_to_fetch = set(source.tags.get_reverse_tag_dict())
1075
except errors.TagsNotSupported:
1076
tags_to_fetch = set()
1077
fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
1078
source.repository, [revid],
1079
if_present_ids=tags_to_fetch).execute()
1080
self.repository.fetch(source.repository, fetch_spec=fetch_spec)
1095
1081
self.set_last_revision_info(revno, revid)
1096
return (revno, revid)
1098
1083
def revision_id_to_revno(self, revision_id):
1099
1084
"""Given a revision id, return its revno"""
1100
1085
if _mod_revision.is_null(revision_id):
1102
history = self._revision_history()
1087
history = self.revision_history()
1104
1089
return history.index(revision_id) + 1
1105
1090
except ValueError:
1132
1117
stop_revision=stop_revision,
1133
1118
possible_transports=possible_transports, *args, **kwargs)
1135
def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1120
def push(self, target, overwrite=False, stop_revision=None, *args,
1137
1122
"""Mirror this branch into target.
1139
1124
This branch is considered to be 'local', having low latency.
1141
1126
return InterBranch.get(self, target).push(overwrite, stop_revision,
1142
lossy, *args, **kwargs)
1129
def lossy_push(self, target, stop_revision=None):
1130
"""Push deltas into another branch.
1132
:note: This does not, like push, retain the revision ids from
1133
the source branch and will, rather than adding bzr-specific
1134
metadata, push only those semantics of the revision that can be
1135
natively represented by this branch' VCS.
1137
:param target: Target branch
1138
:param stop_revision: Revision to push, defaults to last revision.
1139
:return: BranchPushResult with an extra member revidmap:
1140
A dictionary mapping revision ids from the target branch
1141
to new revision ids in the target branch, for each
1142
revision that was pushed.
1144
inter = InterBranch.get(self, target)
1145
lossy_push = getattr(inter, "lossy_push", None)
1146
if lossy_push is None:
1147
raise errors.LossyPushToSameVCS(self, target)
1148
return lossy_push(stop_revision)
1144
1150
def basis_tree(self):
1145
1151
"""Return `Tree` object for last revision."""
1457
1461
t = transport.get_transport(to_location)
1458
1462
t.ensure_base()
1459
format = self._get_checkout_format(lightweight=lightweight)
1464
format = self._get_checkout_format()
1461
1465
checkout = format.initialize_on_transport(t)
1462
except errors.AlreadyControlDirError:
1463
# It's fine if the control directory already exists,
1464
# as long as there is no existing branch and working tree.
1465
checkout = controldir.ControlDir.open_from_transport(t)
1467
checkout.open_branch()
1468
except errors.NotBranchError:
1471
raise errors.AlreadyControlDirError(t.base)
1472
if checkout.control_transport.base == self.bzrdir.control_transport.base:
1473
# When checking out to the same control directory,
1474
# always create a lightweight checkout
1478
from_branch = checkout.set_branch_reference(target_branch=self)
1466
from_branch = BranchReferenceFormat().initialize(checkout,
1480
policy = checkout.determine_repository_policy()
1481
repo = policy.acquire_repository()[0]
1482
checkout_branch = checkout.create_branch()
1469
format = self._get_checkout_format()
1470
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1471
to_location, force_new_tree=False, format=format)
1472
checkout = checkout_branch.bzrdir
1483
1473
checkout_branch.bind(self)
1484
1474
# pull up to the specified revision_id to set the initial
1485
1475
# branch tip correctly, and seed it with history.
1486
1476
checkout_branch.pull(self, stop_revision=revision_id)
1488
1478
tree = checkout.create_workingtree(revision_id,
1489
1479
from_branch=from_branch,
1490
1480
accelerator_tree=accelerator_tree,
1571
1560
raise AssertionError("invalid heads: %r" % (heads,))
1573
def heads_to_fetch(self):
1574
"""Return the heads that must and that should be fetched to copy this
1575
branch into another repo.
1577
:returns: a 2-tuple of (must_fetch, if_present_fetch). must_fetch is a
1578
set of heads that must be fetched. if_present_fetch is a set of
1579
heads that must be fetched if present, but no error is necessary if
1580
they are not present.
1582
# For bzr native formats must_fetch is just the tip, and
1583
# if_present_fetch are the tags.
1584
must_fetch = set([self.last_revision()])
1585
if_present_fetch = set()
1586
if self.get_config_stack().get('branch.fetch_tags'):
1588
if_present_fetch = set(self.tags.get_reverse_tag_dict())
1589
except errors.TagsNotSupported:
1591
must_fetch.discard(_mod_revision.NULL_REVISION)
1592
if_present_fetch.discard(_mod_revision.NULL_REVISION)
1593
return must_fetch, if_present_fetch
1596
class BranchFormat(controldir.ControlComponentFormat):
1563
class BranchFormat(object):
1597
1564
"""An encapsulation of the initialization and open routines for a format.
1599
1566
Formats provide three things:
1600
1567
* An initialization routine,
1601
* a format description
1602
1569
* an open routine.
1604
1571
Formats are placed in an dict by their format string for reference
1618
1596
return not (self == other)
1621
@deprecated_method(deprecated_in((2, 4, 0)))
1599
def find_format(klass, a_bzrdir, name=None):
1600
"""Return the format for the branch object in a_bzrdir."""
1602
transport = a_bzrdir.get_branch_transport(None, name=name)
1603
format_string = transport.get_bytes("format")
1604
format = klass._formats[format_string]
1605
if isinstance(format, MetaDirBranchFormatFactory):
1608
except errors.NoSuchFile:
1609
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1611
raise errors.UnknownFormatError(format=format_string, kind='branch')
1622
1614
def get_default_format(klass):
1623
1615
"""Return the current default format."""
1624
return format_registry.get_default()
1616
return klass._default_format
1627
@deprecated_method(deprecated_in((2, 4, 0)))
1628
1619
def get_formats(klass):
1629
1620
"""Get all the known formats.
1631
1622
Warning: This triggers a load of all lazy registered formats: do not
1632
1623
use except when that is desireed.
1634
return format_registry._get_all()
1626
for fmt in klass._formats.values():
1627
if isinstance(fmt, MetaDirBranchFormatFactory):
1630
return result + klass._extra_formats
1636
def get_reference(self, controldir, name=None):
1637
"""Get the target reference of the branch in controldir.
1632
def get_reference(self, a_bzrdir, name=None):
1633
"""Get the target reference of the branch in a_bzrdir.
1639
1635
format probing must have been completed before calling
1640
1636
this method - it is assumed that the format of the branch
1641
in controldir is correct.
1637
in a_bzrdir is correct.
1643
:param controldir: The controldir to get the branch data from.
1639
:param a_bzrdir: The bzrdir to get the branch data from.
1644
1640
:param name: Name of the colocated branch to fetch
1645
1641
:return: None if the branch is not a reference branch.
1650
def set_reference(self, controldir, name, to_branch):
1651
"""Set the target reference of the branch in controldir.
1646
def set_reference(self, a_bzrdir, name, to_branch):
1647
"""Set the target reference of the branch in a_bzrdir.
1653
1649
format probing must have been completed before calling
1654
1650
this method - it is assumed that the format of the branch
1655
in controldir is correct.
1651
in a_bzrdir is correct.
1657
:param controldir: The controldir to set the branch reference for.
1653
:param a_bzrdir: The bzrdir to set the branch reference for.
1658
1654
:param name: Name of colocated branch to set, None for default
1659
1655
:param to_branch: branch that the checkout is to reference
1661
1657
raise NotImplementedError(self.set_reference)
1659
def get_format_string(self):
1660
"""Return the ASCII format string that identifies this format."""
1661
raise NotImplementedError(self.get_format_string)
1663
1663
def get_format_description(self):
1664
1664
"""Return the short format description for this format."""
1665
1665
raise NotImplementedError(self.get_format_description)
1667
def _run_post_branch_init_hooks(self, controldir, name, branch):
1667
def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
1668
1668
hooks = Branch.hooks['post_branch_init']
1671
params = BranchInitHookParams(self, controldir, name, branch)
1671
params = BranchInitHookParams(self, a_bzrdir, name, branch)
1672
1672
for hook in hooks:
1675
def initialize(self, controldir, name=None, repository=None,
1676
append_revisions_only=None):
1677
"""Create a branch of this format in controldir.
1675
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1677
"""Initialize a branch in a bzrdir, with specified files
1679
:param a_bzrdir: The bzrdir to initialize the branch in
1680
:param utf8_files: The files to create as a list of
1681
(filename, content) tuples
1682
:param name: Name of colocated branch to create, if any
1683
:return: a branch in this format
1685
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1686
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1687
control_files = lockable_files.LockableFiles(branch_transport,
1688
'lock', lockdir.LockDir)
1689
control_files.create_lock()
1690
control_files.lock_write()
1692
utf8_files += [('format', self.get_format_string())]
1693
for (filename, content) in utf8_files:
1694
branch_transport.put_bytes(
1696
mode=a_bzrdir._get_file_mode())
1698
control_files.unlock()
1699
branch = self.open(a_bzrdir, name, _found=True,
1700
found_repository=repository)
1701
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1704
def initialize(self, a_bzrdir, name=None, repository=None):
1705
"""Create a branch of this format in a_bzrdir.
1679
1707
:param name: Name of the colocated branch to create.
1681
1709
raise NotImplementedError(self.initialize)
1727
1755
raise NotImplementedError(self.open)
1730
@deprecated_method(deprecated_in((2, 4, 0)))
1758
def register_extra_format(klass, format):
1759
"""Register a branch format that can not be part of a metadir.
1761
This is mainly useful to allow custom branch formats, such as
1762
older Bazaar formats and foreign formats, to be tested
1764
klass._extra_formats.append(format)
1765
network_format_registry.register(
1766
format.network_name(), format.__class__)
1731
1769
def register_format(klass, format):
1732
1770
"""Register a metadir format.
1734
1772
See MetaDirBranchFormatFactory for the ability to register a format
1735
1773
without loading the code the format needs until it is actually used.
1737
format_registry.register(format)
1775
klass._formats[format.get_format_string()] = format
1776
# Metadir formats have a network name of their format string, and get
1777
# registered as factories.
1778
if isinstance(format, MetaDirBranchFormatFactory):
1779
network_format_registry.register(format.get_format_string(), format)
1781
network_format_registry.register(format.get_format_string(),
1740
@deprecated_method(deprecated_in((2, 4, 0)))
1741
1785
def set_default_format(klass, format):
1742
format_registry.set_default(format)
1786
klass._default_format = format
1744
1788
def supports_set_append_revisions_only(self):
1745
1789
"""True if this format supports set_append_revisions_only."""
1815
1850
These are all empty initially, because by default nothing should get
1818
Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1819
self.add_hook('set_rh',
1853
Hooks.__init__(self)
1854
self.create_hook(HookPoint('set_rh',
1820
1855
"Invoked whenever the revision history has been set via "
1821
1856
"set_revision_history. The api signature is (branch, "
1822
1857
"revision_history), and the branch will be write-locked. "
1823
1858
"The set_rh hook can be expensive for bzr to trigger, a better "
1824
"hook to use is Branch.post_change_branch_tip.", (0, 15))
1825
self.add_hook('open',
1859
"hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1860
self.create_hook(HookPoint('open',
1826
1861
"Called with the Branch object that has been opened after a "
1827
"branch is opened.", (1, 8))
1828
self.add_hook('post_push',
1862
"branch is opened.", (1, 8), None))
1863
self.create_hook(HookPoint('post_push',
1829
1864
"Called after a push operation completes. post_push is called "
1830
1865
"with a bzrlib.branch.BranchPushResult object and only runs in the "
1831
"bzr client.", (0, 15))
1832
self.add_hook('post_pull',
1866
"bzr client.", (0, 15), None))
1867
self.create_hook(HookPoint('post_pull',
1833
1868
"Called after a pull operation completes. post_pull is called "
1834
1869
"with a bzrlib.branch.PullResult object and only runs in the "
1835
"bzr client.", (0, 15))
1836
self.add_hook('pre_commit',
1870
"bzr client.", (0, 15), None))
1871
self.create_hook(HookPoint('pre_commit',
1837
1872
"Called after a commit is calculated but before it is "
1838
1873
"completed. pre_commit is called with (local, master, old_revno, "
1839
1874
"old_revid, future_revno, future_revid, tree_delta, future_tree"
1842
1877
"basis revision. hooks MUST NOT modify this delta. "
1843
1878
" future_tree is an in-memory tree obtained from "
1844
1879
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1846
self.add_hook('post_commit',
1880
"tree.", (0,91), None))
1881
self.create_hook(HookPoint('post_commit',
1847
1882
"Called in the bzr client after a commit has completed. "
1848
1883
"post_commit is called with (local, master, old_revno, old_revid, "
1849
1884
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1850
"commit to a branch.", (0, 15))
1851
self.add_hook('post_uncommit',
1885
"commit to a branch.", (0, 15), None))
1886
self.create_hook(HookPoint('post_uncommit',
1852
1887
"Called in the bzr client after an uncommit completes. "
1853
1888
"post_uncommit is called with (local, master, old_revno, "
1854
1889
"old_revid, new_revno, new_revid) where local is the local branch "
1855
1890
"or None, master is the target branch, and an empty branch "
1856
"receives new_revno of 0, new_revid of None.", (0, 15))
1857
self.add_hook('pre_change_branch_tip',
1891
"receives new_revno of 0, new_revid of None.", (0, 15), None))
1892
self.create_hook(HookPoint('pre_change_branch_tip',
1858
1893
"Called in bzr client and server before a change to the tip of a "
1859
1894
"branch is made. pre_change_branch_tip is called with a "
1860
1895
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1861
"commit, uncommit will all trigger this hook.", (1, 6))
1862
self.add_hook('post_change_branch_tip',
1896
"commit, uncommit will all trigger this hook.", (1, 6), None))
1897
self.create_hook(HookPoint('post_change_branch_tip',
1863
1898
"Called in bzr client and server after a change to the tip of a "
1864
1899
"branch is made. post_change_branch_tip is called with a "
1865
1900
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1866
"commit, uncommit will all trigger this hook.", (1, 4))
1867
self.add_hook('transform_fallback_location',
1901
"commit, uncommit will all trigger this hook.", (1, 4), None))
1902
self.create_hook(HookPoint('transform_fallback_location',
1868
1903
"Called when a stacked branch is activating its fallback "
1869
1904
"locations. transform_fallback_location is called with (branch, "
1870
1905
"url), and should return a new url. Returning the same url "
1875
1910
"fallback locations have not been activated. When there are "
1876
1911
"multiple hooks installed for transform_fallback_location, "
1877
1912
"all are called with the url returned from the previous hook."
1878
"The order is however undefined.", (1, 9))
1879
self.add_hook('automatic_tag_name',
1913
"The order is however undefined.", (1, 9), None))
1914
self.create_hook(HookPoint('automatic_tag_name',
1880
1915
"Called to determine an automatic tag name for a revision. "
1881
1916
"automatic_tag_name is called with (branch, revision_id) and "
1882
1917
"should return a tag name or None if no tag name could be "
1883
1918
"determined. The first non-None tag name returned will be used.",
1885
self.add_hook('post_branch_init',
1920
self.create_hook(HookPoint('post_branch_init',
1886
1921
"Called after new branch initialization completes. "
1887
1922
"post_branch_init is called with a "
1888
1923
"bzrlib.branch.BranchInitHookParams. "
1889
1924
"Note that init, branch and checkout (both heavyweight and "
1890
"lightweight) will all trigger this hook.", (2, 2))
1891
self.add_hook('post_switch',
1925
"lightweight) will all trigger this hook.", (2, 2), None))
1926
self.create_hook(HookPoint('post_switch',
1892
1927
"Called after a checkout switches branch. "
1893
1928
"post_switch is called with a "
1894
"bzrlib.branch.SwitchHookParams.", (2, 2))
1929
"bzrlib.branch.SwitchHookParams.", (2, 2), None))
2010
2045
self.revision_id)
2013
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
2014
"""Base class for branch formats that live in meta directories.
2018
BranchFormat.__init__(self)
2019
bzrdir.BzrFormat.__init__(self)
2022
def find_format(klass, controldir, name=None):
2023
"""Return the format for the branch object in controldir."""
2025
transport = controldir.get_branch_transport(None, name=name)
2026
except errors.NoSuchFile:
2027
raise errors.NotBranchError(path=name, bzrdir=controldir)
2029
format_string = transport.get_bytes("format")
2030
except errors.NoSuchFile:
2031
raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
2032
return klass._find_format(format_registry, 'branch', format_string)
2048
class BranchFormatMetadir(BranchFormat):
2049
"""Common logic for meta-dir based branch formats."""
2034
2051
def _branch_class(self):
2035
2052
"""What class to instantiate on open calls."""
2036
2053
raise NotImplementedError(self._branch_class)
2038
def _get_initial_config(self, append_revisions_only=None):
2039
if append_revisions_only:
2040
return "append_revisions_only = True\n"
2042
# Avoid writing anything if append_revisions_only is disabled,
2043
# as that is the default.
2046
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
2048
"""Initialize a branch in a bzrdir, with specified files
2050
:param a_bzrdir: The bzrdir to initialize the branch in
2051
:param utf8_files: The files to create as a list of
2052
(filename, content) tuples
2053
:param name: Name of colocated branch to create, if any
2054
:return: a branch in this format
2055
def network_name(self):
2056
"""A simple byte string uniquely identifying this format for RPC calls.
2058
Metadir branch formats use their format string.
2057
name = a_bzrdir._get_selected_branch()
2058
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
2059
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2060
control_files = lockable_files.LockableFiles(branch_transport,
2061
'lock', lockdir.LockDir)
2062
control_files.create_lock()
2063
control_files.lock_write()
2065
utf8_files += [('format', self.as_string())]
2066
for (filename, content) in utf8_files:
2067
branch_transport.put_bytes(
2069
mode=a_bzrdir._get_file_mode())
2071
control_files.unlock()
2072
branch = self.open(a_bzrdir, name, _found=True,
2073
found_repository=repository)
2074
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2060
return self.get_format_string()
2077
2062
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2078
found_repository=None, possible_transports=None):
2063
found_repository=None):
2079
2064
"""See BranchFormat.open()."""
2081
name = a_bzrdir._get_selected_branch()
2083
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2066
format = BranchFormat.find_format(a_bzrdir, name=name)
2084
2067
if format.__class__ != self.__class__:
2085
2068
raise AssertionError("wrong format %r found for %r" %
2086
2069
(format, self))
2096
2079
a_bzrdir=a_bzrdir,
2097
2080
_repository=found_repository,
2098
ignore_fallbacks=ignore_fallbacks,
2099
possible_transports=possible_transports)
2081
ignore_fallbacks=ignore_fallbacks)
2100
2082
except errors.NoSuchFile:
2101
2083
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2104
def _matchingbzrdir(self):
2105
ret = bzrdir.BzrDirMetaFormat1()
2106
ret.set_branch_format(self)
2086
super(BranchFormatMetadir, self).__init__()
2087
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2088
self._matchingbzrdir.set_branch_format(self)
2109
2090
def supports_tags(self):
2112
def supports_leaving_lock(self):
2115
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
2117
BranchFormat.check_support_status(self,
2118
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
2120
bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
2121
recommend_upgrade=recommend_upgrade, basedir=basedir)
2124
2094
class BzrBranchFormat5(BranchFormatMetadir):
2125
2095
"""Bzr branch format 5.
2216
2178
"""See BranchFormat.get_format_description()."""
2217
2179
return "Branch format 8"
2219
def initialize(self, a_bzrdir, name=None, repository=None,
2220
append_revisions_only=None):
2181
def initialize(self, a_bzrdir, name=None, repository=None):
2221
2182
"""Create a branch of this format in a_bzrdir."""
2222
2183
utf8_files = [('last-revision', '0 null:\n'),
2224
self._get_initial_config(append_revisions_only)),
2184
('branch.conf', ''),
2226
2186
('references', '')
2228
2188
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2191
super(BzrBranchFormat8, self).__init__()
2192
self._matchingbzrdir.repository_format = \
2193
RepositoryFormatKnitPack5RichRoot()
2230
2195
def make_tags(self, branch):
2231
2196
"""See bzrlib.branch.BranchFormat.make_tags()."""
2232
return _mod_tag.BasicTags(branch)
2197
return BasicTags(branch)
2234
2199
def supports_set_append_revisions_only(self):
2315
2269
location = transport.put_bytes('location', to_branch.base)
2317
2271
def initialize(self, a_bzrdir, name=None, target_branch=None,
2318
repository=None, append_revisions_only=None):
2319
2273
"""Create a branch of this format in a_bzrdir."""
2320
2274
if target_branch is None:
2321
2275
# this format does not implement branch itself, thus the implicit
2322
2276
# creation contract must see it as uninitializable
2323
2277
raise errors.UninitializableFormat(self)
2324
2278
mutter('creating branch reference in %s', a_bzrdir.user_url)
2325
if a_bzrdir._format.fixed_components:
2326
raise errors.IncompatibleFormat(self, a_bzrdir._format)
2328
name = a_bzrdir._get_selected_branch()
2329
2279
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2330
2280
branch_transport.put_bytes('location',
2331
target_branch.user_url)
2332
branch_transport.put_bytes('format', self.as_string())
2333
branch = self.open(a_bzrdir, name, _found=True,
2281
target_branch.bzrdir.user_url)
2282
branch_transport.put_bytes('format', self.get_format_string())
2284
a_bzrdir, name, _found=True,
2334
2285
possible_transports=[target_branch.bzrdir.root_transport])
2335
2286
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2290
super(BranchReferenceFormat, self).__init__()
2291
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2292
self._matchingbzrdir.set_branch_format(self)
2338
2294
def _make_reference_clone_function(format, a_branch):
2339
2295
"""Create a clone() routine for a branch dynamically."""
2340
2296
def clone(to_bzrdir, revision_id=None,
2462
2400
def __init__(self, _format=None,
2463
2401
_control_files=None, a_bzrdir=None, name=None,
2464
_repository=None, ignore_fallbacks=False,
2465
possible_transports=None):
2402
_repository=None, ignore_fallbacks=False):
2466
2403
"""Create new branch object at a particular location."""
2467
2404
if a_bzrdir is None:
2468
2405
raise ValueError('a_bzrdir must be supplied')
2470
raise ValueError('name must be supplied')
2471
self.bzrdir = a_bzrdir
2472
self._user_transport = self.bzrdir.transport.clone('..')
2474
self._user_transport.set_segment_parameter(
2475
"branch", urlutils.escape(name))
2476
self._base = self._user_transport.base
2407
self.bzrdir = a_bzrdir
2408
self._base = self.bzrdir.transport.clone('..').base
2477
2409
self.name = name
2410
# XXX: We should be able to just do
2411
# self.base = self.bzrdir.root_transport.base
2412
# but this does not quite work yet -- mbp 20080522
2478
2413
self._format = _format
2479
2414
if _control_files is None:
2480
2415
raise ValueError('BzrBranch _control_files is None')
2481
2416
self.control_files = _control_files
2482
2417
self._transport = _control_files._transport
2483
2418
self.repository = _repository
2484
self.conf_store = None
2485
Branch.__init__(self, possible_transports)
2419
Branch.__init__(self)
2487
2421
def __str__(self):
2488
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2422
if self.name is None:
2423
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2425
return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2490
2428
__repr__ = __str__
2588
2515
"""See Branch.print_file."""
2589
2516
return self.repository.print_file(file, revision_id)
2518
def _write_revision_history(self, history):
2519
"""Factored out of set_revision_history.
2521
This performs the actual writing to disk.
2522
It is intended to be called by BzrBranch5.set_revision_history."""
2523
self._transport.put_bytes(
2524
'revision-history', '\n'.join(history),
2525
mode=self.bzrdir._get_file_mode())
2528
def set_revision_history(self, rev_history):
2529
"""See Branch.set_revision_history."""
2530
if 'evil' in debug.debug_flags:
2531
mutter_callsite(3, "set_revision_history scales with history.")
2532
check_not_reserved_id = _mod_revision.check_not_reserved_id
2533
for rev_id in rev_history:
2534
check_not_reserved_id(rev_id)
2535
if Branch.hooks['post_change_branch_tip']:
2536
# Don't calculate the last_revision_info() if there are no hooks
2538
old_revno, old_revid = self.last_revision_info()
2539
if len(rev_history) == 0:
2540
revid = _mod_revision.NULL_REVISION
2542
revid = rev_history[-1]
2543
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2544
self._write_revision_history(rev_history)
2545
self._clear_cached_state()
2546
self._cache_revision_history(rev_history)
2547
for hook in Branch.hooks['set_rh']:
2548
hook(self, rev_history)
2549
if Branch.hooks['post_change_branch_tip']:
2550
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2552
def _synchronize_history(self, destination, revision_id):
2553
"""Synchronize last revision and revision history between branches.
2555
This version is most efficient when the destination is also a
2556
BzrBranch5, but works for BzrBranch6 as long as the revision
2557
history is the true lefthand parent history, and all of the revisions
2558
are in the destination's repository. If not, set_revision_history
2561
:param destination: The branch to copy the history into
2562
:param revision_id: The revision-id to truncate history at. May
2563
be None to copy complete history.
2565
if not isinstance(destination._format, BzrBranchFormat5):
2566
super(BzrBranch, self)._synchronize_history(
2567
destination, revision_id)
2569
if revision_id == _mod_revision.NULL_REVISION:
2572
new_history = self.revision_history()
2573
if revision_id is not None and new_history != []:
2575
new_history = new_history[:new_history.index(revision_id) + 1]
2577
rev = self.repository.get_revision(revision_id)
2578
new_history = rev.get_history(self.repository)[1:]
2579
destination.set_revision_history(new_history)
2591
2581
@needs_write_lock
2592
2582
def set_last_revision_info(self, revno, revision_id):
2593
if not revision_id or not isinstance(revision_id, basestring):
2594
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2583
"""Set the last revision of this branch.
2585
The caller is responsible for checking that the revno is correct
2586
for this revision id.
2588
It may be possible to set the branch last revision to an id not
2589
present in the repository. However, branches can also be
2590
configured to check constraints on history, in which case this may not
2595
2593
revision_id = _mod_revision.ensure_null(revision_id)
2596
old_revno, old_revid = self.last_revision_info()
2597
if self.get_append_revisions_only():
2598
self._check_history_violation(revision_id)
2599
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2600
self._write_last_revision_info(revno, revision_id)
2601
self._clear_cached_state()
2602
self._last_revision_info_cache = revno, revision_id
2603
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2594
# this old format stores the full history, but this api doesn't
2595
# provide it, so we must generate, and might as well check it's
2597
history = self._lefthand_history(revision_id)
2598
if len(history) != revno:
2599
raise AssertionError('%d != %d' % (len(history), revno))
2600
self.set_revision_history(history)
2602
def _gen_revision_history(self):
2603
history = self._transport.get_bytes('revision-history').split('\n')
2604
if history[-1:] == ['']:
2605
# There shouldn't be a trailing newline, but just in case.
2610
def generate_revision_history(self, revision_id, last_rev=None,
2612
"""Create a new revision history that will finish with revision_id.
2614
:param revision_id: the new tip to use.
2615
:param last_rev: The previous last_revision. If not None, then this
2616
must be a ancestory of revision_id, or DivergedBranches is raised.
2617
:param other_branch: The other branch that DivergedBranches should
2618
raise with respect to.
2620
self.set_revision_history(self._lefthand_history(revision_id,
2621
last_rev, other_branch))
2605
2623
def basis_tree(self):
2606
2624
"""See Branch.basis_tree."""
2636
def _basic_push(self, target, overwrite, stop_revision):
2637
"""Basic implementation of push without bound branches or hooks.
2639
Must be called with source read locked and target write locked.
2641
result = BranchPushResult()
2642
result.source_branch = self
2643
result.target_branch = target
2644
result.old_revno, result.old_revid = target.last_revision_info()
2645
self.update_references(target)
2646
if result.old_revid != stop_revision:
2647
# We assume that during 'push' this repository is closer than
2649
graph = self.repository.get_graph(target.repository)
2650
target.update_revisions(self, stop_revision,
2651
overwrite=overwrite, graph=graph)
2652
if self._push_should_merge_tags():
2653
result.tag_conflicts = self.tags.merge_to(target.tags,
2655
result.new_revno, result.new_revid = target.last_revision_info()
2618
2658
def get_stacked_on_url(self):
2619
2659
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2631
2671
self._transport.put_bytes('parent', url + '\n',
2632
2672
mode=self.bzrdir._get_file_mode())
2675
class BzrBranchPreSplitOut(BzrBranch):
2677
def _get_checkout_format(self):
2678
"""Return the most suitable metadir for a checkout of this branch.
2679
Weaves are used if this branch's repository uses weaves.
2681
from bzrlib.repofmt.weaverepo import RepositoryFormat7
2682
from bzrlib.bzrdir import BzrDirMetaFormat1
2683
format = BzrDirMetaFormat1()
2684
format.repository_format = RepositoryFormat7()
2688
class BzrBranch5(BzrBranch):
2689
"""A format 5 branch. This supports new features over plain branches.
2691
It has support for a master_branch which is the data for bound branches.
2694
def get_bound_location(self):
2696
return self._transport.get_bytes('bound')[:-1]
2697
except errors.NoSuchFile:
2701
def get_master_branch(self, possible_transports=None):
2702
"""Return the branch we are bound to.
2704
:return: Either a Branch, or None
2706
This could memoise the branch, but if thats done
2707
it must be revalidated on each new lock.
2708
So for now we just don't memoise it.
2709
# RBC 20060304 review this decision.
2711
bound_loc = self.get_bound_location()
2715
return Branch.open(bound_loc,
2716
possible_transports=possible_transports)
2717
except (errors.NotBranchError, errors.ConnectionError), e:
2718
raise errors.BoundBranchConnectionFailure(
2634
2721
@needs_write_lock
2636
"""If bound, unbind"""
2637
return self.set_bound_location(None)
2722
def set_bound_location(self, location):
2723
"""Set the target where this branch is bound to.
2725
:param location: URL to the target branch
2728
self._transport.put_bytes('bound', location+'\n',
2729
mode=self.bzrdir._get_file_mode())
2732
self._transport.delete('bound')
2733
except errors.NoSuchFile:
2639
2737
@needs_write_lock
2640
2738
def bind(self, other):
2662
2760
# history around
2663
2761
self.set_bound_location(other.base)
2665
def get_bound_location(self):
2667
return self._transport.get_bytes('bound')[:-1]
2668
except errors.NoSuchFile:
2672
def get_master_branch(self, possible_transports=None):
2673
"""Return the branch we are bound to.
2675
:return: Either a Branch, or None
2677
if self._master_branch_cache is None:
2678
self._master_branch_cache = self._get_master_branch(
2679
possible_transports)
2680
return self._master_branch_cache
2682
def _get_master_branch(self, possible_transports):
2683
bound_loc = self.get_bound_location()
2687
return Branch.open(bound_loc,
2688
possible_transports=possible_transports)
2689
except (errors.NotBranchError, errors.ConnectionError), e:
2690
raise errors.BoundBranchConnectionFailure(
2693
2763
@needs_write_lock
2694
def set_bound_location(self, location):
2695
"""Set the target where this branch is bound to.
2697
:param location: URL to the target branch
2699
self._master_branch_cache = None
2701
self._transport.put_bytes('bound', location+'\n',
2702
mode=self.bzrdir._get_file_mode())
2705
self._transport.delete('bound')
2706
except errors.NoSuchFile:
2765
"""If bound, unbind"""
2766
return self.set_bound_location(None)
2710
2768
@needs_write_lock
2711
2769
def update(self, possible_transports=None):
2727
def _read_last_revision_info(self):
2728
revision_string = self._transport.get_bytes('last-revision')
2729
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2730
revision_id = cache_utf8.get_cached_utf8(revision_id)
2732
return revno, revision_id
2734
def _write_last_revision_info(self, revno, revision_id):
2735
"""Simply write out the revision id, with no checks.
2737
Use set_last_revision_info to perform this safely.
2739
Does not update the revision_history cache.
2741
revision_id = _mod_revision.ensure_null(revision_id)
2742
out_string = '%d %s\n' % (revno, revision_id)
2743
self._transport.put_bytes('last-revision', out_string,
2744
mode=self.bzrdir._get_file_mode())
2747
def update_feature_flags(self, updated_flags):
2748
"""Update the feature flags for this branch.
2750
:param updated_flags: Dictionary mapping feature names to necessities
2751
A necessity can be None to indicate the feature should be removed
2753
self._format._update_feature_flags(updated_flags)
2754
self.control_transport.put_bytes('format', self._format.as_string())
2757
class FullHistoryBzrBranch(BzrBranch):
2758
"""Bzr branch which contains the full revision history."""
2761
def set_last_revision_info(self, revno, revision_id):
2762
if not revision_id or not isinstance(revision_id, basestring):
2763
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2764
revision_id = _mod_revision.ensure_null(revision_id)
2765
# this old format stores the full history, but this api doesn't
2766
# provide it, so we must generate, and might as well check it's
2768
history = self._lefthand_history(revision_id)
2769
if len(history) != revno:
2770
raise AssertionError('%d != %d' % (len(history), revno))
2771
self._set_revision_history(history)
2773
def _read_last_revision_info(self):
2774
rh = self._revision_history()
2777
return (revno, rh[-1])
2779
return (0, _mod_revision.NULL_REVISION)
2781
@deprecated_method(deprecated_in((2, 4, 0)))
2783
def set_revision_history(self, rev_history):
2784
"""See Branch.set_revision_history."""
2785
self._set_revision_history(rev_history)
2787
def _set_revision_history(self, rev_history):
2788
if 'evil' in debug.debug_flags:
2789
mutter_callsite(3, "set_revision_history scales with history.")
2790
check_not_reserved_id = _mod_revision.check_not_reserved_id
2791
for rev_id in rev_history:
2792
check_not_reserved_id(rev_id)
2793
if Branch.hooks['post_change_branch_tip']:
2794
# Don't calculate the last_revision_info() if there are no hooks
2796
old_revno, old_revid = self.last_revision_info()
2797
if len(rev_history) == 0:
2798
revid = _mod_revision.NULL_REVISION
2800
revid = rev_history[-1]
2801
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2802
self._write_revision_history(rev_history)
2803
self._clear_cached_state()
2804
self._cache_revision_history(rev_history)
2805
for hook in Branch.hooks['set_rh']:
2806
hook(self, rev_history)
2807
if Branch.hooks['post_change_branch_tip']:
2808
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2810
def _write_revision_history(self, history):
2811
"""Factored out of set_revision_history.
2813
This performs the actual writing to disk.
2814
It is intended to be called by set_revision_history."""
2815
self._transport.put_bytes(
2816
'revision-history', '\n'.join(history),
2817
mode=self.bzrdir._get_file_mode())
2819
def _gen_revision_history(self):
2820
history = self._transport.get_bytes('revision-history').split('\n')
2821
if history[-1:] == ['']:
2822
# There shouldn't be a trailing newline, but just in case.
2826
def _synchronize_history(self, destination, revision_id):
2827
if not isinstance(destination, FullHistoryBzrBranch):
2828
super(BzrBranch, self)._synchronize_history(
2829
destination, revision_id)
2831
if revision_id == _mod_revision.NULL_REVISION:
2834
new_history = self._revision_history()
2835
if revision_id is not None and new_history != []:
2837
new_history = new_history[:new_history.index(revision_id) + 1]
2839
rev = self.repository.get_revision(revision_id)
2840
new_history = rev.get_history(self.repository)[1:]
2841
destination._set_revision_history(new_history)
2844
def generate_revision_history(self, revision_id, last_rev=None,
2846
"""Create a new revision history that will finish with revision_id.
2848
:param revision_id: the new tip to use.
2849
:param last_rev: The previous last_revision. If not None, then this
2850
must be a ancestory of revision_id, or DivergedBranches is raised.
2851
:param other_branch: The other branch that DivergedBranches should
2852
raise with respect to.
2854
self._set_revision_history(self._lefthand_history(revision_id,
2855
last_rev, other_branch))
2858
class BzrBranch5(FullHistoryBzrBranch):
2859
"""A format 5 branch. This supports new features over plain branches.
2861
It has support for a master_branch which is the data for bound branches.
2865
class BzrBranch8(BzrBranch):
2786
class BzrBranch8(BzrBranch5):
2866
2787
"""A branch that stores tree-reference locations."""
2868
def _open_hook(self, possible_transports=None):
2789
def _open_hook(self):
2869
2790
if self._ignore_fallbacks:
2871
if possible_transports is None:
2872
possible_transports = [self.bzrdir.root_transport]
2874
2793
url = self.get_stacked_on_url()
2875
2794
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2897
2815
self._last_revision_info_cache = None
2898
2816
self._reference_info = None
2818
def _last_revision_info(self):
2819
revision_string = self._transport.get_bytes('last-revision')
2820
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2821
revision_id = cache_utf8.get_cached_utf8(revision_id)
2823
return revno, revision_id
2825
def _write_last_revision_info(self, revno, revision_id):
2826
"""Simply write out the revision id, with no checks.
2828
Use set_last_revision_info to perform this safely.
2830
Does not update the revision_history cache.
2831
Intended to be called by set_last_revision_info and
2832
_write_revision_history.
2834
revision_id = _mod_revision.ensure_null(revision_id)
2835
out_string = '%d %s\n' % (revno, revision_id)
2836
self._transport.put_bytes('last-revision', out_string,
2837
mode=self.bzrdir._get_file_mode())
2840
def set_last_revision_info(self, revno, revision_id):
2841
revision_id = _mod_revision.ensure_null(revision_id)
2842
old_revno, old_revid = self.last_revision_info()
2843
if self._get_append_revisions_only():
2844
self._check_history_violation(revision_id)
2845
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2846
self._write_last_revision_info(revno, revision_id)
2847
self._clear_cached_state()
2848
self._last_revision_info_cache = revno, revision_id
2849
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2851
def _synchronize_history(self, destination, revision_id):
2852
"""Synchronize last revision and revision history between branches.
2854
:see: Branch._synchronize_history
2856
# XXX: The base Branch has a fast implementation of this method based
2857
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2858
# that uses set_revision_history. This class inherits from BzrBranch5,
2859
# but wants the fast implementation, so it calls
2860
# Branch._synchronize_history directly.
2861
Branch._synchronize_history(self, destination, revision_id)
2900
2863
def _check_history_violation(self, revision_id):
2901
current_revid = self.last_revision()
2902
last_revision = _mod_revision.ensure_null(current_revid)
2864
last_revision = _mod_revision.ensure_null(self.last_revision())
2903
2865
if _mod_revision.is_null(last_revision):
2905
graph = self.repository.get_graph()
2906
for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2907
if lh_ancestor == current_revid:
2909
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2867
if last_revision not in self._lefthand_history(revision_id):
2868
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2911
2870
def _gen_revision_history(self):
2912
2871
"""Generate the revision history from last revision
3007
2982
def set_bound_location(self, location):
3008
2983
"""See Branch.set_push_location."""
3009
self._master_branch_cache = None
3011
conf = self.get_config_stack()
2985
config = self.get_config()
3012
2986
if location is None:
3013
if not conf.get('bound'):
2987
if config.get_user_option('bound') != 'True':
3016
conf.set('bound', 'False')
2990
config.set_user_option('bound', 'False', warn_masked=True)
3019
2993
self._set_config_location('bound_location', location,
3021
conf.set('bound', 'True')
2995
config.set_user_option('bound', 'True', warn_masked=True)
3024
2998
def _get_bound_location(self, bound):
3025
2999
"""Return the bound location in the config file.
3027
3001
Return None if the bound parameter does not match"""
3028
conf = self.get_config_stack()
3029
if conf.get('bound') != bound:
3002
config = self.get_config()
3003
config_bound = (config.get_user_option('bound') == 'True')
3004
if config_bound != bound:
3031
return self._get_config_location('bound_location', config=conf)
3006
return self._get_config_location('bound_location', config=config)
3033
3008
def get_bound_location(self):
3034
"""See Branch.get_bound_location."""
3009
"""See Branch.set_push_location."""
3035
3010
return self._get_bound_location(True)
3037
3012
def get_old_bound_location(self):
3042
3017
# you can always ask for the URL; but you might not be able to use it
3043
3018
# if the repo can't support stacking.
3044
3019
## self._check_stackable_repo()
3045
# stacked_on_location is only ever defined in branch.conf, so don't
3046
# waste effort reading the whole stack of config files.
3047
conf = _mod_config.BranchOnlyStack(self)
3048
stacked_url = self._get_config_location('stacked_on_location',
3020
stacked_url = self._get_config_location('stacked_on_location')
3050
3021
if stacked_url is None:
3051
3022
raise errors.NotStacked(self)
3052
return stacked_url.encode('utf-8')
3025
def _get_append_revisions_only(self):
3026
return self.get_config(
3027
).get_user_option_as_bool('append_revisions_only')
3030
def generate_revision_history(self, revision_id, last_rev=None,
3032
"""See BzrBranch5.generate_revision_history"""
3033
history = self._lefthand_history(revision_id, last_rev, other_branch)
3034
revno = len(history)
3035
self.set_last_revision_info(revno, revision_id)
3054
3037
@needs_read_lock
3055
3038
def get_rev_id(self, revno, history=None):
3199
3169
return self.new_revno - self.old_revno
3201
3171
def report(self, to_file):
3202
# TODO: This function gets passed a to_file, but then
3203
# ignores it and calls note() instead. This is also
3204
# inconsistent with PullResult(), which writes to stdout.
3205
# -- JRV20110901, bug #838853
3206
tag_conflicts = getattr(self, "tag_conflicts", None)
3207
tag_updates = getattr(self, "tag_updates", None)
3209
if self.old_revid != self.new_revid:
3210
note(gettext('Pushed up to revision %d.') % self.new_revno)
3212
note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
3213
if self.old_revid == self.new_revid and not tag_updates:
3214
if not tag_conflicts:
3215
note(gettext('No new revisions or tags to push.'))
3217
note(gettext('No new revisions to push.'))
3172
"""Write a human-readable description of the result."""
3173
if self.old_revid == self.new_revid:
3174
note('No new revisions to push.')
3176
note('Pushed up to revision %d.' % self.new_revno)
3218
3177
self._show_tag_conficts(to_file)
3251
3210
# Copy source data into target
3252
3211
new_branch._write_last_revision_info(*branch.last_revision_info())
3253
new_branch.lock_write()
3255
new_branch.set_parent(branch.get_parent())
3256
new_branch.set_bound_location(branch.get_bound_location())
3257
new_branch.set_push_location(branch.get_push_location())
3212
new_branch.set_parent(branch.get_parent())
3213
new_branch.set_bound_location(branch.get_bound_location())
3214
new_branch.set_push_location(branch.get_push_location())
3261
3216
# New branch has no tags by default
3262
3217
new_branch.tags._set_tag_dict({})
3264
3219
# Copying done; now update target format
3265
3220
new_branch._transport.put_bytes('format',
3221
format.get_format_string(),
3267
3222
mode=new_branch.bzrdir._get_file_mode())
3269
3224
# Clean up old files
3270
3225
new_branch._transport.delete('revision-history')
3274
branch.set_parent(None)
3275
except errors.NoSuchFile:
3277
branch.set_bound_location(None)
3227
branch.set_parent(None)
3228
except errors.NoSuchFile:
3230
branch.set_bound_location(None)
3282
3233
class Converter6to7(object):
3286
3237
format = BzrBranchFormat7()
3287
3238
branch._set_config_location('stacked_on_location', '')
3288
3239
# update target format
3289
branch._transport.put_bytes('format', format.as_string())
3240
branch._transport.put_bytes('format', format.get_format_string())
3292
3243
class Converter7to8(object):
3293
"""Perform an in-place upgrade of format 7 to format 8"""
3244
"""Perform an in-place upgrade of format 6 to format 7"""
3295
3246
def convert(self, branch):
3296
3247
format = BzrBranchFormat8()
3297
3248
branch._transport.put_bytes('references', '')
3298
3249
# update target format
3299
branch._transport.put_bytes('format', format.as_string())
3250
branch._transport.put_bytes('format', format.get_format_string())
3253
def _run_with_write_locked_target(target, callable, *args, **kwargs):
3254
"""Run ``callable(*args, **kwargs)``, write-locking target for the
3257
_run_with_write_locked_target will attempt to release the lock it acquires.
3259
If an exception is raised by callable, then that exception *will* be
3260
propagated, even if the unlock attempt raises its own error. Thus
3261
_run_with_write_locked_target should be preferred to simply doing::
3265
return callable(*args, **kwargs)
3270
# This is very similar to bzrlib.decorators.needs_write_lock. Perhaps they
3271
# should share code?
3274
result = callable(*args, **kwargs)
3276
exc_info = sys.exc_info()
3280
raise exc_info[0], exc_info[1], exc_info[2]
3302
3286
class InterBranch(InterObject):
3332
3316
raise NotImplementedError(self.pull)
3334
3318
@needs_write_lock
3335
def push(self, overwrite=False, stop_revision=None, lossy=False,
3319
def update_revisions(self, stop_revision=None, overwrite=False,
3320
graph=None, fetch_tags=True):
3321
"""Pull in new perfect-fit revisions.
3323
:param stop_revision: Updated until the given revision
3324
:param overwrite: Always set the branch pointer, rather than checking
3325
to see if it is a proper descendant.
3326
:param graph: A Graph object that can be used to query history
3327
information. This can be None.
3328
:param fetch_tags: Flag that specifies if tags from source should be
3332
raise NotImplementedError(self.update_revisions)
3335
def push(self, overwrite=False, stop_revision=None,
3336
3336
_override_hook_source_branch=None):
3337
3337
"""Mirror the source branch into the target branch.
3398
3389
self.source.tags.merge_to(self.target.tags)
3400
3391
@needs_write_lock
3401
def fetch(self, stop_revision=None, limit=None):
3402
if self.target.base == self.source.base:
3404
self.source.lock_read()
3406
fetch_spec_factory = fetch.FetchSpecFactory()
3407
fetch_spec_factory.source_branch = self.source
3408
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3409
fetch_spec_factory.source_repo = self.source.repository
3410
fetch_spec_factory.target_repo = self.target.repository
3411
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3412
fetch_spec_factory.limit = limit
3413
fetch_spec = fetch_spec_factory.make_fetch_spec()
3414
return self.target.repository.fetch(self.source.repository,
3415
fetch_spec=fetch_spec)
3417
self.source.unlock()
3420
def _update_revisions(self, stop_revision=None, overwrite=False,
3392
def update_revisions(self, stop_revision=None, overwrite=False,
3393
graph=None, fetch_tags=True):
3394
"""See InterBranch.update_revisions()."""
3422
3395
other_revno, other_last_revision = self.source.last_revision_info()
3423
3396
stop_revno = None # unknown
3424
3397
if stop_revision is None:
3497
3472
if master_branch:
3498
3473
master_branch.unlock()
3500
def push(self, overwrite=False, stop_revision=None, lossy=False,
3475
def push(self, overwrite=False, stop_revision=None,
3501
3476
_override_hook_source_branch=None):
3502
3477
"""See InterBranch.push.
3504
3479
This is the basic concrete implementation of push()
3506
:param _override_hook_source_branch: If specified, run the hooks
3507
passing this Branch as the source, rather than self. This is for
3508
use of RemoteBranch, where push is delegated to the underlying
3481
:param _override_hook_source_branch: If specified, run
3482
the hooks passing this Branch as the source, rather than self.
3483
This is for use of RemoteBranch, where push is delegated to the
3484
underlying vfs-based Branch.
3512
raise errors.LossyPushToSameVCS(self.source, self.target)
3513
3486
# TODO: Public option to disable running hooks - should be trivial but
3516
op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3517
op.add_cleanup(self.source.lock_read().unlock)
3518
op.add_cleanup(self.target.lock_write().unlock)
3519
return op.run(overwrite, stop_revision,
3520
_override_hook_source_branch=_override_hook_source_branch)
3522
def _basic_push(self, overwrite, stop_revision):
3523
"""Basic implementation of push without bound branches or hooks.
3525
Must be called with source read locked and target write locked.
3527
result = BranchPushResult()
3528
result.source_branch = self.source
3529
result.target_branch = self.target
3530
result.old_revno, result.old_revid = self.target.last_revision_info()
3531
self.source.update_references(self.target)
3532
if result.old_revid != stop_revision:
3533
# We assume that during 'push' this repository is closer than
3535
graph = self.source.repository.get_graph(self.target.repository)
3536
self._update_revisions(stop_revision, overwrite=overwrite,
3538
if self.source._push_should_merge_tags():
3539
result.tag_updates, result.tag_conflicts = (
3540
self.source.tags.merge_to(self.target.tags, overwrite))
3541
result.new_revno, result.new_revid = self.target.last_revision_info()
3544
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
3488
self.source.lock_read()
3490
return _run_with_write_locked_target(
3491
self.target, self._push_with_bound_branches, overwrite,
3493
_override_hook_source_branch=_override_hook_source_branch)
3495
self.source.unlock()
3497
def _push_with_bound_branches(self, overwrite, stop_revision,
3545
3498
_override_hook_source_branch=None):
3546
3499
"""Push from source into target, and into target's master if any.
3559
3512
# be bound to itself? -- mbp 20070507
3560
3513
master_branch = self.target.get_master_branch()
3561
3514
master_branch.lock_write()
3562
operation.add_cleanup(master_branch.unlock)
3563
# push into the master from the source branch.
3564
master_inter = InterBranch.get(self.source, master_branch)
3565
master_inter._basic_push(overwrite, stop_revision)
3566
# and push into the target branch from the source. Note that
3567
# we push from the source branch again, because it's considered
3568
# the highest bandwidth repository.
3569
result = self._basic_push(overwrite, stop_revision)
3570
result.master_branch = master_branch
3571
result.local_branch = self.target
3516
# push into the master from the source branch.
3517
self.source._basic_push(master_branch, overwrite, stop_revision)
3518
# and push into the target branch from the source. Note that we
3519
# push from the source branch again, because it's considered the
3520
# highest bandwidth repository.
3521
result = self.source._basic_push(self.target, overwrite,
3523
result.master_branch = master_branch
3524
result.local_branch = self.target
3528
master_branch.unlock()
3573
master_branch = None
3574
3530
# no master branch
3575
result = self._basic_push(overwrite, stop_revision)
3531
result = self.source._basic_push(self.target, overwrite,
3576
3533
# TODO: Why set master_branch and local_branch if there's no
3577
3534
# binding? Maybe cleaner to just leave them unset? -- mbp
3579
3536
result.master_branch = self.target
3580
3537
result.local_branch = None
3584
3541
def _pull(self, overwrite=False, stop_revision=None,
3585
3542
possible_transports=None, _hook_master=None, run_hooks=True,
3621
3576
# -- JRV20090506
3622
3577
result.old_revno, result.old_revid = \
3623
3578
self.target.last_revision_info()
3624
self._update_revisions(stop_revision, overwrite=overwrite,
3579
self.target.update_revisions(self.source, stop_revision,
3580
overwrite=overwrite, graph=graph)
3626
3581
# TODO: The old revid should be specified when merging tags,
3627
3582
# so a tags implementation that versions tags can only
3628
3583
# pull in the most recent changes. -- JRV20090506
3629
result.tag_updates, result.tag_conflicts = (
3630
self.source.tags.merge_to(self.target.tags, overwrite,
3631
ignore_master=not merge_tags_to_master))
3584
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3585
overwrite, ignore_master=not merge_tags_to_master)
3632
3586
result.new_revno, result.new_revid = self.target.last_revision_info()
3633
3587
if _hook_master:
3634
3588
result.master_branch = _hook_master