##// END OF EJS Templates
localrepo: strip now incrementally updates the branchheads cache...
Joshua Redstone -
r16716:0311a6ab default
parent child Browse files
Show More
@@ -540,6 +540,9 b' class localrepository(repo.repository):'
540 540 continue
541 541 node, label = l.split(" ", 1)
542 542 label = encoding.tolocal(label.strip())
543 if not node in self:
544 raise ValueError('invalidating branch cache because node '+
545 '%s does not exist' % node)
543 546 partial.setdefault(label, []).append(bin(node))
544 547 except KeyboardInterrupt:
545 548 raise
@@ -561,6 +564,10 b' class localrepository(repo.repository):'
561 564 pass
562 565
563 566 def _updatebranchcache(self, partial, ctxgen):
567 """Given a branchhead cache, partial, that may have extra nodes or be
568 missing heads, and a generator of nodes that are at least a superset of
569 heads missing, this function updates partial to be correct.
570 """
564 571 # collect new branch entries
565 572 newbranches = {}
566 573 for c in ctxgen:
@@ -571,21 +578,42 b' class localrepository(repo.repository):'
571 578 for branch, newnodes in newbranches.iteritems():
572 579 bheads = partial.setdefault(branch, [])
573 580 bheads.extend(newnodes)
574 if len(bheads) <= 1:
575 continue
576 bheads = sorted(bheads, key=lambda x: self[x].rev())
577 # starting from tip means fewer passes over reachable
578 while newnodes:
579 latest = newnodes.pop()
580 if latest not in bheads:
581 continue
582 minbhnode = self[bheads[0]].node()
583 reachable = self.changelog.reachable(latest, minbhnode)
584 reachable.remove(latest)
585 if reachable:
586 bheads = [b for b in bheads if b not in reachable]
581 # Remove duplicates - nodes that are in newnodes and are already in
582 # bheads. This can happen if you strip a node and its parent was
583 # already a head (because they're on different branches).
584 bheads = set(bheads)
585
586 # Remove candidate heads that no longer are in the repo (e.g., as
587 # the result of a strip that just happened).
588 # avoid using 'bhead in self' here because that dives down into
589 # branchcache code somewhat recrusively.
590 bheads = [bhead for bhead in bheads \
591 if self.changelog.hasnode(bhead)]
592 if len(bheads) > 1:
593 bheads = sorted(bheads, key=lambda x: self[x].rev())
594 # starting from tip means fewer passes over reachable
595 while newnodes:
596 latest = newnodes.pop()
597 if latest not in bheads:
598 continue
599 minbhnode = self[bheads[0]].node()
600 reachable = self.changelog.reachable(latest, minbhnode)
601 reachable.remove(latest)
602 if reachable:
603 bheads = [b for b in bheads if b not in reachable]
587 604 partial[branch] = bheads
588 605
606 # There may be branches that cease to exist when the last commit in the
607 # branch was stripped. This code filters them out. Note that the
608 # branch that ceased to exist may not be in newbranches because
609 # newbranches is the set of candidate heads, which when you strip the
610 # last commit in a branch will be the parent branch.
611 for branch in partial.keys():
612 nodes = [head for head in partial[branch] \
613 if self.changelog.hasnode(head)]
614 if len(nodes) < 1:
615 del partial[branch]
616
589 617 def lookup(self, key):
590 618 return self[key].node()
591 619
@@ -848,6 +876,9 b' class localrepository(repo.repository):'
848 876 else:
849 877 ui.status(_('working directory now based on '
850 878 'revision %d\n') % parents)
879 # TODO: if we know which new heads may result from this rollback, pass
880 # them to destroy(), which will prevent the branchhead cache from being
881 # invalidated.
851 882 self.destroyed()
852 883 return 0
853 884
@@ -1291,12 +1322,27 b' class localrepository(repo.repository):'
1291 1322 tr.release()
1292 1323 lock.release()
1293 1324
1294 def destroyed(self):
1325 def destroyed(self, newheadrevs=None):
1295 1326 '''Inform the repository that nodes have been destroyed.
1296 1327 Intended for use by strip and rollback, so there's a common
1297 place for anything that has to be done after destroying history.'''
1298 # XXX it might be nice if we could take the list of destroyed
1299 # nodes, but I don't see an easy way for rollback() to do that
1328 place for anything that has to be done after destroying history.
1329
1330 If you know the branchheadcache was uptodate before nodes were removed
1331 and you also know the set of candidate set of new heads that may have
1332 resulted from the destruction, you can set newheadrevs. This will
1333 enable the code to update the branchheads cache, rather than having
1334 future code decide it's invalid and regenrating it.
1335 '''
1336 if newheadrevs:
1337 tiprev = len(self) - 1
1338 ctxgen = (self[rev] for rev in newheadrevs)
1339 self._updatebranchcache(self._branchcache, ctxgen)
1340 self._writebranchcache(self._branchcache, self.changelog.tip(),
1341 tiprev)
1342 else:
1343 # No info to update the cache. If nodes were destroyed, the cache
1344 # is stale and this will be caught the next time it is read.
1345 pass
1300 1346
1301 1347 # Ensure the persistent tag cache is updated. Doing it now
1302 1348 # means that the tag cache only has to worry about destroyed
@@ -56,6 +56,11 b' def _collectbrokencsets(repo, files, str'
56 56 return s
57 57
58 58 def strip(ui, repo, nodelist, backup="all", topic='backup'):
59 # It simplifies the logic around updating the branchheads cache if we only
60 # have to consider the effect of the stripped revisions and not revisions
61 # missing because the cache is out-of-date.
62 repo.updatebranchcache()
63
59 64 cl = repo.changelog
60 65 # TODO handle undo of merge sets
61 66 if isinstance(nodelist, str):
@@ -63,6 +68,14 b' def strip(ui, repo, nodelist, backup="al'
63 68 striplist = [cl.rev(node) for node in nodelist]
64 69 striprev = min(striplist)
65 70
71 # Set of potential new heads resulting from the strip. The parents of any
72 # node removed could be a new head because the node to be removed could have
73 # been the only child of the parent.
74 # Do a list->set->list conversion to remove duplicates.
75 stringstriplist = [str(rev) for rev in striplist]
76 newheadrevs = set(repo.revs("parents(%lr::) - %lr::", stringstriplist,
77 stringstriplist))
78
66 79 keeppartialbundle = backup == 'strip'
67 80
68 81 # Some revisions with rev > striprev may not be descendants of striprev.
@@ -169,4 +182,4 b' def strip(ui, repo, nodelist, backup="al'
169 182 % chgrpfile)
170 183 raise
171 184
172 repo.destroyed()
185 repo.destroyed(newheadrevs)
@@ -247,6 +247,7 b' Same thing, different code path:'
247 247
248 248 $ echo b >> b
249 249 $ hg ci -m 'reopen branch'
250 created new head
250 251 reopening closed branch head 4
251 252 $ echo b >> b
252 253 $ hg ci --amend --close-branch
@@ -36,7 +36,8 b' mq patch on an empty repo'
36 36 $ hg qrefresh -m 'patch 1'
37 37 $ show_branch_cache
38 38 tip: 0
39 No branch cache
39 d986d5caac23a7d44a46efc0ddaf5eb9665844cf 0
40 d986d5caac23a7d44a46efc0ddaf5eb9665844cf default
40 41
41 42 some regular revisions
42 43
@@ -75,8 +76,8 b' add some mq patches'
75 76 $ hg qrefresh -m 'patch 2'
76 77 $ show_branch_cache 1
77 78 tip: 3
78 c229711f16da3d7591f89b1b8d963b79bda22714 1
79 c229711f16da3d7591f89b1b8d963b79bda22714 bar
79 982611f6955f9c48d3365decea203217c945ef0d 2
80 982611f6955f9c48d3365decea203217c945ef0d bar
80 81 dc25e3827021582e979f600811852e36cbe57341 foo
81 82 branch foo: 3
82 83 branch bar: 2
General Comments 0
You need to be logged in to leave comments. Login now