[PATCH STABLE] sparse-revlog: fix delta validity computation

Boris Feld boris.feld at octobus.net
Thu Aug 16 09:01:11 UTC 2018


# HG changeset patch
# User Boris Feld <boris.feld at octobus.net>
# Date 1534337020 -7200
#      Wed Aug 15 14:43:40 2018 +0200
# Branch stable
# Node ID 1b78bebb7a8793b4b858d335b11f4deaf57dd128
# Parent  7e023ce26c7f5e800c778fb4ff76c6d7726666b3
# EXP-Topic fix-sparse
# Available At https://bitbucket.org/octobus/mercurial-devel/
#              hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 1b78bebb7a87
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).

diff --git a/mercurial/revlog.py b/mercurial/revlog.py
--- a/mercurial/revlog.py
+++ b/mercurial/revlog.py
@@ -262,13 +262,17 @@ def _trimchunk(revlog, revs, startidx, e
     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 @@ def _segmentspan(revlog, revs):
     """
     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 @@ def _slicechunktodensity(revlog, revs, d
         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 @@ class revlog(object):
                 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
 


More information about the Mercurial-devel mailing list