diff --git a/mercurial/branchmap.py b/mercurial/branchmap.py --- a/mercurial/branchmap.py +++ b/mercurial/branchmap.py @@ -358,7 +358,7 @@ class revbranchcache(object): self._repo = repo self._names = [] # branch names in local encoding with static index self._rbcrevs = array('c') # structs of type _rbcrecfmt - self._rbcsnameslen = 0 + self._rbcsnameslen = 0 # length of names read at _rbcsnameslen try: bndata = repo.vfs.read(_rbcnames) self._rbcsnameslen = len(bndata) # for verification before writing @@ -380,7 +380,8 @@ class revbranchcache(object): len(repo.changelog)) if self._rbcrevslen == 0: self._names = [] - self._rbcnamescount = len(self._names) # number of good names on disk + self._rbcnamescount = len(self._names) # number of names read at + # _rbcsnameslen self._namesreverse = dict((b, r) for r, b in enumerate(self._names)) def _clear(self): @@ -416,13 +417,17 @@ class revbranchcache(object): if cachenode == '\0\0\0\0': pass elif cachenode == reponode: - if branchidx < self._rbcnamescount: + try: return self._names[branchidx], close - # referenced branch doesn't exist - rebuild is expensive but needed - self._repo.ui.debug("rebuilding corrupted revision branch cache\n") - self._clear() + except IndexError: + # recover from invalid reference to unknown branch + self._repo.ui.debug("referenced branch names not found" + " - rebuilding revision branch cache from scratch\n") + self._clear() else: # rev/node map has changed, invalidate the cache from here up + self._repo.ui.debug("history modification detected - truncating " + "revision branch cache to revision %s\n" % rev) truncate = rbcrevidx + _rbcrecsize del self._rbcrevs[truncate:] self._rbcrevslen = min(self._rbcrevslen, truncate) diff --git a/tests/test-branches.t b/tests/test-branches.t --- a/tests/test-branches.t +++ b/tests/test-branches.t @@ -587,6 +587,8 @@ recovery from invalid cache file with so $ f --size .hg/cache/rbc-revs* .hg/cache/rbc-revs-v1: size=120 $ hg log -r 'branch(.)' -T '{rev} ' --debug + history modification detected - truncating revision branch cache to revision 13 + history modification detected - truncating revision branch cache to revision 1 3 4 8 9 10 11 12 13 truncating cache/rbc-revs-v1 to 8 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug 5 @@ -632,7 +634,7 @@ situation where the cache is out of sync cache is rebuilt when corruption is detected $ echo > .hg/cache/rbc-names-v1 $ hg log -r '5:&branch(.)' -T '{rev} ' --debug - rebuilding corrupted revision branch cache + referenced branch names not found - rebuilding revision branch cache from scratch 8 9 10 11 12 13 truncating cache/rbc-revs-v1 to 40 $ f --size --hexdump .hg/cache/rbc-* .hg/cache/rbc-names-v1: size=79 @@ -688,16 +690,13 @@ Test for multiple incorrect branch cache 0010: 56 46 78 69 00 00 00 01 |VFxi....| $ : > .hg/cache/rbc-revs-v1 +No superfluous rebuilding of cache: $ hg log -r "branch(null)&branch(branch)" --debug - rebuilding corrupted revision branch cache - rebuilding corrupted revision branch cache - truncating cache/rbc-revs-v1 to 8 -BUG: the cache was declared corrupt multiple times and not fully rebuilt: $ f --size --hexdump .hg/cache/rbc-* .hg/cache/rbc-names-v1: size=14 0000: 64 65 66 61 75 6c 74 00 62 72 61 6e 63 68 |default.branch| .hg/cache/rbc-revs-v1: size=24 - 0000: 00 00 00 00 00 00 00 00 fa 4c 04 e5 00 00 00 00 |.........L......| + 0000: 66 e5 f5 aa 00 00 00 00 fa 4c 04 e5 00 00 00 00 |f........L......| 0010: 56 46 78 69 00 00 00 01 |VFxi....| $ cd .. diff --git a/tests/test-rebase-conflicts.t b/tests/test-rebase-conflicts.t --- a/tests/test-rebase-conflicts.t +++ b/tests/test-rebase-conflicts.t @@ -302,6 +302,7 @@ Check that the right ancestors is used w bundle2-input-part: total payload size 1713 bundle2-input-bundle: 0 parts total invalid branchheads cache (served): tip differs + history modification detected - truncating revision branch cache to revision 9 rebase completed updating the branch cache truncating cache/rbc-revs-v1 to 72