978
956
"""Return last revision id, or NULL_REVISION."""
979
957
return self.last_revision_info()[1]
981
960
def last_revision_info(self):
982
961
"""Return information about the last revision.
984
963
:return: A tuple (revno, revision_id).
986
with self.lock_read():
987
if self._last_revision_info_cache is None:
988
self._last_revision_info_cache = (
989
self._read_last_revision_info())
990
return self._last_revision_info_cache
992
def _read_last_revision_info(self):
993
raise NotImplementedError(self._read_last_revision_info)
995
def import_last_revision_info_and_tags(self, source, revno, revid,
965
if self._last_revision_info_cache is None:
966
self._last_revision_info_cache = self._last_revision_info()
967
return self._last_revision_info_cache
969
def _last_revision_info(self):
970
rh = self.revision_history()
973
return (revno, rh[-1])
975
return (0, _mod_revision.NULL_REVISION)
977
@deprecated_method(deprecated_in((1, 6, 0)))
978
def missing_revisions(self, other, stop_revision=None):
979
"""Return a list of new revisions that would perfectly fit.
981
If self and other have not diverged, return a list of the revisions
982
present in other, but missing from self.
984
self_history = self.revision_history()
985
self_len = len(self_history)
986
other_history = other.revision_history()
987
other_len = len(other_history)
988
common_index = min(self_len, other_len) -1
989
if common_index >= 0 and \
990
self_history[common_index] != other_history[common_index]:
991
raise errors.DivergedBranches(self, other)
993
if stop_revision is None:
994
stop_revision = other_len
996
if stop_revision > other_len:
997
raise errors.NoSuchRevision(self, stop_revision)
998
return other_history[self_len:stop_revision]
1000
def update_revisions(self, other, stop_revision=None, overwrite=False,
1002
"""Pull in new perfect-fit revisions.
1004
:param other: Another Branch to pull from
1005
:param stop_revision: Updated until the given revision
1006
:param overwrite: Always set the branch pointer, rather than checking
1007
to see if it is a proper descendant.
1008
:param graph: A Graph object that can be used to query history
1009
information. This can be None.
1012
return InterBranch.get(other, self).update_revisions(stop_revision,
1015
def import_last_revision_info(self, source_repo, revno, revid):
997
1016
"""Set the last revision info, importing from another repo if necessary.
999
1018
This is used by the bound branch code to upload a revision to
1000
1019
the master branch first before updating the tip of the local branch.
1001
Revisions referenced by source's tags are also transferred.
1003
:param source: Source branch to optionally fetch from
1021
:param source_repo: Source repository to optionally fetch from
1004
1022
:param revno: Revision number of the new tip
1005
1023
:param revid: Revision id of the new tip
1006
:param lossy: Whether to discard metadata that can not be
1007
natively represented
1008
:return: Tuple with the new revision number and revision id
1009
(should only be different from the arguments when lossy=True)
1011
if not self.repository.has_same_location(source.repository):
1012
self.fetch(source, revid)
1025
if not self.repository.has_same_location(source_repo):
1026
self.repository.fetch(source_repo, revision_id=revid)
1013
1027
self.set_last_revision_info(revno, revid)
1014
return (revno, revid)
1016
1029
def revision_id_to_revno(self, revision_id):
1017
1030
"""Given a revision id, return its revno"""
1018
1031
if _mod_revision.is_null(revision_id):
1020
history = self._revision_history()
1033
history = self.revision_history()
1022
1035
return history.index(revision_id) + 1
1023
1036
except ValueError:
1024
1037
raise errors.NoSuchRevision(self, revision_id)
1026
1040
def get_rev_id(self, revno, history=None):
1027
1041
"""Find the revision id of the specified revno."""
1028
with self.lock_read():
1030
return _mod_revision.NULL_REVISION
1031
last_revno, last_revid = self.last_revision_info()
1032
if revno == last_revno:
1034
if revno <= 0 or revno > last_revno:
1035
raise errors.NoSuchRevision(self, revno)
1036
distance_from_last = last_revno - revno
1037
if len(self._partial_revision_history_cache) <= distance_from_last:
1038
self._extend_partial_history(distance_from_last)
1039
return self._partial_revision_history_cache[distance_from_last]
1043
return _mod_revision.NULL_REVISION
1044
last_revno, last_revid = self.last_revision_info()
1045
if revno == last_revno:
1047
if revno <= 0 or revno > last_revno:
1048
raise errors.NoSuchRevision(self, revno)
1049
distance_from_last = last_revno - revno
1050
if len(self._partial_revision_history_cache) <= distance_from_last:
1051
self._extend_partial_history(distance_from_last)
1052
return self._partial_revision_history_cache[distance_from_last]
1041
1054
def pull(self, source, overwrite=False, stop_revision=None,
1042
1055
possible_transports=None, *args, **kwargs):
1513
1526
object will be created every time regardless.
1529
_default_format = None
1530
"""The default format used for new branches."""
1533
"""The known formats."""
1535
can_set_append_revisions_only = True
1516
1537
def __eq__(self, other):
1517
1538
return self.__class__ is other.__class__
1519
1540
def __ne__(self, other):
1520
1541
return not (self == other)
1522
def get_reference(self, controldir, name=None):
1523
"""Get the target reference of the branch in controldir.
1544
def find_format(klass, a_bzrdir, name=None):
1545
"""Return the format for the branch object in a_bzrdir."""
1547
transport = a_bzrdir.get_branch_transport(None, name=name)
1548
format_string = transport.get_bytes("format")
1549
format = klass._formats[format_string]
1550
if isinstance(format, MetaDirBranchFormatFactory):
1553
except errors.NoSuchFile:
1554
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1556
raise errors.UnknownFormatError(format=format_string, kind='branch')
1559
def get_default_format(klass):
1560
"""Return the current default format."""
1561
return klass._default_format
1564
def get_formats(klass):
1565
"""Get all the known formats.
1567
Warning: This triggers a load of all lazy registered formats: do not
1568
use except when that is desireed.
1571
for fmt in klass._formats.values():
1572
if isinstance(fmt, MetaDirBranchFormatFactory):
1577
def get_reference(self, a_bzrdir, name=None):
1578
"""Get the target reference of the branch in a_bzrdir.
1525
1580
format probing must have been completed before calling
1526
1581
this method - it is assumed that the format of the branch
1527
in controldir is correct.
1582
in a_bzrdir is correct.
1529
:param controldir: The controldir to get the branch data from.
1584
:param a_bzrdir: The bzrdir to get the branch data from.
1530
1585
:param name: Name of the colocated branch to fetch
1531
1586
:return: None if the branch is not a reference branch.
1536
def set_reference(self, controldir, name, to_branch):
1537
"""Set the target reference of the branch in controldir.
1591
def set_reference(self, a_bzrdir, name, to_branch):
1592
"""Set the target reference of the branch in a_bzrdir.
1539
1594
format probing must have been completed before calling
1540
1595
this method - it is assumed that the format of the branch
1541
in controldir is correct.
1596
in a_bzrdir is correct.
1543
:param controldir: The controldir to set the branch reference for.
1598
:param a_bzrdir: The bzrdir to set the branch reference for.
1544
1599
:param name: Name of colocated branch to set, None for default
1545
1600
:param to_branch: branch that the checkout is to reference
1547
1602
raise NotImplementedError(self.set_reference)
1604
def get_format_string(self):
1605
"""Return the ASCII format string that identifies this format."""
1606
raise NotImplementedError(self.get_format_string)
1549
1608
def get_format_description(self):
1550
1609
"""Return the short format description for this format."""
1551
1610
raise NotImplementedError(self.get_format_description)
1553
def _run_post_branch_init_hooks(self, controldir, name, branch):
1612
def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
1554
1613
hooks = Branch.hooks['post_branch_init']
1557
params = BranchInitHookParams(self, controldir, name, branch)
1616
params = BranchInitHookParams(self, a_bzrdir, name, branch)
1558
1617
for hook in hooks:
1561
def initialize(self, controldir, name=None, repository=None,
1562
append_revisions_only=None):
1563
"""Create a branch of this format in controldir.
1620
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1621
lock_type='metadir', set_format=True):
1622
"""Initialize a branch in a bzrdir, with specified files
1624
:param a_bzrdir: The bzrdir to initialize the branch in
1625
:param utf8_files: The files to create as a list of
1626
(filename, content) tuples
1627
:param name: Name of colocated branch to create, if any
1628
:param set_format: If True, set the format with
1629
self.get_format_string. (BzrBranch4 has its format set
1631
:return: a branch in this format
1633
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1634
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1636
'metadir': ('lock', lockdir.LockDir),
1637
'branch4': ('branch-lock', lockable_files.TransportLock),
1639
lock_name, lock_class = lock_map[lock_type]
1640
control_files = lockable_files.LockableFiles(branch_transport,
1641
lock_name, lock_class)
1642
control_files.create_lock()
1644
control_files.lock_write()
1645
except errors.LockContention:
1646
if lock_type != 'branch4':
1652
utf8_files += [('format', self.get_format_string())]
1654
for (filename, content) in utf8_files:
1655
branch_transport.put_bytes(
1657
mode=a_bzrdir._get_file_mode())
1660
control_files.unlock()
1661
branch = self.open(a_bzrdir, name, _found=True)
1662
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1665
def initialize(self, a_bzrdir, name=None):
1666
"""Create a branch of this format in a_bzrdir.
1565
1668
:param name: Name of the colocated branch to create.
1567
1670
raise NotImplementedError(self.initialize)
1856
1990
return self.__dict__ == other.__dict__
1858
1992
def __repr__(self):
1859
return "<%s for %s to (%s, %s)>" % (
1860
self.__class__.__name__, self.control_dir, self.to_branch,
1993
return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
1994
self.control_dir, self.to_branch,
1861
1995
self.revision_id)
1864
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
1865
"""Branch format registry."""
1867
def __init__(self, other_registry=None):
1868
super(BranchFormatRegistry, self).__init__(other_registry)
1869
self._default_format = None
1870
self._default_format_key = None
1872
def get_default(self):
1873
"""Return the current default format."""
1874
if (self._default_format_key is not None
1875
and self._default_format is None):
1876
self._default_format = self.get(self._default_format_key)
1877
return self._default_format
1879
def set_default(self, format):
1880
"""Set the default format."""
1881
self._default_format = format
1882
self._default_format_key = None
1884
def set_default_key(self, format_string):
1885
"""Set the default format by its format string."""
1886
self._default_format_key = format_string
1887
self._default_format = None
1998
class BzrBranchFormat4(BranchFormat):
1999
"""Bzr branch format 4.
2002
- a revision-history file.
2003
- a branch-lock lock file [ to be shared with the bzrdir ]
2006
def get_format_description(self):
2007
"""See BranchFormat.get_format_description()."""
2008
return "Branch format 4"
2010
def initialize(self, a_bzrdir, name=None):
2011
"""Create a branch of this format in a_bzrdir."""
2012
utf8_files = [('revision-history', ''),
2013
('branch-name', ''),
2015
return self._initialize_helper(a_bzrdir, utf8_files, name=name,
2016
lock_type='branch4', set_format=False)
2019
super(BzrBranchFormat4, self).__init__()
2020
self._matchingbzrdir = bzrdir.BzrDirFormat6()
2022
def network_name(self):
2023
"""The network name for this format is the control dirs disk label."""
2024
return self._matchingbzrdir.get_format_string()
2026
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2027
"""See BranchFormat.open()."""
2029
# we are being called directly and must probe.
2030
raise NotImplementedError
2031
return BzrBranch(_format=self,
2032
_control_files=a_bzrdir._control_files,
2035
_repository=a_bzrdir.open_repository())
2038
return "Bazaar-NG branch format 4"
2041
class BranchFormatMetadir(BranchFormat):
2042
"""Common logic for meta-dir based branch formats."""
2044
def _branch_class(self):
2045
"""What class to instantiate on open calls."""
2046
raise NotImplementedError(self._branch_class)
2048
def network_name(self):
2049
"""A simple byte string uniquely identifying this format for RPC calls.
2051
Metadir branch formats use their format string.
2053
return self.get_format_string()
2055
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2056
"""See BranchFormat.open()."""
2058
format = BranchFormat.find_format(a_bzrdir, name=name)
2059
if format.__class__ != self.__class__:
2060
raise AssertionError("wrong format %r found for %r" %
2062
transport = a_bzrdir.get_branch_transport(None, name=name)
2064
control_files = lockable_files.LockableFiles(transport, 'lock',
2066
return self._branch_class()(_format=self,
2067
_control_files=control_files,
2070
_repository=a_bzrdir.find_repository(),
2071
ignore_fallbacks=ignore_fallbacks)
2072
except errors.NoSuchFile:
2073
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2076
super(BranchFormatMetadir, self).__init__()
2077
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2078
self._matchingbzrdir.set_branch_format(self)
2080
def supports_tags(self):
2084
class BzrBranchFormat5(BranchFormatMetadir):
2085
"""Bzr branch format 5.
2088
- a revision-history file.
2090
- a lock dir guarding the branch itself
2091
- all of this stored in a branch/ subdirectory
2092
- works with shared repositories.
2094
This format is new in bzr 0.8.
2097
def _branch_class(self):
2100
def get_format_string(self):
2101
"""See BranchFormat.get_format_string()."""
2102
return "Bazaar-NG branch format 5\n"
2104
def get_format_description(self):
2105
"""See BranchFormat.get_format_description()."""
2106
return "Branch format 5"
2108
def initialize(self, a_bzrdir, name=None):
2109
"""Create a branch of this format in a_bzrdir."""
2110
utf8_files = [('revision-history', ''),
2111
('branch-name', ''),
2113
return self._initialize_helper(a_bzrdir, utf8_files, name)
2115
def supports_tags(self):
2119
class BzrBranchFormat6(BranchFormatMetadir):
2120
"""Branch format with last-revision and tags.
2122
Unlike previous formats, this has no explicit revision history. Instead,
2123
this just stores the last-revision, and the left-hand history leading
2124
up to there is the history.
2126
This format was introduced in bzr 0.15
2127
and became the default in 0.91.
2130
def _branch_class(self):
2133
def get_format_string(self):
2134
"""See BranchFormat.get_format_string()."""
2135
return "Bazaar Branch Format 6 (bzr 0.15)\n"
2137
def get_format_description(self):
2138
"""See BranchFormat.get_format_description()."""
2139
return "Branch format 6"
2141
def initialize(self, a_bzrdir, name=None):
2142
"""Create a branch of this format in a_bzrdir."""
2143
utf8_files = [('last-revision', '0 null:\n'),
2144
('branch.conf', ''),
2147
return self._initialize_helper(a_bzrdir, utf8_files, name)
2149
def make_tags(self, branch):
2150
"""See bzrlib.branch.BranchFormat.make_tags()."""
2151
return BasicTags(branch)
2153
def supports_set_append_revisions_only(self):
2157
class BzrBranchFormat8(BranchFormatMetadir):
2158
"""Metadir format supporting storing locations of subtree branches."""
2160
def _branch_class(self):
2163
def get_format_string(self):
2164
"""See BranchFormat.get_format_string()."""
2165
return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2167
def get_format_description(self):
2168
"""See BranchFormat.get_format_description()."""
2169
return "Branch format 8"
2171
def initialize(self, a_bzrdir, name=None):
2172
"""Create a branch of this format in a_bzrdir."""
2173
utf8_files = [('last-revision', '0 null:\n'),
2174
('branch.conf', ''),
2178
return self._initialize_helper(a_bzrdir, utf8_files, name)
2181
super(BzrBranchFormat8, self).__init__()
2182
self._matchingbzrdir.repository_format = \
2183
RepositoryFormatKnitPack5RichRoot()
2185
def make_tags(self, branch):
2186
"""See bzrlib.branch.BranchFormat.make_tags()."""
2187
return BasicTags(branch)
2189
def supports_set_append_revisions_only(self):
2192
def supports_stacking(self):
2195
supports_reference_locations = True
2198
class BzrBranchFormat7(BzrBranchFormat8):
2199
"""Branch format with last-revision, tags, and a stacked location pointer.
2201
The stacked location pointer is passed down to the repository and requires
2202
a repository format with supports_external_lookups = True.
2204
This format was introduced in bzr 1.6.
2207
def initialize(self, a_bzrdir, name=None):
2208
"""Create a branch of this format in a_bzrdir."""
2209
utf8_files = [('last-revision', '0 null:\n'),
2210
('branch.conf', ''),
2213
return self._initialize_helper(a_bzrdir, utf8_files, name)
2215
def _branch_class(self):
2218
def get_format_string(self):
2219
"""See BranchFormat.get_format_string()."""
2220
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2222
def get_format_description(self):
2223
"""See BranchFormat.get_format_description()."""
2224
return "Branch format 7"
2226
def supports_set_append_revisions_only(self):
2229
supports_reference_locations = False
2232
class BranchReferenceFormat(BranchFormat):
2233
"""Bzr branch reference format.
2235
Branch references are used in implementing checkouts, they
2236
act as an alias to the real branch which is at some other url.
2243
def get_format_string(self):
2244
"""See BranchFormat.get_format_string()."""
2245
return "Bazaar-NG Branch Reference Format 1\n"
2247
def get_format_description(self):
2248
"""See BranchFormat.get_format_description()."""
2249
return "Checkout reference format 1"
2251
def get_reference(self, a_bzrdir, name=None):
2252
"""See BranchFormat.get_reference()."""
2253
transport = a_bzrdir.get_branch_transport(None, name=name)
2254
return transport.get_bytes('location')
2256
def set_reference(self, a_bzrdir, name, to_branch):
2257
"""See BranchFormat.set_reference()."""
2258
transport = a_bzrdir.get_branch_transport(None, name=name)
2259
location = transport.put_bytes('location', to_branch.base)
2261
def initialize(self, a_bzrdir, name=None, target_branch=None):
2262
"""Create a branch of this format in a_bzrdir."""
2263
if target_branch is None:
2264
# this format does not implement branch itself, thus the implicit
2265
# creation contract must see it as uninitializable
2266
raise errors.UninitializableFormat(self)
2267
mutter('creating branch reference in %s', a_bzrdir.user_url)
2268
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2269
branch_transport.put_bytes('location',
2270
target_branch.bzrdir.user_url)
2271
branch_transport.put_bytes('format', self.get_format_string())
2273
a_bzrdir, name, _found=True,
2274
possible_transports=[target_branch.bzrdir.root_transport])
2275
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2279
super(BranchReferenceFormat, self).__init__()
2280
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2281
self._matchingbzrdir.set_branch_format(self)
2283
def _make_reference_clone_function(format, a_branch):
2284
"""Create a clone() routine for a branch dynamically."""
2285
def clone(to_bzrdir, revision_id=None,
2286
repository_policy=None):
2287
"""See Branch.clone()."""
2288
return format.initialize(to_bzrdir, target_branch=a_branch)
2289
# cannot obey revision_id limits when cloning a reference ...
2290
# FIXME RBC 20060210 either nuke revision_id for clone, or
2291
# emit some sort of warning/error to the caller ?!
2294
def open(self, a_bzrdir, name=None, _found=False, location=None,
2295
possible_transports=None, ignore_fallbacks=False):
2296
"""Return the branch that the branch reference in a_bzrdir points at.
2298
:param a_bzrdir: A BzrDir that contains a branch.
2299
:param name: Name of colocated branch to open, if any
2300
:param _found: a private parameter, do not use it. It is used to
2301
indicate if format probing has already be done.
2302
:param ignore_fallbacks: when set, no fallback branches will be opened
2303
(if there are any). Default is to open fallbacks.
2304
:param location: The location of the referenced branch. If
2305
unspecified, this will be determined from the branch reference in
2307
:param possible_transports: An optional reusable transports list.
2310
format = BranchFormat.find_format(a_bzrdir, name=name)
2311
if format.__class__ != self.__class__:
2312
raise AssertionError("wrong format %r found for %r" %
2314
if location is None:
2315
location = self.get_reference(a_bzrdir, name)
2316
real_bzrdir = bzrdir.BzrDir.open(
2317
location, possible_transports=possible_transports)
2318
result = real_bzrdir.open_branch(name=name,
2319
ignore_fallbacks=ignore_fallbacks)
2320
# this changes the behaviour of result.clone to create a new reference
2321
# rather than a copy of the content of the branch.
2322
# I did not use a proxy object because that needs much more extensive
2323
# testing, and we are only changing one behaviour at the moment.
2324
# If we decide to alter more behaviours - i.e. the implicit nickname
2325
# then this should be refactored to introduce a tested proxy branch
2326
# and a subclass of that for use in overriding clone() and ....
2328
result.clone = self._make_reference_clone_function(result)
1890
2332
network_format_registry = registry.FormatRegistry()
1895
2337
BranchFormat.network_name() for more detail.
1898
format_registry = BranchFormatRegistry(network_format_registry)
1901
2341
# formats which have no format string are not discoverable
1902
2342
# and not independently creatable, so are not registered.
1903
format_registry.register_lazy(
1904
b"Bazaar-NG branch format 5\n", "breezy.bzr.fullhistory",
1906
format_registry.register_lazy(
1907
b"Bazaar Branch Format 6 (bzr 0.15)\n",
1908
"breezy.bzr.branch", "BzrBranchFormat6")
1909
format_registry.register_lazy(
1910
b"Bazaar Branch Format 7 (needs bzr 1.6)\n",
1911
"breezy.bzr.branch", "BzrBranchFormat7")
1912
format_registry.register_lazy(
1913
b"Bazaar Branch Format 8 (needs bzr 1.15)\n",
1914
"breezy.bzr.branch", "BzrBranchFormat8")
1915
format_registry.register_lazy(
1916
b"Bazaar-NG Branch Reference Format 1\n",
1917
"breezy.bzr.branch", "BranchReferenceFormat")
1919
format_registry.set_default_key(b"Bazaar Branch Format 7 (needs bzr 1.6)\n")
2343
__format5 = BzrBranchFormat5()
2344
__format6 = BzrBranchFormat6()
2345
__format7 = BzrBranchFormat7()
2346
__format8 = BzrBranchFormat8()
2347
BranchFormat.register_format(__format5)
2348
BranchFormat.register_format(BranchReferenceFormat())
2349
BranchFormat.register_format(__format6)
2350
BranchFormat.register_format(__format7)
2351
BranchFormat.register_format(__format8)
2352
BranchFormat.set_default_format(__format7)
2353
_legacy_formats = [BzrBranchFormat4(),
2355
network_format_registry.register(
2356
_legacy_formats[0].network_name(), _legacy_formats[0].__class__)
1922
2359
class BranchWriteLockResult(LogicalLockResult):
1923
2360
"""The result of write locking a branch.
1925
:ivar token: The token obtained from the underlying branch lock, or
2362
:ivar branch_token: The token obtained from the underlying branch lock, or
1927
2364
:ivar unlock: A callable which will unlock the lock.
2367
def __init__(self, unlock, branch_token):
2368
LogicalLockResult.__init__(self, unlock)
2369
self.branch_token = branch_token
1930
2371
def __repr__(self):
1931
return "BranchWriteLockResult(%r, %r)" % (self.unlock, self.token)
2372
return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
2376
class BzrBranch(Branch, _RelockDebugMixin):
2377
"""A branch stored in the actual filesystem.
2379
Note that it's "local" in the context of the filesystem; it doesn't
2380
really matter if it's on an nfs/smb/afs/coda/... share, as long as
2381
it's writable, and can be accessed via the normal filesystem API.
2383
:ivar _transport: Transport for file operations on this branch's
2384
control files, typically pointing to the .bzr/branch directory.
2385
:ivar repository: Repository for this branch.
2386
:ivar base: The url of the base directory for this branch; the one
2387
containing the .bzr directory.
2388
:ivar name: Optional colocated branch name as it exists in the control
2392
def __init__(self, _format=None,
2393
_control_files=None, a_bzrdir=None, name=None,
2394
_repository=None, ignore_fallbacks=False):
2395
"""Create new branch object at a particular location."""
2396
if a_bzrdir is None:
2397
raise ValueError('a_bzrdir must be supplied')
2399
self.bzrdir = a_bzrdir
2400
self._base = self.bzrdir.transport.clone('..').base
2402
# XXX: We should be able to just do
2403
# self.base = self.bzrdir.root_transport.base
2404
# but this does not quite work yet -- mbp 20080522
2405
self._format = _format
2406
if _control_files is None:
2407
raise ValueError('BzrBranch _control_files is None')
2408
self.control_files = _control_files
2409
self._transport = _control_files._transport
2410
self.repository = _repository
2411
Branch.__init__(self)
2414
if self.name is None:
2415
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2417
return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2422
def _get_base(self):
2423
"""Returns the directory containing the control directory."""
2426
base = property(_get_base, doc="The URL for the root of this branch.")
2428
def _get_config(self):
2429
return TransportConfig(self._transport, 'branch.conf')
2431
def is_locked(self):
2432
return self.control_files.is_locked()
2434
def lock_write(self, token=None):
2435
"""Lock the branch for write operations.
2437
:param token: A token to permit reacquiring a previously held and
2439
:return: A BranchWriteLockResult.
2441
if not self.is_locked():
2442
self._note_lock('w')
2443
# All-in-one needs to always unlock/lock.
2444
repo_control = getattr(self.repository, 'control_files', None)
2445
if self.control_files == repo_control or not self.is_locked():
2446
self.repository._warn_if_deprecated(self)
2447
self.repository.lock_write()
2452
return BranchWriteLockResult(self.unlock,
2453
self.control_files.lock_write(token=token))
2456
self.repository.unlock()
2459
def lock_read(self):
2460
"""Lock the branch for read operations.
2462
:return: A bzrlib.lock.LogicalLockResult.
2464
if not self.is_locked():
2465
self._note_lock('r')
2466
# All-in-one needs to always unlock/lock.
2467
repo_control = getattr(self.repository, 'control_files', None)
2468
if self.control_files == repo_control or not self.is_locked():
2469
self.repository._warn_if_deprecated(self)
2470
self.repository.lock_read()
2475
self.control_files.lock_read()
2476
return LogicalLockResult(self.unlock)
2479
self.repository.unlock()
2482
@only_raises(errors.LockNotHeld, errors.LockBroken)
2485
self.control_files.unlock()
2487
# All-in-one needs to always unlock/lock.
2488
repo_control = getattr(self.repository, 'control_files', None)
2489
if (self.control_files == repo_control or
2490
not self.control_files.is_locked()):
2491
self.repository.unlock()
2492
if not self.control_files.is_locked():
2493
# we just released the lock
2494
self._clear_cached_state()
2496
def peek_lock_mode(self):
2497
if self.control_files._lock_count == 0:
2500
return self.control_files._lock_mode
2502
def get_physical_lock_status(self):
2503
return self.control_files.get_physical_lock_status()
2506
def print_file(self, file, revision_id):
2507
"""See Branch.print_file."""
2508
return self.repository.print_file(file, revision_id)
2510
def _write_revision_history(self, history):
2511
"""Factored out of set_revision_history.
2513
This performs the actual writing to disk.
2514
It is intended to be called by BzrBranch5.set_revision_history."""
2515
self._transport.put_bytes(
2516
'revision-history', '\n'.join(history),
2517
mode=self.bzrdir._get_file_mode())
2520
def set_revision_history(self, rev_history):
2521
"""See Branch.set_revision_history."""
2522
if 'evil' in debug.debug_flags:
2523
mutter_callsite(3, "set_revision_history scales with history.")
2524
check_not_reserved_id = _mod_revision.check_not_reserved_id
2525
for rev_id in rev_history:
2526
check_not_reserved_id(rev_id)
2527
if Branch.hooks['post_change_branch_tip']:
2528
# Don't calculate the last_revision_info() if there are no hooks
2530
old_revno, old_revid = self.last_revision_info()
2531
if len(rev_history) == 0:
2532
revid = _mod_revision.NULL_REVISION
2534
revid = rev_history[-1]
2535
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2536
self._write_revision_history(rev_history)
2537
self._clear_cached_state()
2538
self._cache_revision_history(rev_history)
2539
for hook in Branch.hooks['set_rh']:
2540
hook(self, rev_history)
2541
if Branch.hooks['post_change_branch_tip']:
2542
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2544
def _synchronize_history(self, destination, revision_id):
2545
"""Synchronize last revision and revision history between branches.
2547
This version is most efficient when the destination is also a
2548
BzrBranch5, but works for BzrBranch6 as long as the revision
2549
history is the true lefthand parent history, and all of the revisions
2550
are in the destination's repository. If not, set_revision_history
2553
:param destination: The branch to copy the history into
2554
:param revision_id: The revision-id to truncate history at. May
2555
be None to copy complete history.
2557
if not isinstance(destination._format, BzrBranchFormat5):
2558
super(BzrBranch, self)._synchronize_history(
2559
destination, revision_id)
2561
if revision_id == _mod_revision.NULL_REVISION:
2564
new_history = self.revision_history()
2565
if revision_id is not None and new_history != []:
2567
new_history = new_history[:new_history.index(revision_id) + 1]
2569
rev = self.repository.get_revision(revision_id)
2570
new_history = rev.get_history(self.repository)[1:]
2571
destination.set_revision_history(new_history)
2574
def set_last_revision_info(self, revno, revision_id):
2575
"""Set the last revision of this branch.
2577
The caller is responsible for checking that the revno is correct
2578
for this revision id.
2580
It may be possible to set the branch last revision to an id not
2581
present in the repository. However, branches can also be
2582
configured to check constraints on history, in which case this may not
2585
revision_id = _mod_revision.ensure_null(revision_id)
2586
# this old format stores the full history, but this api doesn't
2587
# provide it, so we must generate, and might as well check it's
2589
history = self._lefthand_history(revision_id)
2590
if len(history) != revno:
2591
raise AssertionError('%d != %d' % (len(history), revno))
2592
self.set_revision_history(history)
2594
def _gen_revision_history(self):
2595
history = self._transport.get_bytes('revision-history').split('\n')
2596
if history[-1:] == ['']:
2597
# There shouldn't be a trailing newline, but just in case.
2602
def generate_revision_history(self, revision_id, last_rev=None,
2604
"""Create a new revision history that will finish with revision_id.
2606
:param revision_id: the new tip to use.
2607
:param last_rev: The previous last_revision. If not None, then this
2608
must be a ancestory of revision_id, or DivergedBranches is raised.
2609
:param other_branch: The other branch that DivergedBranches should
2610
raise with respect to.
2612
self.set_revision_history(self._lefthand_history(revision_id,
2613
last_rev, other_branch))
2615
def basis_tree(self):
2616
"""See Branch.basis_tree."""
2617
return self.repository.revision_tree(self.last_revision())
2619
def _get_parent_location(self):
2620
_locs = ['parent', 'pull', 'x-pull']
2623
return self._transport.get_bytes(l).strip('\n')
2624
except errors.NoSuchFile:
2628
def _basic_push(self, target, overwrite, stop_revision):
2629
"""Basic implementation of push without bound branches or hooks.
2631
Must be called with source read locked and target write locked.
2633
result = BranchPushResult()
2634
result.source_branch = self
2635
result.target_branch = target
2636
result.old_revno, result.old_revid = target.last_revision_info()
2637
self.update_references(target)
2638
if result.old_revid != self.last_revision():
2639
# We assume that during 'push' this repository is closer than
2641
graph = self.repository.get_graph(target.repository)
2642
target.update_revisions(self, stop_revision,
2643
overwrite=overwrite, graph=graph)
2644
if self._push_should_merge_tags():
2645
result.tag_conflicts = self.tags.merge_to(target.tags,
2647
result.new_revno, result.new_revid = target.last_revision_info()
2650
def get_stacked_on_url(self):
2651
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2653
def set_push_location(self, location):
2654
"""See Branch.set_push_location."""
2655
self.get_config().set_user_option(
2656
'push_location', location,
2657
store=_mod_config.STORE_LOCATION_NORECURSE)
2659
def _set_parent_location(self, url):
2661
self._transport.delete('parent')
2663
self._transport.put_bytes('parent', url + '\n',
2664
mode=self.bzrdir._get_file_mode())
2667
class BzrBranch5(BzrBranch):
2668
"""A format 5 branch. This supports new features over plain branches.
2670
It has support for a master_branch which is the data for bound branches.
2673
def get_bound_location(self):
2675
return self._transport.get_bytes('bound')[:-1]
2676
except errors.NoSuchFile:
2680
def get_master_branch(self, possible_transports=None):
2681
"""Return the branch we are bound to.
2683
:return: Either a Branch, or None
2685
This could memoise the branch, but if thats done
2686
it must be revalidated on each new lock.
2687
So for now we just don't memoise it.
2688
# RBC 20060304 review this decision.
2690
bound_loc = self.get_bound_location()
2694
return Branch.open(bound_loc,
2695
possible_transports=possible_transports)
2696
except (errors.NotBranchError, errors.ConnectionError), e:
2697
raise errors.BoundBranchConnectionFailure(
2701
def set_bound_location(self, location):
2702
"""Set the target where this branch is bound to.
2704
:param location: URL to the target branch
2707
self._transport.put_bytes('bound', location+'\n',
2708
mode=self.bzrdir._get_file_mode())
2711
self._transport.delete('bound')
2712
except errors.NoSuchFile:
2717
def bind(self, other):
2718
"""Bind this branch to the branch other.
2720
This does not push or pull data between the branches, though it does
2721
check for divergence to raise an error when the branches are not
2722
either the same, or one a prefix of the other. That behaviour may not
2723
be useful, so that check may be removed in future.
2725
:param other: The branch to bind to
2728
# TODO: jam 20051230 Consider checking if the target is bound
2729
# It is debatable whether you should be able to bind to
2730
# a branch which is itself bound.
2731
# Committing is obviously forbidden,
2732
# but binding itself may not be.
2733
# Since we *have* to check at commit time, we don't
2734
# *need* to check here
2736
# we want to raise diverged if:
2737
# last_rev is not in the other_last_rev history, AND
2738
# other_last_rev is not in our history, and do it without pulling
2740
self.set_bound_location(other.base)
2744
"""If bound, unbind"""
2745
return self.set_bound_location(None)
2748
def update(self, possible_transports=None):
2749
"""Synchronise this branch with the master branch if any.
2751
:return: None or the last_revision that was pivoted out during the
2754
master = self.get_master_branch(possible_transports)
2755
if master is not None:
2756
old_tip = _mod_revision.ensure_null(self.last_revision())
2757
self.pull(master, overwrite=True)
2758
if self.repository.get_graph().is_ancestor(old_tip,
2759
_mod_revision.ensure_null(self.last_revision())):
2765
class BzrBranch8(BzrBranch5):
2766
"""A branch that stores tree-reference locations."""
2768
def _open_hook(self):
2769
if self._ignore_fallbacks:
2772
url = self.get_stacked_on_url()
2773
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2774
errors.UnstackableBranchFormat):
2777
for hook in Branch.hooks['transform_fallback_location']:
2778
url = hook(self, url)
2780
hook_name = Branch.hooks.get_hook_name(hook)
2781
raise AssertionError(
2782
"'transform_fallback_location' hook %s returned "
2783
"None, not a URL." % hook_name)
2784
self._activate_fallback_location(url)
2786
def __init__(self, *args, **kwargs):
2787
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2788
super(BzrBranch8, self).__init__(*args, **kwargs)
2789
self._last_revision_info_cache = None
2790
self._reference_info = None
2792
def _clear_cached_state(self):
2793
super(BzrBranch8, self)._clear_cached_state()
2794
self._last_revision_info_cache = None
2795
self._reference_info = None
2797
def _last_revision_info(self):
2798
revision_string = self._transport.get_bytes('last-revision')
2799
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2800
revision_id = cache_utf8.get_cached_utf8(revision_id)
2802
return revno, revision_id
2804
def _write_last_revision_info(self, revno, revision_id):
2805
"""Simply write out the revision id, with no checks.
2807
Use set_last_revision_info to perform this safely.
2809
Does not update the revision_history cache.
2810
Intended to be called by set_last_revision_info and
2811
_write_revision_history.
2813
revision_id = _mod_revision.ensure_null(revision_id)
2814
out_string = '%d %s\n' % (revno, revision_id)
2815
self._transport.put_bytes('last-revision', out_string,
2816
mode=self.bzrdir._get_file_mode())
2819
def set_last_revision_info(self, revno, revision_id):
2820
revision_id = _mod_revision.ensure_null(revision_id)
2821
old_revno, old_revid = self.last_revision_info()
2822
if self._get_append_revisions_only():
2823
self._check_history_violation(revision_id)
2824
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2825
self._write_last_revision_info(revno, revision_id)
2826
self._clear_cached_state()
2827
self._last_revision_info_cache = revno, revision_id
2828
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2830
def _synchronize_history(self, destination, revision_id):
2831
"""Synchronize last revision and revision history between branches.
2833
:see: Branch._synchronize_history
2835
# XXX: The base Branch has a fast implementation of this method based
2836
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2837
# that uses set_revision_history. This class inherits from BzrBranch5,
2838
# but wants the fast implementation, so it calls
2839
# Branch._synchronize_history directly.
2840
Branch._synchronize_history(self, destination, revision_id)
2842
def _check_history_violation(self, revision_id):
2843
last_revision = _mod_revision.ensure_null(self.last_revision())
2844
if _mod_revision.is_null(last_revision):
2846
if last_revision not in self._lefthand_history(revision_id):
2847
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2849
def _gen_revision_history(self):
2850
"""Generate the revision history from last revision
2852
last_revno, last_revision = self.last_revision_info()
2853
self._extend_partial_history(stop_index=last_revno-1)
2854
return list(reversed(self._partial_revision_history_cache))
2856
def _write_revision_history(self, history):
2857
"""Factored out of set_revision_history.
2859
This performs the actual writing to disk, with format-specific checks.
2860
It is intended to be called by BzrBranch5.set_revision_history.
2862
if len(history) == 0:
2863
last_revision = 'null:'
2865
if history != self._lefthand_history(history[-1]):
2866
raise errors.NotLefthandHistory(history)
2867
last_revision = history[-1]
2868
if self._get_append_revisions_only():
2869
self._check_history_violation(last_revision)
2870
self._write_last_revision_info(len(history), last_revision)
2873
def _set_parent_location(self, url):
2874
"""Set the parent branch"""
2875
self._set_config_location('parent_location', url, make_relative=True)
2878
def _get_parent_location(self):
2879
"""Set the parent branch"""
2880
return self._get_config_location('parent_location')
2883
def _set_all_reference_info(self, info_dict):
2884
"""Replace all reference info stored in a branch.
2886
:param info_dict: A dict of {file_id: (tree_path, branch_location)}
2889
writer = rio.RioWriter(s)
2890
for key, (tree_path, branch_location) in info_dict.iteritems():
2891
stanza = rio.Stanza(file_id=key, tree_path=tree_path,
2892
branch_location=branch_location)
2893
writer.write_stanza(stanza)
2894
self._transport.put_bytes('references', s.getvalue())
2895
self._reference_info = info_dict
2898
def _get_all_reference_info(self):
2899
"""Return all the reference info stored in a branch.
2901
:return: A dict of {file_id: (tree_path, branch_location)}
2903
if self._reference_info is not None:
2904
return self._reference_info
2905
rio_file = self._transport.get('references')
2907
stanzas = rio.read_stanzas(rio_file)
2908
info_dict = dict((s['file_id'], (s['tree_path'],
2909
s['branch_location'])) for s in stanzas)
2912
self._reference_info = info_dict
2915
def set_reference_info(self, file_id, tree_path, branch_location):
2916
"""Set the branch location to use for a tree reference.
2918
:param file_id: The file-id of the tree reference.
2919
:param tree_path: The path of the tree reference in the tree.
2920
:param branch_location: The location of the branch to retrieve tree
2923
info_dict = self._get_all_reference_info()
2924
info_dict[file_id] = (tree_path, branch_location)
2925
if None in (tree_path, branch_location):
2926
if tree_path is not None:
2927
raise ValueError('tree_path must be None when branch_location'
2929
if branch_location is not None:
2930
raise ValueError('branch_location must be None when tree_path'
2932
del info_dict[file_id]
2933
self._set_all_reference_info(info_dict)
2935
def get_reference_info(self, file_id):
2936
"""Get the tree_path and branch_location for a tree reference.
2938
:return: a tuple of (tree_path, branch_location)
2940
return self._get_all_reference_info().get(file_id, (None, None))
2942
def reference_parent(self, file_id, path, possible_transports=None):
2943
"""Return the parent branch for a tree-reference file_id.
2945
:param file_id: The file_id of the tree reference
2946
:param path: The path of the file_id in the tree
2947
:return: A branch associated with the file_id
2949
branch_location = self.get_reference_info(file_id)[1]
2950
if branch_location is None:
2951
return Branch.reference_parent(self, file_id, path,
2952
possible_transports)
2953
branch_location = urlutils.join(self.user_url, branch_location)
2954
return Branch.open(branch_location,
2955
possible_transports=possible_transports)
2957
def set_push_location(self, location):
2958
"""See Branch.set_push_location."""
2959
self._set_config_location('push_location', location)
2961
def set_bound_location(self, location):
2962
"""See Branch.set_push_location."""
2964
config = self.get_config()
2965
if location is None:
2966
if config.get_user_option('bound') != 'True':
2969
config.set_user_option('bound', 'False', warn_masked=True)
2972
self._set_config_location('bound_location', location,
2974
config.set_user_option('bound', 'True', warn_masked=True)
2977
def _get_bound_location(self, bound):
2978
"""Return the bound location in the config file.
2980
Return None if the bound parameter does not match"""
2981
config = self.get_config()
2982
config_bound = (config.get_user_option('bound') == 'True')
2983
if config_bound != bound:
2985
return self._get_config_location('bound_location', config=config)
2987
def get_bound_location(self):
2988
"""See Branch.set_push_location."""
2989
return self._get_bound_location(True)
2991
def get_old_bound_location(self):
2992
"""See Branch.get_old_bound_location"""
2993
return self._get_bound_location(False)
2995
def get_stacked_on_url(self):
2996
# you can always ask for the URL; but you might not be able to use it
2997
# if the repo can't support stacking.
2998
## self._check_stackable_repo()
2999
stacked_url = self._get_config_location('stacked_on_location')
3000
if stacked_url is None:
3001
raise errors.NotStacked(self)
3004
def _get_append_revisions_only(self):
3005
return self.get_config(
3006
).get_user_option_as_bool('append_revisions_only')
3009
def generate_revision_history(self, revision_id, last_rev=None,
3011
"""See BzrBranch5.generate_revision_history"""
3012
history = self._lefthand_history(revision_id, last_rev, other_branch)
3013
revno = len(history)
3014
self.set_last_revision_info(revno, revision_id)
3017
def get_rev_id(self, revno, history=None):
3018
"""Find the revision id of the specified revno."""
3020
return _mod_revision.NULL_REVISION
3022
last_revno, last_revision_id = self.last_revision_info()
3023
if revno <= 0 or revno > last_revno:
3024
raise errors.NoSuchRevision(self, revno)
3026
if history is not None:
3027
return history[revno - 1]
3029
index = last_revno - revno
3030
if len(self._partial_revision_history_cache) <= index:
3031
self._extend_partial_history(stop_index=index)
3032
if len(self._partial_revision_history_cache) > index:
3033
return self._partial_revision_history_cache[index]
3035
raise errors.NoSuchRevision(self, revno)
3038
def revision_id_to_revno(self, revision_id):
3039
"""Given a revision id, return its revno"""
3040
if _mod_revision.is_null(revision_id):
3043
index = self._partial_revision_history_cache.index(revision_id)
3045
self._extend_partial_history(stop_revision=revision_id)
3046
index = len(self._partial_revision_history_cache) - 1
3047
if self._partial_revision_history_cache[index] != revision_id:
3048
raise errors.NoSuchRevision(self, revision_id)
3049
return self.revno() - index
3052
class BzrBranch7(BzrBranch8):
3053
"""A branch with support for a fallback repository."""
3055
def set_reference_info(self, file_id, tree_path, branch_location):
3056
Branch.set_reference_info(self, file_id, tree_path, branch_location)
3058
def get_reference_info(self, file_id):
3059
Branch.get_reference_info(self, file_id)
3061
def reference_parent(self, file_id, path, possible_transports=None):
3062
return Branch.reference_parent(self, file_id, path,
3063
possible_transports)
3066
class BzrBranch6(BzrBranch7):
3067
"""See BzrBranchFormat6 for the capabilities of this branch.
3069
This subclass of BzrBranch7 disables the new features BzrBranch7 added,
3073
def get_stacked_on_url(self):
3074
raise errors.UnstackableBranchFormat(self._format, self.user_url)
1934
3077
######################################################################
2130
3322
def _get_branch_formats_to_test(klass):
2131
return [(format_registry.get_default(), format_registry.get_default())]
3323
return [(BranchFormat._default_format, BranchFormat._default_format)]
2134
3326
def unwrap_format(klass, format):
2135
3327
if isinstance(format, remote.RemoteBranchFormat):
2136
3328
format._ensure_real()
2137
3329
return format._custom_format
2140
def copy_content_into(self, revision_id=None, tag_selector=None):
3333
def copy_content_into(self, revision_id=None):
2141
3334
"""Copy the content of source into target
2143
3336
revision_id: if not None, the revision history in the new branch will
2144
3337
be truncated to end with revision_id.
2146
with self.source.lock_read(), self.target.lock_write():
2147
self.source._synchronize_history(self.target, revision_id)
2148
self.update_references()
2150
parent = self.source.get_parent()
2151
except errors.InaccessibleParent as e:
2152
mutter('parent was not accessible to copy: %s', str(e))
2155
self.target.set_parent(parent)
2156
if self.source._push_should_merge_tags():
2157
self.source.tags.merge_to(self.target.tags, selector=tag_selector)
2159
def fetch(self, stop_revision=None, limit=None, lossy=False):
2160
if self.target.base == self.source.base:
2162
with self.source.lock_read(), self.target.lock_write():
2163
fetch_spec_factory = fetch.FetchSpecFactory()
2164
fetch_spec_factory.source_branch = self.source
2165
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
2166
fetch_spec_factory.source_repo = self.source.repository
2167
fetch_spec_factory.target_repo = self.target.repository
2168
fetch_spec_factory.target_repo_kind = (
2169
fetch.TargetRepoKinds.PREEXISTING)
2170
fetch_spec_factory.limit = limit
2171
fetch_spec = fetch_spec_factory.make_fetch_spec()
2172
return self.target.repository.fetch(
2173
self.source.repository,
2175
fetch_spec=fetch_spec)
2177
def _update_revisions(self, stop_revision=None, overwrite=False,
2179
with self.source.lock_read(), self.target.lock_write():
2180
other_revno, other_last_revision = self.source.last_revision_info()
2181
stop_revno = None # unknown
2182
if stop_revision is None:
2183
stop_revision = other_last_revision
2184
if _mod_revision.is_null(stop_revision):
2185
# if there are no commits, we're done.
2187
stop_revno = other_revno
2189
# what's the current last revision, before we fetch [and change it
2191
last_rev = _mod_revision.ensure_null(self.target.last_revision())
2192
# we fetch here so that we don't process data twice in the common
2193
# case of having something to pull, and so that the check for
2194
# already merged can operate on the just fetched graph, which will
2195
# be cached in memory.
2196
self.fetch(stop_revision=stop_revision)
2197
# Check to see if one is an ancestor of the other
2200
graph = self.target.repository.get_graph()
2201
if self.target._check_if_descendant_or_diverged(
2202
stop_revision, last_rev, graph, self.source):
2203
# stop_revision is a descendant of last_rev, but we aren't
2204
# overwriting, so we're done.
2206
if stop_revno is None:
2208
graph = self.target.repository.get_graph()
2209
this_revno, this_last_revision = \
3339
self.source.update_references(self.target)
3340
self.source._synchronize_history(self.target, revision_id)
3342
parent = self.source.get_parent()
3343
except errors.InaccessibleParent, e:
3344
mutter('parent was not accessible to copy: %s', e)
3347
self.target.set_parent(parent)
3348
if self.source._push_should_merge_tags():
3349
self.source.tags.merge_to(self.target.tags)
3352
def update_revisions(self, stop_revision=None, overwrite=False,
3354
"""See InterBranch.update_revisions()."""
3355
other_revno, other_last_revision = self.source.last_revision_info()
3356
stop_revno = None # unknown
3357
if stop_revision is None:
3358
stop_revision = other_last_revision
3359
if _mod_revision.is_null(stop_revision):
3360
# if there are no commits, we're done.
3362
stop_revno = other_revno
3364
# what's the current last revision, before we fetch [and change it
3366
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3367
# we fetch here so that we don't process data twice in the common
3368
# case of having something to pull, and so that the check for
3369
# already merged can operate on the just fetched graph, which will
3370
# be cached in memory.
3371
self.target.fetch(self.source, stop_revision)
3372
# Check to see if one is an ancestor of the other
3375
graph = self.target.repository.get_graph()
3376
if self.target._check_if_descendant_or_diverged(
3377
stop_revision, last_rev, graph, self.source):
3378
# stop_revision is a descendant of last_rev, but we aren't
3379
# overwriting, so we're done.
3381
if stop_revno is None:
3383
graph = self.target.repository.get_graph()
3384
this_revno, this_last_revision = \
2210
3385
self.target.last_revision_info()
2211
stop_revno = graph.find_distance_to_null(
2212
stop_revision, [(other_last_revision, other_revno),
2213
(this_last_revision, this_revno)])
2214
self.target.set_last_revision_info(stop_revno, stop_revision)
3386
stop_revno = graph.find_distance_to_null(stop_revision,
3387
[(other_last_revision, other_revno),
3388
(this_last_revision, this_revno)])
3389
self.target.set_last_revision_info(stop_revno, stop_revision)
2216
3392
def pull(self, overwrite=False, stop_revision=None,
2217
3393
possible_transports=None, run_hooks=True,
2218
_override_hook_target=None, local=False,
3394
_override_hook_target=None, local=False):
2220
3395
"""Pull from source into self, updating my master if any.
2222
3397
:param run_hooks: Private parameter - if false, this branch
2223
3398
is being called because it's the master of the primary branch,
2224
3399
so it should not run its hooks.
2226
with cleanup.ExitStack() as exit_stack:
2227
exit_stack.enter_context(self.target.lock_write())
2228
bound_location = self.target.get_bound_location()
2229
if local and not bound_location:
2230
raise errors.LocalRequiresBoundBranch()
2231
master_branch = None
2232
source_is_master = False
2234
# bound_location comes from a config file, some care has to be
2235
# taken to relate it to source.user_url
2236
normalized = urlutils.normalize_url(bound_location)
2238
relpath = self.source.user_transport.relpath(normalized)
2239
source_is_master = (relpath == '')
2240
except (errors.PathNotChild, urlutils.InvalidURL):
2241
source_is_master = False
2242
if not local and bound_location and not source_is_master:
2243
# not pulling from master, so we need to update master.
2244
master_branch = self.target.get_master_branch(
2245
possible_transports)
2246
exit_stack.enter_context(master_branch.lock_write())
3401
bound_location = self.target.get_bound_location()
3402
if local and not bound_location:
3403
raise errors.LocalRequiresBoundBranch()
3404
master_branch = None
3405
if not local and bound_location and self.source.user_url != bound_location:
3406
# not pulling from master, so we need to update master.
3407
master_branch = self.target.get_master_branch(possible_transports)
3408
master_branch.lock_write()
2247
3410
if master_branch:
2248
3411
# pull from source into master.
2250
self.source, overwrite, stop_revision, run_hooks=False,
2251
tag_selector=tag_selector)
2253
overwrite, stop_revision, _hook_master=master_branch,
3412
master_branch.pull(self.source, overwrite, stop_revision,
3414
return self._pull(overwrite,
3415
stop_revision, _hook_master=master_branch,
2254
3416
run_hooks=run_hooks,
2255
_override_hook_target=_override_hook_target,
2256
merge_tags_to_master=not source_is_master,
2257
tag_selector=tag_selector)
3417
_override_hook_target=_override_hook_target)
3420
master_branch.unlock()
2259
def push(self, overwrite=False, stop_revision=None, lossy=False,
2260
_override_hook_source_branch=None, tag_selector=None):
3422
def push(self, overwrite=False, stop_revision=None,
3423
_override_hook_source_branch=None):
2261
3424
"""See InterBranch.push.
2263
3426
This is the basic concrete implementation of push()
2265
:param _override_hook_source_branch: If specified, run the hooks
2266
passing this Branch as the source, rather than self. This is for
2267
use of RemoteBranch, where push is delegated to the underlying
3428
:param _override_hook_source_branch: If specified, run
3429
the hooks passing this Branch as the source, rather than self.
3430
This is for use of RemoteBranch, where push is delegated to the
3431
underlying vfs-based Branch.
2271
raise errors.LossyPushToSameVCS(self.source, self.target)
2272
3433
# TODO: Public option to disable running hooks - should be trivial but
3435
self.source.lock_read()
3437
return _run_with_write_locked_target(
3438
self.target, self._push_with_bound_branches, overwrite,
3440
_override_hook_source_branch=_override_hook_source_branch)
3442
self.source.unlock()
3444
def _push_with_bound_branches(self, overwrite, stop_revision,
3445
_override_hook_source_branch=None):
3446
"""Push from source into target, and into target's master if any.
2275
3448
def _run_hooks():
2276
3449
if _override_hook_source_branch:
2277
3450
result.source_branch = _override_hook_source_branch
2278
3451
for hook in Branch.hooks['post_push']:
2281
with self.source.lock_read(), self.target.lock_write():
2282
bound_location = self.target.get_bound_location()
2283
if bound_location and self.target.base != bound_location:
2284
# there is a master branch.
2286
# XXX: Why the second check? Is it even supported for a branch
2287
# to be bound to itself? -- mbp 20070507
2288
master_branch = self.target.get_master_branch()
2289
with master_branch.lock_write():
2290
# push into the master from the source branch.
2291
master_inter = InterBranch.get(self.source, master_branch)
2292
master_inter._basic_push(
2293
overwrite, stop_revision, tag_selector=tag_selector)
2294
# and push into the target branch from the source. Note
2295
# that we push from the source branch again, because it's
2296
# considered the highest bandwidth repository.
2297
result = self._basic_push(
2298
overwrite, stop_revision, tag_selector=tag_selector)
2299
result.master_branch = master_branch
2300
result.local_branch = self.target
2303
master_branch = None
2305
result = self._basic_push(
2306
overwrite, stop_revision, tag_selector=tag_selector)
2307
# TODO: Why set master_branch and local_branch if there's no
2308
# binding? Maybe cleaner to just leave them unset? -- mbp
2310
result.master_branch = self.target
2311
result.local_branch = None
3454
bound_location = self.target.get_bound_location()
3455
if bound_location and self.target.base != bound_location:
3456
# there is a master branch.
3458
# XXX: Why the second check? Is it even supported for a branch to
3459
# be bound to itself? -- mbp 20070507
3460
master_branch = self.target.get_master_branch()
3461
master_branch.lock_write()
3463
# push into the master from the source branch.
3464
self.source._basic_push(master_branch, overwrite, stop_revision)
3465
# and push into the target branch from the source. Note that we
3466
# push from the source branch again, because its considered the
3467
# highest bandwidth repository.
3468
result = self.source._basic_push(self.target, overwrite,
3470
result.master_branch = master_branch
3471
result.local_branch = self.target
3475
master_branch.unlock()
3478
result = self.source._basic_push(self.target, overwrite,
3480
# TODO: Why set master_branch and local_branch if there's no
3481
# binding? Maybe cleaner to just leave them unset? -- mbp
3483
result.master_branch = self.target
3484
result.local_branch = None
2315
def _basic_push(self, overwrite, stop_revision, tag_selector=None):
2316
"""Basic implementation of push without bound branches or hooks.
2318
Must be called with source read locked and target write locked.
2320
result = BranchPushResult()
2321
result.source_branch = self.source
2322
result.target_branch = self.target
2323
result.old_revno, result.old_revid = self.target.last_revision_info()
2324
overwrite = _fix_overwrite_type(overwrite)
2325
if result.old_revid != stop_revision:
2326
# We assume that during 'push' this repository is closer than
2328
graph = self.source.repository.get_graph(self.target.repository)
2329
self._update_revisions(
2330
stop_revision, overwrite=("history" in overwrite), graph=graph)
2331
if self.source._push_should_merge_tags():
2332
result.tag_updates, result.tag_conflicts = (
2333
self.source.tags.merge_to(
2334
self.target.tags, "tags" in overwrite, selector=tag_selector))
2335
self.update_references()
2336
result.new_revno, result.new_revid = self.target.last_revision_info()
2339
3488
def _pull(self, overwrite=False, stop_revision=None,
2340
possible_transports=None, _hook_master=None, run_hooks=True,
2341
_override_hook_target=None, local=False,
2342
merge_tags_to_master=True, tag_selector=None):
3489
possible_transports=None, _hook_master=None, run_hooks=True,
3490
_override_hook_target=None, local=False):
2343
3491
"""See Branch.pull.
2345
3493
This function is the core worker, used by GenericInterBranch.pull to