grep: grep the working copy faster
`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.
Differential Revision: https://phab.mercurial-scm.org/D8545
--- a/mercurial/commands.py Sun May 17 13:10:54 2020 -0400
+++ b/mercurial/commands.py Sun May 17 18:33:45 2020 -0400
@@ -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)