hgext3rd/evolve/obsdiscovery.py
changeset 2082 3f787182509f
parent 2081 010a8af416a0
child 2083 778afb036245
--- 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 ###
 #############################