hgext3rd/pullbundle.py
changeset 4138 cfdc6f55599b
parent 4136 be3a94d3105f
child 4139 2bd652bece97
--- 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)