Patchwork D8545: grep: grep the working copy faster

login
register
mail settings
Submitter phabricator
Date May 18, 2020, 12:49 a.m.
Message ID <differential-rev-PHID-DREV-7h6ww3qg4p353nknxa6t-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/46333/
State Superseded
Headers show

Comments

phabricator - May 18, 2020, 12:49 a.m.
valentin.gatienbaron created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  `hg grep qqqq` in the mercurial repo:
  before: 0,859s
  after: 0,233s
  
  `hg grep somethingwithnomatch` in mozilla-central:
  before: 51s
  after: 19s
  
  This is probably also a tiny bug fix, because the code was looking up
  a node for filename `pfn` on a filelog for filename `fn`, which are
  most of the time the same filename, but don't have to be.
  
  Ignoring performance and the bug fix, the code should have the same
  behavior.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D8545

AFFECTED FILES
  mercurial/commands.py

CHANGE DETAILS




To: valentin.gatienbaron, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -3578,18 +3578,27 @@ 
 
     getrenamed = scmutil.getrenamedfn(repo)
 
-    def get_file_content(filename, filelog, filenode, context, revision):
-        try:
-            content = filelog.read(filenode)
-        except error.WdirUnsupported:
-            content = context[filename].data()
-        except error.CensoredNodeError:
-            content = None
-            ui.warn(
-                _(b'cannot search in censored file: %(filename)s:%(revnum)s\n')
-                % {b'filename': filename, b'revnum': pycompat.bytestr(revision)}
-            )
-        return content
+    def readfile(ctx, fn):
+        rev = ctx.rev()
+        if rev is None:
+            fctx = ctx[fn]
+            try:
+                return fctx.data()
+            except IOError as e:
+                if e.errno != errno.ENOENT:
+                    raise
+        else:
+            flog = getfile(fn)
+            fnode = ctx.filenode(fn)
+            try:
+                return flog.read(fnode)
+            except error.CensoredNodeError:
+                ui.warn(
+                    _(
+                        b'cannot search in censored file: %(filename)s:%(revnum)s\n'
+                    )
+                    % {b'filename': fn, b'revnum': pycompat.bytestr(rev),}
+                )
 
     def prep(ctx, fns):
         rev = ctx.rev()
@@ -3600,10 +3609,10 @@ 
             matches.setdefault(parent, {})
         files = revfiles.setdefault(rev, [])
         for fn in fns:
-            flog = getfile(fn)
-            try:
-                fnode = ctx.filenode(fn)
-            except error.LookupError:
+            # fn might not exist in the revision (could be a file removed by the
+            # revision). We could check `fn not in ctx` even when rev is None,
+            # but it's less racy to protect againt that in readfile.
+            if rev is not None and fn not in ctx:
                 continue
 
             copy = None
@@ -3618,20 +3627,12 @@ 
             files.append(fn)
 
             if fn not in matches[rev]:
-                content = get_file_content(fn, flog, fnode, ctx, rev)
-                grepbody(fn, rev, content)
+                grepbody(fn, rev, readfile(ctx, fn))
 
             if diff:
                 pfn = copy or fn
-                if pfn not in matches[parent]:
-                    try:
-                        pfnode = pctx.filenode(pfn)
-                        pcontent = get_file_content(
-                            pfn, flog, pfnode, pctx, parent
-                        )
-                        grepbody(pfn, parent, pcontent)
-                    except error.LookupError:
-                        pass
+                if pfn not in matches[parent] and pfn in pctx:
+                    grepbody(pfn, parent, readfile(pctx, pfn))
 
     ui.pager(b'grep')
     fm = ui.formatter(b'grep', opts)