150
149
if self._partial_revision_history_cache[-1] == _mod_revision.NULL_REVISION:
151
150
self._partial_revision_history_cache.pop()
153
def _get_check_refs(self):
154
"""Get the references needed for check().
158
revid = self.last_revision()
159
return [('revision-existence', revid), ('lefthand-distance', revid)]
162
153
def open(base, _unsupported=False, possible_transports=None):
163
154
"""Open the branch rooted at base.
168
159
control = bzrdir.BzrDir.open(base, _unsupported,
169
160
possible_transports=possible_transports)
170
return control.open_branch(unsupported=_unsupported)
161
return control.open_branch(_unsupported)
173
def open_from_transport(transport, name=None, _unsupported=False):
164
def open_from_transport(transport, _unsupported=False):
174
165
"""Open the branch rooted at transport"""
175
166
control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
176
return control.open_branch(name=name, unsupported=_unsupported)
167
return control.open_branch(_unsupported)
179
170
def open_containing(url, possible_transports=None):
217
208
def _get_fallback_repository(self, url):
218
209
"""Get the repository we fallback to at url."""
219
210
url = urlutils.join(self.base, url)
220
a_branch = Branch.open(url,
211
a_bzrdir = bzrdir.BzrDir.open(url,
221
212
possible_transports=[self.bzrdir.root_transport])
222
return a_branch.repository
213
return a_bzrdir.open_branch().repository
224
215
def _get_tags_bytes(self):
225
216
"""Get the bytes of a serialised tags dict.
447
438
# start_revision_id.
448
439
if self._merge_sorted_revisions_cache is None:
449
440
last_revision = self.last_revision()
450
last_key = (last_revision,)
451
known_graph = self.repository.revisions.get_known_graph_ancestry(
453
self._merge_sorted_revisions_cache = known_graph.merge_sort(
441
graph = self.repository.get_graph()
442
parent_map = dict(((key, value) for key, value in
443
graph.iter_ancestry([last_revision]) if value is not None))
444
revision_graph = repository._strip_NULL_ghosts(parent_map)
445
revs = tsort.merge_sort(revision_graph, last_revision, None,
447
# Drop the sequence # before caching
448
self._merge_sorted_revisions_cache = [r[1:] for r in revs]
455
450
filtered = self._filter_merge_sorted_revisions(
456
451
self._merge_sorted_revisions_cache, start_revision_id,
457
452
stop_revision_id, stop_rule)
467
462
"""Iterate over an inclusive range of sorted revisions."""
468
463
rev_iter = iter(merge_sorted_revisions)
469
464
if start_revision_id is not None:
470
for node in rev_iter:
471
rev_id = node.key[-1]
465
for rev_id, depth, revno, end_of_merge in rev_iter:
472
466
if rev_id != start_revision_id:
475
469
# The decision to include the start or not
476
470
# depends on the stop_rule if a stop is provided
477
# so pop this node back into the iterator
478
rev_iter = chain(iter([node]), rev_iter)
472
iter([(rev_id, depth, revno, end_of_merge)]),
480
475
if stop_revision_id is None:
482
for node in rev_iter:
483
rev_id = node.key[-1]
484
yield (rev_id, node.merge_depth, node.revno,
476
for rev_id, depth, revno, end_of_merge in rev_iter:
477
yield rev_id, depth, revno, end_of_merge
486
478
elif stop_rule == 'exclude':
487
for node in rev_iter:
488
rev_id = node.key[-1]
479
for rev_id, depth, revno, end_of_merge in rev_iter:
489
480
if rev_id == stop_revision_id:
491
yield (rev_id, node.merge_depth, node.revno,
482
yield rev_id, depth, revno, end_of_merge
493
483
elif stop_rule == 'include':
494
for node in rev_iter:
495
rev_id = node.key[-1]
496
yield (rev_id, node.merge_depth, node.revno,
484
for rev_id, depth, revno, end_of_merge in rev_iter:
485
yield rev_id, depth, revno, end_of_merge
498
486
if rev_id == stop_revision_id:
500
488
elif stop_rule == 'with-merges':
503
491
left_parent = stop_rev.parent_ids[0]
505
493
left_parent = _mod_revision.NULL_REVISION
506
# left_parent is the actual revision we want to stop logging at,
507
# since we want to show the merged revisions after the stop_rev too
508
reached_stop_revision_id = False
509
revision_id_whitelist = []
510
for node in rev_iter:
511
rev_id = node.key[-1]
494
for rev_id, depth, revno, end_of_merge in rev_iter:
512
495
if rev_id == left_parent:
513
# reached the left parent after the stop_revision
515
if (not reached_stop_revision_id or
516
rev_id in revision_id_whitelist):
517
yield (rev_id, node.merge_depth, node.revno,
519
if reached_stop_revision_id or rev_id == stop_revision_id:
520
# only do the merged revs of rev_id from now on
521
rev = self.repository.get_revision(rev_id)
523
reached_stop_revision_id = True
524
revision_id_whitelist.extend(rev.parent_ids)
497
yield rev_id, depth, revno, end_of_merge
526
499
raise ValueError('invalid stop_rule %r' % stop_rule)
700
670
except (errors.NotStacked, errors.UnstackableBranchFormat,
701
671
errors.UnstackableRepositoryFormat):
674
# XXX: Lock correctness - should unlock our old repo if we were
676
# repositories don't offer an interface to remove fallback
677
# repositories today; take the conceptually simpler option and just
679
self.repository = self.bzrdir.find_repository()
680
self.repository.lock_write()
681
# for every revision reference the branch has, ensure it is pulled
683
source_repository = self._get_fallback_repository(old_url)
684
for revision_id in chain([self.last_revision()],
685
self.tags.get_reverse_tag_dict()):
686
self.repository.fetch(source_repository, revision_id,
705
689
self._activate_fallback_location(url)
706
690
# write this out after the repository is stacked to avoid setting a
707
691
# stacked config that doesn't work.
708
692
self._set_config_location('stacked_on_location', url)
711
"""Change a branch to be unstacked, copying data as needed.
713
Don't call this directly, use set_stacked_on_url(None).
715
pb = ui.ui_factory.nested_progress_bar()
717
pb.update("Unstacking")
718
# The basic approach here is to fetch the tip of the branch,
719
# including all available ghosts, from the existing stacked
720
# repository into a new repository object without the fallbacks.
722
# XXX: See <https://launchpad.net/bugs/397286> - this may not be
723
# correct for CHKMap repostiories
724
old_repository = self.repository
725
if len(old_repository._fallback_repositories) != 1:
726
raise AssertionError("can't cope with fallback repositories "
727
"of %r" % (self.repository,))
728
# unlock it, including unlocking the fallback
729
old_repository.unlock()
730
old_repository.lock_read()
732
# Repositories don't offer an interface to remove fallback
733
# repositories today; take the conceptually simpler option and just
734
# reopen it. We reopen it starting from the URL so that we
735
# get a separate connection for RemoteRepositories and can
736
# stream from one of them to the other. This does mean doing
737
# separate SSH connection setup, but unstacking is not a
738
# common operation so it's tolerable.
739
new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
740
new_repository = new_bzrdir.find_repository()
741
self.repository = new_repository
742
if self.repository._fallback_repositories:
743
raise AssertionError("didn't expect %r to have "
744
"fallback_repositories"
745
% (self.repository,))
746
# this is not paired with an unlock because it's just restoring
747
# the previous state; the lock's released when set_stacked_on_url
749
self.repository.lock_write()
750
# XXX: If you unstack a branch while it has a working tree
751
# with a pending merge, the pending-merged revisions will no
752
# longer be present. You can (probably) revert and remerge.
754
# XXX: This only fetches up to the tip of the repository; it
755
# doesn't bring across any tags. That's fairly consistent
756
# with how branch works, but perhaps not ideal.
757
self.repository.fetch(old_repository,
758
revision_id=self.last_revision(),
761
old_repository.unlock()
765
695
def _set_tags_bytes(self, bytes):
766
696
"""Mirror method for _get_tags_bytes.
1244
1179
Callers will typically also want to check the repository.
1246
:param refs: Calculated refs for this branch as specified by
1247
branch._get_check_refs()
1248
1181
:return: A BranchCheckResult.
1250
result = BranchCheckResult(self)
1183
ret = BranchCheckResult(self)
1184
mainline_parent_id = None
1251
1185
last_revno, last_revision_id = self.last_revision_info()
1252
actual_revno = refs[('lefthand-distance', last_revision_id)]
1253
if actual_revno != last_revno:
1254
result.errors.append(errors.BzrCheckError(
1255
'revno does not match len(mainline) %s != %s' % (
1256
last_revno, actual_revno)))
1257
# TODO: We should probably also check that self.revision_history
1258
# matches the repository for older branch formats.
1259
# If looking for the code that cross-checks repository parents against
1260
# the iter_reverse_revision_history output, that is now a repository
1186
real_rev_history = []
1188
for revid in self.repository.iter_reverse_revision_history(
1190
real_rev_history.append(revid)
1191
except errors.RevisionNotPresent:
1192
ret.ghosts_in_mainline = True
1194
ret.ghosts_in_mainline = False
1195
real_rev_history.reverse()
1196
if len(real_rev_history) != last_revno:
1197
raise errors.BzrCheckError('revno does not match len(mainline)'
1198
' %s != %s' % (last_revno, len(real_rev_history)))
1199
# TODO: We should probably also check that real_rev_history actually
1200
# matches self.revision_history()
1201
for revision_id in real_rev_history:
1203
revision = self.repository.get_revision(revision_id)
1204
except errors.NoSuchRevision, e:
1205
raise errors.BzrCheckError("mainline revision {%s} not in repository"
1207
# In general the first entry on the revision history has no parents.
1208
# But it's not illegal for it to have parents listed; this can happen
1209
# in imports from Arch when the parents weren't reachable.
1210
if mainline_parent_id is not None:
1211
if mainline_parent_id not in revision.parent_ids:
1212
raise errors.BzrCheckError("previous revision {%s} not listed among "
1214
% (mainline_parent_id, revision_id))
1215
mainline_parent_id = revision_id
1264
1218
def _get_checkout_format(self):
1265
1219
"""Return the most suitable metadir for a checkout of this branch.
1290
1244
# clone call. Or something. 20090224 RBC/spiv.
1291
1245
if revision_id is None:
1292
1246
revision_id = self.last_revision()
1293
dir_to = self.bzrdir.clone_on_transport(to_transport,
1294
revision_id=revision_id, stacked_on=stacked_on,
1295
create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1248
dir_to = self.bzrdir.clone_on_transport(to_transport,
1249
revision_id=revision_id, stacked_on=stacked_on,
1250
create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1251
except errors.FileExists:
1252
if not use_existing_dir:
1254
except errors.NoSuchFile:
1255
if not create_prefix:
1296
1257
return dir_to.open_branch()
1298
1259
def create_checkout(self, to_location, revision_id=None,
1366
1326
def supports_tags(self):
1367
1327
return self._format.supports_tags()
1369
def automatic_tag_name(self, revision_id):
1370
"""Try to automatically find the tag name for a revision.
1372
:param revision_id: Revision id of the revision.
1373
:return: A tag name or None if no tag name could be determined.
1375
for hook in Branch.hooks['automatic_tag_name']:
1376
ret = hook(self, revision_id)
1381
1329
def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1383
1331
"""Ensure that revision_b is a descendant of revision_a.
1447
1395
return not (self == other)
1450
def find_format(klass, a_bzrdir, name=None):
1398
def find_format(klass, a_bzrdir):
1451
1399
"""Return the format for the branch object in a_bzrdir."""
1453
transport = a_bzrdir.get_branch_transport(None, name=name)
1454
format_string = transport.get_bytes("format")
1401
transport = a_bzrdir.get_branch_transport(None)
1402
format_string = transport.get("format").read()
1455
1403
return klass._formats[format_string]
1456
1404
except errors.NoSuchFile:
1457
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1405
raise errors.NotBranchError(path=transport.base)
1458
1406
except KeyError:
1459
1407
raise errors.UnknownFormatError(format=format_string, kind='branch')
1496
1444
"""Return the short format description for this format."""
1497
1445
raise NotImplementedError(self.get_format_description)
1499
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1500
lock_type='metadir', set_format=True):
1447
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
1501
1449
"""Initialize a branch in a bzrdir, with specified files
1503
1451
:param a_bzrdir: The bzrdir to initialize the branch in
1504
1452
:param utf8_files: The files to create as a list of
1505
1453
(filename, content) tuples
1506
:param name: Name of colocated branch to create, if any
1507
1454
:param set_format: If True, set the format with
1508
1455
self.get_format_string. (BzrBranch4 has its format set
1510
1457
:return: a branch in this format
1512
1459
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1513
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1460
branch_transport = a_bzrdir.get_branch_transport(self)
1515
1462
'metadir': ('lock', lockdir.LockDir),
1516
1463
'branch4': ('branch-lock', lockable_files.TransportLock),
1539
1486
control_files.unlock()
1540
return self.open(a_bzrdir, name, _found=True)
1487
return self.open(a_bzrdir, _found=True)
1542
def initialize(self, a_bzrdir, name=None):
1543
"""Create a branch of this format in a_bzrdir.
1545
:param name: Name of the colocated branch to create.
1489
def initialize(self, a_bzrdir):
1490
"""Create a branch of this format in a_bzrdir."""
1547
1491
raise NotImplementedError(self.initialize)
1549
1493
def is_supported(self):
1580
1524
raise NotImplementedError(self.network_name)
1582
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1526
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1583
1527
"""Return the branch object for a_bzrdir
1585
1529
:param a_bzrdir: A BzrDir that contains a branch.
1586
:param name: Name of colocated branch to open
1587
1530
:param _found: a private parameter, do not use it. It is used to
1588
1531
indicate if format probing has already be done.
1589
1532
:param ignore_fallbacks: when set, no fallback branches will be opened
1697
1640
"multiple hooks installed for transform_fallback_location, "
1698
1641
"all are called with the url returned from the previous hook."
1699
1642
"The order is however undefined.", (1, 9), None))
1700
self.create_hook(HookPoint('automatic_tag_name',
1701
"Called to determine an automatic tag name for a revision."
1702
"automatic_tag_name is called with (branch, revision_id) and "
1703
"should return a tag name or None if no tag name could be "
1704
"determined. The first non-None tag name returned will be used.",
1709
1645
# install the default hooks into the Branch class.
1760
1696
"""See BranchFormat.get_format_description()."""
1761
1697
return "Branch format 4"
1763
def initialize(self, a_bzrdir, name=None):
1699
def initialize(self, a_bzrdir):
1764
1700
"""Create a branch of this format in a_bzrdir."""
1765
1701
utf8_files = [('revision-history', ''),
1766
1702
('branch-name', ''),
1768
return self._initialize_helper(a_bzrdir, utf8_files, name=name,
1704
return self._initialize_helper(a_bzrdir, utf8_files,
1769
1705
lock_type='branch4', set_format=False)
1771
1707
def __init__(self):
1806
1741
return self.get_format_string()
1808
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1743
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1809
1744
"""See BranchFormat.open()."""
1811
format = BranchFormat.find_format(a_bzrdir, name=name)
1746
format = BranchFormat.find_format(a_bzrdir)
1812
1747
if format.__class__ != self.__class__:
1813
1748
raise AssertionError("wrong format %r found for %r" %
1814
1749
(format, self))
1816
transport = a_bzrdir.get_branch_transport(None, name=name)
1751
transport = a_bzrdir.get_branch_transport(None)
1817
1752
control_files = lockable_files.LockableFiles(transport, 'lock',
1818
1753
lockdir.LockDir)
1819
1754
return self._branch_class()(_format=self,
1820
1755
_control_files=control_files,
1822
1756
a_bzrdir=a_bzrdir,
1823
1757
_repository=a_bzrdir.find_repository(),
1824
1758
ignore_fallbacks=ignore_fallbacks)
1825
1759
except errors.NoSuchFile:
1826
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1760
raise errors.NotBranchError(path=transport.base)
1828
1762
def __init__(self):
1829
1763
super(BranchFormatMetadir, self).__init__()
1858
1792
"""See BranchFormat.get_format_description()."""
1859
1793
return "Branch format 5"
1861
def initialize(self, a_bzrdir, name=None):
1795
def initialize(self, a_bzrdir):
1862
1796
"""Create a branch of this format in a_bzrdir."""
1863
1797
utf8_files = [('revision-history', ''),
1864
1798
('branch-name', ''),
1866
return self._initialize_helper(a_bzrdir, utf8_files, name)
1800
return self._initialize_helper(a_bzrdir, utf8_files)
1868
1802
def supports_tags(self):
1891
1825
"""See BranchFormat.get_format_description()."""
1892
1826
return "Branch format 6"
1894
def initialize(self, a_bzrdir, name=None):
1828
def initialize(self, a_bzrdir):
1895
1829
"""Create a branch of this format in a_bzrdir."""
1896
1830
utf8_files = [('last-revision', '0 null:\n'),
1897
1831
('branch.conf', ''),
1900
return self._initialize_helper(a_bzrdir, utf8_files, name)
1834
return self._initialize_helper(a_bzrdir, utf8_files)
1902
1836
def make_tags(self, branch):
1903
1837
"""See bzrlib.branch.BranchFormat.make_tags()."""
1921
1855
"""See BranchFormat.get_format_description()."""
1922
1856
return "Branch format 8"
1924
def initialize(self, a_bzrdir, name=None):
1858
def initialize(self, a_bzrdir):
1925
1859
"""Create a branch of this format in a_bzrdir."""
1926
1860
utf8_files = [('last-revision', '0 null:\n'),
1927
1861
('branch.conf', ''),
1929
1863
('references', '')
1931
return self._initialize_helper(a_bzrdir, utf8_files, name)
1865
return self._initialize_helper(a_bzrdir, utf8_files)
1933
1867
def __init__(self):
1934
1868
super(BzrBranchFormat8, self).__init__()
1957
1891
This format was introduced in bzr 1.6.
1960
def initialize(self, a_bzrdir, name=None):
1894
def initialize(self, a_bzrdir):
1961
1895
"""Create a branch of this format in a_bzrdir."""
1962
1896
utf8_files = [('last-revision', '0 null:\n'),
1963
1897
('branch.conf', ''),
1966
return self._initialize_helper(a_bzrdir, utf8_files, name)
1900
return self._initialize_helper(a_bzrdir, utf8_files)
1968
1902
def _branch_class(self):
1969
1903
return BzrBranch7
2004
1938
def get_reference(self, a_bzrdir):
2005
1939
"""See BranchFormat.get_reference()."""
2006
1940
transport = a_bzrdir.get_branch_transport(None)
2007
return transport.get_bytes('location')
1941
return transport.get('location').read()
2009
1943
def set_reference(self, a_bzrdir, to_branch):
2010
1944
"""See BranchFormat.set_reference()."""
2011
1945
transport = a_bzrdir.get_branch_transport(None)
2012
1946
location = transport.put_bytes('location', to_branch.base)
2014
def initialize(self, a_bzrdir, name=None, target_branch=None):
1948
def initialize(self, a_bzrdir, target_branch=None):
2015
1949
"""Create a branch of this format in a_bzrdir."""
2016
1950
if target_branch is None:
2017
1951
# this format does not implement branch itself, thus the implicit
2018
1952
# creation contract must see it as uninitializable
2019
1953
raise errors.UninitializableFormat(self)
2020
1954
mutter('creating branch reference in %s', a_bzrdir.transport.base)
2021
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1955
branch_transport = a_bzrdir.get_branch_transport(self)
2022
1956
branch_transport.put_bytes('location',
2023
1957
target_branch.bzrdir.root_transport.base)
2024
1958
branch_transport.put_bytes('format', self.get_format_string())
2025
1959
return self.open(
2026
a_bzrdir, name, _found=True,
1960
a_bzrdir, _found=True,
2027
1961
possible_transports=[target_branch.bzrdir.root_transport])
2029
1963
def __init__(self):
2036
1970
def clone(to_bzrdir, revision_id=None,
2037
1971
repository_policy=None):
2038
1972
"""See Branch.clone()."""
2039
return format.initialize(to_bzrdir, target_branch=a_branch)
1973
return format.initialize(to_bzrdir, a_branch)
2040
1974
# cannot obey revision_id limits when cloning a reference ...
2041
1975
# FIXME RBC 20060210 either nuke revision_id for clone, or
2042
1976
# emit some sort of warning/error to the caller ?!
2045
def open(self, a_bzrdir, name=None, _found=False, location=None,
1979
def open(self, a_bzrdir, _found=False, location=None,
2046
1980
possible_transports=None, ignore_fallbacks=False):
2047
1981
"""Return the branch that the branch reference in a_bzrdir points at.
2049
1983
:param a_bzrdir: A BzrDir that contains a branch.
2050
:param name: Name of colocated branch to open, if any
2051
1984
:param _found: a private parameter, do not use it. It is used to
2052
1985
indicate if format probing has already be done.
2053
1986
:param ignore_fallbacks: when set, no fallback branches will be opened
2066
1999
location = self.get_reference(a_bzrdir)
2067
2000
real_bzrdir = bzrdir.BzrDir.open(
2068
2001
location, possible_transports=possible_transports)
2069
result = real_bzrdir.open_branch(name=name,
2070
ignore_fallbacks=ignore_fallbacks)
2002
result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
2071
2003
# this changes the behaviour of result.clone to create a new reference
2072
2004
# rather than a copy of the content of the branch.
2073
2005
# I did not use a proxy object because that needs much more extensive
2100
2032
BranchFormat.register_format(__format6)
2101
2033
BranchFormat.register_format(__format7)
2102
2034
BranchFormat.register_format(__format8)
2103
BranchFormat.set_default_format(__format7)
2035
BranchFormat.set_default_format(__format6)
2104
2036
_legacy_formats = [BzrBranchFormat4(),
2106
2038
network_format_registry.register(
2107
2039
_legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2110
class BzrBranch(Branch, _RelockDebugMixin):
2042
class BzrBranch(Branch):
2111
2043
"""A branch stored in the actual filesystem.
2113
2045
Note that it's "local" in the context of the filesystem; it doesn't
2119
2051
:ivar repository: Repository for this branch.
2120
2052
:ivar base: The url of the base directory for this branch; the one
2121
2053
containing the .bzr directory.
2122
:ivar name: Optional colocated branch name as it exists in the control
2126
2056
def __init__(self, _format=None,
2127
_control_files=None, a_bzrdir=None, name=None,
2128
_repository=None, ignore_fallbacks=False):
2057
_control_files=None, a_bzrdir=None, _repository=None,
2058
ignore_fallbacks=False):
2129
2059
"""Create new branch object at a particular location."""
2130
2060
if a_bzrdir is None:
2131
2061
raise ValueError('a_bzrdir must be supplied')
2133
2063
self.bzrdir = a_bzrdir
2134
2064
self._base = self.bzrdir.transport.clone('..').base
2136
2065
# XXX: We should be able to just do
2137
2066
# self.base = self.bzrdir.root_transport.base
2138
2067
# but this does not quite work yet -- mbp 20080522
2885
2804
:param verbose: Requests more detailed display of what was checked,
2888
note('checked branch %s format %s', self.branch.base,
2889
self.branch._format)
2890
for error in self.errors:
2891
note('found error:%s', error)
2807
note('checked branch %s format %s',
2809
self.branch._format)
2810
if self.ghosts_in_mainline:
2811
note('branch contains ghosts in mainline')
2894
2814
class Converter5to6(object):