148
150
if self._partial_revision_history_cache[-1] == _mod_revision.NULL_REVISION:
149
151
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)]
152
162
def open(base, _unsupported=False, possible_transports=None):
153
163
"""Open the branch rooted at base.
437
447
# start_revision_id.
438
448
if self._merge_sorted_revisions_cache is None:
439
449
last_revision = self.last_revision()
440
graph = self.repository.get_graph()
441
parent_map = dict(((key, value) for key, value in
442
graph.iter_ancestry([last_revision]) if value is not None))
443
revision_graph = repository._strip_NULL_ghosts(parent_map)
444
revs = tsort.merge_sort(revision_graph, last_revision, None,
446
# Drop the sequence # before caching
447
self._merge_sorted_revisions_cache = [r[1:] for r in revs]
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(
449
455
filtered = self._filter_merge_sorted_revisions(
450
456
self._merge_sorted_revisions_cache, start_revision_id,
451
457
stop_revision_id, stop_rule)
461
467
"""Iterate over an inclusive range of sorted revisions."""
462
468
rev_iter = iter(merge_sorted_revisions)
463
469
if start_revision_id is not None:
464
for rev_id, depth, revno, end_of_merge in rev_iter:
470
for node in rev_iter:
471
rev_id = node.key[-1]
465
472
if rev_id != start_revision_id:
468
475
# The decision to include the start or not
469
476
# depends on the stop_rule if a stop is provided
471
iter([(rev_id, depth, revno, end_of_merge)]),
477
# so pop this node back into the iterator
478
rev_iter = chain(iter([node]), rev_iter)
474
480
if stop_revision_id is None:
475
for rev_id, depth, revno, end_of_merge in rev_iter:
476
yield rev_id, depth, revno, end_of_merge
482
for node in rev_iter:
483
rev_id = node.key[-1]
484
yield (rev_id, node.merge_depth, node.revno,
477
486
elif stop_rule == 'exclude':
478
for rev_id, depth, revno, end_of_merge in rev_iter:
487
for node in rev_iter:
488
rev_id = node.key[-1]
479
489
if rev_id == stop_revision_id:
481
yield rev_id, depth, revno, end_of_merge
491
yield (rev_id, node.merge_depth, node.revno,
482
493
elif stop_rule == 'include':
483
for rev_id, depth, revno, end_of_merge in rev_iter:
484
yield rev_id, depth, revno, end_of_merge
494
for node in rev_iter:
495
rev_id = node.key[-1]
496
yield (rev_id, node.merge_depth, node.revno,
485
498
if rev_id == stop_revision_id:
487
500
elif stop_rule == 'with-merges':
490
503
left_parent = stop_rev.parent_ids[0]
492
505
left_parent = _mod_revision.NULL_REVISION
493
for rev_id, depth, revno, end_of_merge in rev_iter:
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
512
if rev_id == left_parent:
513
# reached the left parent after the stop_revision
496
yield rev_id, depth, revno, end_of_merge
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)
498
526
raise ValueError('invalid stop_rule %r' % stop_rule)
669
700
except (errors.NotStacked, errors.UnstackableBranchFormat,
670
701
errors.UnstackableRepositoryFormat):
673
# XXX: Lock correctness - should unlock our old repo if we were
675
# repositories don't offer an interface to remove fallback
676
# repositories today; take the conceptually simpler option and just
678
self.repository = self.bzrdir.find_repository()
679
self.repository.lock_write()
680
# for every revision reference the branch has, ensure it is pulled
682
source_repository = self._get_fallback_repository(old_url)
683
for revision_id in chain([self.last_revision()],
684
self.tags.get_reverse_tag_dict()):
685
self.repository.fetch(source_repository, revision_id,
688
705
self._activate_fallback_location(url)
689
706
# write this out after the repository is stacked to avoid setting a
690
707
# stacked config that doesn't work.
691
708
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()
694
765
def _set_tags_bytes(self, bytes):
695
766
"""Mirror method for _get_tags_bytes.
1178
1252
Callers will typically also want to check the repository.
1254
:param refs: Calculated refs for this branch as specified by
1255
branch._get_check_refs()
1180
1256
:return: A BranchCheckResult.
1182
ret = BranchCheckResult(self)
1183
mainline_parent_id = None
1258
result = BranchCheckResult(self)
1184
1259
last_revno, last_revision_id = self.last_revision_info()
1185
real_rev_history = []
1187
for revid in self.repository.iter_reverse_revision_history(
1189
real_rev_history.append(revid)
1190
except errors.RevisionNotPresent:
1191
ret.ghosts_in_mainline = True
1193
ret.ghosts_in_mainline = False
1194
real_rev_history.reverse()
1195
if len(real_rev_history) != last_revno:
1196
raise errors.BzrCheckError('revno does not match len(mainline)'
1197
' %s != %s' % (last_revno, len(real_rev_history)))
1198
# TODO: We should probably also check that real_rev_history actually
1199
# matches self.revision_history()
1200
for revision_id in real_rev_history:
1202
revision = self.repository.get_revision(revision_id)
1203
except errors.NoSuchRevision, e:
1204
raise errors.BzrCheckError("mainline revision {%s} not in repository"
1206
# In general the first entry on the revision history has no parents.
1207
# But it's not illegal for it to have parents listed; this can happen
1208
# in imports from Arch when the parents weren't reachable.
1209
if mainline_parent_id is not None:
1210
if mainline_parent_id not in revision.parent_ids:
1211
raise errors.BzrCheckError("previous revision {%s} not listed among "
1213
% (mainline_parent_id, revision_id))
1214
mainline_parent_id = revision_id
1260
actual_revno = refs[('lefthand-distance', last_revision_id)]
1261
if actual_revno != last_revno:
1262
result.errors.append(errors.BzrCheckError(
1263
'revno does not match len(mainline) %s != %s' % (
1264
last_revno, actual_revno)))
1265
# TODO: We should probably also check that self.revision_history
1266
# matches the repository for older branch formats.
1267
# If looking for the code that cross-checks repository parents against
1268
# the iter_reverse_revision_history output, that is now a repository
1217
1272
def _get_checkout_format(self):
1218
1273
"""Return the most suitable metadir for a checkout of this branch.
1243
1298
# clone call. Or something. 20090224 RBC/spiv.
1244
1299
if revision_id is None:
1245
1300
revision_id = self.last_revision()
1247
dir_to = self.bzrdir.clone_on_transport(to_transport,
1248
revision_id=revision_id, stacked_on=stacked_on,
1249
create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1250
except errors.FileExists:
1251
if not use_existing_dir:
1253
except errors.NoSuchFile:
1254
if not create_prefix:
1301
dir_to = self.bzrdir.clone_on_transport(to_transport,
1302
revision_id=revision_id, stacked_on=stacked_on,
1303
create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1256
1304
return dir_to.open_branch()
1258
1306
def create_checkout(self, to_location, revision_id=None,
2031
2079
BranchFormat.register_format(__format6)
2032
2080
BranchFormat.register_format(__format7)
2033
2081
BranchFormat.register_format(__format8)
2034
BranchFormat.set_default_format(__format6)
2082
BranchFormat.set_default_format(__format7)
2035
2083
_legacy_formats = [BzrBranchFormat4(),
2037
2085
network_format_registry.register(
2038
2086
_legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2041
class BzrBranch(Branch):
2089
class BzrBranch(Branch, _RelockDebugMixin):
2042
2090
"""A branch stored in the actual filesystem.
2044
2092
Note that it's "local" in the context of the filesystem; it doesn't
2803
2856
:param verbose: Requests more detailed display of what was checked,
2806
note('checked branch %s format %s',
2808
self.branch._format)
2809
if self.ghosts_in_mainline:
2810
note('branch contains ghosts in mainline')
2859
note('checked branch %s format %s', self.branch.base,
2860
self.branch._format)
2861
for error in self.errors:
2862
note('found error:%s', error)
2813
2865
class Converter5to6(object):