changeset 50657:e77ca247b85b stable

delta-find: fix pulled-delta-reuse-policy=forced behavior The code that select delta still has too many oportunity to discard the delta is has been forcibly asked to reuse. However is is fairly easy to use a dedicated fastpath for this case. So we do so. Cleaning other code that tries to enforce that policy will be done on default.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Mon, 05 Jun 2023 03:11:26 +0200
parents f2d78fb29f61
children 1e2c6cda2309
files mercurial/revlogutils/deltas.py tests/test-revlog-delta-find.t
diffstat 2 files changed, 73 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/revlogutils/deltas.py	Thu Jun 08 03:49:44 2023 +0200
+++ b/mercurial/revlogutils/deltas.py	Mon Jun 05 03:11:26 2023 +0200
@@ -1262,8 +1262,10 @@
         gather_debug = self._gather_debug
         cachedelta = revinfo.cachedelta
         revlog = self.revlog
+        p1r = p2r = None
 
-        p1r = p2r = None
+        if excluded_bases is None:
+            excluded_bases = set()
 
         if gather_debug:
             start = util.timer()
@@ -1310,15 +1312,80 @@
                     'delta-base'
                 ] = deltainfo.base  # pytype: disable=attribute-error
                 dbg['search_round_count'] = 0
-                dbg['using-cached-base'] = True
+                dbg['using-cached-base'] = False
                 dbg['delta_try_count'] = 0
                 dbg['type'] = b"full"
                 dbg['snapshot-depth'] = 0
                 self._dbg_process_data(dbg)
             return deltainfo
 
-        if excluded_bases is None:
-            excluded_bases = set()
+        deltainfo = None
+
+        # If this source delta are to be forcibly reuse, let us comply early.
+        if (
+            revlog._generaldelta
+            and revinfo.cachedelta is not None
+            and revinfo.cachedelta[2] == DELTA_BASE_REUSE_FORCE
+        ):
+            base = revinfo.cachedelta[0]
+            if base == nullrev:
+                dbg_type = b"full"
+                deltainfo = self._fullsnapshotinfo(fh, revinfo, target_rev)
+                if gather_debug:
+                    snapshotdepth = 0
+            elif base not in excluded_bases:
+                delta = revinfo.cachedelta[1]
+                header, data = revlog.compress(delta)
+                deltalen = len(header) + len(data)
+                if gather_debug:
+                    offset = revlog.end(len(revlog) - 1)
+                    chainbase = revlog.chainbase(base)
+                    distance = deltalen + offset - revlog.start(chainbase)
+                    chainlen, compresseddeltalen = revlog._chaininfo(base)
+                    chainlen += 1
+                    compresseddeltalen += deltalen
+                    if base == p1r or base == p2r:
+                        dbg_type = b"delta"
+                        snapshotdepth = None
+                    elif not revlog.issnapshot(base):
+                        snapshotdepth = None
+                    else:
+                        dbg_type = b"snapshot"
+                        snapshotdepth = revlog.snapshotdepth(base) + 1
+                else:
+                    distance = None
+                    chainbase = None
+                    chainlen = None
+                    compresseddeltalen = None
+                    snapshotdepth = None
+                deltainfo = _deltainfo(
+                    distance=distance,
+                    deltalen=deltalen,
+                    data=(header, data),
+                    base=base,
+                    chainbase=chainbase,
+                    chainlen=chainlen,
+                    compresseddeltalen=compresseddeltalen,
+                    snapshotdepth=snapshotdepth,
+                )
+
+            if deltainfo is not None:
+                if gather_debug:
+                    end = util.timer()
+                    dbg['duration'] = end - start
+                    dbg[
+                        'delta-base'
+                    ] = deltainfo.base  # pytype: disable=attribute-error
+                    dbg['search_round_count'] = 0
+                    dbg['using-cached-base'] = True
+                    dbg['delta_try_count'] = 0
+                    dbg['type'] = b"full"
+                    if snapshotdepth is None:
+                        dbg['snapshot-depth'] = 0
+                    else:
+                        dbg['snapshot-depth'] = snapshotdepth
+                    self._dbg_process_data(dbg)
+                return deltainfo
 
         # count the number of different delta we tried (for debug purpose)
         dbg_try_count = 0
@@ -1326,7 +1393,6 @@
         dbg_try_rounds = 0
         dbg_type = b'unknown'
 
-        deltainfo = None
         if p1r is None:
             p1r = revlog.rev(revinfo.p1)
             p2r = revlog.rev(revinfo.p2)
--- a/tests/test-revlog-delta-find.t	Thu Jun 08 03:49:44 2023 +0200
+++ b/tests/test-revlog-delta-find.t	Mon Jun 05 03:11:26 2023 +0200
@@ -329,8 +329,8 @@
   DBG-DELTAS: CHANGELOG: * (glob)
   DBG-DELTAS: MANIFESTLOG: * (glob)
   DBG-DELTAS: MANIFESTLOG: * (glob)
-  DBG-DELTAS: FILELOG:my-file.txt: rev=3: delta-base=2 is-cached=1 *search-rounds=1 try-count=1* (glob)
-  DBG-DELTAS: FILELOG:my-file.txt: rev=4: delta-base=3 is-cached=1 *search-rounds=1 try-count=1* (glob)
+  DBG-DELTAS: FILELOG:my-file.txt: rev=3: delta-base=2 is-cached=1 *search-rounds=0 try-count=0* (glob)
+  DBG-DELTAS: FILELOG:my-file.txt: rev=4: delta-base=3 is-cached=1 *search-rounds=0 try-count=0* (glob)
 
 Check that running "forced" on a non-general delta repository does not corrupt it
 ---------------------------------------------------------------------------------