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,
35
revision as _mod_revision,
43
from bzrlib.config import BranchConfig, TransportConfig
44
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
45
from bzrlib.tag import (
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
51
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
52
from bzrlib.hooks import HookPoint, Hooks
65
53
from bzrlib.inter import InterObject
66
54
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
67
55
from bzrlib import registry
102
94
self._partial_revision_history_cache = []
103
95
self._tags_bytes = None
104
96
self._last_revision_info_cache = None
105
self._master_branch_cache = None
106
97
self._merge_sorted_revisions_cache = None
107
self._open_hook(possible_transports)
108
99
hooks = Branch.hooks['open']
109
100
for hook in hooks:
112
def _open_hook(self, possible_transports):
103
def _open_hook(self):
113
104
"""Called by init to allow simpler extension of the base class."""
115
def _activate_fallback_location(self, url, possible_transports):
106
def _activate_fallback_location(self, url):
116
107
"""Activate the branch/repository from url as a fallback repository."""
117
for existing_fallback_repo in self.repository._fallback_repositories:
118
if existing_fallback_repo.user_url == url:
119
# This fallback is already configured. This probably only
120
# happens because ControlDir.sprout is a horrible mess. To avoid
121
# confusing _unstack we don't add this a second time.
122
mutter('duplicate activation of fallback %r on %r', url, self)
124
repo = self._get_fallback_repository(url, possible_transports)
108
repo = self._get_fallback_repository(url)
125
109
if repo.has_same_location(self.repository):
126
110
raise errors.UnstackableLocationError(self.user_url, url)
127
111
self.repository.add_fallback_repository(repo)
181
165
For instance, if the branch is at URL/.bzr/branch,
182
166
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)
168
control = bzrdir.BzrDir.open(base, _unsupported,
169
possible_transports=possible_transports)
170
return control.open_branch(unsupported=_unsupported)
190
def open_from_transport(transport, name=None, _unsupported=False,
191
possible_transports=None):
173
def open_from_transport(transport, name=None, _unsupported=False):
192
174
"""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)
175
control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
176
return control.open_branch(name=name, unsupported=_unsupported)
198
179
def open_containing(url, possible_transports=None):
527
496
rev_iter = iter(merge_sorted_revisions)
528
497
if start_revision_id is not None:
529
498
for node in rev_iter:
499
rev_id = node.key[-1]
531
500
if rev_id != start_revision_id:
534
503
# The decision to include the start or not
535
504
# depends on the stop_rule if a stop is provided
536
505
# so pop this node back into the iterator
537
rev_iter = itertools.chain(iter([node]), rev_iter)
506
rev_iter = chain(iter([node]), rev_iter)
539
508
if stop_revision_id is None:
540
509
# Yield everything
541
510
for node in rev_iter:
511
rev_id = node.key[-1]
543
512
yield (rev_id, node.merge_depth, node.revno,
544
513
node.end_of_merge)
545
514
elif stop_rule == 'exclude':
546
515
for node in rev_iter:
516
rev_id = node.key[-1]
548
517
if rev_id == stop_revision_id:
550
519
yield (rev_id, node.merge_depth, node.revno,
551
520
node.end_of_merge)
552
521
elif stop_rule == 'include':
553
522
for node in rev_iter:
523
rev_id = node.key[-1]
555
524
yield (rev_id, node.merge_depth, node.revno,
556
525
node.end_of_merge)
557
526
if rev_id == stop_revision_id:
686
653
raise errors.UnsupportedOperation(self.get_reference_info, self)
688
655
@needs_write_lock
689
def fetch(self, from_branch, last_revision=None, limit=None):
656
def fetch(self, from_branch, last_revision=None, pb=None):
690
657
"""Copy revisions from from_branch into this branch.
692
659
:param from_branch: Where to copy from.
693
660
:param last_revision: What revision to stop at (None for at the end
695
:param limit: Optional rough limit of revisions to fetch
662
:param pb: An optional progress bar to use.
698
return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
665
if self.base == from_branch.base:
668
symbol_versioning.warn(
669
symbol_versioning.deprecated_in((1, 14, 0))
670
% "pb parameter to fetch()")
671
from_branch.lock_read()
673
if last_revision is None:
674
last_revision = from_branch.last_revision()
675
last_revision = _mod_revision.ensure_null(last_revision)
676
return self.repository.fetch(from_branch.repository,
677
revision_id=last_revision,
700
682
def get_bound_location(self):
701
683
"""Return the URL of the branch we are bound to.
722
704
:param committer: Optional committer to set for commit.
723
705
:param revprops: Optional dictionary of revision properties.
724
706
: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()
710
config = self.get_config()
732
return self.repository.get_commit_builder(self, parents, config_stack,
733
timestamp, timezone, committer, revprops, revision_id,
712
return self.repository.get_commit_builder(self, parents, config,
713
timestamp, timezone, committer, revprops, revision_id)
736
715
def get_master_branch(self, possible_transports=None):
737
716
"""Return the branch we are bound to.
766
743
"""Print `file` to stdout."""
767
744
raise NotImplementedError(self.print_file)
770
def set_last_revision_info(self, revno, revision_id):
771
"""Set the last revision of this branch.
773
The caller is responsible for checking that the revno is correct
774
for this revision id.
776
It may be possible to set the branch last revision to an id not
777
present in the repository. However, branches can also be
778
configured to check constraints on history, in which case this may not
781
raise NotImplementedError(self.set_last_revision_info)
784
def generate_revision_history(self, revision_id, last_rev=None,
786
"""See Branch.generate_revision_history"""
787
graph = self.repository.get_graph()
788
(last_revno, last_revid) = self.last_revision_info()
789
known_revision_ids = [
790
(last_revid, last_revno),
791
(_mod_revision.NULL_REVISION, 0),
793
if last_rev is not None:
794
if not graph.is_ancestor(last_rev, revision_id):
795
# our previous tip is not merged into stop_revision
796
raise errors.DivergedBranches(self, other_branch)
797
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
798
self.set_last_revision_info(revno, revision_id)
746
def set_revision_history(self, rev_history):
747
raise NotImplementedError(self.set_revision_history)
800
749
@needs_write_lock
801
750
def set_parent(self, url):
1028
977
:return: A tuple (revno, revision_id).
1030
979
if self._last_revision_info_cache is None:
1031
self._last_revision_info_cache = self._read_last_revision_info()
980
self._last_revision_info_cache = self._last_revision_info()
1032
981
return self._last_revision_info_cache
1034
def _read_last_revision_info(self):
1035
raise NotImplementedError(self._read_last_revision_info)
1037
@deprecated_method(deprecated_in((2, 4, 0)))
983
def _last_revision_info(self):
984
rh = self.revision_history()
987
return (revno, rh[-1])
989
return (0, _mod_revision.NULL_REVISION)
991
@deprecated_method(deprecated_in((1, 6, 0)))
992
def missing_revisions(self, other, stop_revision=None):
993
"""Return a list of new revisions that would perfectly fit.
995
If self and other have not diverged, return a list of the revisions
996
present in other, but missing from self.
998
self_history = self.revision_history()
999
self_len = len(self_history)
1000
other_history = other.revision_history()
1001
other_len = len(other_history)
1002
common_index = min(self_len, other_len) -1
1003
if common_index >= 0 and \
1004
self_history[common_index] != other_history[common_index]:
1005
raise errors.DivergedBranches(self, other)
1007
if stop_revision is None:
1008
stop_revision = other_len
1010
if stop_revision > other_len:
1011
raise errors.NoSuchRevision(self, stop_revision)
1012
return other_history[self_len:stop_revision]
1014
def update_revisions(self, other, stop_revision=None, overwrite=False,
1016
"""Pull in new perfect-fit revisions.
1018
:param other: Another Branch to pull from
1019
:param stop_revision: Updated until the given revision
1020
:param overwrite: Always set the branch pointer, rather than checking
1021
to see if it is a proper descendant.
1022
:param graph: A Graph object that can be used to query history
1023
information. This can be None.
1026
return InterBranch.get(other, self).update_revisions(stop_revision,
1038
1029
def import_last_revision_info(self, source_repo, revno, revid):
1039
1030
"""Set the last revision info, importing from another repo if necessary.
1032
This is used by the bound branch code to upload a revision to
1033
the master branch first before updating the tip of the local branch.
1041
1035
:param source_repo: Source repository to optionally fetch from
1042
1036
:param revno: Revision number of the new tip
1043
1037
:param revid: Revision id of the new tip
1046
1040
self.repository.fetch(source_repo, revision_id=revid)
1047
1041
self.set_last_revision_info(revno, revid)
1049
def import_last_revision_info_and_tags(self, source, revno, revid,
1051
"""Set the last revision info, importing from another repo if necessary.
1053
This is used by the bound branch code to upload a revision to
1054
the master branch first before updating the tip of the local branch.
1055
Revisions referenced by source's tags are also transferred.
1057
:param source: Source branch to optionally fetch from
1058
:param revno: Revision number of the new tip
1059
:param revid: Revision id of the new tip
1060
:param lossy: Whether to discard metadata that can not be
1061
natively represented
1062
:return: Tuple with the new revision number and revision id
1063
(should only be different from the arguments when lossy=True)
1065
if not self.repository.has_same_location(source.repository):
1066
self.fetch(source, revid)
1067
self.set_last_revision_info(revno, revid)
1068
return (revno, revid)
1070
1043
def revision_id_to_revno(self, revision_id):
1071
1044
"""Given a revision id, return its revno"""
1072
1045
if _mod_revision.is_null(revision_id):
1074
history = self._revision_history()
1047
history = self.revision_history()
1076
1049
return history.index(revision_id) + 1
1077
1050
except ValueError:
1104
1077
stop_revision=stop_revision,
1105
1078
possible_transports=possible_transports, *args, **kwargs)
1107
def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1080
def push(self, target, overwrite=False, stop_revision=None, *args,
1109
1082
"""Mirror this branch into target.
1111
1084
This branch is considered to be 'local', having low latency.
1113
1086
return InterBranch.get(self, target).push(overwrite, stop_revision,
1114
lossy, *args, **kwargs)
1089
def lossy_push(self, target, stop_revision=None):
1090
"""Push deltas into another branch.
1092
:note: This does not, like push, retain the revision ids from
1093
the source branch and will, rather than adding bzr-specific
1094
metadata, push only those semantics of the revision that can be
1095
natively represented by this branch' VCS.
1097
:param target: Target branch
1098
:param stop_revision: Revision to push, defaults to last revision.
1099
:return: BranchPushResult with an extra member revidmap:
1100
A dictionary mapping revision ids from the target branch
1101
to new revision ids in the target branch, for each
1102
revision that was pushed.
1104
inter = InterBranch.get(self, target)
1105
lossy_push = getattr(inter, "lossy_push", None)
1106
if lossy_push is None:
1107
raise errors.LossyPushToSameVCS(self, target)
1108
return lossy_push(stop_revision)
1116
1110
def basis_tree(self):
1117
1111
"""Return `Tree` object for last revision."""
1372
1363
# TODO: We should probably also check that self.revision_history
1373
1364
# matches the repository for older branch formats.
1374
1365
# If looking for the code that cross-checks repository parents against
1375
# the Graph.iter_lefthand_ancestry output, that is now a repository
1366
# the iter_reverse_revision_history output, that is now a repository
1376
1367
# specific check.
1379
def _get_checkout_format(self, lightweight=False):
1370
def _get_checkout_format(self):
1380
1371
"""Return the most suitable metadir for a checkout of this branch.
1381
1372
Weaves are used if this branch's repository uses weaves.
1383
format = self.repository.bzrdir.checkout_metadir()
1384
format.set_branch_format(self._format)
1374
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
1375
from bzrlib.repofmt import weaverepo
1376
format = bzrdir.BzrDirMetaFormat1()
1377
format.repository_format = weaverepo.RepositoryFormat7()
1379
format = self.repository.bzrdir.checkout_metadir()
1380
format.set_branch_format(self._format)
1387
1383
def create_clone_on_transport(self, to_transport, revision_id=None,
1429
1425
t = transport.get_transport(to_location)
1430
1426
t.ensure_base()
1431
format = self._get_checkout_format(lightweight=lightweight)
1428
format = self._get_checkout_format()
1433
1429
checkout = format.initialize_on_transport(t)
1434
except errors.AlreadyControlDirError:
1435
# It's fine if the control directory already exists,
1436
# as long as there is no existing branch and working tree.
1437
checkout = controldir.ControlDir.open_from_transport(t)
1439
checkout.open_branch()
1440
except errors.NotBranchError:
1443
raise errors.AlreadyControlDirError(t.base)
1444
if checkout.control_transport.base == self.bzrdir.control_transport.base:
1445
# When checking out to the same control directory,
1446
# always create a lightweight checkout
1450
from_branch = checkout.set_branch_reference(target_branch=self)
1430
from_branch = BranchReferenceFormat().initialize(checkout,
1452
policy = checkout.determine_repository_policy()
1453
repo = policy.acquire_repository()[0]
1454
checkout_branch = checkout.create_branch()
1433
format = self._get_checkout_format()
1434
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1435
to_location, force_new_tree=False, format=format)
1436
checkout = checkout_branch.bzrdir
1455
1437
checkout_branch.bind(self)
1456
1438
# pull up to the specified revision_id to set the initial
1457
1439
# branch tip correctly, and seed it with history.
1458
1440
checkout_branch.pull(self, stop_revision=revision_id)
1460
1442
tree = checkout.create_workingtree(revision_id,
1461
1443
from_branch=from_branch,
1462
1444
accelerator_tree=accelerator_tree,
1543
1524
raise AssertionError("invalid heads: %r" % (heads,))
1545
def heads_to_fetch(self):
1546
"""Return the heads that must and that should be fetched to copy this
1547
branch into another repo.
1549
:returns: a 2-tuple of (must_fetch, if_present_fetch). must_fetch is a
1550
set of heads that must be fetched. if_present_fetch is a set of
1551
heads that must be fetched if present, but no error is necessary if
1552
they are not present.
1554
# For bzr native formats must_fetch is just the tip, and
1555
# if_present_fetch are the tags.
1556
must_fetch = set([self.last_revision()])
1557
if_present_fetch = set()
1558
if self.get_config_stack().get('branch.fetch_tags'):
1560
if_present_fetch = set(self.tags.get_reverse_tag_dict())
1561
except errors.TagsNotSupported:
1563
must_fetch.discard(_mod_revision.NULL_REVISION)
1564
if_present_fetch.discard(_mod_revision.NULL_REVISION)
1565
return must_fetch, if_present_fetch
1568
class BranchFormat(controldir.ControlComponentFormat):
1527
class BranchFormat(object):
1569
1528
"""An encapsulation of the initialization and open routines for a format.
1571
1530
Formats provide three things:
1572
1531
* An initialization routine,
1573
* a format description
1574
1533
* an open routine.
1576
1535
Formats are placed in an dict by their format string for reference
1583
1542
object will be created every time regardless.
1545
_default_format = None
1546
"""The default format used for new branches."""
1549
"""The known formats."""
1551
can_set_append_revisions_only = True
1586
1553
def __eq__(self, other):
1587
1554
return self.__class__ is other.__class__
1589
1556
def __ne__(self, other):
1590
1557
return not (self == other)
1592
def get_reference(self, controldir, name=None):
1593
"""Get the target reference of the branch in controldir.
1560
def find_format(klass, a_bzrdir, name=None):
1561
"""Return the format for the branch object in a_bzrdir."""
1563
transport = a_bzrdir.get_branch_transport(None, name=name)
1564
format_string = transport.get_bytes("format")
1565
format = klass._formats[format_string]
1566
if isinstance(format, MetaDirBranchFormatFactory):
1569
except errors.NoSuchFile:
1570
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1572
raise errors.UnknownFormatError(format=format_string, kind='branch')
1575
def get_default_format(klass):
1576
"""Return the current default format."""
1577
return klass._default_format
1580
def get_formats(klass):
1581
"""Get all the known formats.
1583
Warning: This triggers a load of all lazy registered formats: do not
1584
use except when that is desireed.
1587
for fmt in klass._formats.values():
1588
if isinstance(fmt, MetaDirBranchFormatFactory):
1593
def get_reference(self, a_bzrdir, name=None):
1594
"""Get the target reference of the branch in a_bzrdir.
1595
1596
format probing must have been completed before calling
1596
1597
this method - it is assumed that the format of the branch
1597
in controldir is correct.
1598
in a_bzrdir is correct.
1599
:param controldir: The controldir to get the branch data from.
1600
:param a_bzrdir: The bzrdir to get the branch data from.
1600
1601
:param name: Name of the colocated branch to fetch
1601
1602
:return: None if the branch is not a reference branch.
1606
def set_reference(self, controldir, name, to_branch):
1607
"""Set the target reference of the branch in controldir.
1607
def set_reference(self, a_bzrdir, name, to_branch):
1608
"""Set the target reference of the branch in a_bzrdir.
1609
1610
format probing must have been completed before calling
1610
1611
this method - it is assumed that the format of the branch
1611
in controldir is correct.
1612
in a_bzrdir is correct.
1613
:param controldir: The controldir to set the branch reference for.
1614
:param a_bzrdir: The bzrdir to set the branch reference for.
1614
1615
:param name: Name of colocated branch to set, None for default
1615
1616
:param to_branch: branch that the checkout is to reference
1617
1618
raise NotImplementedError(self.set_reference)
1620
def get_format_string(self):
1621
"""Return the ASCII format string that identifies this format."""
1622
raise NotImplementedError(self.get_format_string)
1619
1624
def get_format_description(self):
1620
1625
"""Return the short format description for this format."""
1621
1626
raise NotImplementedError(self.get_format_description)
1623
def _run_post_branch_init_hooks(self, controldir, name, branch):
1628
def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
1624
1629
hooks = Branch.hooks['post_branch_init']
1627
params = BranchInitHookParams(self, controldir, name, branch)
1632
params = BranchInitHookParams(self, a_bzrdir, name, branch)
1628
1633
for hook in hooks:
1631
def initialize(self, controldir, name=None, repository=None,
1632
append_revisions_only=None):
1633
"""Create a branch of this format in controldir.
1636
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1637
lock_type='metadir', set_format=True):
1638
"""Initialize a branch in a bzrdir, with specified files
1640
:param a_bzrdir: The bzrdir to initialize the branch in
1641
:param utf8_files: The files to create as a list of
1642
(filename, content) tuples
1643
:param name: Name of colocated branch to create, if any
1644
:param set_format: If True, set the format with
1645
self.get_format_string. (BzrBranch4 has its format set
1647
:return: a branch in this format
1649
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1650
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1652
'metadir': ('lock', lockdir.LockDir),
1653
'branch4': ('branch-lock', lockable_files.TransportLock),
1655
lock_name, lock_class = lock_map[lock_type]
1656
control_files = lockable_files.LockableFiles(branch_transport,
1657
lock_name, lock_class)
1658
control_files.create_lock()
1660
control_files.lock_write()
1661
except errors.LockContention:
1662
if lock_type != 'branch4':
1668
utf8_files += [('format', self.get_format_string())]
1670
for (filename, content) in utf8_files:
1671
branch_transport.put_bytes(
1673
mode=a_bzrdir._get_file_mode())
1676
control_files.unlock()
1677
branch = self.open(a_bzrdir, name, _found=True)
1678
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1681
def initialize(self, a_bzrdir, name=None):
1682
"""Create a branch of this format in a_bzrdir.
1635
1684
:param name: Name of the colocated branch to create.
1637
1686
raise NotImplementedError(self.initialize)
1756
1811
These are all empty initially, because by default nothing should get
1759
Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1760
self.add_hook('open',
1814
Hooks.__init__(self)
1815
self.create_hook(HookPoint('set_rh',
1816
"Invoked whenever the revision history has been set via "
1817
"set_revision_history. The api signature is (branch, "
1818
"revision_history), and the branch will be write-locked. "
1819
"The set_rh hook can be expensive for bzr to trigger, a better "
1820
"hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1821
self.create_hook(HookPoint('open',
1761
1822
"Called with the Branch object that has been opened after a "
1762
"branch is opened.", (1, 8))
1763
self.add_hook('post_push',
1823
"branch is opened.", (1, 8), None))
1824
self.create_hook(HookPoint('post_push',
1764
1825
"Called after a push operation completes. post_push is called "
1765
1826
"with a bzrlib.branch.BranchPushResult object and only runs in the "
1766
"bzr client.", (0, 15))
1767
self.add_hook('post_pull',
1827
"bzr client.", (0, 15), None))
1828
self.create_hook(HookPoint('post_pull',
1768
1829
"Called after a pull operation completes. post_pull is called "
1769
1830
"with a bzrlib.branch.PullResult object and only runs in the "
1770
"bzr client.", (0, 15))
1771
self.add_hook('pre_commit',
1831
"bzr client.", (0, 15), None))
1832
self.create_hook(HookPoint('pre_commit',
1772
1833
"Called after a commit is calculated but before it is "
1773
1834
"completed. pre_commit is called with (local, master, old_revno, "
1774
1835
"old_revid, future_revno, future_revid, tree_delta, future_tree"
1777
1838
"basis revision. hooks MUST NOT modify this delta. "
1778
1839
" future_tree is an in-memory tree obtained from "
1779
1840
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1781
self.add_hook('post_commit',
1841
"tree.", (0,91), None))
1842
self.create_hook(HookPoint('post_commit',
1782
1843
"Called in the bzr client after a commit has completed. "
1783
1844
"post_commit is called with (local, master, old_revno, old_revid, "
1784
1845
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1785
"commit to a branch.", (0, 15))
1786
self.add_hook('post_uncommit',
1846
"commit to a branch.", (0, 15), None))
1847
self.create_hook(HookPoint('post_uncommit',
1787
1848
"Called in the bzr client after an uncommit completes. "
1788
1849
"post_uncommit is called with (local, master, old_revno, "
1789
1850
"old_revid, new_revno, new_revid) where local is the local branch "
1790
1851
"or None, master is the target branch, and an empty branch "
1791
"receives new_revno of 0, new_revid of None.", (0, 15))
1792
self.add_hook('pre_change_branch_tip',
1852
"receives new_revno of 0, new_revid of None.", (0, 15), None))
1853
self.create_hook(HookPoint('pre_change_branch_tip',
1793
1854
"Called in bzr client and server before a change to the tip of a "
1794
1855
"branch is made. pre_change_branch_tip is called with a "
1795
1856
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1796
"commit, uncommit will all trigger this hook.", (1, 6))
1797
self.add_hook('post_change_branch_tip',
1857
"commit, uncommit will all trigger this hook.", (1, 6), None))
1858
self.create_hook(HookPoint('post_change_branch_tip',
1798
1859
"Called in bzr client and server after a change to the tip of a "
1799
1860
"branch is made. post_change_branch_tip is called with a "
1800
1861
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1801
"commit, uncommit will all trigger this hook.", (1, 4))
1802
self.add_hook('transform_fallback_location',
1862
"commit, uncommit will all trigger this hook.", (1, 4), None))
1863
self.create_hook(HookPoint('transform_fallback_location',
1803
1864
"Called when a stacked branch is activating its fallback "
1804
1865
"locations. transform_fallback_location is called with (branch, "
1805
1866
"url), and should return a new url. Returning the same url "
1810
1871
"fallback locations have not been activated. When there are "
1811
1872
"multiple hooks installed for transform_fallback_location, "
1812
1873
"all are called with the url returned from the previous hook."
1813
"The order is however undefined.", (1, 9))
1814
self.add_hook('automatic_tag_name',
1874
"The order is however undefined.", (1, 9), None))
1875
self.create_hook(HookPoint('automatic_tag_name',
1815
1876
"Called to determine an automatic tag name for a revision. "
1816
1877
"automatic_tag_name is called with (branch, revision_id) and "
1817
1878
"should return a tag name or None if no tag name could be "
1818
1879
"determined. The first non-None tag name returned will be used.",
1820
self.add_hook('post_branch_init',
1881
self.create_hook(HookPoint('post_branch_init',
1821
1882
"Called after new branch initialization completes. "
1822
1883
"post_branch_init is called with a "
1823
1884
"bzrlib.branch.BranchInitHookParams. "
1824
1885
"Note that init, branch and checkout (both heavyweight and "
1825
"lightweight) will all trigger this hook.", (2, 2))
1826
self.add_hook('post_switch',
1886
"lightweight) will all trigger this hook.", (2, 2), None))
1887
self.create_hook(HookPoint('post_switch',
1827
1888
"Called after a checkout switches branch. "
1828
1889
"post_switch is called with a "
1829
"bzrlib.branch.SwitchHookParams.", (2, 2))
1890
"bzrlib.branch.SwitchHookParams.", (2, 2), None))
1945
2006
self.revision_id)
1948
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
1949
"""Base class for branch formats that live in meta directories.
2009
class BzrBranchFormat4(BranchFormat):
2010
"""Bzr branch format 4.
2013
- a revision-history file.
2014
- a branch-lock lock file [ to be shared with the bzrdir ]
2017
def get_format_description(self):
2018
"""See BranchFormat.get_format_description()."""
2019
return "Branch format 4"
2021
def initialize(self, a_bzrdir, name=None):
2022
"""Create a branch of this format in a_bzrdir."""
2023
utf8_files = [('revision-history', ''),
2024
('branch-name', ''),
2026
return self._initialize_helper(a_bzrdir, utf8_files, name=name,
2027
lock_type='branch4', set_format=False)
1952
2029
def __init__(self):
1953
BranchFormat.__init__(self)
1954
bzrdir.BzrFormat.__init__(self)
1957
def find_format(klass, controldir, name=None):
1958
"""Return the format for the branch object in controldir."""
1960
transport = controldir.get_branch_transport(None, name=name)
1961
except errors.NoSuchFile:
1962
raise errors.NotBranchError(path=name, bzrdir=controldir)
1964
format_string = transport.get_bytes("format")
1965
except errors.NoSuchFile:
1966
raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1967
return klass._find_format(format_registry, 'branch', format_string)
2030
super(BzrBranchFormat4, self).__init__()
2031
self._matchingbzrdir = bzrdir.BzrDirFormat6()
2033
def network_name(self):
2034
"""The network name for this format is the control dirs disk label."""
2035
return self._matchingbzrdir.get_format_string()
2037
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2038
"""See BranchFormat.open()."""
2040
# we are being called directly and must probe.
2041
raise NotImplementedError
2042
return BzrBranch(_format=self,
2043
_control_files=a_bzrdir._control_files,
2046
_repository=a_bzrdir.open_repository())
2049
return "Bazaar-NG branch format 4"
2052
class BranchFormatMetadir(BranchFormat):
2053
"""Common logic for meta-dir based branch formats."""
1969
2055
def _branch_class(self):
1970
2056
"""What class to instantiate on open calls."""
1971
2057
raise NotImplementedError(self._branch_class)
1973
def _get_initial_config(self, append_revisions_only=None):
1974
if append_revisions_only:
1975
return "append_revisions_only = True\n"
1977
# Avoid writing anything if append_revisions_only is disabled,
1978
# as that is the default.
1981
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1983
"""Initialize a branch in a control dir, with specified files
1985
:param a_bzrdir: The bzrdir to initialize the branch in
1986
:param utf8_files: The files to create as a list of
1987
(filename, content) tuples
1988
:param name: Name of colocated branch to create, if any
1989
:return: a branch in this format
2059
def network_name(self):
2060
"""A simple byte string uniquely identifying this format for RPC calls.
2062
Metadir branch formats use their format string.
1992
name = a_bzrdir._get_selected_branch()
1993
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1994
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1995
control_files = lockable_files.LockableFiles(branch_transport,
1996
'lock', lockdir.LockDir)
1997
control_files.create_lock()
1998
control_files.lock_write()
2000
utf8_files += [('format', self.as_string())]
2001
for (filename, content) in utf8_files:
2002
branch_transport.put_bytes(
2004
mode=a_bzrdir._get_file_mode())
2006
control_files.unlock()
2007
branch = self.open(a_bzrdir, name, _found=True,
2008
found_repository=repository)
2009
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2064
return self.get_format_string()
2012
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2013
found_repository=None, possible_transports=None):
2066
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2014
2067
"""See BranchFormat.open()."""
2016
name = a_bzrdir._get_selected_branch()
2018
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2069
format = BranchFormat.find_format(a_bzrdir, name=name)
2019
2070
if format.__class__ != self.__class__:
2020
2071
raise AssertionError("wrong format %r found for %r" %
2021
2072
(format, self))
2024
2075
control_files = lockable_files.LockableFiles(transport, 'lock',
2025
2076
lockdir.LockDir)
2026
if found_repository is None:
2027
found_repository = a_bzrdir.find_repository()
2028
2077
return self._branch_class()(_format=self,
2029
2078
_control_files=control_files,
2031
2080
a_bzrdir=a_bzrdir,
2032
_repository=found_repository,
2033
ignore_fallbacks=ignore_fallbacks,
2034
possible_transports=possible_transports)
2081
_repository=a_bzrdir.find_repository(),
2082
ignore_fallbacks=ignore_fallbacks)
2035
2083
except errors.NoSuchFile:
2036
2084
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2039
def _matchingbzrdir(self):
2040
ret = bzrdir.BzrDirMetaFormat1()
2041
ret.set_branch_format(self)
2087
super(BranchFormatMetadir, self).__init__()
2088
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2089
self._matchingbzrdir.set_branch_format(self)
2044
2091
def supports_tags(self):
2047
def supports_leaving_lock(self):
2050
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
2052
BranchFormat.check_support_status(self,
2053
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
2055
bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
2056
recommend_upgrade=recommend_upgrade, basedir=basedir)
2059
2095
class BzrBranchFormat5(BranchFormatMetadir):
2060
2096
"""Bzr branch format 5.
2151
2179
"""See BranchFormat.get_format_description()."""
2152
2180
return "Branch format 8"
2154
def initialize(self, a_bzrdir, name=None, repository=None,
2155
append_revisions_only=None):
2182
def initialize(self, a_bzrdir, name=None):
2156
2183
"""Create a branch of this format in a_bzrdir."""
2157
2184
utf8_files = [('last-revision', '0 null:\n'),
2159
self._get_initial_config(append_revisions_only)),
2185
('branch.conf', ''),
2161
2187
('references', '')
2163
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2189
return self._initialize_helper(a_bzrdir, utf8_files, name)
2192
super(BzrBranchFormat8, self).__init__()
2193
self._matchingbzrdir.repository_format = \
2194
RepositoryFormatKnitPack5RichRoot()
2165
2196
def make_tags(self, branch):
2166
2197
"""See bzrlib.branch.BranchFormat.make_tags()."""
2167
return _mod_tag.BasicTags(branch)
2198
return BasicTags(branch)
2169
2200
def supports_set_append_revisions_only(self):
2249
2269
transport = a_bzrdir.get_branch_transport(None, name=name)
2250
2270
location = transport.put_bytes('location', to_branch.base)
2252
def initialize(self, a_bzrdir, name=None, target_branch=None,
2253
repository=None, append_revisions_only=None):
2272
def initialize(self, a_bzrdir, name=None, target_branch=None):
2254
2273
"""Create a branch of this format in a_bzrdir."""
2255
2274
if target_branch is None:
2256
2275
# this format does not implement branch itself, thus the implicit
2257
2276
# creation contract must see it as uninitializable
2258
2277
raise errors.UninitializableFormat(self)
2259
2278
mutter('creating branch reference in %s', a_bzrdir.user_url)
2260
if a_bzrdir._format.fixed_components:
2261
raise errors.IncompatibleFormat(self, a_bzrdir._format)
2263
name = a_bzrdir._get_selected_branch()
2264
2279
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2265
2280
branch_transport.put_bytes('location',
2266
target_branch.user_url)
2267
branch_transport.put_bytes('format', self.as_string())
2268
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,
2269
2285
possible_transports=[target_branch.bzrdir.root_transport])
2270
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)
2273
2294
def _make_reference_clone_function(format, a_branch):
2274
2295
"""Create a clone() routine for a branch dynamically."""
2275
2296
def clone(to_bzrdir, revision_id=None,
2298
2318
:param possible_transports: An optional reusable transports list.
2301
name = a_bzrdir._get_selected_branch()
2303
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2321
format = BranchFormat.find_format(a_bzrdir, name=name)
2304
2322
if format.__class__ != self.__class__:
2305
2323
raise AssertionError("wrong format %r found for %r" %
2306
2324
(format, self))
2307
2325
if location is None:
2308
2326
location = self.get_reference(a_bzrdir, name)
2309
real_bzrdir = controldir.ControlDir.open(
2327
real_bzrdir = bzrdir.BzrDir.open(
2310
2328
location, possible_transports=possible_transports)
2311
result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
2312
possible_transports=possible_transports)
2329
result = real_bzrdir.open_branch(name=name,
2330
ignore_fallbacks=ignore_fallbacks)
2313
2331
# this changes the behaviour of result.clone to create a new reference
2314
2332
# rather than a copy of the content of the branch.
2315
2333
# I did not use a proxy object because that needs much more extensive
2397
2403
def __init__(self, _format=None,
2398
2404
_control_files=None, a_bzrdir=None, name=None,
2399
_repository=None, ignore_fallbacks=False,
2400
possible_transports=None):
2405
_repository=None, ignore_fallbacks=False):
2401
2406
"""Create new branch object at a particular location."""
2402
2407
if a_bzrdir is None:
2403
2408
raise ValueError('a_bzrdir must be supplied')
2405
raise ValueError('name must be supplied')
2406
self.bzrdir = a_bzrdir
2407
self._user_transport = self.bzrdir.transport.clone('..')
2409
self._user_transport.set_segment_parameter(
2410
"branch", urlutils.escape(name))
2411
self._base = self._user_transport.base
2410
self.bzrdir = a_bzrdir
2411
self._base = self.bzrdir.transport.clone('..').base
2412
2412
self.name = name
2413
# XXX: We should be able to just do
2414
# self.base = self.bzrdir.root_transport.base
2415
# but this does not quite work yet -- mbp 20080522
2413
2416
self._format = _format
2414
2417
if _control_files is None:
2415
2418
raise ValueError('BzrBranch _control_files is None')
2416
2419
self.control_files = _control_files
2417
2420
self._transport = _control_files._transport
2418
2421
self.repository = _repository
2419
self.conf_store = None
2420
Branch.__init__(self, possible_transports)
2422
Branch.__init__(self)
2422
2424
def __str__(self):
2423
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2425
if self.name is None:
2426
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2428
return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2425
2431
__repr__ = __str__
2523
2518
"""See Branch.print_file."""
2524
2519
return self.repository.print_file(file, revision_id)
2521
def _write_revision_history(self, history):
2522
"""Factored out of set_revision_history.
2524
This performs the actual writing to disk.
2525
It is intended to be called by BzrBranch5.set_revision_history."""
2526
self._transport.put_bytes(
2527
'revision-history', '\n'.join(history),
2528
mode=self.bzrdir._get_file_mode())
2531
def set_revision_history(self, rev_history):
2532
"""See Branch.set_revision_history."""
2533
if 'evil' in debug.debug_flags:
2534
mutter_callsite(3, "set_revision_history scales with history.")
2535
check_not_reserved_id = _mod_revision.check_not_reserved_id
2536
for rev_id in rev_history:
2537
check_not_reserved_id(rev_id)
2538
if Branch.hooks['post_change_branch_tip']:
2539
# Don't calculate the last_revision_info() if there are no hooks
2541
old_revno, old_revid = self.last_revision_info()
2542
if len(rev_history) == 0:
2543
revid = _mod_revision.NULL_REVISION
2545
revid = rev_history[-1]
2546
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2547
self._write_revision_history(rev_history)
2548
self._clear_cached_state()
2549
self._cache_revision_history(rev_history)
2550
for hook in Branch.hooks['set_rh']:
2551
hook(self, rev_history)
2552
if Branch.hooks['post_change_branch_tip']:
2553
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2555
def _synchronize_history(self, destination, revision_id):
2556
"""Synchronize last revision and revision history between branches.
2558
This version is most efficient when the destination is also a
2559
BzrBranch5, but works for BzrBranch6 as long as the revision
2560
history is the true lefthand parent history, and all of the revisions
2561
are in the destination's repository. If not, set_revision_history
2564
:param destination: The branch to copy the history into
2565
:param revision_id: The revision-id to truncate history at. May
2566
be None to copy complete history.
2568
if not isinstance(destination._format, BzrBranchFormat5):
2569
super(BzrBranch, self)._synchronize_history(
2570
destination, revision_id)
2572
if revision_id == _mod_revision.NULL_REVISION:
2575
new_history = self.revision_history()
2576
if revision_id is not None and new_history != []:
2578
new_history = new_history[:new_history.index(revision_id) + 1]
2580
rev = self.repository.get_revision(revision_id)
2581
new_history = rev.get_history(self.repository)[1:]
2582
destination.set_revision_history(new_history)
2526
2584
@needs_write_lock
2527
2585
def set_last_revision_info(self, revno, revision_id):
2528
if not revision_id or not isinstance(revision_id, basestring):
2529
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2586
"""Set the last revision of this branch.
2588
The caller is responsible for checking that the revno is correct
2589
for this revision id.
2591
It may be possible to set the branch last revision to an id not
2592
present in the repository. However, branches can also be
2593
configured to check constraints on history, in which case this may not
2530
2596
revision_id = _mod_revision.ensure_null(revision_id)
2531
old_revno, old_revid = self.last_revision_info()
2532
if self.get_append_revisions_only():
2533
self._check_history_violation(revision_id)
2534
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2535
self._write_last_revision_info(revno, revision_id)
2536
self._clear_cached_state()
2537
self._last_revision_info_cache = revno, revision_id
2538
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2597
# this old format stores the full history, but this api doesn't
2598
# provide it, so we must generate, and might as well check it's
2600
history = self._lefthand_history(revision_id)
2601
if len(history) != revno:
2602
raise AssertionError('%d != %d' % (len(history), revno))
2603
self.set_revision_history(history)
2605
def _gen_revision_history(self):
2606
history = self._transport.get_bytes('revision-history').split('\n')
2607
if history[-1:] == ['']:
2608
# There shouldn't be a trailing newline, but just in case.
2613
def generate_revision_history(self, revision_id, last_rev=None,
2615
"""Create a new revision history that will finish with revision_id.
2617
:param revision_id: the new tip to use.
2618
:param last_rev: The previous last_revision. If not None, then this
2619
must be a ancestory of revision_id, or DivergedBranches is raised.
2620
:param other_branch: The other branch that DivergedBranches should
2621
raise with respect to.
2623
self.set_revision_history(self._lefthand_history(revision_id,
2624
last_rev, other_branch))
2540
2626
def basis_tree(self):
2541
2627
"""See Branch.basis_tree."""
2639
def _basic_push(self, target, overwrite, stop_revision):
2640
"""Basic implementation of push without bound branches or hooks.
2642
Must be called with source read locked and target write locked.
2644
result = BranchPushResult()
2645
result.source_branch = self
2646
result.target_branch = target
2647
result.old_revno, result.old_revid = target.last_revision_info()
2648
self.update_references(target)
2649
if result.old_revid != self.last_revision():
2650
# We assume that during 'push' this repository is closer than
2652
graph = self.repository.get_graph(target.repository)
2653
target.update_revisions(self, stop_revision,
2654
overwrite=overwrite, graph=graph)
2655
if self._push_should_merge_tags():
2656
result.tag_conflicts = self.tags.merge_to(target.tags,
2658
result.new_revno, result.new_revid = target.last_revision_info()
2553
2661
def get_stacked_on_url(self):
2554
2662
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2566
2674
self._transport.put_bytes('parent', url + '\n',
2567
2675
mode=self.bzrdir._get_file_mode())
2678
class BzrBranch5(BzrBranch):
2679
"""A format 5 branch. This supports new features over plain branches.
2681
It has support for a master_branch which is the data for bound branches.
2684
def get_bound_location(self):
2686
return self._transport.get_bytes('bound')[:-1]
2687
except errors.NoSuchFile:
2691
def get_master_branch(self, possible_transports=None):
2692
"""Return the branch we are bound to.
2694
:return: Either a Branch, or None
2696
This could memoise the branch, but if thats done
2697
it must be revalidated on each new lock.
2698
So for now we just don't memoise it.
2699
# RBC 20060304 review this decision.
2701
bound_loc = self.get_bound_location()
2705
return Branch.open(bound_loc,
2706
possible_transports=possible_transports)
2707
except (errors.NotBranchError, errors.ConnectionError), e:
2708
raise errors.BoundBranchConnectionFailure(
2569
2711
@needs_write_lock
2571
"""If bound, unbind"""
2572
return self.set_bound_location(None)
2712
def set_bound_location(self, location):
2713
"""Set the target where this branch is bound to.
2715
:param location: URL to the target branch
2718
self._transport.put_bytes('bound', location+'\n',
2719
mode=self.bzrdir._get_file_mode())
2722
self._transport.delete('bound')
2723
except errors.NoSuchFile:
2574
2727
@needs_write_lock
2575
2728
def bind(self, other):
2597
2750
# history around
2598
2751
self.set_bound_location(other.base)
2600
def get_bound_location(self):
2602
return self._transport.get_bytes('bound')[:-1]
2603
except errors.NoSuchFile:
2607
def get_master_branch(self, possible_transports=None):
2608
"""Return the branch we are bound to.
2610
:return: Either a Branch, or None
2612
if self._master_branch_cache is None:
2613
self._master_branch_cache = self._get_master_branch(
2614
possible_transports)
2615
return self._master_branch_cache
2617
def _get_master_branch(self, possible_transports):
2618
bound_loc = self.get_bound_location()
2622
return Branch.open(bound_loc,
2623
possible_transports=possible_transports)
2624
except (errors.NotBranchError, errors.ConnectionError), e:
2625
raise errors.BoundBranchConnectionFailure(
2628
2753
@needs_write_lock
2629
def set_bound_location(self, location):
2630
"""Set the target where this branch is bound to.
2632
:param location: URL to the target branch
2634
self._master_branch_cache = None
2636
self._transport.put_bytes('bound', location+'\n',
2637
mode=self.bzrdir._get_file_mode())
2640
self._transport.delete('bound')
2641
except errors.NoSuchFile:
2755
"""If bound, unbind"""
2756
return self.set_bound_location(None)
2645
2758
@needs_write_lock
2646
2759
def update(self, possible_transports=None):
2662
def _read_last_revision_info(self):
2663
revision_string = self._transport.get_bytes('last-revision')
2664
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2665
revision_id = cache_utf8.get_cached_utf8(revision_id)
2667
return revno, revision_id
2669
def _write_last_revision_info(self, revno, revision_id):
2670
"""Simply write out the revision id, with no checks.
2672
Use set_last_revision_info to perform this safely.
2674
Does not update the revision_history cache.
2676
revision_id = _mod_revision.ensure_null(revision_id)
2677
out_string = '%d %s\n' % (revno, revision_id)
2678
self._transport.put_bytes('last-revision', out_string,
2679
mode=self.bzrdir._get_file_mode())
2682
def update_feature_flags(self, updated_flags):
2683
"""Update the feature flags for this branch.
2685
:param updated_flags: Dictionary mapping feature names to necessities
2686
A necessity can be None to indicate the feature should be removed
2688
self._format._update_feature_flags(updated_flags)
2689
self.control_transport.put_bytes('format', self._format.as_string())
2692
class FullHistoryBzrBranch(BzrBranch):
2693
"""Bzr branch which contains the full revision history."""
2696
def set_last_revision_info(self, revno, revision_id):
2697
if not revision_id or not isinstance(revision_id, basestring):
2698
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2699
revision_id = _mod_revision.ensure_null(revision_id)
2700
# this old format stores the full history, but this api doesn't
2701
# provide it, so we must generate, and might as well check it's
2703
history = self._lefthand_history(revision_id)
2704
if len(history) != revno:
2705
raise AssertionError('%d != %d' % (len(history), revno))
2706
self._set_revision_history(history)
2708
def _read_last_revision_info(self):
2709
rh = self._revision_history()
2712
return (revno, rh[-1])
2714
return (0, _mod_revision.NULL_REVISION)
2716
def _set_revision_history(self, rev_history):
2717
if 'evil' in debug.debug_flags:
2718
mutter_callsite(3, "set_revision_history scales with history.")
2719
check_not_reserved_id = _mod_revision.check_not_reserved_id
2720
for rev_id in rev_history:
2721
check_not_reserved_id(rev_id)
2722
if Branch.hooks['post_change_branch_tip']:
2723
# Don't calculate the last_revision_info() if there are no hooks
2725
old_revno, old_revid = self.last_revision_info()
2726
if len(rev_history) == 0:
2727
revid = _mod_revision.NULL_REVISION
2729
revid = rev_history[-1]
2730
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2731
self._write_revision_history(rev_history)
2732
self._clear_cached_state()
2733
self._cache_revision_history(rev_history)
2734
if Branch.hooks['post_change_branch_tip']:
2735
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2737
def _write_revision_history(self, history):
2738
"""Factored out of set_revision_history.
2740
This performs the actual writing to disk.
2741
It is intended to be called by set_revision_history."""
2742
self._transport.put_bytes(
2743
'revision-history', '\n'.join(history),
2744
mode=self.bzrdir._get_file_mode())
2746
def _gen_revision_history(self):
2747
history = self._transport.get_bytes('revision-history').split('\n')
2748
if history[-1:] == ['']:
2749
# There shouldn't be a trailing newline, but just in case.
2753
def _synchronize_history(self, destination, revision_id):
2754
if not isinstance(destination, FullHistoryBzrBranch):
2755
super(BzrBranch, self)._synchronize_history(
2756
destination, revision_id)
2758
if revision_id == _mod_revision.NULL_REVISION:
2761
new_history = self._revision_history()
2762
if revision_id is not None and new_history != []:
2764
new_history = new_history[:new_history.index(revision_id) + 1]
2766
rev = self.repository.get_revision(revision_id)
2767
new_history = rev.get_history(self.repository)[1:]
2768
destination._set_revision_history(new_history)
2771
def generate_revision_history(self, revision_id, last_rev=None,
2773
"""Create a new revision history that will finish with revision_id.
2775
:param revision_id: the new tip to use.
2776
:param last_rev: The previous last_revision. If not None, then this
2777
must be a ancestory of revision_id, or DivergedBranches is raised.
2778
:param other_branch: The other branch that DivergedBranches should
2779
raise with respect to.
2781
self._set_revision_history(self._lefthand_history(revision_id,
2782
last_rev, other_branch))
2785
class BzrBranch5(FullHistoryBzrBranch):
2786
"""A format 5 branch. This supports new features over plain branches.
2788
It has support for a master_branch which is the data for bound branches.
2792
class BzrBranch8(BzrBranch):
2776
class BzrBranch8(BzrBranch5):
2793
2777
"""A branch that stores tree-reference locations."""
2795
def _open_hook(self, possible_transports=None):
2779
def _open_hook(self):
2796
2780
if self._ignore_fallbacks:
2798
if possible_transports is None:
2799
possible_transports = [self.bzrdir.root_transport]
2801
2783
url = self.get_stacked_on_url()
2802
2784
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2824
2805
self._last_revision_info_cache = None
2825
2806
self._reference_info = None
2808
def _last_revision_info(self):
2809
revision_string = self._transport.get_bytes('last-revision')
2810
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2811
revision_id = cache_utf8.get_cached_utf8(revision_id)
2813
return revno, revision_id
2815
def _write_last_revision_info(self, revno, revision_id):
2816
"""Simply write out the revision id, with no checks.
2818
Use set_last_revision_info to perform this safely.
2820
Does not update the revision_history cache.
2821
Intended to be called by set_last_revision_info and
2822
_write_revision_history.
2824
revision_id = _mod_revision.ensure_null(revision_id)
2825
out_string = '%d %s\n' % (revno, revision_id)
2826
self._transport.put_bytes('last-revision', out_string,
2827
mode=self.bzrdir._get_file_mode())
2830
def set_last_revision_info(self, revno, revision_id):
2831
revision_id = _mod_revision.ensure_null(revision_id)
2832
old_revno, old_revid = self.last_revision_info()
2833
if self._get_append_revisions_only():
2834
self._check_history_violation(revision_id)
2835
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2836
self._write_last_revision_info(revno, revision_id)
2837
self._clear_cached_state()
2838
self._last_revision_info_cache = revno, revision_id
2839
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2841
def _synchronize_history(self, destination, revision_id):
2842
"""Synchronize last revision and revision history between branches.
2844
:see: Branch._synchronize_history
2846
# XXX: The base Branch has a fast implementation of this method based
2847
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2848
# that uses set_revision_history. This class inherits from BzrBranch5,
2849
# but wants the fast implementation, so it calls
2850
# Branch._synchronize_history directly.
2851
Branch._synchronize_history(self, destination, revision_id)
2827
2853
def _check_history_violation(self, revision_id):
2828
current_revid = self.last_revision()
2829
last_revision = _mod_revision.ensure_null(current_revid)
2854
last_revision = _mod_revision.ensure_null(self.last_revision())
2830
2855
if _mod_revision.is_null(last_revision):
2832
graph = self.repository.get_graph()
2833
for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2834
if lh_ancestor == current_revid:
2836
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2857
if last_revision not in self._lefthand_history(revision_id):
2858
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2838
2860
def _gen_revision_history(self):
2839
2861
"""Generate the revision history from last revision
2934
2972
def set_bound_location(self, location):
2935
2973
"""See Branch.set_push_location."""
2936
self._master_branch_cache = None
2938
conf = self.get_config_stack()
2975
config = self.get_config()
2939
2976
if location is None:
2940
if not conf.get('bound'):
2977
if config.get_user_option('bound') != 'True':
2943
conf.set('bound', 'False')
2980
config.set_user_option('bound', 'False', warn_masked=True)
2946
2983
self._set_config_location('bound_location', location,
2948
conf.set('bound', 'True')
2985
config.set_user_option('bound', 'True', warn_masked=True)
2951
2988
def _get_bound_location(self, bound):
2952
2989
"""Return the bound location in the config file.
2954
2991
Return None if the bound parameter does not match"""
2955
conf = self.get_config_stack()
2956
if conf.get('bound') != bound:
2992
config = self.get_config()
2993
config_bound = (config.get_user_option('bound') == 'True')
2994
if config_bound != bound:
2958
return self._get_config_location('bound_location', config=conf)
2996
return self._get_config_location('bound_location', config=config)
2960
2998
def get_bound_location(self):
2961
"""See Branch.get_bound_location."""
2999
"""See Branch.set_push_location."""
2962
3000
return self._get_bound_location(True)
2964
3002
def get_old_bound_location(self):
2969
3007
# you can always ask for the URL; but you might not be able to use it
2970
3008
# if the repo can't support stacking.
2971
3009
## self._check_stackable_repo()
2972
# stacked_on_location is only ever defined in branch.conf, so don't
2973
# waste effort reading the whole stack of config files.
2974
conf = _mod_config.BranchOnlyStack(self)
2975
stacked_url = self._get_config_location('stacked_on_location',
3010
stacked_url = self._get_config_location('stacked_on_location')
2977
3011
if stacked_url is None:
2978
3012
raise errors.NotStacked(self)
2979
return stacked_url.encode('utf-8')
3015
def _get_append_revisions_only(self):
3016
return self.get_config(
3017
).get_user_option_as_bool('append_revisions_only')
3020
def generate_revision_history(self, revision_id, last_rev=None,
3022
"""See BzrBranch5.generate_revision_history"""
3023
history = self._lefthand_history(revision_id, last_rev, other_branch)
3024
revno = len(history)
3025
self.set_last_revision_info(revno, revision_id)
2981
3027
@needs_read_lock
2982
3028
def get_rev_id(self, revno, history=None):
3071
3112
:ivar local_branch: target branch if there is a Master, else None
3072
3113
:ivar target_branch: Target/destination branch object. (write locked)
3073
3114
:ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
3074
:ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3117
@deprecated_method(deprecated_in((2, 3, 0)))
3119
"""Return the relative change in revno.
3121
:deprecated: Use `new_revno` and `old_revno` instead.
3123
return self.new_revno - self.old_revno
3077
3125
def report(self, to_file):
3078
tag_conflicts = getattr(self, "tag_conflicts", None)
3079
tag_updates = getattr(self, "tag_updates", None)
3080
3126
if not is_quiet():
3081
if self.old_revid != self.new_revid:
3127
if self.old_revid == self.new_revid:
3128
to_file.write('No revisions to pull.\n')
3082
3130
to_file.write('Now on revision %d.\n' % self.new_revno)
3084
to_file.write('%d tag(s) updated.\n' % len(tag_updates))
3085
if self.old_revid == self.new_revid and not tag_updates:
3086
if not tag_conflicts:
3087
to_file.write('No revisions or tags to pull.\n')
3089
to_file.write('No revisions to pull.\n')
3090
3131
self._show_tag_conficts(to_file)
3109
3150
target, otherwise it will be None.
3153
@deprecated_method(deprecated_in((2, 3, 0)))
3155
"""Return the relative change in revno.
3157
:deprecated: Use `new_revno` and `old_revno` instead.
3159
return self.new_revno - self.old_revno
3112
3161
def report(self, to_file):
3113
# TODO: This function gets passed a to_file, but then
3114
# ignores it and calls note() instead. This is also
3115
# inconsistent with PullResult(), which writes to stdout.
3116
# -- JRV20110901, bug #838853
3117
tag_conflicts = getattr(self, "tag_conflicts", None)
3118
tag_updates = getattr(self, "tag_updates", None)
3120
if self.old_revid != self.new_revid:
3121
note(gettext('Pushed up to revision %d.') % self.new_revno)
3123
note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
3124
if self.old_revid == self.new_revid and not tag_updates:
3125
if not tag_conflicts:
3126
note(gettext('No new revisions or tags to push.'))
3128
note(gettext('No new revisions to push.'))
3162
"""Write a human-readable description of the result."""
3163
if self.old_revid == self.new_revid:
3164
note('No new revisions to push.')
3166
note('Pushed up to revision %d.' % self.new_revno)
3129
3167
self._show_tag_conficts(to_file)
3162
3200
# Copy source data into target
3163
3201
new_branch._write_last_revision_info(*branch.last_revision_info())
3164
new_branch.lock_write()
3166
new_branch.set_parent(branch.get_parent())
3167
new_branch.set_bound_location(branch.get_bound_location())
3168
new_branch.set_push_location(branch.get_push_location())
3202
new_branch.set_parent(branch.get_parent())
3203
new_branch.set_bound_location(branch.get_bound_location())
3204
new_branch.set_push_location(branch.get_push_location())
3172
3206
# New branch has no tags by default
3173
3207
new_branch.tags._set_tag_dict({})
3175
3209
# Copying done; now update target format
3176
3210
new_branch._transport.put_bytes('format',
3211
format.get_format_string(),
3178
3212
mode=new_branch.bzrdir._get_file_mode())
3180
3214
# Clean up old files
3181
3215
new_branch._transport.delete('revision-history')
3185
branch.set_parent(None)
3186
except errors.NoSuchFile:
3188
branch.set_bound_location(None)
3217
branch.set_parent(None)
3218
except errors.NoSuchFile:
3220
branch.set_bound_location(None)
3193
3223
class Converter6to7(object):
3197
3227
format = BzrBranchFormat7()
3198
3228
branch._set_config_location('stacked_on_location', '')
3199
3229
# update target format
3200
branch._transport.put_bytes('format', format.as_string())
3230
branch._transport.put_bytes('format', format.get_format_string())
3203
3233
class Converter7to8(object):
3204
"""Perform an in-place upgrade of format 7 to format 8"""
3234
"""Perform an in-place upgrade of format 6 to format 7"""
3206
3236
def convert(self, branch):
3207
3237
format = BzrBranchFormat8()
3208
3238
branch._transport.put_bytes('references', '')
3209
3239
# update target format
3210
branch._transport.put_bytes('format', format.as_string())
3240
branch._transport.put_bytes('format', format.get_format_string())
3243
def _run_with_write_locked_target(target, callable, *args, **kwargs):
3244
"""Run ``callable(*args, **kwargs)``, write-locking target for the
3247
_run_with_write_locked_target will attempt to release the lock it acquires.
3249
If an exception is raised by callable, then that exception *will* be
3250
propagated, even if the unlock attempt raises its own error. Thus
3251
_run_with_write_locked_target should be preferred to simply doing::
3255
return callable(*args, **kwargs)
3260
# This is very similar to bzrlib.decorators.needs_write_lock. Perhaps they
3261
# should share code?
3264
result = callable(*args, **kwargs)
3266
exc_info = sys.exc_info()
3270
raise exc_info[0], exc_info[1], exc_info[2]
3213
3276
class InterBranch(InterObject):
3318
3377
self.source.tags.merge_to(self.target.tags)
3320
3379
@needs_write_lock
3321
def fetch(self, stop_revision=None, limit=None):
3322
if self.target.base == self.source.base:
3324
self.source.lock_read()
3326
fetch_spec_factory = fetch.FetchSpecFactory()
3327
fetch_spec_factory.source_branch = self.source
3328
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3329
fetch_spec_factory.source_repo = self.source.repository
3330
fetch_spec_factory.target_repo = self.target.repository
3331
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3332
fetch_spec_factory.limit = limit
3333
fetch_spec = fetch_spec_factory.make_fetch_spec()
3334
return self.target.repository.fetch(self.source.repository,
3335
fetch_spec=fetch_spec)
3337
self.source.unlock()
3340
def _update_revisions(self, stop_revision=None, overwrite=False,
3380
def update_revisions(self, stop_revision=None, overwrite=False,
3382
"""See InterBranch.update_revisions()."""
3342
3383
other_revno, other_last_revision = self.source.last_revision_info()
3343
3384
stop_revno = None # unknown
3344
3385
if stop_revision is None:
3411
3442
return self._pull(overwrite,
3412
3443
stop_revision, _hook_master=master_branch,
3413
3444
run_hooks=run_hooks,
3414
_override_hook_target=_override_hook_target,
3415
merge_tags_to_master=not source_is_master)
3445
_override_hook_target=_override_hook_target)
3417
3447
if master_branch:
3418
3448
master_branch.unlock()
3420
def push(self, overwrite=False, stop_revision=None, lossy=False,
3450
def push(self, overwrite=False, stop_revision=None,
3421
3451
_override_hook_source_branch=None):
3422
3452
"""See InterBranch.push.
3424
3454
This is the basic concrete implementation of push()
3426
:param _override_hook_source_branch: If specified, run the hooks
3427
passing this Branch as the source, rather than self. This is for
3428
use of RemoteBranch, where push is delegated to the underlying
3456
:param _override_hook_source_branch: If specified, run
3457
the hooks passing this Branch as the source, rather than self.
3458
This is for use of RemoteBranch, where push is delegated to the
3459
underlying vfs-based Branch.
3432
raise errors.LossyPushToSameVCS(self.source, self.target)
3433
3461
# TODO: Public option to disable running hooks - should be trivial but
3436
op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3437
op.add_cleanup(self.source.lock_read().unlock)
3438
op.add_cleanup(self.target.lock_write().unlock)
3439
return op.run(overwrite, stop_revision,
3440
_override_hook_source_branch=_override_hook_source_branch)
3442
def _basic_push(self, overwrite, stop_revision):
3443
"""Basic implementation of push without bound branches or hooks.
3445
Must be called with source read locked and target write locked.
3447
result = BranchPushResult()
3448
result.source_branch = self.source
3449
result.target_branch = self.target
3450
result.old_revno, result.old_revid = self.target.last_revision_info()
3451
self.source.update_references(self.target)
3452
overwrite = _fix_overwrite_type(overwrite)
3453
if result.old_revid != stop_revision:
3454
# We assume that during 'push' this repository is closer than
3456
graph = self.source.repository.get_graph(self.target.repository)
3457
self._update_revisions(stop_revision,
3458
overwrite=("history" in overwrite),
3460
if self.source._push_should_merge_tags():
3461
result.tag_updates, result.tag_conflicts = (
3462
self.source.tags.merge_to(
3463
self.target.tags, "tags" in overwrite))
3464
result.new_revno, result.new_revid = self.target.last_revision_info()
3467
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
3463
self.source.lock_read()
3465
return _run_with_write_locked_target(
3466
self.target, self._push_with_bound_branches, overwrite,
3468
_override_hook_source_branch=_override_hook_source_branch)
3470
self.source.unlock()
3472
def _push_with_bound_branches(self, overwrite, stop_revision,
3468
3473
_override_hook_source_branch=None):
3469
3474
"""Push from source into target, and into target's master if any.
3482
3487
# be bound to itself? -- mbp 20070507
3483
3488
master_branch = self.target.get_master_branch()
3484
3489
master_branch.lock_write()
3485
operation.add_cleanup(master_branch.unlock)
3486
# push into the master from the source branch.
3487
master_inter = InterBranch.get(self.source, master_branch)
3488
master_inter._basic_push(overwrite, stop_revision)
3489
# and push into the target branch from the source. Note that
3490
# we push from the source branch again, because it's considered
3491
# the highest bandwidth repository.
3492
result = self._basic_push(overwrite, stop_revision)
3493
result.master_branch = master_branch
3494
result.local_branch = self.target
3491
# push into the master from the source branch.
3492
self.source._basic_push(master_branch, overwrite, stop_revision)
3493
# and push into the target branch from the source. Note that we
3494
# push from the source branch again, because it's considered the
3495
# highest bandwidth repository.
3496
result = self.source._basic_push(self.target, overwrite,
3498
result.master_branch = master_branch
3499
result.local_branch = self.target
3503
master_branch.unlock()
3496
master_branch = None
3497
3505
# no master branch
3498
result = self._basic_push(overwrite, stop_revision)
3506
result = self.source._basic_push(self.target, overwrite,
3499
3508
# TODO: Why set master_branch and local_branch if there's no
3500
3509
# binding? Maybe cleaner to just leave them unset? -- mbp
3502
3511
result.master_branch = self.target
3503
3512
result.local_branch = None
3507
3516
def _pull(self, overwrite=False, stop_revision=None,
3508
3517
possible_transports=None, _hook_master=None, run_hooks=True,
3509
_override_hook_target=None, local=False,
3510
merge_tags_to_master=True):
3518
_override_hook_target=None, local=False):
3511
3519
"""See Branch.pull.
3513
3521
This function is the core worker, used by GenericInterBranch.pull to
3544
3550
# -- JRV20090506
3545
3551
result.old_revno, result.old_revid = \
3546
3552
self.target.last_revision_info()
3547
overwrite = _fix_overwrite_type(overwrite)
3548
self._update_revisions(stop_revision,
3549
overwrite=("history" in overwrite),
3553
self.target.update_revisions(self.source, stop_revision,
3554
overwrite=overwrite, graph=graph)
3551
3555
# TODO: The old revid should be specified when merging tags,
3552
3556
# so a tags implementation that versions tags can only
3553
3557
# pull in the most recent changes. -- JRV20090506
3554
result.tag_updates, result.tag_conflicts = (
3555
self.source.tags.merge_to(self.target.tags,
3556
"tags" in overwrite,
3557
ignore_master=not merge_tags_to_master))
3558
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3558
3560
result.new_revno, result.new_revid = self.target.last_revision_info()
3559
3561
if _hook_master:
3560
3562
result.master_branch = _hook_master