Patchwork [2,of,8,V3] context: fix introrev to avoid computation as initially intended

login
register
mail settings
Submitter Boris Feld
Date Oct. 3, 2018, 7:10 p.m.
Message ID <355a69e274a2ec851c6d.1538593845@localhost.localdomain>
Download mbox | patch
Permalink /patch/35416/
State New
Headers show

Comments

Boris Feld - Oct. 3, 2018, 7:10 p.m.
# HG changeset patch
# User Boris Feld <boris.feld@octobus.net>
# Date 1536254177 14400
#      Thu Sep 06 13:16:17 2018 -0400
# Node ID 355a69e274a2ec851c6d13cfbbac45f5e197294c
# Parent  68ec0cf339c7e65ee4349f543e3024068fbbe591
# EXP-Topic copy-perf
# Available At https://bitbucket.org/octobus/mercurial-devel/
#              hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 355a69e274a2
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.

Patch

diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -765,6 +765,23 @@  class basefilectx(object):
             # result is crash somewhere else at to some point.
         return lkr
 
+    def _lazyrevavailable(self):
+        """return True if self.rev() is available without computation,
+
+        If finding the rev would trigger a possibly expensive computation, we
+        return False."""
+        attrs = vars(self)
+        if r'_changeid' in attrs:
+            # We have a cached value already
+            return True
+        elif r'_changectx' in attrs:
+            # We know which changelog entry we are coming from
+            return True
+        elif r'_descendantrev' not in attrs:
+            # we have no context, so linkrev will be used
+            return True
+        return False
+
     def introrev(self):
         """return the rev of the changeset which introduced this file revision
 
@@ -776,7 +793,7 @@  class basefilectx(object):
         """
         lkr = self.linkrev()
         attrs = vars(self)
-        lazyavailable = r'_changeid' in attrs or r'_changectx' in attrs
+        lazyavailable = self._lazyrevavailable()
         if lazyavailable:
             rev = self.rev()
             if rev == lkr: