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
18
21
from cStringIO import StringIO
21
23
from bzrlib.lazy_import import lazy_import
22
24
lazy_import(globals(), """
23
from itertools import chain
24
26
from bzrlib import (
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 (
31
config as _mod_config,
40
revision as _mod_revision,
49
from bzrlib.i18n import gettext, ngettext
53
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
54
from bzrlib.hooks import HookPoint, Hooks
52
# Explicitly import bzrlib.bzrdir so that the BzrProber
53
# is guaranteed to be registered.
60
from bzrlib.decorators import (
65
from bzrlib.hooks import Hooks
55
66
from bzrlib.inter import InterObject
56
67
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
57
68
from bzrlib import registry
96
103
self._partial_revision_history_cache = []
97
104
self._tags_bytes = None
98
105
self._last_revision_info_cache = None
106
self._master_branch_cache = None
99
107
self._merge_sorted_revisions_cache = None
108
self._open_hook(possible_transports)
101
109
hooks = Branch.hooks['open']
102
110
for hook in hooks:
105
def _open_hook(self):
113
def _open_hook(self, possible_transports):
106
114
"""Called by init to allow simpler extension of the base class."""
108
def _activate_fallback_location(self, url):
116
def _activate_fallback_location(self, url, possible_transports):
109
117
"""Activate the branch/repository from url as a fallback repository."""
110
118
for existing_fallback_repo in self.repository._fallback_repositories:
111
119
if existing_fallback_repo.user_url == url:
112
120
# This fallback is already configured. This probably only
113
# happens because BzrDir.sprout is a horrible mess. To avoid
121
# happens because ControlDir.sprout is a horrible mess. To avoid
114
122
# confusing _unstack we don't add this a second time.
115
123
mutter('duplicate activation of fallback %r on %r', url, self)
117
repo = self._get_fallback_repository(url)
125
repo = self._get_fallback_repository(url, possible_transports)
118
126
if repo.has_same_location(self.repository):
119
127
raise errors.UnstackableLocationError(self.user_url, url)
120
128
self.repository.add_fallback_repository(repo)
174
182
For instance, if the branch is at URL/.bzr/branch,
175
183
Branch.open(URL) -> a Branch instance.
177
control = bzrdir.BzrDir.open(base, _unsupported,
178
possible_transports=possible_transports)
179
return control.open_branch(unsupported=_unsupported)
185
control = controldir.ControlDir.open(base,
186
possible_transports=possible_transports, _unsupported=_unsupported)
187
return control.open_branch(unsupported=_unsupported,
188
possible_transports=possible_transports)
182
def open_from_transport(transport, name=None, _unsupported=False):
191
def open_from_transport(transport, name=None, _unsupported=False,
192
possible_transports=None):
183
193
"""Open the branch rooted at transport"""
184
control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
185
return control.open_branch(name=name, unsupported=_unsupported)
194
control = controldir.ControlDir.open_from_transport(transport, _unsupported)
195
return control.open_branch(name=name, unsupported=_unsupported,
196
possible_transports=possible_transports)
188
199
def open_containing(url, possible_transports=None):
231
253
raise NotImplementedError(self._get_config)
233
def _get_fallback_repository(self, url):
255
def store_uncommitted(self, creator):
256
"""Store uncommitted changes from a ShelfCreator.
258
:param creator: The ShelfCreator containing uncommitted changes, or
259
None to delete any stored changes.
260
:raises: ChangesAlreadyStored if the branch already has changes.
262
raise NotImplementedError(self.store_uncommitted)
264
def get_unshelver(self, tree):
265
"""Return a shelf.Unshelver for this branch and tree.
267
:param tree: The tree to use to construct the Unshelver.
268
:return: an Unshelver or None if no changes are stored.
270
raise NotImplementedError(self.get_unshelver)
272
def _get_fallback_repository(self, url, possible_transports):
234
273
"""Get the repository we fallback to at url."""
235
274
url = urlutils.join(self.base, url)
236
a_branch = Branch.open(url,
237
possible_transports=[self.bzrdir.root_transport])
275
a_branch = Branch.open(url, possible_transports=possible_transports)
238
276
return a_branch.repository
662
704
raise errors.UnsupportedOperation(self.get_reference_info, self)
664
706
@needs_write_lock
665
def fetch(self, from_branch, last_revision=None, fetch_spec=None):
707
def fetch(self, from_branch, last_revision=None, limit=None):
666
708
"""Copy revisions from from_branch into this branch.
668
710
:param from_branch: Where to copy from.
669
711
:param last_revision: What revision to stop at (None for at the end
671
:param fetch_spec: If specified, a SearchResult or
672
PendingAncestryResult that describes which revisions to copy. This
673
allows copying multiple heads at once. Mutually exclusive with
713
:param limit: Optional rough limit of revisions to fetch
677
if fetch_spec is not None and last_revision is not None:
678
raise AssertionError(
679
"fetch_spec and last_revision are mutually exclusive.")
680
if self.base == from_branch.base:
682
from_branch.lock_read()
684
if last_revision is None and fetch_spec is None:
685
last_revision = from_branch.last_revision()
686
last_revision = _mod_revision.ensure_null(last_revision)
687
return self.repository.fetch(from_branch.repository,
688
revision_id=last_revision,
689
fetch_spec=fetch_spec)
716
return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
693
718
def get_bound_location(self):
694
719
"""Return the URL of the branch we are bound to.
715
740
:param committer: Optional committer to set for commit.
716
741
:param revprops: Optional dictionary of revision properties.
717
742
:param revision_id: Optional revision id.
743
:param lossy: Whether to discard data that can not be natively
744
represented, when pushing to a foreign VCS
721
config = self.get_config()
747
if config_stack is None:
748
config_stack = self.get_config_stack()
723
return self.repository.get_commit_builder(self, parents, config,
724
timestamp, timezone, committer, revprops, revision_id)
750
return self.repository.get_commit_builder(self, parents, config_stack,
751
timestamp, timezone, committer, revprops, revision_id,
726
754
def get_master_branch(self, possible_transports=None):
727
755
"""Return the branch we are bound to.
754
784
"""Print `file` to stdout."""
755
785
raise NotImplementedError(self.print_file)
757
def set_revision_history(self, rev_history):
758
raise NotImplementedError(self.set_revision_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)
760
818
@needs_write_lock
761
819
def set_parent(self, url):
990
1046
:return: A tuple (revno, revision_id).
992
1048
if self._last_revision_info_cache is None:
993
self._last_revision_info_cache = self._last_revision_info()
1049
self._last_revision_info_cache = self._read_last_revision_info()
994
1050
return self._last_revision_info_cache
996
def _last_revision_info(self):
997
rh = self.revision_history()
1000
return (revno, rh[-1])
1002
return (0, _mod_revision.NULL_REVISION)
1004
def update_revisions(self, other, stop_revision=None, overwrite=False,
1005
graph=None, fetch_tags=True):
1006
"""Pull in new perfect-fit revisions.
1008
:param other: Another Branch to pull from
1009
:param stop_revision: Updated until the given revision
1010
:param overwrite: Always set the branch pointer, rather than checking
1011
to see if it is a proper descendant.
1012
:param graph: A Graph object that can be used to query history
1013
information. This can be None.
1014
:param fetch_tags: Flag that specifies if tags from other should be
1018
return InterBranch.get(other, self).update_revisions(stop_revision,
1019
overwrite, graph, fetch_tags=fetch_tags)
1021
@deprecated_method(deprecated_in((2, 4, 0)))
1022
def import_last_revision_info(self, source_repo, revno, revid):
1023
"""Set the last revision info, importing from another repo if necessary.
1025
:param source_repo: Source repository to optionally fetch from
1026
:param revno: Revision number of the new tip
1027
:param revid: Revision id of the new tip
1029
if not self.repository.has_same_location(source_repo):
1030
self.repository.fetch(source_repo, revision_id=revid)
1031
self.set_last_revision_info(revno, revid)
1033
def import_last_revision_info_and_tags(self, source, revno, revid):
1052
def _read_last_revision_info(self):
1053
raise NotImplementedError(self._read_last_revision_info)
1055
def import_last_revision_info_and_tags(self, source, revno, revid,
1034
1057
"""Set the last revision info, importing from another repo if necessary.
1036
1059
This is used by the bound branch code to upload a revision to
1040
1063
:param source: Source branch to optionally fetch from
1041
1064
:param revno: Revision number of the new tip
1042
1065
:param revid: Revision id of the new tip
1066
:param lossy: Whether to discard metadata that can not be
1067
natively represented
1068
:return: Tuple with the new revision number and revision id
1069
(should only be different from the arguments when lossy=True)
1044
1071
if not self.repository.has_same_location(source.repository):
1046
tags_to_fetch = set(source.tags.get_reverse_tag_dict())
1047
except errors.TagsNotSupported:
1048
tags_to_fetch = set()
1049
fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
1050
source.repository, [revid],
1051
if_present_ids=tags_to_fetch).execute()
1052
self.repository.fetch(source.repository, fetch_spec=fetch_spec)
1072
self.fetch(source, revid)
1053
1073
self.set_last_revision_info(revno, revid)
1074
return (revno, revid)
1055
1076
def revision_id_to_revno(self, revision_id):
1056
1077
"""Given a revision id, return its revno"""
1057
1078
if _mod_revision.is_null(revision_id):
1059
history = self.revision_history()
1080
history = self._revision_history()
1061
1082
return history.index(revision_id) + 1
1062
1083
except ValueError:
1089
1110
stop_revision=stop_revision,
1090
1111
possible_transports=possible_transports, *args, **kwargs)
1092
def push(self, target, overwrite=False, stop_revision=None, *args,
1113
def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1094
1115
"""Mirror this branch into target.
1096
1117
This branch is considered to be 'local', having low latency.
1098
1119
return InterBranch.get(self, target).push(overwrite, stop_revision,
1101
def lossy_push(self, target, stop_revision=None):
1102
"""Push deltas into another branch.
1104
:note: This does not, like push, retain the revision ids from
1105
the source branch and will, rather than adding bzr-specific
1106
metadata, push only those semantics of the revision that can be
1107
natively represented by this branch' VCS.
1109
:param target: Target branch
1110
:param stop_revision: Revision to push, defaults to last revision.
1111
:return: BranchPushResult with an extra member revidmap:
1112
A dictionary mapping revision ids from the target branch
1113
to new revision ids in the target branch, for each
1114
revision that was pushed.
1116
inter = InterBranch.get(self, target)
1117
lossy_push = getattr(inter, "lossy_push", None)
1118
if lossy_push is None:
1119
raise errors.LossyPushToSameVCS(self, target)
1120
return lossy_push(stop_revision)
1120
lossy, *args, **kwargs)
1122
1122
def basis_tree(self):
1123
1123
"""Return `Tree` object for last revision."""
1433
1435
t = transport.get_transport(to_location)
1434
1436
t.ensure_base()
1437
format = self._get_checkout_format(lightweight=lightweight)
1439
checkout = format.initialize_on_transport(t)
1440
except errors.AlreadyControlDirError:
1441
# It's fine if the control directory already exists,
1442
# as long as there is no existing branch and working tree.
1443
checkout = controldir.ControlDir.open_from_transport(t)
1445
checkout.open_branch()
1446
except errors.NotBranchError:
1449
raise errors.AlreadyControlDirError(t.base)
1450
if checkout.control_transport.base == self.bzrdir.control_transport.base:
1451
# When checking out to the same control directory,
1452
# always create a lightweight checkout
1435
1455
if lightweight:
1436
format = self._get_checkout_format()
1437
checkout = format.initialize_on_transport(t)
1438
from_branch = BranchReferenceFormat().initialize(checkout,
1456
from_branch = checkout.set_branch_reference(target_branch=self)
1441
format = self._get_checkout_format()
1442
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1443
to_location, force_new_tree=False, format=format)
1444
checkout = checkout_branch.bzrdir
1458
policy = checkout.determine_repository_policy()
1459
repo = policy.acquire_repository()[0]
1460
checkout_branch = checkout.create_branch()
1445
1461
checkout_branch.bind(self)
1446
1462
# pull up to the specified revision_id to set the initial
1447
1463
# branch tip correctly, and seed it with history.
1448
1464
checkout_branch.pull(self, stop_revision=revision_id)
1450
1466
tree = checkout.create_workingtree(revision_id,
1451
1467
from_branch=from_branch,
1452
1468
accelerator_tree=accelerator_tree,
1570
1589
object will be created every time regardless.
1573
can_set_append_revisions_only = True
1575
1592
def __eq__(self, other):
1576
1593
return self.__class__ is other.__class__
1578
1595
def __ne__(self, other):
1579
1596
return not (self == other)
1582
def find_format(klass, a_bzrdir, name=None):
1583
"""Return the format for the branch object in a_bzrdir."""
1585
transport = a_bzrdir.get_branch_transport(None, name=name)
1586
format_string = transport.get_bytes("format")
1587
return format_registry.get(format_string)
1588
except errors.NoSuchFile:
1589
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1591
raise errors.UnknownFormatError(format=format_string, kind='branch')
1594
@deprecated_method(deprecated_in((2, 4, 0)))
1595
def get_default_format(klass):
1596
"""Return the current default format."""
1597
return format_registry.get_default()
1600
@deprecated_method(deprecated_in((2, 4, 0)))
1601
def get_formats(klass):
1602
"""Get all the known formats.
1604
Warning: This triggers a load of all lazy registered formats: do not
1605
use except when that is desireed.
1607
return format_registry._get_all()
1609
def get_reference(self, a_bzrdir, name=None):
1610
"""Get the target reference of the branch in a_bzrdir.
1598
def get_reference(self, controldir, name=None):
1599
"""Get the target reference of the branch in controldir.
1612
1601
format probing must have been completed before calling
1613
1602
this method - it is assumed that the format of the branch
1614
in a_bzrdir is correct.
1603
in controldir is correct.
1616
:param a_bzrdir: The bzrdir to get the branch data from.
1605
:param controldir: The controldir to get the branch data from.
1617
1606
:param name: Name of the colocated branch to fetch
1618
1607
:return: None if the branch is not a reference branch.
1623
def set_reference(self, a_bzrdir, name, to_branch):
1624
"""Set the target reference of the branch in a_bzrdir.
1612
def set_reference(self, controldir, name, to_branch):
1613
"""Set the target reference of the branch in controldir.
1626
1615
format probing must have been completed before calling
1627
1616
this method - it is assumed that the format of the branch
1628
in a_bzrdir is correct.
1617
in controldir is correct.
1630
:param a_bzrdir: The bzrdir to set the branch reference for.
1619
:param controldir: The controldir to set the branch reference for.
1631
1620
:param name: Name of colocated branch to set, None for default
1632
1621
:param to_branch: branch that the checkout is to reference
1634
1623
raise NotImplementedError(self.set_reference)
1636
def get_format_string(self):
1637
"""Return the ASCII format string that identifies this format."""
1638
raise NotImplementedError(self.get_format_string)
1640
1625
def get_format_description(self):
1641
1626
"""Return the short format description for this format."""
1642
1627
raise NotImplementedError(self.get_format_description)
1644
def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
1629
def _run_post_branch_init_hooks(self, controldir, name, branch):
1645
1630
hooks = Branch.hooks['post_branch_init']
1648
params = BranchInitHookParams(self, a_bzrdir, name, branch)
1633
params = BranchInitHookParams(self, controldir, name, branch)
1649
1634
for hook in hooks:
1652
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1653
repository=None, lock_type='metadir',
1655
"""Initialize a branch in a bzrdir, with specified files
1657
:param a_bzrdir: The bzrdir to initialize the branch in
1658
:param utf8_files: The files to create as a list of
1659
(filename, content) tuples
1660
:param name: Name of colocated branch to create, if any
1661
:param set_format: If True, set the format with
1662
self.get_format_string. (BzrBranch4 has its format set
1664
:return: a branch in this format
1666
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1667
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1669
'metadir': ('lock', lockdir.LockDir),
1670
'branch4': ('branch-lock', lockable_files.TransportLock),
1672
lock_name, lock_class = lock_map[lock_type]
1673
control_files = lockable_files.LockableFiles(branch_transport,
1674
lock_name, lock_class)
1675
control_files.create_lock()
1677
control_files.lock_write()
1678
except errors.LockContention:
1679
if lock_type != 'branch4':
1685
utf8_files += [('format', self.get_format_string())]
1687
for (filename, content) in utf8_files:
1688
branch_transport.put_bytes(
1690
mode=a_bzrdir._get_file_mode())
1693
control_files.unlock()
1694
branch = self.open(a_bzrdir, name, _found=True,
1695
found_repository=repository)
1696
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1699
def initialize(self, a_bzrdir, name=None, repository=None):
1700
"""Create a branch of this format in a_bzrdir.
1637
def initialize(self, controldir, name=None, repository=None,
1638
append_revisions_only=None):
1639
"""Create a branch of this format in controldir.
1702
1641
:param name: Name of the colocated branch to create.
1704
1643
raise NotImplementedError(self.initialize)
1830
1757
These are all empty initially, because by default nothing should get
1833
Hooks.__init__(self)
1834
self.create_hook(HookPoint('set_rh',
1835
"Invoked whenever the revision history has been set via "
1836
"set_revision_history. The api signature is (branch, "
1837
"revision_history), and the branch will be write-locked. "
1838
"The set_rh hook can be expensive for bzr to trigger, a better "
1839
"hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1840
self.create_hook(HookPoint('open',
1760
Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1761
self.add_hook('open',
1841
1762
"Called with the Branch object that has been opened after a "
1842
"branch is opened.", (1, 8), None))
1843
self.create_hook(HookPoint('post_push',
1763
"branch is opened.", (1, 8))
1764
self.add_hook('post_push',
1844
1765
"Called after a push operation completes. post_push is called "
1845
1766
"with a bzrlib.branch.BranchPushResult object and only runs in the "
1846
"bzr client.", (0, 15), None))
1847
self.create_hook(HookPoint('post_pull',
1767
"bzr client.", (0, 15))
1768
self.add_hook('post_pull',
1848
1769
"Called after a pull operation completes. post_pull is called "
1849
1770
"with a bzrlib.branch.PullResult object and only runs in the "
1850
"bzr client.", (0, 15), None))
1851
self.create_hook(HookPoint('pre_commit',
1771
"bzr client.", (0, 15))
1772
self.add_hook('pre_commit',
1852
1773
"Called after a commit is calculated but before it is "
1853
1774
"completed. pre_commit is called with (local, master, old_revno, "
1854
1775
"old_revid, future_revno, future_revid, tree_delta, future_tree"
1857
1778
"basis revision. hooks MUST NOT modify this delta. "
1858
1779
" future_tree is an in-memory tree obtained from "
1859
1780
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1860
"tree.", (0,91), None))
1861
self.create_hook(HookPoint('post_commit',
1782
self.add_hook('post_commit',
1862
1783
"Called in the bzr client after a commit has completed. "
1863
1784
"post_commit is called with (local, master, old_revno, old_revid, "
1864
1785
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1865
"commit to a branch.", (0, 15), None))
1866
self.create_hook(HookPoint('post_uncommit',
1786
"commit to a branch.", (0, 15))
1787
self.add_hook('post_uncommit',
1867
1788
"Called in the bzr client after an uncommit completes. "
1868
1789
"post_uncommit is called with (local, master, old_revno, "
1869
1790
"old_revid, new_revno, new_revid) where local is the local branch "
1870
1791
"or None, master is the target branch, and an empty branch "
1871
"receives new_revno of 0, new_revid of None.", (0, 15), None))
1872
self.create_hook(HookPoint('pre_change_branch_tip',
1792
"receives new_revno of 0, new_revid of None.", (0, 15))
1793
self.add_hook('pre_change_branch_tip',
1873
1794
"Called in bzr client and server before a change to the tip of a "
1874
1795
"branch is made. pre_change_branch_tip is called with a "
1875
1796
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1876
"commit, uncommit will all trigger this hook.", (1, 6), None))
1877
self.create_hook(HookPoint('post_change_branch_tip',
1797
"commit, uncommit will all trigger this hook.", (1, 6))
1798
self.add_hook('post_change_branch_tip',
1878
1799
"Called in bzr client and server after a change to the tip of a "
1879
1800
"branch is made. post_change_branch_tip is called with a "
1880
1801
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1881
"commit, uncommit will all trigger this hook.", (1, 4), None))
1882
self.create_hook(HookPoint('transform_fallback_location',
1802
"commit, uncommit will all trigger this hook.", (1, 4))
1803
self.add_hook('transform_fallback_location',
1883
1804
"Called when a stacked branch is activating its fallback "
1884
1805
"locations. transform_fallback_location is called with (branch, "
1885
1806
"url), and should return a new url. Returning the same url "
1890
1811
"fallback locations have not been activated. When there are "
1891
1812
"multiple hooks installed for transform_fallback_location, "
1892
1813
"all are called with the url returned from the previous hook."
1893
"The order is however undefined.", (1, 9), None))
1894
self.create_hook(HookPoint('automatic_tag_name',
1814
"The order is however undefined.", (1, 9))
1815
self.add_hook('automatic_tag_name',
1895
1816
"Called to determine an automatic tag name for a revision. "
1896
1817
"automatic_tag_name is called with (branch, revision_id) and "
1897
1818
"should return a tag name or None if no tag name could be "
1898
1819
"determined. The first non-None tag name returned will be used.",
1900
self.create_hook(HookPoint('post_branch_init',
1821
self.add_hook('post_branch_init',
1901
1822
"Called after new branch initialization completes. "
1902
1823
"post_branch_init is called with a "
1903
1824
"bzrlib.branch.BranchInitHookParams. "
1904
1825
"Note that init, branch and checkout (both heavyweight and "
1905
"lightweight) will all trigger this hook.", (2, 2), None))
1906
self.create_hook(HookPoint('post_switch',
1826
"lightweight) will all trigger this hook.", (2, 2))
1827
self.add_hook('post_switch',
1907
1828
"Called after a checkout switches branch. "
1908
1829
"post_switch is called with a "
1909
"bzrlib.branch.SwitchHookParams.", (2, 2), None))
1830
"bzrlib.branch.SwitchHookParams.", (2, 2))
2025
1946
self.revision_id)
2028
class BzrBranchFormat4(BranchFormat):
2029
"""Bzr branch format 4.
2032
- a revision-history file.
2033
- a branch-lock lock file [ to be shared with the bzrdir ]
1949
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
1950
"""Base class for branch formats that live in meta directories.
2036
def get_format_description(self):
2037
"""See BranchFormat.get_format_description()."""
2038
return "Branch format 4"
2040
def initialize(self, a_bzrdir, name=None, repository=None):
2041
"""Create a branch of this format in a_bzrdir."""
2042
if repository is not None:
2043
raise NotImplementedError(
2044
"initialize(repository=<not None>) on %r" % (self,))
2045
utf8_files = [('revision-history', ''),
2046
('branch-name', ''),
2048
return self._initialize_helper(a_bzrdir, utf8_files, name=name,
2049
lock_type='branch4', set_format=False)
2051
1953
def __init__(self):
2052
super(BzrBranchFormat4, self).__init__()
2053
self._matchingbzrdir = bzrdir.BzrDirFormat6()
2055
def network_name(self):
2056
"""The network name for this format is the control dirs disk label."""
2057
return self._matchingbzrdir.get_format_string()
2059
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2060
found_repository=None):
2061
"""See BranchFormat.open()."""
2063
# we are being called directly and must probe.
2064
raise NotImplementedError
2065
if found_repository is None:
2066
found_repository = a_bzrdir.open_repository()
2067
return BzrBranchPreSplitOut(_format=self,
2068
_control_files=a_bzrdir._control_files,
2071
_repository=found_repository)
2074
return "Bazaar-NG branch format 4"
2076
def supports_leaving_lock(self):
2080
class BranchFormatMetadir(BranchFormat):
2081
"""Common logic for meta-dir based branch formats."""
1954
BranchFormat.__init__(self)
1955
bzrdir.BzrFormat.__init__(self)
1958
def find_format(klass, controldir, name=None):
1959
"""Return the format for the branch object in controldir."""
1961
transport = controldir.get_branch_transport(None, name=name)
1962
except errors.NoSuchFile:
1963
raise errors.NotBranchError(path=name, bzrdir=controldir)
1965
format_string = transport.get_bytes("format")
1966
except errors.NoSuchFile:
1967
raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1968
return klass._find_format(format_registry, 'branch', format_string)
2083
1970
def _branch_class(self):
2084
1971
"""What class to instantiate on open calls."""
2085
1972
raise NotImplementedError(self._branch_class)
2087
def network_name(self):
2088
"""A simple byte string uniquely identifying this format for RPC calls.
2090
Metadir branch formats use their format string.
1974
def _get_initial_config(self, append_revisions_only=None):
1975
if append_revisions_only:
1976
return "append_revisions_only = True\n"
1978
# Avoid writing anything if append_revisions_only is disabled,
1979
# as that is the default.
1982
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1984
"""Initialize a branch in a control dir, with specified files
1986
:param a_bzrdir: The bzrdir to initialize the branch in
1987
:param utf8_files: The files to create as a list of
1988
(filename, content) tuples
1989
:param name: Name of colocated branch to create, if any
1990
:return: a branch in this format
2092
return self.get_format_string()
1993
name = a_bzrdir._get_selected_branch()
1994
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1995
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1996
control_files = lockable_files.LockableFiles(branch_transport,
1997
'lock', lockdir.LockDir)
1998
control_files.create_lock()
1999
control_files.lock_write()
2001
utf8_files += [('format', self.as_string())]
2002
for (filename, content) in utf8_files:
2003
branch_transport.put_bytes(
2005
mode=a_bzrdir._get_file_mode())
2007
control_files.unlock()
2008
branch = self.open(a_bzrdir, name, _found=True,
2009
found_repository=repository)
2010
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2094
2013
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2095
found_repository=None):
2014
found_repository=None, possible_transports=None):
2096
2015
"""See BranchFormat.open()."""
2017
name = a_bzrdir._get_selected_branch()
2098
format = BranchFormat.find_format(a_bzrdir, name=name)
2019
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2099
2020
if format.__class__ != self.__class__:
2100
2021
raise AssertionError("wrong format %r found for %r" %
2101
2022
(format, self))
2125
2048
def supports_leaving_lock(self):
2129
class BzrBranchFormat5(BranchFormatMetadir):
2130
"""Bzr branch format 5.
2133
- a revision-history file.
2135
- a lock dir guarding the branch itself
2136
- all of this stored in a branch/ subdirectory
2137
- works with shared repositories.
2139
This format is new in bzr 0.8.
2142
def _branch_class(self):
2145
def get_format_string(self):
2146
"""See BranchFormat.get_format_string()."""
2147
return "Bazaar-NG branch format 5\n"
2149
def get_format_description(self):
2150
"""See BranchFormat.get_format_description()."""
2151
return "Branch format 5"
2153
def initialize(self, a_bzrdir, name=None, repository=None):
2154
"""Create a branch of this format in a_bzrdir."""
2155
utf8_files = [('revision-history', ''),
2156
('branch-name', ''),
2158
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2160
def supports_tags(self):
2051
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
2053
BranchFormat.check_support_status(self,
2054
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
2056
bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
2057
recommend_upgrade=recommend_upgrade, basedir=basedir)
2164
2060
class BzrBranchFormat6(BranchFormatMetadir):
2213
2113
"""See BranchFormat.get_format_description()."""
2214
2114
return "Branch format 8"
2216
def initialize(self, a_bzrdir, name=None, repository=None):
2116
def initialize(self, a_bzrdir, name=None, repository=None,
2117
append_revisions_only=None):
2217
2118
"""Create a branch of this format in a_bzrdir."""
2218
2119
utf8_files = [('last-revision', '0 null:\n'),
2219
('branch.conf', ''),
2121
self._get_initial_config(append_revisions_only)),
2221
2123
('references', '')
2223
2125
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2226
super(BzrBranchFormat8, self).__init__()
2227
self._matchingbzrdir.repository_format = \
2228
RepositoryFormatKnitPack5RichRoot()
2230
2127
def make_tags(self, branch):
2231
2128
"""See bzrlib.branch.BranchFormat.make_tags()."""
2232
return BasicTags(branch)
2129
return _mod_tag.BasicTags(branch)
2234
2131
def supports_set_append_revisions_only(self):
2304
2212
location = transport.put_bytes('location', to_branch.base)
2306
2214
def initialize(self, a_bzrdir, name=None, target_branch=None,
2215
repository=None, append_revisions_only=None):
2308
2216
"""Create a branch of this format in a_bzrdir."""
2309
2217
if target_branch is None:
2310
2218
# this format does not implement branch itself, thus the implicit
2311
2219
# creation contract must see it as uninitializable
2312
2220
raise errors.UninitializableFormat(self)
2313
2221
mutter('creating branch reference in %s', a_bzrdir.user_url)
2222
if a_bzrdir._format.fixed_components:
2223
raise errors.IncompatibleFormat(self, a_bzrdir._format)
2225
name = a_bzrdir._get_selected_branch()
2314
2226
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2315
2227
branch_transport.put_bytes('location',
2316
target_branch.bzrdir.user_url)
2317
branch_transport.put_bytes('format', self.get_format_string())
2319
a_bzrdir, name, _found=True,
2228
target_branch.user_url)
2229
branch_transport.put_bytes('format', self.as_string())
2230
branch = self.open(a_bzrdir, name, _found=True,
2320
2231
possible_transports=[target_branch.bzrdir.root_transport])
2321
2232
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2325
super(BranchReferenceFormat, self).__init__()
2326
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2327
self._matchingbzrdir.set_branch_format(self)
2329
2235
def _make_reference_clone_function(format, a_branch):
2330
2236
"""Create a clone() routine for a branch dynamically."""
2331
2237
def clone(to_bzrdir, revision_id=None,
2454
2359
def __init__(self, _format=None,
2455
2360
_control_files=None, a_bzrdir=None, name=None,
2456
_repository=None, ignore_fallbacks=False):
2361
_repository=None, ignore_fallbacks=False,
2362
possible_transports=None):
2457
2363
"""Create new branch object at a particular location."""
2458
2364
if a_bzrdir is None:
2459
2365
raise ValueError('a_bzrdir must be supplied')
2461
self.bzrdir = a_bzrdir
2462
self._base = self.bzrdir.transport.clone('..').base
2367
raise ValueError('name must be supplied')
2368
self.bzrdir = a_bzrdir
2369
self._user_transport = self.bzrdir.transport.clone('..')
2371
self._user_transport.set_segment_parameter(
2372
"branch", urlutils.escape(name))
2373
self._base = self._user_transport.base
2463
2374
self.name = name
2464
# XXX: We should be able to just do
2465
# self.base = self.bzrdir.root_transport.base
2466
# but this does not quite work yet -- mbp 20080522
2467
2375
self._format = _format
2468
2376
if _control_files is None:
2469
2377
raise ValueError('BzrBranch _control_files is None')
2470
2378
self.control_files = _control_files
2471
2379
self._transport = _control_files._transport
2472
2380
self.repository = _repository
2473
Branch.__init__(self)
2381
self.conf_store = None
2382
Branch.__init__(self, possible_transports)
2475
2384
def __str__(self):
2476
if self.name is None:
2477
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2479
return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2385
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2482
2387
__repr__ = __str__
2488
2393
base = property(_get_base, doc="The URL for the root of this branch.")
2396
def user_transport(self):
2397
return self._user_transport
2490
2399
def _get_config(self):
2491
return TransportConfig(self._transport, 'branch.conf')
2400
return _mod_config.TransportConfig(self._transport, 'branch.conf')
2402
def _get_config_store(self):
2403
if self.conf_store is None:
2404
self.conf_store = _mod_config.BranchStore(self)
2405
return self.conf_store
2407
def _uncommitted_branch(self):
2408
"""Return the branch that may contain uncommitted changes."""
2409
master = self.get_master_branch()
2410
if master is not None:
2415
def store_uncommitted(self, creator):
2416
"""Store uncommitted changes from a ShelfCreator.
2418
:param creator: The ShelfCreator containing uncommitted changes, or
2419
None to delete any stored changes.
2420
:raises: ChangesAlreadyStored if the branch already has changes.
2422
branch = self._uncommitted_branch()
2424
branch._transport.delete('stored-transform')
2426
if branch._transport.has('stored-transform'):
2427
raise errors.ChangesAlreadyStored
2428
transform = StringIO()
2429
creator.write_shelf(transform)
2431
branch._transport.put_file('stored-transform', transform)
2433
def get_unshelver(self, tree):
2434
"""Return a shelf.Unshelver for this branch and tree.
2436
:param tree: The tree to use to construct the Unshelver.
2437
:return: an Unshelver or None if no changes are stored.
2439
branch = self._uncommitted_branch()
2441
transform = branch._transport.get('stored-transform')
2442
except errors.NoSuchFile:
2444
return shelf.Unshelver.from_tree_and_shelf(tree, transform)
2493
2446
def is_locked(self):
2494
2447
return self.control_files.is_locked()
2569
2514
"""See Branch.print_file."""
2570
2515
return self.repository.print_file(file, revision_id)
2572
def _write_revision_history(self, history):
2573
"""Factored out of set_revision_history.
2575
This performs the actual writing to disk.
2576
It is intended to be called by BzrBranch5.set_revision_history."""
2577
self._transport.put_bytes(
2578
'revision-history', '\n'.join(history),
2579
mode=self.bzrdir._get_file_mode())
2582
def set_revision_history(self, rev_history):
2583
"""See Branch.set_revision_history."""
2584
if 'evil' in debug.debug_flags:
2585
mutter_callsite(3, "set_revision_history scales with history.")
2586
check_not_reserved_id = _mod_revision.check_not_reserved_id
2587
for rev_id in rev_history:
2588
check_not_reserved_id(rev_id)
2589
if Branch.hooks['post_change_branch_tip']:
2590
# Don't calculate the last_revision_info() if there are no hooks
2592
old_revno, old_revid = self.last_revision_info()
2593
if len(rev_history) == 0:
2594
revid = _mod_revision.NULL_REVISION
2596
revid = rev_history[-1]
2597
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2598
self._write_revision_history(rev_history)
2599
self._clear_cached_state()
2600
self._cache_revision_history(rev_history)
2601
for hook in Branch.hooks['set_rh']:
2602
hook(self, rev_history)
2603
if Branch.hooks['post_change_branch_tip']:
2604
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2606
def _synchronize_history(self, destination, revision_id):
2607
"""Synchronize last revision and revision history between branches.
2609
This version is most efficient when the destination is also a
2610
BzrBranch5, but works for BzrBranch6 as long as the revision
2611
history is the true lefthand parent history, and all of the revisions
2612
are in the destination's repository. If not, set_revision_history
2615
:param destination: The branch to copy the history into
2616
:param revision_id: The revision-id to truncate history at. May
2617
be None to copy complete history.
2619
if not isinstance(destination._format, BzrBranchFormat5):
2620
super(BzrBranch, self)._synchronize_history(
2621
destination, revision_id)
2623
if revision_id == _mod_revision.NULL_REVISION:
2626
new_history = self.revision_history()
2627
if revision_id is not None and new_history != []:
2629
new_history = new_history[:new_history.index(revision_id) + 1]
2631
rev = self.repository.get_revision(revision_id)
2632
new_history = rev.get_history(self.repository)[1:]
2633
destination.set_revision_history(new_history)
2635
2517
@needs_write_lock
2636
2518
def set_last_revision_info(self, revno, revision_id):
2637
"""Set the last revision of this branch.
2639
The caller is responsible for checking that the revno is correct
2640
for this revision id.
2642
It may be possible to set the branch last revision to an id not
2643
present in the repository. However, branches can also be
2644
configured to check constraints on history, in which case this may not
2519
if not revision_id or not isinstance(revision_id, basestring):
2520
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2647
2521
revision_id = _mod_revision.ensure_null(revision_id)
2648
# this old format stores the full history, but this api doesn't
2649
# provide it, so we must generate, and might as well check it's
2651
history = self._lefthand_history(revision_id)
2652
if len(history) != revno:
2653
raise AssertionError('%d != %d' % (len(history), revno))
2654
self.set_revision_history(history)
2656
def _gen_revision_history(self):
2657
history = self._transport.get_bytes('revision-history').split('\n')
2658
if history[-1:] == ['']:
2659
# There shouldn't be a trailing newline, but just in case.
2664
def generate_revision_history(self, revision_id, last_rev=None,
2666
"""Create a new revision history that will finish with revision_id.
2668
:param revision_id: the new tip to use.
2669
:param last_rev: The previous last_revision. If not None, then this
2670
must be a ancestory of revision_id, or DivergedBranches is raised.
2671
:param other_branch: The other branch that DivergedBranches should
2672
raise with respect to.
2674
self.set_revision_history(self._lefthand_history(revision_id,
2675
last_rev, other_branch))
2522
old_revno, old_revid = self.last_revision_info()
2523
if self.get_append_revisions_only():
2524
self._check_history_violation(revision_id)
2525
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2526
self._write_last_revision_info(revno, revision_id)
2527
self._clear_cached_state()
2528
self._last_revision_info_cache = revno, revision_id
2529
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2677
2531
def basis_tree(self):
2678
2532
"""See Branch.basis_tree."""
2690
def _basic_push(self, target, overwrite, stop_revision):
2691
"""Basic implementation of push without bound branches or hooks.
2693
Must be called with source read locked and target write locked.
2695
result = BranchPushResult()
2696
result.source_branch = self
2697
result.target_branch = target
2698
result.old_revno, result.old_revid = target.last_revision_info()
2699
self.update_references(target)
2700
if result.old_revid != stop_revision:
2701
# We assume that during 'push' this repository is closer than
2703
graph = self.repository.get_graph(target.repository)
2704
target.update_revisions(self, stop_revision,
2705
overwrite=overwrite, graph=graph)
2706
if self._push_should_merge_tags():
2707
result.tag_conflicts = self.tags.merge_to(target.tags,
2709
result.new_revno, result.new_revid = target.last_revision_info()
2712
2544
def get_stacked_on_url(self):
2713
2545
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2725
2557
self._transport.put_bytes('parent', url + '\n',
2726
2558
mode=self.bzrdir._get_file_mode())
2729
class BzrBranchPreSplitOut(BzrBranch):
2731
def _get_checkout_format(self):
2732
"""Return the most suitable metadir for a checkout of this branch.
2733
Weaves are used if this branch's repository uses weaves.
2735
from bzrlib.repofmt.weaverepo import RepositoryFormat7
2736
from bzrlib.bzrdir import BzrDirMetaFormat1
2737
format = BzrDirMetaFormat1()
2738
format.repository_format = RepositoryFormat7()
2742
class BzrBranch5(BzrBranch):
2743
"""A format 5 branch. This supports new features over plain branches.
2745
It has support for a master_branch which is the data for bound branches.
2748
def get_bound_location(self):
2750
return self._transport.get_bytes('bound')[:-1]
2751
except errors.NoSuchFile:
2755
def get_master_branch(self, possible_transports=None):
2756
"""Return the branch we are bound to.
2758
:return: Either a Branch, or None
2760
This could memoise the branch, but if thats done
2761
it must be revalidated on each new lock.
2762
So for now we just don't memoise it.
2763
# RBC 20060304 review this decision.
2765
bound_loc = self.get_bound_location()
2769
return Branch.open(bound_loc,
2770
possible_transports=possible_transports)
2771
except (errors.NotBranchError, errors.ConnectionError), e:
2772
raise errors.BoundBranchConnectionFailure(
2775
2560
@needs_write_lock
2776
def set_bound_location(self, location):
2777
"""Set the target where this branch is bound to.
2779
:param location: URL to the target branch
2782
self._transport.put_bytes('bound', location+'\n',
2783
mode=self.bzrdir._get_file_mode())
2786
self._transport.delete('bound')
2787
except errors.NoSuchFile:
2562
"""If bound, unbind"""
2563
return self.set_bound_location(None)
2791
2565
@needs_write_lock
2792
2566
def bind(self, other):
2814
2588
# history around
2815
2589
self.set_bound_location(other.base)
2591
def get_bound_location(self):
2593
return self._transport.get_bytes('bound')[:-1]
2594
except errors.NoSuchFile:
2598
def get_master_branch(self, possible_transports=None):
2599
"""Return the branch we are bound to.
2601
:return: Either a Branch, or None
2603
if self._master_branch_cache is None:
2604
self._master_branch_cache = self._get_master_branch(
2605
possible_transports)
2606
return self._master_branch_cache
2608
def _get_master_branch(self, possible_transports):
2609
bound_loc = self.get_bound_location()
2613
return Branch.open(bound_loc,
2614
possible_transports=possible_transports)
2615
except (errors.NotBranchError, errors.ConnectionError), e:
2616
raise errors.BoundBranchConnectionFailure(
2817
2619
@needs_write_lock
2819
"""If bound, unbind"""
2820
return self.set_bound_location(None)
2620
def set_bound_location(self, location):
2621
"""Set the target where this branch is bound to.
2623
:param location: URL to the target branch
2625
self._master_branch_cache = None
2627
self._transport.put_bytes('bound', location+'\n',
2628
mode=self.bzrdir._get_file_mode())
2631
self._transport.delete('bound')
2632
except errors.NoSuchFile:
2822
2636
@needs_write_lock
2823
2637
def update(self, possible_transports=None):
2840
class BzrBranch8(BzrBranch5):
2653
def _read_last_revision_info(self):
2654
revision_string = self._transport.get_bytes('last-revision')
2655
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2656
revision_id = cache_utf8.get_cached_utf8(revision_id)
2658
return revno, revision_id
2660
def _write_last_revision_info(self, revno, revision_id):
2661
"""Simply write out the revision id, with no checks.
2663
Use set_last_revision_info to perform this safely.
2665
Does not update the revision_history cache.
2667
revision_id = _mod_revision.ensure_null(revision_id)
2668
out_string = '%d %s\n' % (revno, revision_id)
2669
self._transport.put_bytes('last-revision', out_string,
2670
mode=self.bzrdir._get_file_mode())
2673
def update_feature_flags(self, updated_flags):
2674
"""Update the feature flags for this branch.
2676
:param updated_flags: Dictionary mapping feature names to necessities
2677
A necessity can be None to indicate the feature should be removed
2679
self._format._update_feature_flags(updated_flags)
2680
self.control_transport.put_bytes('format', self._format.as_string())
2683
class BzrBranch8(BzrBranch):
2841
2684
"""A branch that stores tree-reference locations."""
2843
def _open_hook(self):
2686
def _open_hook(self, possible_transports=None):
2844
2687
if self._ignore_fallbacks:
2689
if possible_transports is None:
2690
possible_transports = [self.bzrdir.root_transport]
2847
2692
url = self.get_stacked_on_url()
2848
2693
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2869
2715
self._last_revision_info_cache = None
2870
2716
self._reference_info = None
2872
def _last_revision_info(self):
2873
revision_string = self._transport.get_bytes('last-revision')
2874
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2875
revision_id = cache_utf8.get_cached_utf8(revision_id)
2877
return revno, revision_id
2879
def _write_last_revision_info(self, revno, revision_id):
2880
"""Simply write out the revision id, with no checks.
2882
Use set_last_revision_info to perform this safely.
2884
Does not update the revision_history cache.
2885
Intended to be called by set_last_revision_info and
2886
_write_revision_history.
2888
revision_id = _mod_revision.ensure_null(revision_id)
2889
out_string = '%d %s\n' % (revno, revision_id)
2890
self._transport.put_bytes('last-revision', out_string,
2891
mode=self.bzrdir._get_file_mode())
2894
def set_last_revision_info(self, revno, revision_id):
2895
revision_id = _mod_revision.ensure_null(revision_id)
2896
old_revno, old_revid = self.last_revision_info()
2897
if self._get_append_revisions_only():
2898
self._check_history_violation(revision_id)
2899
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2900
self._write_last_revision_info(revno, revision_id)
2901
self._clear_cached_state()
2902
self._last_revision_info_cache = revno, revision_id
2903
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2905
def _synchronize_history(self, destination, revision_id):
2906
"""Synchronize last revision and revision history between branches.
2908
:see: Branch._synchronize_history
2910
# XXX: The base Branch has a fast implementation of this method based
2911
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2912
# that uses set_revision_history. This class inherits from BzrBranch5,
2913
# but wants the fast implementation, so it calls
2914
# Branch._synchronize_history directly.
2915
Branch._synchronize_history(self, destination, revision_id)
2917
2718
def _check_history_violation(self, revision_id):
2918
last_revision = _mod_revision.ensure_null(self.last_revision())
2719
current_revid = self.last_revision()
2720
last_revision = _mod_revision.ensure_null(current_revid)
2919
2721
if _mod_revision.is_null(last_revision):
2921
if last_revision not in self._lefthand_history(revision_id):
2922
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2723
graph = self.repository.get_graph()
2724
for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2725
if lh_ancestor == current_revid:
2727
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2924
2729
def _gen_revision_history(self):
2925
2730
"""Generate the revision history from last revision
3036
2825
def set_bound_location(self, location):
3037
2826
"""See Branch.set_push_location."""
2827
self._master_branch_cache = None
3039
config = self.get_config()
2829
conf = self.get_config_stack()
3040
2830
if location is None:
3041
if config.get_user_option('bound') != 'True':
2831
if not conf.get('bound'):
3044
config.set_user_option('bound', 'False', warn_masked=True)
2834
conf.set('bound', 'False')
3047
2837
self._set_config_location('bound_location', location,
3049
config.set_user_option('bound', 'True', warn_masked=True)
2839
conf.set('bound', 'True')
3052
2842
def _get_bound_location(self, bound):
3053
2843
"""Return the bound location in the config file.
3055
2845
Return None if the bound parameter does not match"""
3056
config = self.get_config()
3057
config_bound = (config.get_user_option('bound') == 'True')
3058
if config_bound != bound:
2846
conf = self.get_config_stack()
2847
if conf.get('bound') != bound:
3060
return self._get_config_location('bound_location', config=config)
2849
return self._get_config_location('bound_location', config=conf)
3062
2851
def get_bound_location(self):
3063
"""See Branch.set_push_location."""
2852
"""See Branch.get_bound_location."""
3064
2853
return self._get_bound_location(True)
3066
2855
def get_old_bound_location(self):
3071
2860
# you can always ask for the URL; but you might not be able to use it
3072
2861
# if the repo can't support stacking.
3073
2862
## self._check_stackable_repo()
3074
stacked_url = self._get_config_location('stacked_on_location')
2863
# stacked_on_location is only ever defined in branch.conf, so don't
2864
# waste effort reading the whole stack of config files.
2865
conf = _mod_config.BranchOnlyStack(self)
2866
stacked_url = self._get_config_location('stacked_on_location',
3075
2868
if stacked_url is None:
3076
2869
raise errors.NotStacked(self)
3079
def _get_append_revisions_only(self):
3080
return self.get_config(
3081
).get_user_option_as_bool('append_revisions_only')
3084
def generate_revision_history(self, revision_id, last_rev=None,
3086
"""See BzrBranch5.generate_revision_history"""
3087
history = self._lefthand_history(revision_id, last_rev, other_branch)
3088
revno = len(history)
3089
self.set_last_revision_info(revno, revision_id)
2870
return stacked_url.encode('utf-8')
3091
2872
@needs_read_lock
3092
2873
def get_rev_id(self, revno, history=None):
3179
2962
:ivar local_branch: target branch if there is a Master, else None
3180
2963
:ivar target_branch: Target/destination branch object. (write locked)
3181
2964
:ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
2965
:ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3184
@deprecated_method(deprecated_in((2, 3, 0)))
3186
"""Return the relative change in revno.
3188
:deprecated: Use `new_revno` and `old_revno` instead.
3190
return self.new_revno - self.old_revno
3192
2968
def report(self, to_file):
2969
tag_conflicts = getattr(self, "tag_conflicts", None)
2970
tag_updates = getattr(self, "tag_updates", None)
3193
2971
if not is_quiet():
3194
if self.old_revid == self.new_revid:
3195
to_file.write('No revisions to pull.\n')
2972
if self.old_revid != self.new_revid:
3197
2973
to_file.write('Now on revision %d.\n' % self.new_revno)
2975
to_file.write('%d tag(s) updated.\n' % len(tag_updates))
2976
if self.old_revid == self.new_revid and not tag_updates:
2977
if not tag_conflicts:
2978
to_file.write('No revisions or tags to pull.\n')
2980
to_file.write('No revisions to pull.\n')
3198
2981
self._show_tag_conficts(to_file)
3217
3000
target, otherwise it will be None.
3220
@deprecated_method(deprecated_in((2, 3, 0)))
3222
"""Return the relative change in revno.
3224
:deprecated: Use `new_revno` and `old_revno` instead.
3226
return self.new_revno - self.old_revno
3228
3003
def report(self, to_file):
3229
"""Write a human-readable description of the result."""
3230
if self.old_revid == self.new_revid:
3231
note('No new revisions to push.')
3233
note('Pushed up to revision %d.' % self.new_revno)
3004
# TODO: This function gets passed a to_file, but then
3005
# ignores it and calls note() instead. This is also
3006
# inconsistent with PullResult(), which writes to stdout.
3007
# -- JRV20110901, bug #838853
3008
tag_conflicts = getattr(self, "tag_conflicts", None)
3009
tag_updates = getattr(self, "tag_updates", None)
3011
if self.old_revid != self.new_revid:
3012
note(gettext('Pushed up to revision %d.') % self.new_revno)
3014
note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
3015
if self.old_revid == self.new_revid and not tag_updates:
3016
if not tag_conflicts:
3017
note(gettext('No new revisions or tags to push.'))
3019
note(gettext('No new revisions to push.'))
3234
3020
self._show_tag_conficts(to_file)
3267
3053
# Copy source data into target
3268
3054
new_branch._write_last_revision_info(*branch.last_revision_info())
3269
new_branch.set_parent(branch.get_parent())
3270
new_branch.set_bound_location(branch.get_bound_location())
3271
new_branch.set_push_location(branch.get_push_location())
3055
new_branch.lock_write()
3057
new_branch.set_parent(branch.get_parent())
3058
new_branch.set_bound_location(branch.get_bound_location())
3059
new_branch.set_push_location(branch.get_push_location())
3273
3063
# New branch has no tags by default
3274
3064
new_branch.tags._set_tag_dict({})
3276
3066
# Copying done; now update target format
3277
3067
new_branch._transport.put_bytes('format',
3278
format.get_format_string(),
3279
3069
mode=new_branch.bzrdir._get_file_mode())
3281
3071
# Clean up old files
3282
3072
new_branch._transport.delete('revision-history')
3284
branch.set_parent(None)
3285
except errors.NoSuchFile:
3287
branch.set_bound_location(None)
3076
branch.set_parent(None)
3077
except errors.NoSuchFile:
3079
branch.set_bound_location(None)
3290
3084
class Converter6to7(object):
3294
3088
format = BzrBranchFormat7()
3295
3089
branch._set_config_location('stacked_on_location', '')
3296
3090
# update target format
3297
branch._transport.put_bytes('format', format.get_format_string())
3091
branch._transport.put_bytes('format', format.as_string())
3300
3094
class Converter7to8(object):
3301
"""Perform an in-place upgrade of format 6 to format 7"""
3095
"""Perform an in-place upgrade of format 7 to format 8"""
3303
3097
def convert(self, branch):
3304
3098
format = BzrBranchFormat8()
3305
3099
branch._transport.put_bytes('references', '')
3306
3100
# update target format
3307
branch._transport.put_bytes('format', format.get_format_string())
3310
def _run_with_write_locked_target(target, callable, *args, **kwargs):
3311
"""Run ``callable(*args, **kwargs)``, write-locking target for the
3314
_run_with_write_locked_target will attempt to release the lock it acquires.
3316
If an exception is raised by callable, then that exception *will* be
3317
propagated, even if the unlock attempt raises its own error. Thus
3318
_run_with_write_locked_target should be preferred to simply doing::
3322
return callable(*args, **kwargs)
3327
# This is very similar to bzrlib.decorators.needs_write_lock. Perhaps they
3328
# should share code?
3331
result = callable(*args, **kwargs)
3333
exc_info = sys.exc_info()
3337
raise exc_info[0], exc_info[1], exc_info[2]
3101
branch._transport.put_bytes('format', format.as_string())
3343
3104
class InterBranch(InterObject):
3373
3134
raise NotImplementedError(self.pull)
3375
3136
@needs_write_lock
3376
def update_revisions(self, stop_revision=None, overwrite=False,
3377
graph=None, fetch_tags=True):
3378
"""Pull in new perfect-fit revisions.
3380
:param stop_revision: Updated until the given revision
3381
:param overwrite: Always set the branch pointer, rather than checking
3382
to see if it is a proper descendant.
3383
:param graph: A Graph object that can be used to query history
3384
information. This can be None.
3385
:param fetch_tags: Flag that specifies if tags from source should be
3389
raise NotImplementedError(self.update_revisions)
3392
def push(self, overwrite=False, stop_revision=None,
3137
def push(self, overwrite=False, stop_revision=None, lossy=False,
3393
3138
_override_hook_source_branch=None):
3394
3139
"""Mirror the source branch into the target branch.
3446
3209
self.source.tags.merge_to(self.target.tags)
3448
3211
@needs_write_lock
3449
def update_revisions(self, stop_revision=None, overwrite=False,
3450
graph=None, fetch_tags=True):
3451
"""See InterBranch.update_revisions()."""
3212
def fetch(self, stop_revision=None, limit=None):
3213
if self.target.base == self.source.base:
3215
self.source.lock_read()
3217
fetch_spec_factory = fetch.FetchSpecFactory()
3218
fetch_spec_factory.source_branch = self.source
3219
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3220
fetch_spec_factory.source_repo = self.source.repository
3221
fetch_spec_factory.target_repo = self.target.repository
3222
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3223
fetch_spec_factory.limit = limit
3224
fetch_spec = fetch_spec_factory.make_fetch_spec()
3225
return self.target.repository.fetch(self.source.repository,
3226
fetch_spec=fetch_spec)
3228
self.source.unlock()
3231
def _update_revisions(self, stop_revision=None, overwrite=False,
3452
3233
other_revno, other_last_revision = self.source.last_revision_info()
3453
3234
stop_revno = None # unknown
3454
3235
if stop_revision is None:
3529
3308
if master_branch:
3530
3309
master_branch.unlock()
3532
def push(self, overwrite=False, stop_revision=None,
3311
def push(self, overwrite=False, stop_revision=None, lossy=False,
3533
3312
_override_hook_source_branch=None):
3534
3313
"""See InterBranch.push.
3536
3315
This is the basic concrete implementation of push()
3538
:param _override_hook_source_branch: If specified, run
3539
the hooks passing this Branch as the source, rather than self.
3540
This is for use of RemoteBranch, where push is delegated to the
3541
underlying vfs-based Branch.
3317
:param _override_hook_source_branch: If specified, run the hooks
3318
passing this Branch as the source, rather than self. This is for
3319
use of RemoteBranch, where push is delegated to the underlying
3323
raise errors.LossyPushToSameVCS(self.source, self.target)
3543
3324
# TODO: Public option to disable running hooks - should be trivial but
3545
self.source.lock_read()
3547
return _run_with_write_locked_target(
3548
self.target, self._push_with_bound_branches, overwrite,
3550
_override_hook_source_branch=_override_hook_source_branch)
3552
self.source.unlock()
3554
def _push_with_bound_branches(self, overwrite, stop_revision,
3327
op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3328
op.add_cleanup(self.source.lock_read().unlock)
3329
op.add_cleanup(self.target.lock_write().unlock)
3330
return op.run(overwrite, stop_revision,
3331
_override_hook_source_branch=_override_hook_source_branch)
3333
def _basic_push(self, overwrite, stop_revision):
3334
"""Basic implementation of push without bound branches or hooks.
3336
Must be called with source read locked and target write locked.
3338
result = BranchPushResult()
3339
result.source_branch = self.source
3340
result.target_branch = self.target
3341
result.old_revno, result.old_revid = self.target.last_revision_info()
3342
self.source.update_references(self.target)
3343
overwrite = _fix_overwrite_type(overwrite)
3344
if result.old_revid != stop_revision:
3345
# We assume that during 'push' this repository is closer than
3347
graph = self.source.repository.get_graph(self.target.repository)
3348
self._update_revisions(stop_revision,
3349
overwrite=("history" in overwrite),
3351
if self.source._push_should_merge_tags():
3352
result.tag_updates, result.tag_conflicts = (
3353
self.source.tags.merge_to(
3354
self.target.tags, "tags" in overwrite))
3355
result.new_revno, result.new_revid = self.target.last_revision_info()
3358
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
3555
3359
_override_hook_source_branch=None):
3556
3360
"""Push from source into target, and into target's master if any.
3569
3373
# be bound to itself? -- mbp 20070507
3570
3374
master_branch = self.target.get_master_branch()
3571
3375
master_branch.lock_write()
3573
# push into the master from the source branch.
3574
self.source._basic_push(master_branch, overwrite, stop_revision)
3575
# and push into the target branch from the source. Note that we
3576
# push from the source branch again, because it's considered the
3577
# highest bandwidth repository.
3578
result = self.source._basic_push(self.target, overwrite,
3580
result.master_branch = master_branch
3581
result.local_branch = self.target
3585
master_branch.unlock()
3376
operation.add_cleanup(master_branch.unlock)
3377
# push into the master from the source branch.
3378
master_inter = InterBranch.get(self.source, master_branch)
3379
master_inter._basic_push(overwrite, stop_revision)
3380
# and push into the target branch from the source. Note that
3381
# we push from the source branch again, because it's considered
3382
# the highest bandwidth repository.
3383
result = self._basic_push(overwrite, stop_revision)
3384
result.master_branch = master_branch
3385
result.local_branch = self.target
3387
master_branch = None
3587
3388
# no master branch
3588
result = self.source._basic_push(self.target, overwrite,
3389
result = self._basic_push(overwrite, stop_revision)
3590
3390
# TODO: Why set master_branch and local_branch if there's no
3591
3391
# binding? Maybe cleaner to just leave them unset? -- mbp
3593
3393
result.master_branch = self.target
3594
3394
result.local_branch = None
3598
3398
def _pull(self, overwrite=False, stop_revision=None,
3599
3399
possible_transports=None, _hook_master=None, run_hooks=True,
3635
3435
# -- JRV20090506
3636
3436
result.old_revno, result.old_revid = \
3637
3437
self.target.last_revision_info()
3638
self.target.update_revisions(self.source, stop_revision,
3639
overwrite=overwrite, graph=graph)
3438
overwrite = _fix_overwrite_type(overwrite)
3439
self._update_revisions(stop_revision,
3440
overwrite=("history" in overwrite),
3640
3442
# TODO: The old revid should be specified when merging tags,
3641
3443
# so a tags implementation that versions tags can only
3642
3444
# pull in the most recent changes. -- JRV20090506
3643
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3644
overwrite, ignore_master=not merge_tags_to_master)
3445
result.tag_updates, result.tag_conflicts = (
3446
self.source.tags.merge_to(self.target.tags,
3447
"tags" in overwrite,
3448
ignore_master=not merge_tags_to_master))
3645
3449
result.new_revno, result.new_revid = self.target.last_revision_info()
3646
3450
if _hook_master:
3647
3451
result.master_branch = _hook_master