Patchwork [1,of,7,V2] context: fix introrev to avoid computation as initially intended

mail settings
Submitter Boris Feld
Date Oct. 1, 2018, 4:46 p.m.
Message ID <fd322b34c1c7c9d71bdf.1538412384@localhost.localdomain>
Download mbox | patch
Permalink /patch/35254/
State New
Headers show


Boris Feld - Oct. 1, 2018, 4:46 p.m.
# HG changeset patch
# User Boris Feld <>
# Date 1536254177 14400
#      Thu Sep 06 13:16:17 2018 -0400
# Node ID fd322b34c1c7c9d71bdf294f1fc82e3dea0f126c
# Parent  a269fa55467e4952a8cb9d1f8743dd12e1a57943
# EXP-Topic copy-perf
# Available At
#              hg pull -r fd322b34c1c7
context: fix introrev to avoid computation as initially intended

The `filerev.introrev()` method has various logic be as efficient as possible.
In particular, it tries to restrict the range covered by the
`ctx._adjustlinkrev(...)` call. However, it does so using the value returned by
`ctx.rev()`. In some case (eg: copy tracing), that `ctx.rev()` call would do an
`_adjustlinkrev(...)` call on its own, defeating the optimization purpose and
doing the computation twice.

We are about to improve graph traversal associated with copy tracing using code
based on `ctx.introrev()`, so we need this fixed before proceeding further.


diff --git a/mercurial/ b/mercurial/
--- a/mercurial/
+++ b/mercurial/
@@ -827,6 +827,23 @@  class basefilectx(object):
             # result is crash somewhere else at to some point.
         return lkr
+    def _lazyrev(self):
+        """return self.rev() if it is available without computation,
+        If finding the rev would trigger a possibly expensive computation, we
+        return None."""
+        attrs = vars(self)
+        if r'_changeid' in attrs:
+            # We have a cached value already
+            return self.rev()
+        elif r'_changectx' in attrs:
+            # We know which changelog entry we are coming from
+            return self.rev()
+        elif r'_descendantrev' not in attrs:
+            # we have no context, so linkrev will be used
+            return self.rev()
+        return None
     def introrev(self):
         """return the rev of the changeset which introduced this file revision
@@ -837,11 +854,14 @@  class basefilectx(object):
         lkr = self.linkrev()
-        attrs = vars(self)
-        noctx = not (r'_changeid' in attrs or r'_changectx' in attrs)
-        if noctx or self.rev() == lkr:
-            return self.linkrev()
-        return self._adjustlinkrev(self.rev(), inclusive=True)
+        lazyrev = self._lazyrev()
+        if lazyrev is not None:
+            if lazyrev == lkr:
+                return lazyrev
+            else:
+                return self._adjustlinkrev(lazyrev, inclusive=True)
+        else:
+            return self.rev()
     def introfilectx(self):
         """Return filectx having identical contents, but pointing to the