comparison mercurial/localrepo.py @ 16745:27b2e1823e83

branchcache: backout 0311a6abd38a
author Matt Mackall <mpm@selenic.com>
date Thu, 17 May 2012 15:34:59 -0500
parents e825a89de5d7
children b3435385f99f
comparison
equal deleted inserted replaced
16744:1c9f58a6c8f1 16745:27b2e1823e83
548 for l in lines: 548 for l in lines:
549 if not l: 549 if not l:
550 continue 550 continue
551 node, label = l.split(" ", 1) 551 node, label = l.split(" ", 1)
552 label = encoding.tolocal(label.strip()) 552 label = encoding.tolocal(label.strip())
553 if not node in self:
554 raise ValueError('invalidating branch cache because node '+
555 '%s does not exist' % node)
556 partial.setdefault(label, []).append(bin(node)) 553 partial.setdefault(label, []).append(bin(node))
557 except KeyboardInterrupt: 554 except KeyboardInterrupt:
558 raise 555 raise
559 except Exception, inst: 556 except Exception, inst:
560 if self.ui.debugflag: 557 if self.ui.debugflag:
572 f.close() 569 f.close()
573 except (IOError, OSError): 570 except (IOError, OSError):
574 pass 571 pass
575 572
576 def _updatebranchcache(self, partial, ctxgen): 573 def _updatebranchcache(self, partial, ctxgen):
577 """Given a branchhead cache, partial, that may have extra nodes or be
578 missing heads, and a generator of nodes that are at least a superset of
579 heads missing, this function updates partial to be correct.
580 """
581 # collect new branch entries 574 # collect new branch entries
582 newbranches = {} 575 newbranches = {}
583 for c in ctxgen: 576 for c in ctxgen:
584 newbranches.setdefault(c.branch(), []).append(c.node()) 577 newbranches.setdefault(c.branch(), []).append(c.node())
585 # if older branchheads are reachable from new ones, they aren't 578 # if older branchheads are reachable from new ones, they aren't
586 # really branchheads. Note checking parents is insufficient: 579 # really branchheads. Note checking parents is insufficient:
587 # 1 (branch a) -> 2 (branch b) -> 3 (branch a) 580 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
588 for branch, newnodes in newbranches.iteritems(): 581 for branch, newnodes in newbranches.iteritems():
589 bheads = partial.setdefault(branch, []) 582 bheads = partial.setdefault(branch, [])
590 bheads.extend(newnodes) 583 bheads.extend(newnodes)
591 # Remove duplicates - nodes that are in newnodes and are already in 584 if len(bheads) <= 1:
592 # bheads. This can happen if you strip a node and its parent was 585 continue
593 # already a head (because they're on different branches). 586 bheads = sorted(bheads, key=lambda x: self[x].rev())
594 bheads = set(bheads) 587 # starting from tip means fewer passes over reachable
595 588 while newnodes:
596 # Remove candidate heads that no longer are in the repo (e.g., as 589 latest = newnodes.pop()
597 # the result of a strip that just happened). 590 if latest not in bheads:
598 # avoid using 'bhead in self' here because that dives down into 591 continue
599 # branchcache code somewhat recrusively. 592 minbhnode = self[bheads[0]].node()
600 bheads = [bhead for bhead in bheads \ 593 reachable = self.changelog.reachable(latest, minbhnode)
601 if self.changelog.hasnode(bhead)] 594 reachable.remove(latest)
602 if len(bheads) > 1: 595 if reachable:
603 bheads = sorted(bheads, key=lambda x: self[x].rev()) 596 bheads = [b for b in bheads if b not in reachable]
604 # starting from tip means fewer passes over reachable
605 while newnodes:
606 latest = newnodes.pop()
607 if latest not in bheads:
608 continue
609 minbhnode = self[bheads[0]].node()
610 reachable = self.changelog.reachable(latest, minbhnode)
611 reachable.remove(latest)
612 if reachable:
613 bheads = [b for b in bheads if b not in reachable]
614 partial[branch] = bheads 597 partial[branch] = bheads
615
616 # There may be branches that cease to exist when the last commit in the
617 # branch was stripped. This code filters them out. Note that the
618 # branch that ceased to exist may not be in newbranches because
619 # newbranches is the set of candidate heads, which when you strip the
620 # last commit in a branch will be the parent branch.
621 for branch in partial.keys():
622 nodes = [head for head in partial[branch] \
623 if self.changelog.hasnode(head)]
624 if len(nodes) < 1:
625 del partial[branch]
626 598
627 def lookup(self, key): 599 def lookup(self, key):
628 return self[key].node() 600 return self[key].node()
629 601
630 def lookupbranch(self, key, remote=None): 602 def lookupbranch(self, key, remote=None):
884 ui.status(_('working directory now based on ' 856 ui.status(_('working directory now based on '
885 'revisions %d and %d\n') % parents) 857 'revisions %d and %d\n') % parents)
886 else: 858 else:
887 ui.status(_('working directory now based on ' 859 ui.status(_('working directory now based on '
888 'revision %d\n') % parents) 860 'revision %d\n') % parents)
889 # TODO: if we know which new heads may result from this rollback, pass
890 # them to destroy(), which will prevent the branchhead cache from being
891 # invalidated.
892 self.destroyed() 861 self.destroyed()
893 return 0 862 return 0
894 863
895 def invalidatecaches(self): 864 def invalidatecaches(self):
896 def delcache(name): 865 def delcache(name):
1330 finally: 1299 finally:
1331 if tr: 1300 if tr:
1332 tr.release() 1301 tr.release()
1333 lock.release() 1302 lock.release()
1334 1303
1335 def destroyed(self, newheadrevs=None): 1304 def destroyed(self):
1336 '''Inform the repository that nodes have been destroyed. 1305 '''Inform the repository that nodes have been destroyed.
1337 Intended for use by strip and rollback, so there's a common 1306 Intended for use by strip and rollback, so there's a common
1338 place for anything that has to be done after destroying history. 1307 place for anything that has to be done after destroying history.'''
1339 1308 # XXX it might be nice if we could take the list of destroyed
1340 If you know the branchheadcache was uptodate before nodes were removed 1309 # nodes, but I don't see an easy way for rollback() to do that
1341 and you also know the set of candidate set of new heads that may have
1342 resulted from the destruction, you can set newheadrevs. This will
1343 enable the code to update the branchheads cache, rather than having
1344 future code decide it's invalid and regenrating it.
1345 '''
1346 if newheadrevs:
1347 tiprev = len(self) - 1
1348 ctxgen = (self[rev] for rev in newheadrevs)
1349 self._updatebranchcache(self._branchcache, ctxgen)
1350 self._writebranchcache(self._branchcache, self.changelog.tip(),
1351 tiprev)
1352 else:
1353 # No info to update the cache. If nodes were destroyed, the cache
1354 # is stale and this will be caught the next time it is read.
1355 pass
1356 1310
1357 # Ensure the persistent tag cache is updated. Doing it now 1311 # Ensure the persistent tag cache is updated. Doing it now
1358 # means that the tag cache only has to worry about destroyed 1312 # means that the tag cache only has to worry about destroyed
1359 # heads immediately after a strip/rollback. That in turn 1313 # heads immediately after a strip/rollback. That in turn
1360 # guarantees that "cachetip == currenttip" (comparing both rev 1314 # guarantees that "cachetip == currenttip" (comparing both rev