changeset 49223:13e523228623

debugdeltachain: detect a special case where parents are "skipped" See inline comment for details, this is a case where the delta is neither against p1 or p2, Yet it is still a simple delta part of a simple chain. We now display them as `skip1/skip2` instead of `other`.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Thu, 19 May 2022 00:51:36 +0100
parents e7d23c512d3d
children cdb85d0512b8
files mercurial/debugcommands.py
diffstat 1 files changed, 44 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/debugcommands.py	Wed May 18 17:29:03 2022 +0100
+++ b/mercurial/debugcommands.py	Thu May 19 00:51:36 2022 +0100
@@ -768,6 +768,10 @@
                     - snap:  an intermediate snapshot
                     - p1:    a delta against the first parent
                     - p2:    a delta against the second parent
+                    - skip1: a delta against the same base as p1
+                              (when p1 has empty delta
+                    - skip2: a delta against the same base as p2
+                              (when p2 has empty delta
                     - prev:  a delta against the previous revision
                     - other: a delta against an arbitrary revision
     :``compsize``:  compressed size of revision
@@ -803,6 +807,9 @@
     generaldelta = r._generaldelta
     withsparseread = getattr(r, '_withsparseread', False)
 
+    # security to avoid crash on corrupted revlogs
+    total_revs = len(index)
+
     def revinfo(rev):
         e = index[rev]
         compsize = e[revlog_constants.ENTRY_DATA_COMPRESSED_LENGTH]
@@ -813,6 +820,39 @@
         p1 = e[revlog_constants.ENTRY_PARENT_1]
         p2 = e[revlog_constants.ENTRY_PARENT_2]
 
+        # If the parents of a revision has an empty delta, we never try to delta
+        # against that parent, but directly against the delta base of that
+        # parent (recursively). It avoids adding a useless entry in the chain.
+        #
+        # However we need to detect that as a special case for delta-type, that
+        # is not simply "other".
+        p1_base = p1
+        if p1 != nullrev and p1 < total_revs:
+            e1 = index[p1]
+            while e1[revlog_constants.ENTRY_DATA_COMPRESSED_LENGTH] == 0:
+                new_base = e1[revlog_constants.ENTRY_DELTA_BASE]
+                if (
+                    new_base == p1_base
+                    or new_base == nullrev
+                    or new_base >= total_revs
+                ):
+                    break
+                p1_base = new_base
+                e1 = index[p1_base]
+        p2_base = p2
+        if p2 != nullrev and p2 < total_revs:
+            e2 = index[p2]
+            while e2[revlog_constants.ENTRY_DATA_COMPRESSED_LENGTH] == 0:
+                new_base = e2[revlog_constants.ENTRY_DELTA_BASE]
+                if (
+                    new_base == p2_base
+                    or new_base == nullrev
+                    or new_base >= total_revs
+                ):
+                    break
+                p2_base = new_base
+                e2 = index[p2_base]
+
         if generaldelta:
             if base == p1:
                 deltatype = b'p1'
@@ -820,6 +860,10 @@
                 deltatype = b'p2'
             elif base == rev:
                 deltatype = b'base'
+            elif base == p1_base:
+                deltatype = b'skip1'
+            elif base == p2_base:
+                deltatype = b'skip2'
             elif r.issnapshot(rev):
                 deltatype = b'snap'
             elif base == rev - 1: