[PATCH 4 of 4 V2] annotate: simplify annotate parent function

Durham Goode durham at fb.com
Tue Jun 4 14:59:30 CDT 2013


# HG changeset patch
# User Durham Goode <durham at fb.com>
# Date 1369967343 25200
#      Thu May 30 19:29:03 2013 -0700
# Node ID d0a0580f790466ff5566682bb7b29d8623acb1da
# Parent  a1a98fba2858dab9aa58431f4e4991f3d91649ab
annotate: simplify annotate parent function

The annotate algorithm used a custom parents() function to try to reuse
filectx and filelogs. I simplified it a bit to rely more heavily on the
self.parents() which makes it work well with alternative filectx
implementations. I tested performance on a file with 5000+ revisions
but no renames, and on a file with 500 revisions repeating a series of
4 edits+renames and saw zero performance hit.  In fact, it was reliably a
couple milliseconds faster now.

Added the perfannotate command to contrib/perf.py for future use.

diff --git a/contrib/perf.py b/contrib/perf.py
--- a/contrib/perf.py
+++ b/contrib/perf.py
@@ -45,6 +45,11 @@
         except Exception:
             timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
 
+ at command('perfannotate')
+def perfannotate(ui, repo, f):
+    fc = repo['.'][f]
+    timer(lambda: len(fc.annotate(True)))
+
 @command('perfstatus',
          [('u', 'unknown', False,
            'ask status to look for unknown files')])
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -649,25 +649,21 @@
             return child
 
         getlog = util.lrucachefunc(lambda x: self._repo.file(x))
-        def getctx(path, fileid):
-            log = path == self._path and self._filelog or getlog(path)
-            return filectx(self._repo, path, fileid=fileid, filelog=log)
-        getctx = util.lrucachefunc(getctx)
 
         def parents(f):
-            # we want to reuse filectx objects as much as possible
-            p = f._path
-            if f._filerev is None: # working dir
-                pl = [(n.path(), n.filerev()) for n in f.parents()]
-            else:
-                pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
+            pl = f.parents()
 
-            if follow:
-                r = f.renamed()
-                if r:
-                    pl[0] = (r[0], getlog(r[0]).rev(r[1]))
+            # Don't return renamed parents if we aren't following.
+            if not follow:
+                pl = [p for p in pl if p.path() == f.path()]
 
-            return [getctx(p, n) for p, n in pl if n != nullrev]
+            # renamed filectx won't have a filelog yet, so set it
+            # from the cache to save time
+            for p in pl:
+                if not '_filelog' in p.__dict__:
+                    p._filelog = getlog(p.path())
+
+            return pl
 
         # use linkrev to find the first changeset where self appeared
         if self.rev() != self.linkrev():


More information about the Mercurial-devel mailing list