18
18
from cStringIO import StringIO
20
21
from bzrlib.lazy_import import lazy_import
21
22
lazy_import(globals(), """
23
from itertools import chain
23
24
from bzrlib import (
27
27
config as _mod_config,
36
35
revision as _mod_revision,
43
from bzrlib.i18n import gettext, ngettext
43
from bzrlib.config import BranchConfig, TransportConfig
44
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
45
from bzrlib.tag import (
49
from bzrlib.decorators import (
54
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
55
53
from bzrlib.inter import InterObject
56
54
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
57
55
from bzrlib import registry
514
503
rev_iter = iter(merge_sorted_revisions)
515
504
if start_revision_id is not None:
516
505
for node in rev_iter:
506
rev_id = node.key[-1]
518
507
if rev_id != start_revision_id:
521
510
# The decision to include the start or not
522
511
# depends on the stop_rule if a stop is provided
523
512
# so pop this node back into the iterator
524
rev_iter = itertools.chain(iter([node]), rev_iter)
513
rev_iter = chain(iter([node]), rev_iter)
526
515
if stop_revision_id is None:
527
516
# Yield everything
528
517
for node in rev_iter:
518
rev_id = node.key[-1]
530
519
yield (rev_id, node.merge_depth, node.revno,
531
520
node.end_of_merge)
532
521
elif stop_rule == 'exclude':
533
522
for node in rev_iter:
523
rev_id = node.key[-1]
535
524
if rev_id == stop_revision_id:
537
526
yield (rev_id, node.merge_depth, node.revno,
538
527
node.end_of_merge)
539
528
elif stop_rule == 'include':
540
529
for node in rev_iter:
530
rev_id = node.key[-1]
542
531
yield (rev_id, node.merge_depth, node.revno,
543
532
node.end_of_merge)
544
533
if rev_id == stop_revision_id:
679
660
raise errors.UnsupportedOperation(self.get_reference_info, self)
681
662
@needs_write_lock
682
def fetch(self, from_branch, last_revision=None, limit=None):
663
def fetch(self, from_branch, last_revision=None, pb=None):
683
664
"""Copy revisions from from_branch into this branch.
685
666
:param from_branch: Where to copy from.
686
667
:param last_revision: What revision to stop at (None for at the end
688
:param limit: Optional rough limit of revisions to fetch
669
:param pb: An optional progress bar to use.
691
return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
672
if self.base == from_branch.base:
675
symbol_versioning.warn(
676
symbol_versioning.deprecated_in((1, 14, 0))
677
% "pb parameter to fetch()")
678
from_branch.lock_read()
680
if last_revision is None:
681
last_revision = from_branch.last_revision()
682
last_revision = _mod_revision.ensure_null(last_revision)
683
return self.repository.fetch(from_branch.repository,
684
revision_id=last_revision,
693
689
def get_bound_location(self):
694
690
"""Return the URL of the branch we are bound to.
757
750
"""Print `file` to stdout."""
758
751
raise NotImplementedError(self.print_file)
760
@deprecated_method(deprecated_in((2, 4, 0)))
761
753
def set_revision_history(self, rev_history):
762
"""See Branch.set_revision_history."""
763
self._set_revision_history(rev_history)
766
def _set_revision_history(self, rev_history):
767
if len(rev_history) == 0:
768
revid = _mod_revision.NULL_REVISION
770
revid = rev_history[-1]
771
if rev_history != self._lefthand_history(revid):
772
raise errors.NotLefthandHistory(rev_history)
773
self.set_last_revision_info(len(rev_history), revid)
774
self._cache_revision_history(rev_history)
775
for hook in Branch.hooks['set_rh']:
776
hook(self, rev_history)
779
def set_last_revision_info(self, revno, revision_id):
780
"""Set the last revision of this branch.
782
The caller is responsible for checking that the revno is correct
783
for this revision id.
785
It may be possible to set the branch last revision to an id not
786
present in the repository. However, branches can also be
787
configured to check constraints on history, in which case this may not
790
raise NotImplementedError(self.set_last_revision_info)
793
def generate_revision_history(self, revision_id, last_rev=None,
795
"""See Branch.generate_revision_history"""
796
graph = self.repository.get_graph()
797
(last_revno, last_revid) = self.last_revision_info()
798
known_revision_ids = [
799
(last_revid, last_revno),
800
(_mod_revision.NULL_REVISION, 0),
802
if last_rev is not None:
803
if not graph.is_ancestor(last_rev, revision_id):
804
# our previous tip is not merged into stop_revision
805
raise errors.DivergedBranches(self, other_branch)
806
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
807
self.set_last_revision_info(revno, revision_id)
754
raise NotImplementedError(self.set_revision_history)
809
756
@needs_write_lock
810
757
def set_parent(self, url):
924
871
# XXX: If you unstack a branch while it has a working tree
925
872
# with a pending merge, the pending-merged revisions will no
926
873
# longer be present. You can (probably) revert and remerge.
928
tags_to_fetch = set(self.tags.get_reverse_tag_dict())
929
except errors.TagsNotSupported:
930
tags_to_fetch = set()
931
fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
932
old_repository, required_ids=[self.last_revision()],
933
if_present_ids=tags_to_fetch, find_ghosts=True).execute()
934
self.repository.fetch(old_repository, fetch_spec=fetch_spec)
875
# XXX: This only fetches up to the tip of the repository; it
876
# doesn't bring across any tags. That's fairly consistent
877
# with how branch works, but perhaps not ideal.
878
self.repository.fetch(old_repository,
879
revision_id=self.last_revision(),
936
882
old_repository.unlock()
1041
985
:return: A tuple (revno, revision_id).
1043
987
if self._last_revision_info_cache is None:
1044
self._last_revision_info_cache = self._read_last_revision_info()
988
self._last_revision_info_cache = self._last_revision_info()
1045
989
return self._last_revision_info_cache
1047
def _read_last_revision_info(self):
1048
raise NotImplementedError(self._read_last_revision_info)
1050
@deprecated_method(deprecated_in((2, 4, 0)))
991
def _last_revision_info(self):
992
rh = self.revision_history()
995
return (revno, rh[-1])
997
return (0, _mod_revision.NULL_REVISION)
999
@deprecated_method(deprecated_in((1, 6, 0)))
1000
def missing_revisions(self, other, stop_revision=None):
1001
"""Return a list of new revisions that would perfectly fit.
1003
If self and other have not diverged, return a list of the revisions
1004
present in other, but missing from self.
1006
self_history = self.revision_history()
1007
self_len = len(self_history)
1008
other_history = other.revision_history()
1009
other_len = len(other_history)
1010
common_index = min(self_len, other_len) -1
1011
if common_index >= 0 and \
1012
self_history[common_index] != other_history[common_index]:
1013
raise errors.DivergedBranches(self, other)
1015
if stop_revision is None:
1016
stop_revision = other_len
1018
if stop_revision > other_len:
1019
raise errors.NoSuchRevision(self, stop_revision)
1020
return other_history[self_len:stop_revision]
1022
def update_revisions(self, other, stop_revision=None, overwrite=False,
1024
"""Pull in new perfect-fit revisions.
1026
:param other: Another Branch to pull from
1027
:param stop_revision: Updated until the given revision
1028
:param overwrite: Always set the branch pointer, rather than checking
1029
to see if it is a proper descendant.
1030
:param graph: A Graph object that can be used to query history
1031
information. This can be None.
1034
return InterBranch.get(other, self).update_revisions(stop_revision,
1051
1037
def import_last_revision_info(self, source_repo, revno, revid):
1052
1038
"""Set the last revision info, importing from another repo if necessary.
1040
This is used by the bound branch code to upload a revision to
1041
the master branch first before updating the tip of the local branch.
1054
1043
:param source_repo: Source repository to optionally fetch from
1055
1044
:param revno: Revision number of the new tip
1056
1045
:param revid: Revision id of the new tip
1059
1048
self.repository.fetch(source_repo, revision_id=revid)
1060
1049
self.set_last_revision_info(revno, revid)
1062
def import_last_revision_info_and_tags(self, source, revno, revid,
1064
"""Set the last revision info, importing from another repo if necessary.
1066
This is used by the bound branch code to upload a revision to
1067
the master branch first before updating the tip of the local branch.
1068
Revisions referenced by source's tags are also transferred.
1070
:param source: Source branch to optionally fetch from
1071
:param revno: Revision number of the new tip
1072
:param revid: Revision id of the new tip
1073
:param lossy: Whether to discard metadata that can not be
1074
natively represented
1075
:return: Tuple with the new revision number and revision id
1076
(should only be different from the arguments when lossy=True)
1078
if not self.repository.has_same_location(source.repository):
1079
self.fetch(source, revid)
1080
self.set_last_revision_info(revno, revid)
1081
return (revno, revid)
1083
1051
def revision_id_to_revno(self, revision_id):
1084
1052
"""Given a revision id, return its revno"""
1085
1053
if _mod_revision.is_null(revision_id):
1117
1085
stop_revision=stop_revision,
1118
1086
possible_transports=possible_transports, *args, **kwargs)
1120
def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1088
def push(self, target, overwrite=False, stop_revision=None, *args,
1122
1090
"""Mirror this branch into target.
1124
1092
This branch is considered to be 'local', having low latency.
1126
1094
return InterBranch.get(self, target).push(overwrite, stop_revision,
1127
lossy, *args, **kwargs)
1097
def lossy_push(self, target, stop_revision=None):
1098
"""Push deltas into another branch.
1100
:note: This does not, like push, retain the revision ids from
1101
the source branch and will, rather than adding bzr-specific
1102
metadata, push only those semantics of the revision that can be
1103
natively represented by this branch' VCS.
1105
:param target: Target branch
1106
:param stop_revision: Revision to push, defaults to last revision.
1107
:return: BranchPushResult with an extra member revidmap:
1108
A dictionary mapping revision ids from the target branch
1109
to new revision ids in the target branch, for each
1110
revision that was pushed.
1112
inter = InterBranch.get(self, target)
1113
lossy_push = getattr(inter, "lossy_push", None)
1114
if lossy_push is None:
1115
raise errors.LossyPushToSameVCS(self, target)
1116
return lossy_push(stop_revision)
1129
1118
def basis_tree(self):
1130
1119
"""Return `Tree` object for last revision."""
1543
1528
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 if_present_fetch
1556
must_fetch = set([self.last_revision()])
1557
if_present_fetch = set()
1558
c = self.get_config()
1559
include_tags = c.get_user_option_as_bool('branch.fetch_tags',
1563
if_present_fetch = set(self.tags.get_reverse_tag_dict())
1564
except errors.TagsNotSupported:
1566
must_fetch.discard(_mod_revision.NULL_REVISION)
1567
if_present_fetch.discard(_mod_revision.NULL_REVISION)
1568
return must_fetch, if_present_fetch
1571
class BranchFormat(controldir.ControlComponentFormat):
1531
class BranchFormat(object):
1572
1532
"""An encapsulation of the initialization and open routines for a format.
1574
1534
Formats provide three things:
1599
1567
transport = a_bzrdir.get_branch_transport(None, name=name)
1600
1568
format_string = transport.get_bytes("format")
1601
return format_registry.get(format_string)
1569
format = klass._formats[format_string]
1570
if isinstance(format, MetaDirBranchFormatFactory):
1602
1573
except errors.NoSuchFile:
1603
1574
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1604
1575
except KeyError:
1605
1576
raise errors.UnknownFormatError(format=format_string, kind='branch')
1608
@deprecated_method(deprecated_in((2, 4, 0)))
1609
1579
def get_default_format(klass):
1610
1580
"""Return the current default format."""
1611
return format_registry.get_default()
1581
return klass._default_format
1614
@deprecated_method(deprecated_in((2, 4, 0)))
1615
1584
def get_formats(klass):
1616
1585
"""Get all the known formats.
1618
1587
Warning: This triggers a load of all lazy registered formats: do not
1619
1588
use except when that is desireed.
1621
return format_registry._get_all()
1591
for fmt in klass._formats.values():
1592
if isinstance(fmt, MetaDirBranchFormatFactory):
1623
1597
def get_reference(self, a_bzrdir, name=None):
1624
1598
"""Get the target reference of the branch in a_bzrdir.
1663
1637
for hook in hooks:
1666
def initialize(self, a_bzrdir, name=None, repository=None,
1667
append_revisions_only=None):
1640
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1641
repository=None, lock_type='metadir',
1643
"""Initialize a branch in a bzrdir, with specified files
1645
:param a_bzrdir: The bzrdir to initialize the branch in
1646
:param utf8_files: The files to create as a list of
1647
(filename, content) tuples
1648
:param name: Name of colocated branch to create, if any
1649
:param set_format: If True, set the format with
1650
self.get_format_string. (BzrBranch4 has its format set
1652
:return: a branch in this format
1654
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1655
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1657
'metadir': ('lock', lockdir.LockDir),
1658
'branch4': ('branch-lock', lockable_files.TransportLock),
1660
lock_name, lock_class = lock_map[lock_type]
1661
control_files = lockable_files.LockableFiles(branch_transport,
1662
lock_name, lock_class)
1663
control_files.create_lock()
1665
control_files.lock_write()
1666
except errors.LockContention:
1667
if lock_type != 'branch4':
1673
utf8_files += [('format', self.get_format_string())]
1675
for (filename, content) in utf8_files:
1676
branch_transport.put_bytes(
1678
mode=a_bzrdir._get_file_mode())
1681
control_files.unlock()
1682
branch = self.open(a_bzrdir, name, _found=True,
1683
found_repository=repository)
1684
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1687
def initialize(self, a_bzrdir, name=None, repository=None):
1668
1688
"""Create a branch of this format in a_bzrdir.
1670
1690
:param name: Name of the colocated branch to create.
1718
1738
raise NotImplementedError(self.open)
1721
@deprecated_method(deprecated_in((2, 4, 0)))
1722
1741
def register_format(klass, format):
1723
1742
"""Register a metadir format.
1725
1744
See MetaDirBranchFormatFactory for the ability to register a format
1726
1745
without loading the code the format needs until it is actually used.
1728
format_registry.register(format)
1747
klass._formats[format.get_format_string()] = format
1748
# Metadir formats have a network name of their format string, and get
1749
# registered as factories.
1750
if isinstance(format, MetaDirBranchFormatFactory):
1751
network_format_registry.register(format.get_format_string(), format)
1753
network_format_registry.register(format.get_format_string(),
1731
@deprecated_method(deprecated_in((2, 4, 0)))
1732
1757
def set_default_format(klass, format):
1733
format_registry.set_default(format)
1758
klass._default_format = format
1735
1760
def supports_set_append_revisions_only(self):
1736
1761
"""True if this format supports set_append_revisions_only."""
1806
1818
These are all empty initially, because by default nothing should get
1809
Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1810
self.add_hook('set_rh',
1821
Hooks.__init__(self)
1822
self.create_hook(HookPoint('set_rh',
1811
1823
"Invoked whenever the revision history has been set via "
1812
1824
"set_revision_history. The api signature is (branch, "
1813
1825
"revision_history), and the branch will be write-locked. "
1814
1826
"The set_rh hook can be expensive for bzr to trigger, a better "
1815
"hook to use is Branch.post_change_branch_tip.", (0, 15))
1816
self.add_hook('open',
1827
"hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1828
self.create_hook(HookPoint('open',
1817
1829
"Called with the Branch object that has been opened after a "
1818
"branch is opened.", (1, 8))
1819
self.add_hook('post_push',
1830
"branch is opened.", (1, 8), None))
1831
self.create_hook(HookPoint('post_push',
1820
1832
"Called after a push operation completes. post_push is called "
1821
1833
"with a bzrlib.branch.BranchPushResult object and only runs in the "
1822
"bzr client.", (0, 15))
1823
self.add_hook('post_pull',
1834
"bzr client.", (0, 15), None))
1835
self.create_hook(HookPoint('post_pull',
1824
1836
"Called after a pull operation completes. post_pull is called "
1825
1837
"with a bzrlib.branch.PullResult object and only runs in the "
1826
"bzr client.", (0, 15))
1827
self.add_hook('pre_commit',
1838
"bzr client.", (0, 15), None))
1839
self.create_hook(HookPoint('pre_commit',
1828
1840
"Called after a commit is calculated but before it is "
1829
1841
"completed. pre_commit is called with (local, master, old_revno, "
1830
1842
"old_revid, future_revno, future_revid, tree_delta, future_tree"
1833
1845
"basis revision. hooks MUST NOT modify this delta. "
1834
1846
" future_tree is an in-memory tree obtained from "
1835
1847
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1837
self.add_hook('post_commit',
1848
"tree.", (0,91), None))
1849
self.create_hook(HookPoint('post_commit',
1838
1850
"Called in the bzr client after a commit has completed. "
1839
1851
"post_commit is called with (local, master, old_revno, old_revid, "
1840
1852
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1841
"commit to a branch.", (0, 15))
1842
self.add_hook('post_uncommit',
1853
"commit to a branch.", (0, 15), None))
1854
self.create_hook(HookPoint('post_uncommit',
1843
1855
"Called in the bzr client after an uncommit completes. "
1844
1856
"post_uncommit is called with (local, master, old_revno, "
1845
1857
"old_revid, new_revno, new_revid) where local is the local branch "
1846
1858
"or None, master is the target branch, and an empty branch "
1847
"receives new_revno of 0, new_revid of None.", (0, 15))
1848
self.add_hook('pre_change_branch_tip',
1859
"receives new_revno of 0, new_revid of None.", (0, 15), None))
1860
self.create_hook(HookPoint('pre_change_branch_tip',
1849
1861
"Called in bzr client and server before a change to the tip of a "
1850
1862
"branch is made. pre_change_branch_tip is called with a "
1851
1863
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1852
"commit, uncommit will all trigger this hook.", (1, 6))
1853
self.add_hook('post_change_branch_tip',
1864
"commit, uncommit will all trigger this hook.", (1, 6), None))
1865
self.create_hook(HookPoint('post_change_branch_tip',
1854
1866
"Called in bzr client and server after a change to the tip of a "
1855
1867
"branch is made. post_change_branch_tip is called with a "
1856
1868
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1857
"commit, uncommit will all trigger this hook.", (1, 4))
1858
self.add_hook('transform_fallback_location',
1869
"commit, uncommit will all trigger this hook.", (1, 4), None))
1870
self.create_hook(HookPoint('transform_fallback_location',
1859
1871
"Called when a stacked branch is activating its fallback "
1860
1872
"locations. transform_fallback_location is called with (branch, "
1861
1873
"url), and should return a new url. Returning the same url "
1866
1878
"fallback locations have not been activated. When there are "
1867
1879
"multiple hooks installed for transform_fallback_location, "
1868
1880
"all are called with the url returned from the previous hook."
1869
"The order is however undefined.", (1, 9))
1870
self.add_hook('automatic_tag_name',
1881
"The order is however undefined.", (1, 9), None))
1882
self.create_hook(HookPoint('automatic_tag_name',
1871
1883
"Called to determine an automatic tag name for a revision. "
1872
1884
"automatic_tag_name is called with (branch, revision_id) and "
1873
1885
"should return a tag name or None if no tag name could be "
1874
1886
"determined. The first non-None tag name returned will be used.",
1876
self.add_hook('post_branch_init',
1888
self.create_hook(HookPoint('post_branch_init',
1877
1889
"Called after new branch initialization completes. "
1878
1890
"post_branch_init is called with a "
1879
1891
"bzrlib.branch.BranchInitHookParams. "
1880
1892
"Note that init, branch and checkout (both heavyweight and "
1881
"lightweight) will all trigger this hook.", (2, 2))
1882
self.add_hook('post_switch',
1893
"lightweight) will all trigger this hook.", (2, 2), None))
1894
self.create_hook(HookPoint('post_switch',
1883
1895
"Called after a checkout switches branch. "
1884
1896
"post_switch is called with a "
1885
"bzrlib.branch.SwitchHookParams.", (2, 2))
1897
"bzrlib.branch.SwitchHookParams.", (2, 2), None))
2001
2013
self.revision_id)
2016
class BzrBranchFormat4(BranchFormat):
2017
"""Bzr branch format 4.
2020
- a revision-history file.
2021
- a branch-lock lock file [ to be shared with the bzrdir ]
2024
def get_format_description(self):
2025
"""See BranchFormat.get_format_description()."""
2026
return "Branch format 4"
2028
def initialize(self, a_bzrdir, name=None, repository=None):
2029
"""Create a branch of this format in a_bzrdir."""
2030
if repository is not None:
2031
raise NotImplementedError(
2032
"initialize(repository=<not None>) on %r" % (self,))
2033
utf8_files = [('revision-history', ''),
2034
('branch-name', ''),
2036
return self._initialize_helper(a_bzrdir, utf8_files, name=name,
2037
lock_type='branch4', set_format=False)
2040
super(BzrBranchFormat4, self).__init__()
2041
self._matchingbzrdir = bzrdir.BzrDirFormat6()
2043
def network_name(self):
2044
"""The network name for this format is the control dirs disk label."""
2045
return self._matchingbzrdir.get_format_string()
2047
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2048
found_repository=None):
2049
"""See BranchFormat.open()."""
2051
# we are being called directly and must probe.
2052
raise NotImplementedError
2053
if found_repository is None:
2054
found_repository = a_bzrdir.open_repository()
2055
return BzrBranchPreSplitOut(_format=self,
2056
_control_files=a_bzrdir._control_files,
2059
_repository=found_repository)
2062
return "Bazaar-NG branch format 4"
2004
2065
class BranchFormatMetadir(BranchFormat):
2005
2066
"""Common logic for meta-dir based branch formats."""
2008
2069
"""What class to instantiate on open calls."""
2009
2070
raise NotImplementedError(self._branch_class)
2011
def _get_initial_config(self, append_revisions_only=None):
2012
if append_revisions_only:
2013
return "append_revisions_only = True\n"
2015
# Avoid writing anything if append_revisions_only is disabled,
2016
# as that is the default.
2019
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
2021
"""Initialize a branch in a bzrdir, with specified files
2023
:param a_bzrdir: The bzrdir to initialize the branch in
2024
:param utf8_files: The files to create as a list of
2025
(filename, content) tuples
2026
:param name: Name of colocated branch to create, if any
2027
:return: a branch in this format
2029
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
2030
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2031
control_files = lockable_files.LockableFiles(branch_transport,
2032
'lock', lockdir.LockDir)
2033
control_files.create_lock()
2034
control_files.lock_write()
2036
utf8_files += [('format', self.get_format_string())]
2037
for (filename, content) in utf8_files:
2038
branch_transport.put_bytes(
2040
mode=a_bzrdir._get_file_mode())
2042
control_files.unlock()
2043
branch = self.open(a_bzrdir, name, _found=True,
2044
found_repository=repository)
2045
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2048
2072
def network_name(self):
2049
2073
"""A simple byte string uniquely identifying this format for RPC calls.
2147
2165
"""See BranchFormat.get_format_description()."""
2148
2166
return "Branch format 6"
2150
def initialize(self, a_bzrdir, name=None, repository=None,
2151
append_revisions_only=None):
2168
def initialize(self, a_bzrdir, name=None, repository=None):
2152
2169
"""Create a branch of this format in a_bzrdir."""
2153
2170
utf8_files = [('last-revision', '0 null:\n'),
2155
self._get_initial_config(append_revisions_only)),
2171
('branch.conf', ''),
2158
2174
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2160
2176
def make_tags(self, branch):
2161
2177
"""See bzrlib.branch.BranchFormat.make_tags()."""
2162
return _mod_tag.BasicTags(branch)
2178
return BasicTags(branch)
2164
2180
def supports_set_append_revisions_only(self):
2179
2195
"""See BranchFormat.get_format_description()."""
2180
2196
return "Branch format 8"
2182
def initialize(self, a_bzrdir, name=None, repository=None,
2183
append_revisions_only=None):
2198
def initialize(self, a_bzrdir, name=None, repository=None):
2184
2199
"""Create a branch of this format in a_bzrdir."""
2185
2200
utf8_files = [('last-revision', '0 null:\n'),
2187
self._get_initial_config(append_revisions_only)),
2201
('branch.conf', ''),
2189
2203
('references', '')
2191
2205
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2208
super(BzrBranchFormat8, self).__init__()
2209
self._matchingbzrdir.repository_format = \
2210
RepositoryFormatKnitPack5RichRoot()
2193
2212
def make_tags(self, branch):
2194
2213
"""See bzrlib.branch.BranchFormat.make_tags()."""
2195
return _mod_tag.BasicTags(branch)
2214
return BasicTags(branch)
2197
2216
def supports_set_append_revisions_only(self):
2276
2286
location = transport.put_bytes('location', to_branch.base)
2278
2288
def initialize(self, a_bzrdir, name=None, target_branch=None,
2279
repository=None, append_revisions_only=None):
2280
2290
"""Create a branch of this format in a_bzrdir."""
2281
2291
if target_branch is None:
2282
2292
# this format does not implement branch itself, thus the implicit
2283
2293
# creation contract must see it as uninitializable
2284
2294
raise errors.UninitializableFormat(self)
2285
2295
mutter('creating branch reference in %s', a_bzrdir.user_url)
2286
if a_bzrdir._format.fixed_components:
2287
raise errors.IncompatibleFormat(self, a_bzrdir._format)
2288
2296
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2289
2297
branch_transport.put_bytes('location',
2290
2298
target_branch.bzrdir.user_url)
2381
2373
__format6 = BzrBranchFormat6()
2382
2374
__format7 = BzrBranchFormat7()
2383
2375
__format8 = BzrBranchFormat8()
2384
format_registry.register(__format5)
2385
format_registry.register(BranchReferenceFormat())
2386
format_registry.register(__format6)
2387
format_registry.register(__format7)
2388
format_registry.register(__format8)
2389
format_registry.set_default(__format7)
2376
BranchFormat.register_format(__format5)
2377
BranchFormat.register_format(BranchReferenceFormat())
2378
BranchFormat.register_format(__format6)
2379
BranchFormat.register_format(__format7)
2380
BranchFormat.register_format(__format8)
2381
BranchFormat.set_default_format(__format7)
2382
_legacy_formats = [BzrBranchFormat4(),
2384
network_format_registry.register(
2385
_legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2392
2388
class BranchWriteLockResult(LogicalLockResult):
2540
2536
"""See Branch.print_file."""
2541
2537
return self.repository.print_file(file, revision_id)
2539
def _write_revision_history(self, history):
2540
"""Factored out of set_revision_history.
2542
This performs the actual writing to disk.
2543
It is intended to be called by BzrBranch5.set_revision_history."""
2544
self._transport.put_bytes(
2545
'revision-history', '\n'.join(history),
2546
mode=self.bzrdir._get_file_mode())
2549
def set_revision_history(self, rev_history):
2550
"""See Branch.set_revision_history."""
2551
if 'evil' in debug.debug_flags:
2552
mutter_callsite(3, "set_revision_history scales with history.")
2553
check_not_reserved_id = _mod_revision.check_not_reserved_id
2554
for rev_id in rev_history:
2555
check_not_reserved_id(rev_id)
2556
if Branch.hooks['post_change_branch_tip']:
2557
# Don't calculate the last_revision_info() if there are no hooks
2559
old_revno, old_revid = self.last_revision_info()
2560
if len(rev_history) == 0:
2561
revid = _mod_revision.NULL_REVISION
2563
revid = rev_history[-1]
2564
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2565
self._write_revision_history(rev_history)
2566
self._clear_cached_state()
2567
self._cache_revision_history(rev_history)
2568
for hook in Branch.hooks['set_rh']:
2569
hook(self, rev_history)
2570
if Branch.hooks['post_change_branch_tip']:
2571
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2573
def _synchronize_history(self, destination, revision_id):
2574
"""Synchronize last revision and revision history between branches.
2576
This version is most efficient when the destination is also a
2577
BzrBranch5, but works for BzrBranch6 as long as the revision
2578
history is the true lefthand parent history, and all of the revisions
2579
are in the destination's repository. If not, set_revision_history
2582
:param destination: The branch to copy the history into
2583
:param revision_id: The revision-id to truncate history at. May
2584
be None to copy complete history.
2586
if not isinstance(destination._format, BzrBranchFormat5):
2587
super(BzrBranch, self)._synchronize_history(
2588
destination, revision_id)
2590
if revision_id == _mod_revision.NULL_REVISION:
2593
new_history = self.revision_history()
2594
if revision_id is not None and new_history != []:
2596
new_history = new_history[:new_history.index(revision_id) + 1]
2598
rev = self.repository.get_revision(revision_id)
2599
new_history = rev.get_history(self.repository)[1:]
2600
destination.set_revision_history(new_history)
2543
2602
@needs_write_lock
2544
2603
def set_last_revision_info(self, revno, revision_id):
2545
if not revision_id or not isinstance(revision_id, basestring):
2546
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2604
"""Set the last revision of this branch.
2606
The caller is responsible for checking that the revno is correct
2607
for this revision id.
2609
It may be possible to set the branch last revision to an id not
2610
present in the repository. However, branches can also be
2611
configured to check constraints on history, in which case this may not
2547
2614
revision_id = _mod_revision.ensure_null(revision_id)
2548
old_revno, old_revid = self.last_revision_info()
2549
if self.get_append_revisions_only():
2550
self._check_history_violation(revision_id)
2551
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2552
self._write_last_revision_info(revno, revision_id)
2553
self._clear_cached_state()
2554
self._last_revision_info_cache = revno, revision_id
2555
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2615
# this old format stores the full history, but this api doesn't
2616
# provide it, so we must generate, and might as well check it's
2618
history = self._lefthand_history(revision_id)
2619
if len(history) != revno:
2620
raise AssertionError('%d != %d' % (len(history), revno))
2621
self.set_revision_history(history)
2623
def _gen_revision_history(self):
2624
history = self._transport.get_bytes('revision-history').split('\n')
2625
if history[-1:] == ['']:
2626
# There shouldn't be a trailing newline, but just in case.
2631
def generate_revision_history(self, revision_id, last_rev=None,
2633
"""Create a new revision history that will finish with revision_id.
2635
:param revision_id: the new tip to use.
2636
:param last_rev: The previous last_revision. If not None, then this
2637
must be a ancestory of revision_id, or DivergedBranches is raised.
2638
:param other_branch: The other branch that DivergedBranches should
2639
raise with respect to.
2641
self.set_revision_history(self._lefthand_history(revision_id,
2642
last_rev, other_branch))
2557
2644
def basis_tree(self):
2558
2645
"""See Branch.basis_tree."""
2657
def _basic_push(self, target, overwrite, stop_revision):
2658
"""Basic implementation of push without bound branches or hooks.
2660
Must be called with source read locked and target write locked.
2662
result = BranchPushResult()
2663
result.source_branch = self
2664
result.target_branch = target
2665
result.old_revno, result.old_revid = target.last_revision_info()
2666
self.update_references(target)
2667
if result.old_revid != self.last_revision():
2668
# We assume that during 'push' this repository is closer than
2670
graph = self.repository.get_graph(target.repository)
2671
target.update_revisions(self, stop_revision,
2672
overwrite=overwrite, graph=graph)
2673
if self._push_should_merge_tags():
2674
result.tag_conflicts = self.tags.merge_to(target.tags,
2676
result.new_revno, result.new_revid = target.last_revision_info()
2570
2679
def get_stacked_on_url(self):
2571
2680
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2583
2692
self._transport.put_bytes('parent', url + '\n',
2584
2693
mode=self.bzrdir._get_file_mode())
2696
class BzrBranchPreSplitOut(BzrBranch):
2698
def _get_checkout_format(self):
2699
"""Return the most suitable metadir for a checkout of this branch.
2700
Weaves are used if this branch's repository uses weaves.
2702
from bzrlib.repofmt.weaverepo import RepositoryFormat7
2703
from bzrlib.bzrdir import BzrDirMetaFormat1
2704
format = BzrDirMetaFormat1()
2705
format.repository_format = RepositoryFormat7()
2709
class BzrBranch5(BzrBranch):
2710
"""A format 5 branch. This supports new features over plain branches.
2712
It has support for a master_branch which is the data for bound branches.
2715
def get_bound_location(self):
2717
return self._transport.get_bytes('bound')[:-1]
2718
except errors.NoSuchFile:
2722
def get_master_branch(self, possible_transports=None):
2723
"""Return the branch we are bound to.
2725
:return: Either a Branch, or None
2727
This could memoise the branch, but if thats done
2728
it must be revalidated on each new lock.
2729
So for now we just don't memoise it.
2730
# RBC 20060304 review this decision.
2732
bound_loc = self.get_bound_location()
2736
return Branch.open(bound_loc,
2737
possible_transports=possible_transports)
2738
except (errors.NotBranchError, errors.ConnectionError), e:
2739
raise errors.BoundBranchConnectionFailure(
2586
2742
@needs_write_lock
2588
"""If bound, unbind"""
2589
return self.set_bound_location(None)
2743
def set_bound_location(self, location):
2744
"""Set the target where this branch is bound to.
2746
:param location: URL to the target branch
2749
self._transport.put_bytes('bound', location+'\n',
2750
mode=self.bzrdir._get_file_mode())
2753
self._transport.delete('bound')
2754
except errors.NoSuchFile:
2591
2758
@needs_write_lock
2592
2759
def bind(self, other):
2614
2781
# history around
2615
2782
self.set_bound_location(other.base)
2617
def get_bound_location(self):
2619
return self._transport.get_bytes('bound')[:-1]
2620
except errors.NoSuchFile:
2624
def get_master_branch(self, possible_transports=None):
2625
"""Return the branch we are bound to.
2627
:return: Either a Branch, or None
2629
if self._master_branch_cache is None:
2630
self._master_branch_cache = self._get_master_branch(
2631
possible_transports)
2632
return self._master_branch_cache
2634
def _get_master_branch(self, possible_transports):
2635
bound_loc = self.get_bound_location()
2639
return Branch.open(bound_loc,
2640
possible_transports=possible_transports)
2641
except (errors.NotBranchError, errors.ConnectionError), e:
2642
raise errors.BoundBranchConnectionFailure(
2645
2784
@needs_write_lock
2646
def set_bound_location(self, location):
2647
"""Set the target where this branch is bound to.
2649
:param location: URL to the target branch
2651
self._master_branch_cache = None
2653
self._transport.put_bytes('bound', location+'\n',
2654
mode=self.bzrdir._get_file_mode())
2657
self._transport.delete('bound')
2658
except errors.NoSuchFile:
2786
"""If bound, unbind"""
2787
return self.set_bound_location(None)
2662
2789
@needs_write_lock
2663
2790
def update(self, possible_transports=None):
2679
def _read_last_revision_info(self):
2680
revision_string = self._transport.get_bytes('last-revision')
2681
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2682
revision_id = cache_utf8.get_cached_utf8(revision_id)
2684
return revno, revision_id
2686
def _write_last_revision_info(self, revno, revision_id):
2687
"""Simply write out the revision id, with no checks.
2689
Use set_last_revision_info to perform this safely.
2691
Does not update the revision_history cache.
2693
revision_id = _mod_revision.ensure_null(revision_id)
2694
out_string = '%d %s\n' % (revno, revision_id)
2695
self._transport.put_bytes('last-revision', out_string,
2696
mode=self.bzrdir._get_file_mode())
2699
class FullHistoryBzrBranch(BzrBranch):
2700
"""Bzr branch which contains the full revision history."""
2703
def set_last_revision_info(self, revno, revision_id):
2704
if not revision_id or not isinstance(revision_id, basestring):
2705
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2706
revision_id = _mod_revision.ensure_null(revision_id)
2707
# this old format stores the full history, but this api doesn't
2708
# provide it, so we must generate, and might as well check it's
2710
history = self._lefthand_history(revision_id)
2711
if len(history) != revno:
2712
raise AssertionError('%d != %d' % (len(history), revno))
2713
self._set_revision_history(history)
2715
def _read_last_revision_info(self):
2716
rh = self.revision_history()
2719
return (revno, rh[-1])
2721
return (0, _mod_revision.NULL_REVISION)
2723
@deprecated_method(deprecated_in((2, 4, 0)))
2725
def set_revision_history(self, rev_history):
2726
"""See Branch.set_revision_history."""
2727
self._set_revision_history(rev_history)
2729
def _set_revision_history(self, rev_history):
2730
if 'evil' in debug.debug_flags:
2731
mutter_callsite(3, "set_revision_history scales with history.")
2732
check_not_reserved_id = _mod_revision.check_not_reserved_id
2733
for rev_id in rev_history:
2734
check_not_reserved_id(rev_id)
2735
if Branch.hooks['post_change_branch_tip']:
2736
# Don't calculate the last_revision_info() if there are no hooks
2738
old_revno, old_revid = self.last_revision_info()
2739
if len(rev_history) == 0:
2740
revid = _mod_revision.NULL_REVISION
2742
revid = rev_history[-1]
2743
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2744
self._write_revision_history(rev_history)
2745
self._clear_cached_state()
2746
self._cache_revision_history(rev_history)
2747
for hook in Branch.hooks['set_rh']:
2748
hook(self, rev_history)
2749
if Branch.hooks['post_change_branch_tip']:
2750
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2752
def _write_revision_history(self, history):
2753
"""Factored out of set_revision_history.
2755
This performs the actual writing to disk.
2756
It is intended to be called by set_revision_history."""
2757
self._transport.put_bytes(
2758
'revision-history', '\n'.join(history),
2759
mode=self.bzrdir._get_file_mode())
2761
def _gen_revision_history(self):
2762
history = self._transport.get_bytes('revision-history').split('\n')
2763
if history[-1:] == ['']:
2764
# There shouldn't be a trailing newline, but just in case.
2768
def _synchronize_history(self, destination, revision_id):
2769
if not isinstance(destination, FullHistoryBzrBranch):
2770
super(BzrBranch, self)._synchronize_history(
2771
destination, revision_id)
2773
if revision_id == _mod_revision.NULL_REVISION:
2776
new_history = self.revision_history()
2777
if revision_id is not None and new_history != []:
2779
new_history = new_history[:new_history.index(revision_id) + 1]
2781
rev = self.repository.get_revision(revision_id)
2782
new_history = rev.get_history(self.repository)[1:]
2783
destination._set_revision_history(new_history)
2786
def generate_revision_history(self, revision_id, last_rev=None,
2788
"""Create a new revision history that will finish with revision_id.
2790
:param revision_id: the new tip to use.
2791
:param last_rev: The previous last_revision. If not None, then this
2792
must be a ancestory of revision_id, or DivergedBranches is raised.
2793
:param other_branch: The other branch that DivergedBranches should
2794
raise with respect to.
2796
self._set_revision_history(self._lefthand_history(revision_id,
2797
last_rev, other_branch))
2800
class BzrBranch5(FullHistoryBzrBranch):
2801
"""A format 5 branch. This supports new features over plain branches.
2803
It has support for a master_branch which is the data for bound branches.
2807
class BzrBranch8(BzrBranch):
2807
class BzrBranch8(BzrBranch5):
2808
2808
"""A branch that stores tree-reference locations."""
2810
2810
def _open_hook(self):
2836
2836
self._last_revision_info_cache = None
2837
2837
self._reference_info = None
2839
def _last_revision_info(self):
2840
revision_string = self._transport.get_bytes('last-revision')
2841
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2842
revision_id = cache_utf8.get_cached_utf8(revision_id)
2844
return revno, revision_id
2846
def _write_last_revision_info(self, revno, revision_id):
2847
"""Simply write out the revision id, with no checks.
2849
Use set_last_revision_info to perform this safely.
2851
Does not update the revision_history cache.
2852
Intended to be called by set_last_revision_info and
2853
_write_revision_history.
2855
revision_id = _mod_revision.ensure_null(revision_id)
2856
out_string = '%d %s\n' % (revno, revision_id)
2857
self._transport.put_bytes('last-revision', out_string,
2858
mode=self.bzrdir._get_file_mode())
2861
def set_last_revision_info(self, revno, revision_id):
2862
revision_id = _mod_revision.ensure_null(revision_id)
2863
old_revno, old_revid = self.last_revision_info()
2864
if self._get_append_revisions_only():
2865
self._check_history_violation(revision_id)
2866
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2867
self._write_last_revision_info(revno, revision_id)
2868
self._clear_cached_state()
2869
self._last_revision_info_cache = revno, revision_id
2870
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2872
def _synchronize_history(self, destination, revision_id):
2873
"""Synchronize last revision and revision history between branches.
2875
:see: Branch._synchronize_history
2877
# XXX: The base Branch has a fast implementation of this method based
2878
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2879
# that uses set_revision_history. This class inherits from BzrBranch5,
2880
# but wants the fast implementation, so it calls
2881
# Branch._synchronize_history directly.
2882
Branch._synchronize_history(self, destination, revision_id)
2839
2884
def _check_history_violation(self, revision_id):
2840
current_revid = self.last_revision()
2841
last_revision = _mod_revision.ensure_null(current_revid)
2885
last_revision = _mod_revision.ensure_null(self.last_revision())
2842
2886
if _mod_revision.is_null(last_revision):
2844
graph = self.repository.get_graph()
2845
for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2846
if lh_ancestor == current_revid:
2848
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2888
if last_revision not in self._lefthand_history(revision_id):
2889
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2850
2891
def _gen_revision_history(self):
2851
2892
"""Generate the revision history from last revision
2982
3038
# you can always ask for the URL; but you might not be able to use it
2983
3039
# if the repo can't support stacking.
2984
3040
## self._check_stackable_repo()
2985
# stacked_on_location is only ever defined in branch.conf, so don't
2986
# waste effort reading the whole stack of config files.
2987
config = self.get_config()._get_branch_data_config()
2988
stacked_url = self._get_config_location('stacked_on_location',
3041
stacked_url = self._get_config_location('stacked_on_location')
2990
3042
if stacked_url is None:
2991
3043
raise errors.NotStacked(self)
2992
3044
return stacked_url
3046
def _get_append_revisions_only(self):
3047
return self.get_config(
3048
).get_user_option_as_bool('append_revisions_only')
3051
def generate_revision_history(self, revision_id, last_rev=None,
3053
"""See BzrBranch5.generate_revision_history"""
3054
history = self._lefthand_history(revision_id, last_rev, other_branch)
3055
revno = len(history)
3056
self.set_last_revision_info(revno, revision_id)
2994
3058
@needs_read_lock
2995
3059
def get_rev_id(self, revno, history=None):
2996
3060
"""Find the revision id of the specified revno."""
3137
3190
return self.new_revno - self.old_revno
3139
3192
def report(self, to_file):
3140
# TODO: This function gets passed a to_file, but then
3141
# ignores it and calls note() instead. This is also
3142
# inconsistent with PullResult(), which writes to stdout.
3143
# -- JRV20110901, bug #838853
3144
tag_conflicts = getattr(self, "tag_conflicts", None)
3145
tag_updates = getattr(self, "tag_updates", None)
3147
if self.old_revid != self.new_revid:
3148
note(gettext('Pushed up to revision %d.') % self.new_revno)
3150
note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
3151
if self.old_revid == self.new_revid and not tag_updates:
3152
if not tag_conflicts:
3153
note(gettext('No new revisions or tags to push.'))
3155
note(gettext('No new revisions to push.'))
3193
"""Write a human-readable description of the result."""
3194
if self.old_revid == self.new_revid:
3195
note('No new revisions to push.')
3197
note('Pushed up to revision %d.' % self.new_revno)
3156
3198
self._show_tag_conficts(to_file)
3229
3271
branch._transport.put_bytes('format', format.get_format_string())
3274
def _run_with_write_locked_target(target, callable, *args, **kwargs):
3275
"""Run ``callable(*args, **kwargs)``, write-locking target for the
3278
_run_with_write_locked_target will attempt to release the lock it acquires.
3280
If an exception is raised by callable, then that exception *will* be
3281
propagated, even if the unlock attempt raises its own error. Thus
3282
_run_with_write_locked_target should be preferred to simply doing::
3286
return callable(*args, **kwargs)
3291
# This is very similar to bzrlib.decorators.needs_write_lock. Perhaps they
3292
# should share code?
3295
result = callable(*args, **kwargs)
3297
exc_info = sys.exc_info()
3301
raise exc_info[0], exc_info[1], exc_info[2]
3232
3307
class InterBranch(InterObject):
3233
3308
"""This class represents operations taking place between two branches.
3262
3337
raise NotImplementedError(self.pull)
3264
3339
@needs_write_lock
3265
def push(self, overwrite=False, stop_revision=None, lossy=False,
3340
def update_revisions(self, stop_revision=None, overwrite=False,
3342
"""Pull in new perfect-fit revisions.
3344
:param stop_revision: Updated until the given revision
3345
:param overwrite: Always set the branch pointer, rather than checking
3346
to see if it is a proper descendant.
3347
:param graph: A Graph object that can be used to query history
3348
information. This can be None.
3351
raise NotImplementedError(self.update_revisions)
3354
def push(self, overwrite=False, stop_revision=None,
3266
3355
_override_hook_source_branch=None):
3267
3356
"""Mirror the source branch into the target branch.
3328
3408
self.source.tags.merge_to(self.target.tags)
3330
3410
@needs_write_lock
3331
def fetch(self, stop_revision=None, limit=None):
3332
if self.target.base == self.source.base:
3334
self.source.lock_read()
3336
fetch_spec_factory = fetch.FetchSpecFactory()
3337
fetch_spec_factory.source_branch = self.source
3338
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3339
fetch_spec_factory.source_repo = self.source.repository
3340
fetch_spec_factory.target_repo = self.target.repository
3341
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3342
fetch_spec_factory.limit = limit
3343
fetch_spec = fetch_spec_factory.make_fetch_spec()
3344
return self.target.repository.fetch(self.source.repository,
3345
fetch_spec=fetch_spec)
3347
self.source.unlock()
3350
def _update_revisions(self, stop_revision=None, overwrite=False,
3411
def update_revisions(self, stop_revision=None, overwrite=False,
3413
"""See InterBranch.update_revisions()."""
3352
3414
other_revno, other_last_revision = self.source.last_revision_info()
3353
3415
stop_revno = None # unknown
3354
3416
if stop_revision is None:
3427
3480
if master_branch:
3428
3481
master_branch.unlock()
3430
def push(self, overwrite=False, stop_revision=None, lossy=False,
3483
def push(self, overwrite=False, stop_revision=None,
3431
3484
_override_hook_source_branch=None):
3432
3485
"""See InterBranch.push.
3434
3487
This is the basic concrete implementation of push()
3436
:param _override_hook_source_branch: If specified, run the hooks
3437
passing this Branch as the source, rather than self. This is for
3438
use of RemoteBranch, where push is delegated to the underlying
3489
:param _override_hook_source_branch: If specified, run
3490
the hooks passing this Branch as the source, rather than self.
3491
This is for use of RemoteBranch, where push is delegated to the
3492
underlying vfs-based Branch.
3442
raise errors.LossyPushToSameVCS(self.source, self.target)
3443
3494
# TODO: Public option to disable running hooks - should be trivial but
3446
op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3447
op.add_cleanup(self.source.lock_read().unlock)
3448
op.add_cleanup(self.target.lock_write().unlock)
3449
return op.run(overwrite, stop_revision,
3450
_override_hook_source_branch=_override_hook_source_branch)
3452
def _basic_push(self, overwrite, stop_revision):
3453
"""Basic implementation of push without bound branches or hooks.
3455
Must be called with source read locked and target write locked.
3457
result = BranchPushResult()
3458
result.source_branch = self.source
3459
result.target_branch = self.target
3460
result.old_revno, result.old_revid = self.target.last_revision_info()
3461
self.source.update_references(self.target)
3462
if result.old_revid != stop_revision:
3463
# We assume that during 'push' this repository is closer than
3465
graph = self.source.repository.get_graph(self.target.repository)
3466
self._update_revisions(stop_revision, overwrite=overwrite,
3468
if self.source._push_should_merge_tags():
3469
result.tag_updates, result.tag_conflicts = (
3470
self.source.tags.merge_to(self.target.tags, overwrite))
3471
result.new_revno, result.new_revid = self.target.last_revision_info()
3474
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
3496
self.source.lock_read()
3498
return _run_with_write_locked_target(
3499
self.target, self._push_with_bound_branches, overwrite,
3501
_override_hook_source_branch=_override_hook_source_branch)
3503
self.source.unlock()
3505
def _push_with_bound_branches(self, overwrite, stop_revision,
3475
3506
_override_hook_source_branch=None):
3476
3507
"""Push from source into target, and into target's master if any.
3489
3520
# be bound to itself? -- mbp 20070507
3490
3521
master_branch = self.target.get_master_branch()
3491
3522
master_branch.lock_write()
3492
operation.add_cleanup(master_branch.unlock)
3493
# push into the master from the source branch.
3494
master_inter = InterBranch.get(self.source, master_branch)
3495
master_inter._basic_push(overwrite, stop_revision)
3496
# and push into the target branch from the source. Note that
3497
# we push from the source branch again, because it's considered
3498
# the highest bandwidth repository.
3499
result = self._basic_push(overwrite, stop_revision)
3500
result.master_branch = master_branch
3501
result.local_branch = self.target
3524
# push into the master from the source branch.
3525
self.source._basic_push(master_branch, overwrite, stop_revision)
3526
# and push into the target branch from the source. Note that we
3527
# push from the source branch again, because it's considered the
3528
# highest bandwidth repository.
3529
result = self.source._basic_push(self.target, overwrite,
3531
result.master_branch = master_branch
3532
result.local_branch = self.target
3536
master_branch.unlock()
3503
master_branch = None
3504
3538
# no master branch
3505
result = self._basic_push(overwrite, stop_revision)
3539
result = self.source._basic_push(self.target, overwrite,
3506
3541
# TODO: Why set master_branch and local_branch if there's no
3507
3542
# binding? Maybe cleaner to just leave them unset? -- mbp
3509
3544
result.master_branch = self.target
3510
3545
result.local_branch = None
3514
3549
def _pull(self, overwrite=False, stop_revision=None,
3515
3550
possible_transports=None, _hook_master=None, run_hooks=True,
3551
3584
# -- JRV20090506
3552
3585
result.old_revno, result.old_revid = \
3553
3586
self.target.last_revision_info()
3554
self._update_revisions(stop_revision, overwrite=overwrite,
3587
self.target.update_revisions(self.source, stop_revision,
3588
overwrite=overwrite, graph=graph)
3556
3589
# TODO: The old revid should be specified when merging tags,
3557
3590
# so a tags implementation that versions tags can only
3558
3591
# pull in the most recent changes. -- JRV20090506
3559
result.tag_updates, result.tag_conflicts = (
3560
self.source.tags.merge_to(self.target.tags, overwrite,
3561
ignore_master=not merge_tags_to_master))
3592
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3593
overwrite, ignore_master=not merge_tags_to_master)
3562
3594
result.new_revno, result.new_revid = self.target.last_revision_info()
3563
3595
if _hook_master:
3564
3596
result.master_branch = _hook_master