Show More
@@ -540,6 +540,9 b' class localrepository(repo.repository):' | |||||
540 | continue |
|
540 | continue | |
541 | node, label = l.split(" ", 1) |
|
541 | node, label = l.split(" ", 1) | |
542 | label = encoding.tolocal(label.strip()) |
|
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 | partial.setdefault(label, []).append(bin(node)) |
|
546 | partial.setdefault(label, []).append(bin(node)) | |
544 | except KeyboardInterrupt: |
|
547 | except KeyboardInterrupt: | |
545 | raise |
|
548 | raise | |
@@ -561,6 +564,10 b' class localrepository(repo.repository):' | |||||
561 | pass |
|
564 | pass | |
562 |
|
565 | |||
563 | def _updatebranchcache(self, partial, ctxgen): |
|
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 | # collect new branch entries |
|
571 | # collect new branch entries | |
565 | newbranches = {} |
|
572 | newbranches = {} | |
566 | for c in ctxgen: |
|
573 | for c in ctxgen: | |
@@ -571,21 +578,42 b' class localrepository(repo.repository):' | |||||
571 | for branch, newnodes in newbranches.iteritems(): |
|
578 | for branch, newnodes in newbranches.iteritems(): | |
572 | bheads = partial.setdefault(branch, []) |
|
579 | bheads = partial.setdefault(branch, []) | |
573 | bheads.extend(newnodes) |
|
580 | bheads.extend(newnodes) | |
574 | if len(bheads) <= 1: |
|
581 | # Remove duplicates - nodes that are in newnodes and are already in | |
575 | continue |
|
582 | # bheads. This can happen if you strip a node and its parent was | |
576 | bheads = sorted(bheads, key=lambda x: self[x].rev()) |
|
583 | # already a head (because they're on different branches). | |
577 | # starting from tip means fewer passes over reachable |
|
584 | bheads = set(bheads) | |
578 | while newnodes: |
|
585 | ||
579 | latest = newnodes.pop() |
|
586 | # Remove candidate heads that no longer are in the repo (e.g., as | |
580 | if latest not in bheads: |
|
587 | # the result of a strip that just happened). | |
581 | continue |
|
588 | # avoid using 'bhead in self' here because that dives down into | |
582 | minbhnode = self[bheads[0]].node() |
|
589 | # branchcache code somewhat recrusively. | |
583 | reachable = self.changelog.reachable(latest, minbhnode) |
|
590 | bheads = [bhead for bhead in bheads \ | |
584 | reachable.remove(latest) |
|
591 | if self.changelog.hasnode(bhead)] | |
585 |
|
|
592 | if len(bheads) > 1: | |
586 | bheads = [b for b in bheads if b not in reachable] |
|
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 | partial[branch] = bheads |
|
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 | def lookup(self, key): |
|
617 | def lookup(self, key): | |
590 | return self[key].node() |
|
618 | return self[key].node() | |
591 |
|
619 | |||
@@ -848,6 +876,9 b' class localrepository(repo.repository):' | |||||
848 | else: |
|
876 | else: | |
849 | ui.status(_('working directory now based on ' |
|
877 | ui.status(_('working directory now based on ' | |
850 | 'revision %d\n') % parents) |
|
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 | self.destroyed() |
|
882 | self.destroyed() | |
852 | return 0 |
|
883 | return 0 | |
853 |
|
884 | |||
@@ -1291,12 +1322,27 b' class localrepository(repo.repository):' | |||||
1291 | tr.release() |
|
1322 | tr.release() | |
1292 | lock.release() |
|
1323 | lock.release() | |
1293 |
|
1324 | |||
1294 | def destroyed(self): |
|
1325 | def destroyed(self, newheadrevs=None): | |
1295 | '''Inform the repository that nodes have been destroyed. |
|
1326 | '''Inform the repository that nodes have been destroyed. | |
1296 | Intended for use by strip and rollback, so there's a common |
|
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. |
|
1328 | 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 |
|
1329 | ||
1299 | # nodes, but I don't see an easy way for rollback() to do that |
|
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 | # Ensure the persistent tag cache is updated. Doing it now |
|
1347 | # Ensure the persistent tag cache is updated. Doing it now | |
1302 | # means that the tag cache only has to worry about destroyed |
|
1348 | # means that the tag cache only has to worry about destroyed |
@@ -56,6 +56,11 b' def _collectbrokencsets(repo, files, str' | |||||
56 | return s |
|
56 | return s | |
57 |
|
57 | |||
58 | def strip(ui, repo, nodelist, backup="all", topic='backup'): |
|
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 | cl = repo.changelog |
|
64 | cl = repo.changelog | |
60 | # TODO handle undo of merge sets |
|
65 | # TODO handle undo of merge sets | |
61 | if isinstance(nodelist, str): |
|
66 | if isinstance(nodelist, str): | |
@@ -63,6 +68,14 b' def strip(ui, repo, nodelist, backup="al' | |||||
63 | striplist = [cl.rev(node) for node in nodelist] |
|
68 | striplist = [cl.rev(node) for node in nodelist] | |
64 | striprev = min(striplist) |
|
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 | keeppartialbundle = backup == 'strip' |
|
79 | keeppartialbundle = backup == 'strip' | |
67 |
|
80 | |||
68 | # Some revisions with rev > striprev may not be descendants of striprev. |
|
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 | % chgrpfile) |
|
182 | % chgrpfile) | |
170 | raise |
|
183 | raise | |
171 |
|
184 | |||
172 | repo.destroyed() |
|
185 | repo.destroyed(newheadrevs) |
@@ -247,6 +247,7 b' Same thing, different code path:' | |||||
247 |
|
247 | |||
248 | $ echo b >> b |
|
248 | $ echo b >> b | |
249 | $ hg ci -m 'reopen branch' |
|
249 | $ hg ci -m 'reopen branch' | |
|
250 | created new head | |||
250 | reopening closed branch head 4 |
|
251 | reopening closed branch head 4 | |
251 | $ echo b >> b |
|
252 | $ echo b >> b | |
252 | $ hg ci --amend --close-branch |
|
253 | $ hg ci --amend --close-branch |
@@ -36,7 +36,8 b' mq patch on an empty repo' | |||||
36 | $ hg qrefresh -m 'patch 1' |
|
36 | $ hg qrefresh -m 'patch 1' | |
37 | $ show_branch_cache |
|
37 | $ show_branch_cache | |
38 | tip: 0 |
|
38 | tip: 0 | |
39 | No branch cache |
|
39 | d986d5caac23a7d44a46efc0ddaf5eb9665844cf 0 | |
|
40 | d986d5caac23a7d44a46efc0ddaf5eb9665844cf default | |||
40 |
|
41 | |||
41 | some regular revisions |
|
42 | some regular revisions | |
42 |
|
43 | |||
@@ -75,8 +76,8 b' add some mq patches' | |||||
75 | $ hg qrefresh -m 'patch 2' |
|
76 | $ hg qrefresh -m 'patch 2' | |
76 | $ show_branch_cache 1 |
|
77 | $ show_branch_cache 1 | |
77 | tip: 3 |
|
78 | tip: 3 | |
78 | c229711f16da3d7591f89b1b8d963b79bda22714 1 |
|
79 | 982611f6955f9c48d3365decea203217c945ef0d 2 | |
79 | c229711f16da3d7591f89b1b8d963b79bda22714 bar |
|
80 | 982611f6955f9c48d3365decea203217c945ef0d bar | |
80 | dc25e3827021582e979f600811852e36cbe57341 foo |
|
81 | dc25e3827021582e979f600811852e36cbe57341 foo | |
81 | branch foo: 3 |
|
82 | branch foo: 3 | |
82 | branch bar: 2 |
|
83 | branch bar: 2 |
General Comments 0
You need to be logged in to leave comments.
Login now