compare grep result between target and its parent
I found that typical case is that grep target is added at (*) revision
in the tree shown below.
+--- 1(*) --- 3
0
+--- 2 ------ 4
Now, I expect 'hg grep --all' to show only rev:1 which is first
appearance of target line.
But 'hg grep --all' will tell:
target line dis-appeared at 3 => 4
target line appeared at 2 => 3
target line dis-appeared at 1 => 2
target line appeared at 0 => 1
because current 'hg grep' implementation compares not between target
revision and its parent, but between neighbor revisions in walkthrough
order.
I checked performance of this patch by "hg grep --follow --all
walkchangerevs" on whole Mercurial repo, and patched version could
complete as fast as un-patched one.
--- a/mercurial/commands.py Sat Jun 20 17:09:49 2009 +0200
+++ b/mercurial/commands.py Tue May 19 16:49:54 2009 +0900
@@ -1230,16 +1230,14 @@
for i in xrange(blo, bhi):
yield ('+', b[i])
- prev = {}
- def display(fn, rev, states, prevstates):
+ def display(fn, r, pstates, states):
datefunc = ui.quiet and util.shortdate or util.datestr
found = False
filerevmatches = {}
- r = prev.get(fn, -1)
if opts.get('all'):
- iter = difflinestates(states, prevstates)
+ iter = difflinestates(pstates, states)
else:
- iter = [('', l) for l in prevstates]
+ iter = [('', l) for l in states]
for change, l in iter:
cols = [fn, str(r)]
if opts.get('line_number'):
@@ -1261,8 +1259,8 @@
found = True
return found
- fstate = {}
skip = {}
+ revfiles = {}
get = util.cachefunc(lambda r: repo[r].changeset())
changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
found = False
@@ -1270,46 +1268,58 @@
for st, rev, fns in changeiter:
if st == 'window':
matches.clear()
+ revfiles.clear()
elif st == 'add':
ctx = repo[rev]
- matches[rev] = {}
+ pctx = ctx.parents()[0]
+ parent = pctx.rev()
+ matches.setdefault(rev, {})
+ matches.setdefault(parent, {})
+ files = revfiles.setdefault(rev, [])
for fn in fns:
- if fn in skip:
- continue
+ flog = getfile(fn)
try:
- grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
- fstate.setdefault(fn, [])
- if follow:
- copied = getfile(fn).renamed(ctx.filenode(fn))
- if copied:
- copies.setdefault(rev, {})[fn] = copied[0]
+ fnode = ctx.filenode(fn)
except error.LookupError:
- pass
+ continue
+
+ copied = flog.renamed(fnode)
+ copy = follow and copied and copied[0]
+ if copy:
+ copies.setdefault(rev, {})[fn] = copy
+ if fn in skip:
+ if copy:
+ skip[copy] = True
+ continue
+ files.append(fn)
+
+ if not matches[rev].has_key(fn):
+ grepbody(fn, rev, flog.read(fnode))
+
+ pfn = copy or fn
+ if not matches[parent].has_key(pfn):
+ try:
+ fnode = pctx.filenode(pfn)
+ grepbody(pfn, parent, flog.read(fnode))
+ except error.LookupError:
+ pass
elif st == 'iter':
- for fn, m in sorted(matches[rev].items()):
+ parent = repo[rev].parents()[0].rev()
+ for fn in sorted(revfiles.get(rev, [])):
+ states = matches[rev][fn]
copy = copies.get(rev, {}).get(fn)
if fn in skip:
if copy:
skip[copy] = True
continue
- if fn in prev or fstate[fn]:
- r = display(fn, rev, m, fstate[fn])
+ pstates = matches.get(parent, {}).get(copy or fn, [])
+ if pstates or states:
+ r = display(fn, rev, pstates, states)
found = found or r
if r and not opts.get('all'):
skip[fn] = True
if copy:
skip[copy] = True
- fstate[fn] = m
- if copy:
- fstate[copy] = m
- prev[fn] = rev
-
- for fn, state in sorted(fstate.items()):
- if fn in skip:
- continue
- if fn not in copies.get(prev[fn], {}):
- found = display(fn, rev, {}, state) or found
- return (not found and 1) or 0
def heads(ui, repo, *branchrevs, **opts):
"""show current repository heads or show branch heads
--- a/tests/test-grep Sat Jun 20 17:09:49 2009 +0200
+++ b/tests/test-grep Tue May 19 16:49:54 2009 +0900
@@ -73,3 +73,25 @@
# Used to crash here
hg grep -r 1 octarine
+# Issue337: grep did not compared changesets by their revision numbers
+# instead of following parent-child relationships.
+cd ..
+echo % issue 337
+hg init issue337
+cd issue337
+
+echo white > color
+hg commit -A -m "0 white"
+
+echo red > color
+hg commit -A -m "1 red"
+
+hg update 0
+echo black > color
+hg commit -A -m "2 black"
+
+hg update --clean 1
+echo blue > color
+hg commit -A -m "3 blue"
+
+hg grep --all red
--- a/tests/test-grep.out Sat Jun 20 17:09:49 2009 +0200
+++ b/tests/test-grep.out Tue May 19 16:49:54 2009 +0900
@@ -38,6 +38,13 @@
noeol:4:no infinite loo
% issue 685
adding color
+colour:1:octarine
color:0:octarine
colour:1:octarine
-colour:1:octarine
+% issue 337
+adding color
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+color:3:-:red
+color:1:+:red