sparse-revlog: fix delta validity computation stable
authorBoris Feld <boris.feld@octobus.net>
Wed, 15 Aug 2018 14:43:40 +0200
branchstable
changeset 39139 3730b779ed5b
parent 39021 7e023ce26c7f
child 39140 1732db2f8210
sparse-revlog: fix delta validity computation When considering the validity of a delta with sparse-revlog, we check the size of the largest read. To do so, we use some regular logic with the extra delta information. Some of this logic was not handling this extra delta properly, confusing it with "nullrev". This confusion with nullrev lead to wrong results for this computation but preventing a crash. Changeset 781b2720d2ac on default revealed this error, crashing. This changeset fixes the logic on stable so that the computation is correct (and the crash is averted). The fix is made on stable as this will impact 4.7 clients interacting with sparse-revlog repositories (eg: created by later version).
mercurial/revlog.py
--- a/mercurial/revlog.py	Tue Aug 14 14:00:35 2018 -0400
+++ b/mercurial/revlog.py	Wed Aug 15 14:43:40 2018 +0200
@@ -262,13 +262,17 @@
     if endidx is None:
         endidx = len(revs)
 
-    # Trim empty revs at the end, but never the very first revision of a chain
-    while endidx > 1 and endidx > startidx and length(revs[endidx - 1]) == 0:
-        endidx -= 1
+    # If we have a non-emtpy delta candidate, there are nothing to trim
+    if revs[endidx - 1] < len(revlog):
+        # Trim empty revs at the end, except the very first revision of a chain
+        while (endidx > 1
+                and endidx > startidx
+                and length(revs[endidx - 1]) == 0):
+            endidx -= 1
 
     return revs[startidx:endidx]
 
-def _segmentspan(revlog, revs):
+def _segmentspan(revlog, revs, deltainfo=None):
     """Get the byte span of a segment of revisions
 
     revs is a sorted array of revision numbers
@@ -294,7 +298,14 @@
     """
     if not revs:
         return 0
-    return revlog.end(revs[-1]) - revlog.start(revs[0])
+    if deltainfo is not None and len(revlog) <= revs[-1]:
+        if len(revs) == 1:
+            return deltainfo.deltalen
+        offset = revlog.end(len(revlog) - 1)
+        end = deltainfo.deltalen + offset
+    else:
+        end = revlog.end(revs[-1])
+    return end - revlog.start(revs[0])
 
 def _slicechunk(revlog, revs, deltainfo=None, targetsize=None):
     """slice revs to reduce the amount of unrelated data to be read from disk.
@@ -526,7 +537,7 @@
         yield revs
         return
 
-    if deltainfo is not None:
+    if deltainfo is not None and deltainfo.deltalen:
         revs = list(revs)
         revs.append(nextrev)
 
@@ -2444,7 +2455,8 @@
                 deltachain = []
 
             chunks = _slicechunk(self, deltachain, deltainfo)
-            distance = max(map(lambda revs:_segmentspan(self, revs), chunks))
+            all_span = [_segmentspan(self, revs, deltainfo) for revs in chunks]
+            distance = max(all_span)
         else:
             distance = deltainfo.distance