delta-find: explicitly deal with usage of the cached revision
We can remove this from the general logic path and directly deal with this
corner case early.
This result in a small change in test-generaldelta.t as it turns out that:
- at commit time we (sometimes) precompute a delta against p1 and pass it as the
cached delta.
- since cached delta where going through the same filtering as everything, we
could "optimize" the base if it applied to an empty delta, resulting in not
using the pre-computed delta.
The simpler logic fix the second item, making the cached delta base always actually
tested when requested.
Note that the computation of a fast delta against p1 only is questionable, but
looking into that is out of scope for this series.
--- a/mercurial/revlogutils/deltas.py Sun Jan 07 03:02:30 2024 +0100
+++ b/mercurial/revlogutils/deltas.py Sun Jan 07 03:08:46 2024 +0100
@@ -840,7 +840,24 @@
assert self.revlog.delta_config.general_delta
self._candidates_iterator = self._refined_groups()
self._last_good = None
- self._next_internal_group()
+ if (
+ self.cachedelta is not None
+ and self.cachedelta[2] > DELTA_BASE_REUSE_NO
+ and self._pre_filter_rev(self.cachedelta[0])
+ ):
+ # First we try to reuse a the delta contained in the bundle. (or from
+ # the source revlog)
+ #
+ # This logic only applies to general delta repositories and can be
+ # disabled through configuration. Disabling reuse source delta is
+ # useful when we want to make sure we recomputed "optimal" deltas.
+ self.current_stage = _STAGE_CACHED
+ self._internal_group = (self.cachedelta[0],)
+ self._internal_idx = 0
+ self.current_group = self._internal_group
+ self.tested.update(self.current_group)
+ else:
+ self._next_internal_group()
def _next_internal_group(self):
# self._internal_group can be larger than self.current_group
@@ -868,6 +885,14 @@
old_good = self._last_good
if good_delta is not None:
self._last_good = good_delta.base
+ if self.current_stage == _STAGE_CACHED and good_delta is not None:
+ # the cache is good, let us use the cache as requested
+ self._candidates_iterator = None
+ self._internal_group = None
+ self._internal_idx = None
+ self.current_group = None
+ return
+
if (self._internal_idx < len(self._internal_group)) and (
old_good != good_delta
):
@@ -1032,23 +1057,6 @@
def _refined_groups(self):
good = None
- # First we try to reuse a the delta contained in the bundle. (or from
- # the source revlog)
- #
- # This logic only applies to general delta repositories and can be
- # disabled through configuration. Disabling reuse source delta is
- # useful when we want to make sure we recomputed "optimal" deltas.
- if (
- self.cachedelta is not None
- and self.cachedelta[2] > DELTA_BASE_REUSE_NO
- ):
- # Assume what we received from the server is a good choice
- # build delta will reuse the cache
- self.current_stage = _STAGE_CACHED
- good = yield (self.cachedelta[0],)
- if good is not None:
- yield None
- return
groups = self._raw_groups()
for candidates in groups:
good = yield candidates
--- a/tests/test-generaldelta.t Sun Jan 07 03:02:30 2024 +0100
+++ b/tests/test-generaldelta.t Sun Jan 07 03:08:46 2024 +0100
@@ -271,7 +271,7 @@
51 17 -1 4 3 50 prev 3?? 5?? 6?? 1.0???? 6?? 0 0.00000 (glob)
52 51 -1 4 4 51 p1 58 640 6?? 1.0???? 6?? 0 0.00000 (glob)
53 52 -1 5 1 -1 base 0 0 0 0.00000 0 0 0.00000
- 54 53 -1 6 1 -1 base 3?? 640 3?? 0.5???? 3?? 0 0.00000 (glob)
+ 54 53 -1 5 2 53 p1 3?? 640 3?? 0.5???? 3?? 0 0.00000 (glob)
$ hg clone --pull source-repo --config experimental.maxdeltachainspan=2800 relax-chain --config format.generaldelta=yes
requesting all changes
adding changesets