[PATCH] grep: introduce a manifest search method
Steve Borho
steve at borho.org
Mon May 17 21:21:32 CDT 2010
# HG changeset patch
# User Steve Borho <steve at borho.org>
# Date 1273812860 18000
# Node ID 7abcaef62d30d58f9247c42d5ae8269c55f65717
# Parent c45a47bc41148b6037cd7337c4529a5e67b92222
grep: introduce a manifest search method
If no revision is specified, perform a workingctx scan.
If one revision is specified, perform a changectx scan.
If --all or --rev N:M are specified, fall back to original behavior.
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -1311,18 +1311,22 @@
def grep(ui, repo, pattern, *pats, **opts):
"""search for a pattern in specified files and revisions
- Search revisions of files for a regular expression.
+ Search files or revisions for a regular expression.
This command behaves differently than Unix grep. It only accepts
- Python/Perl regexps. It searches repository history, not the
- working directory. It always prints the revision number in which a
- match appears.
-
- By default, grep only prints output for the first revision of a
- file in which it finds a match. To get it to print every revision
- that contains a change in match status ("-" for a match that
- becomes a non-match, or "+" for a non-match that becomes a match),
- use the --all flag.
+ Python/Perl regexps.
+
+ If no revision is specified, grep searches the working directory.
+ If one revision is specified, grep searches the file contents at
+ that revision.
+
+ If a revision range is specified, grep walks the changesets in the
+ specified order and reports the first match in each file, only
+ inspecting the files modified at each changeset.
+
+ To get it to print every revision that contains a change in match
+ status ("-" for a match that becomes a non-match, or "+" for a
+ non-match that becomes a match), use the --all flag.
Returns 0 if a match is found, 1 otherwise.
"""
@@ -1340,6 +1344,70 @@
getfile = util.lrucachefunc(repo.file)
+ cache = {}
+ def annotateline(fn, lineno, rev):
+ if opts.get('user') or opts.get('date') or opts.get('follow'):
+ if rev is None:
+ raise util.Abort(_("grep: cannot annotate working changes, "
+ "try hg grep --rev ."))
+ elif fn in cache:
+ data = cache[fn]
+ else:
+ annopts = {'user':opts.get('user'), 'date':opts.get('date'),
+ 'file':opts.get('follow'), 'number':True,
+ 'rev':str(rev)}
+ data = []
+ ui.pushbuffer()
+ annotate(ui, repo, fn, **annopts)
+ for l in ui.popbuffer().splitlines():
+ prefix = l.split(':', 1)[0]
+ data.append([s.strip() for s in prefix.split()])
+ cache.clear()
+ cache[fn] = data
+ if opts.get('follow'):
+ # annotate lists the "followed" filename last
+ cols = data[lineno][-1:] + data[lineno][:-1]
+ else:
+ cols = [fn] + data[lineno]
+ else:
+ cols = [fn]
+ if opts.get('line_number'):
+ cols.append(str(lineno))
+ return cols
+
+ def manifestgrep(ctx, matchfn):
+ total = len(ctx.manifest())
+ found = False
+ for count, fn in enumerate(ctx):
+ ui.progress(_('searching'), count, fn, _('files'), total)
+ if not matchfn(fn):
+ continue
+ data = ctx[fn].data()
+ if '\0' in data:
+ continue
+ for i, line in enumerate(data.splitlines()):
+ if opts.get('files_with_matches'):
+ if regexp.search(line):
+ ui.write(fn + eol)
+ found = True
+ break
+ continue
+ pos = 0
+ t = []
+ for m in regexp.finditer(line):
+ s, e = m.span()
+ t.append(line[pos:s])
+ t.append(ui.label(line[s:e], label='grep.match'))
+ pos = e
+ if pos:
+ t.append(line[pos:])
+ cols = annotateline(fn, i, ctx.rev())
+ cols.append(''.join(t))
+ ui.write(sep.join(cols) + eol)
+ found = True
+ ui.progress(_('searching'), None)
+ return found
+
def matchlines(body):
begin = 0
linenum = 0
@@ -1434,6 +1502,16 @@
matchfn = cmdutil.match(repo, pats, opts)
found = False
follow = opts.get('follow')
+ revs = opts.get('rev')
+
+ node1, node2 = cmdutil.revpair(repo, revs)
+ if not opts.get('all') and not node2:
+ # no annotation required, do fast grep
+ if revs:
+ ctx = repo[node1]
+ else:
+ ctx = repo[None]
+ return not manifestgrep(ctx, matchfn)
def prep(ctx, fns):
rev = ctx.rev()
diff --git a/tests/test-grep.out b/tests/test-grep.out
--- a/tests/test-grep.out
+++ b/tests/test-grep.out
@@ -1,13 +1,13 @@
% pattern error
grep: invalid match pattern: nothing to repeat
% simple
-port:4:export
-port:4:vaportight
-port:4:import/export
+port:export
+port:vaportight
+port:import/export
% simple with color
-port:4:ex[0;31;1mport[0m
-port:4:va[0;31;1mport[0might
-port:4:im[0;31;1mport[0m/export
+port:ex[0;31;1mport[0m
+port:va[0;31;1mport[0might
+port:im[0;31;1mport[0m/ex[0;31;1mport[0m
% all
port:4:4:-:spam:import/export
port:3:4:+:eggs:import/export
@@ -19,7 +19,7 @@
port:1:2:+:eggs:export
port:0:1:+:spam:import
% other
-port:4:import/export
+port:import/export
% follow
port:0:import
port2:6:4:+:eggs:deport
@@ -32,18 +32,17 @@
port:2:3:+:spam:import/export
port:1:2:+:eggs:export
port:0:1:+:spam:import
-color:3:orange
+color:orange
color:3:+:orange
color:2:-:orange
color:1:+:orange
% match in last line without newline
adding noeol
% last character omitted in output to avoid infinite loop
-noeol:4:no infinite loo
+noeol:no infinite loop
% issue 685
adding color
-colour:1:octarine
-color:0:octarine
+colour:octarine
colour:1:octarine
% issue 337
adding color
More information about the Mercurial-devel
mailing list