compare grep result between target and its parent
authorFUJIWARA Katsunori <foozy@lares.dti.ne.jp>
Tue, 19 May 2009 16:49:54 +0900
changeset 8849 80cc4b1a62d0
parent 8848 89b71acdac9a
child 8850 9db1c8e1cf17
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.
mercurial/commands.py
tests/test-grep
tests/test-grep.out
--- 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