# HG changeset patch # User Boris Feld # Date 1534337020 -7200 # Node ID 3730b779ed5ba269729144afbae797a2c783670f # Parent 7e023ce26c7f5e800c778fb4ff76c6d7726666b3 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 -r 7e023ce26c7f -r 3730b779ed5b 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