--- a/hgext3rd/evolve/obsdiscovery.py Thu Mar 09 19:18:11 2017 -0800
+++ b/hgext3rd/evolve/obsdiscovery.py Thu Mar 09 22:57:41 2017 -0800
@@ -16,6 +16,7 @@
import collections
import hashlib
+import math
from mercurial import (
cmdutil,
@@ -29,6 +30,7 @@
obsolete,
scmutil,
setdiscovery,
+ util,
wireproto,
)
from mercurial.hgweb import hgweb_mod
@@ -268,6 +270,131 @@
stack.extend(cs) # stack the rest for later
return result
+##############################
+### Range Hash computation ###
+##############################
+@eh.command(
+ 'debugstablerange',
+ [
+ ('', 'rev', [], 'heads to start from'),
+ ],
+ _(''))
+def debugstablerange(ui, repo, **opts):
+ """display the ::REVS set topologically sorted in a stable way
+ """
+ n = repo.changelog.node
+ s = node.short
+ revs = scmutil.revrange(repo, opts['rev'])
+ toproceed = [_range(repo, r, 0, ) for r in revs]
+ ranges = set(toproceed)
+ while toproceed:
+ entry = toproceed.pop()
+ for r in entry.subranges():
+ if r not in ranges:
+ ranges.add(r)
+ toproceed.append(r)
+ ranges = list(ranges)
+ ranges.sort(key=lambda r: (-len(r), n(r.head)))
+ ui.status('rev node index size depth\n')
+ for r in ranges:
+ ui.status('%3d %s %5d %4d %5d\n'
+ % (r.head, s(n(r.head)), r.index, len(r), r.depth))
+
+_depthcache = {}
+def _depth(repo, rev):
+ cl = repo.changelog
+ n = cl.node(rev)
+ depth = _depthcache.get(n, None)
+ if depth is None:
+ depth = _depthcache[n] = len(list(cl.ancestors([rev], inclusive=True)))
+ return depth
+
+def _hlp2(i):
+ """return highest power of two lower than 'i'"""
+ return 2 ** int(math.log(i - 1, 2))
+
+
+class _range(object):
+
+ def __init__(self, repo, head, index, revs=None):
+ self._repo = repo
+ self.head = head
+ self.index = index
+ if revs is not None:
+ self._revs = revs
+ assert index < self.depth, (head, index, self.depth, revs)
+
+ def __hash__(self):
+ return self._id
+
+ def __eq__(self, other):
+ if type(self) != type(other):
+ raise NotImplementedError()
+ return self.stablekey == other.stablekey
+
+ @util.propertycache
+ def _id(self):
+ return hash(self.stablekey)
+
+ @util.propertycache
+ def stablekey(self):
+ return (self.node, self.index)
+
+ @util.propertycache
+ def node(self):
+ return self._repo.changelog.node(self.head)
+
+ def __len__(self):
+ return len(self._revs)
+
+ @util.propertycache
+ def depth(self):
+ return _depth(self._repo, self.head)
+
+ @util.propertycache
+ def _revs(self):
+ return _stablesort(self._repo, [self.head])[self.index:]
+
+ def _slicesat(self, globalindex):
+ localindex = globalindex - self.index
+
+ cl = self._repo.changelog
+
+ bottom = self._revs[:localindex]
+ top = self._revs[localindex:]
+ bheads = self._repo.revs('heads(%ld)', bottom)
+ result = []
+ if len(bheads) == 1:
+ newhead = bottom[-1]
+ newstart = _depth(self._repo, newhead) - len(bottom)
+ result.append(_range(self._repo, newhead, newstart, bottom))
+ else:
+ cl = self._repo.changelog
+ for h in bheads:
+ subset = cl.ancestors([h], inclusive=True)
+ hrevs = [r for r in bottom if r in subset]
+ start = _depth(self._repo, h) - len(hrevs)
+ entry = _range(self._repo, h, start, [r for r in bottom if r in subset])
+ result.append(entry)
+ result.append(_range(self._repo, self.head, globalindex, top))
+ return result
+
+ def subranges(self):
+ if len(self) == 1:
+ return []
+ step = _hlp2(self.depth)
+ standard_start = 0
+ while standard_start < self.index and 0 < step:
+ if standard_start + step < self.depth:
+ standard_start += step
+ step //= 2
+ if self.index == standard_start:
+ slicesize = _hlp2(len(self))
+ return self._slicesat(self.index + slicesize)
+ else:
+ assert standard_start < self.depth
+ return self._slicesat(standard_start)
+
#############################
### Tree Hash computation ###
#############################