diff hgext3rd/pullbundle.py @ 4138:cfdc6f55599b

pullbundle: improve slicing of the lower part of range The previous method could get confuse by merge and overslice. The new method is better at using sticking on power of two boundaries.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Tue, 25 Sep 2018 12:20:26 +0200
parents be3a94d3105f
children 2bd652bece97
line wrap: on
line diff
--- a/hgext3rd/pullbundle.py	Tue Sep 25 12:19:41 2018 +0200
+++ b/hgext3rd/pullbundle.py	Tue Sep 25 12:20:26 2018 +0200
@@ -213,6 +213,16 @@
         size = rangelength(rangeid)
         slices.append((rangeid, nodes[idx:idx + size]))
         idx += size
+    ### slow code block to validate ranges content
+    # rev = repo.changelog.nodemap.get
+    # for ri, ns in slices:
+    #     a = set(rev(n) for n in ns)
+    #     b = set(repo.stablerange.revsfromrange(repo, ri))
+    #     l = repo.stablerange.rangelength(repo, ri)
+    #     repo.ui.write('range-length: %d-%d %s %s\n' % (ri[0], ri[1], l, len(a)))
+    #     if a != b:
+    #         d =  (ri[0], ri[1], b - a, a - b)
+    #         repo.ui.write("mismatching content: %d-%d -%s +%s\n" % d)
     return slices
 
 def canonicalsubranges(repo, stablerange, rangeid):
@@ -246,38 +256,51 @@
 
     # 2. optimise, bottom part
     if skip != cut:
-        tailranges = []
-        tailsize = cut - skip
+        currentsize = tailsize = cut - skip
         assert 0 < tailsize, tailsize
+
+        # we need to take several "standard cut" in the bottom part
+        #
+        # This is similar to what we will do for the top part, we reusing the
+        # existing structure is a bit more complex.
+        allcuts = list(reversed(standardcut(tailsize)))
         prerange = (headrev, precut)
-        size = stablerange.rangelength(repo, prerange)
-        sub = stablerange.subranges(repo, prerange)[:-1]
-        # This power of two check is too simplistic and misbehave when too many
-        # merge are involved. because de merge, there can be "canonical" range
-        # with various size.
-        while not poweroftwo(tailsize):
-            for prerange in reversed(sub):
-                if tailsize <= 0:
-                    break
+        ### slow code block to check we operate on the right data
+        # rev = repo.changelog.nodemap.get
+        # allrevs = [rev(n) for n in nodes]
+        # allrevs.reverse()
+        # prerevs = repo.stablerange.revsfromrange(repo, prerange)
+        # assert allrevs == prerevs[(len(prerevs) - len(allrevs)):]
+        # end of check
+        sub = list(stablerange.subranges(repo, prerange)[:-1])
 
-                assert stablerange.depthrev(repo, prerange[0]) != prerange[1], prerange
-                tailrev, tailskip = prerange
-                size = stablerange.rangelength(repo, (tailrev, tailskip))
-                if tailsize < size:
-                    tailskip += size - tailsize
-                    size = tailsize
-                tailranges.append((tailrev, tailskip))
-                tailsize -= size
-            else:
-                # size of the last block
-                tailsize = stablerange.rangelength(repo, tailranges[-1])
-                if poweroftwo(tailsize):
-                    continue # exit the loop
-                prerange = tailranges.pop()
-                sub = stablerange.subranges(repo, prerange)
-
-        tailranges.reverse()
-        canonicals.extend(tailranges)
+        bottomranges = []
+        # XXX we might be able to reuse core stable-range logic instead of
+        # redoing this manually
+        currentrange = sub.pop()
+        currentsize = stablerange.rangelength(repo, currentrange)
+        currentcut = None
+        while allcuts or currentcut is not None:
+            # get the next cut if needed
+            if currentcut is None:
+                currentcut = allcuts.pop()
+            # deal attemp a cut
+            if currentsize == currentcut:
+                bottomranges.append(currentrange)
+                currentcut = None
+            elif currentsize < currentcut:
+                bottomranges.append(currentrange)
+                currentcut -= currentsize
+            else: # currentsize > currentcut
+                newskip = currentrange[1] + (currentsize - currentcut)
+                currentsub = stablerange._slicesrangeat(repo, currentrange, newskip)
+                bottomranges.append(currentsub.pop())
+                sub.extend(currentsub)
+                currentcut = None
+            currentrange = sub.pop()
+            currentsize = stablerange.rangelength(repo, currentrange)
+        bottomranges.reverse()
+        canonicals.extend(bottomranges)
 
     # 3. take recursive subrange until we get to a power of two size?
     current = (headrev, cut)
@@ -289,6 +312,22 @@
 
     return canonicals
 
+def standardcut(size):
+    assert 0 < size
+    # 0. find the first power of 2 higher than this range depth
+    cut = 1
+    while cut <= size:
+        cut *= 2
+
+    allcuts = []
+    # 1. find all standard expected cut
+    while 1 < cut and size:
+        cut //= 2
+        if cut <= size:
+            allcuts.append(cut)
+            size -= cut
+    return allcuts
+
 def poweroftwo(num):
     return num and not num & (num - 1)