# HG changeset patch # User FUJIWARA Katsunori # Date 1242719394 -32400 # Node ID 80cc4b1a62d01a450cb162f261bb75f4904ee236 # Parent 89b71acdac9a2c29fc570c398c730295e47142a2 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. diff -r 89b71acdac9a -r 80cc4b1a62d0 mercurial/commands.py --- 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 diff -r 89b71acdac9a -r 80cc4b1a62d0 tests/test-grep --- 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 diff -r 89b71acdac9a -r 80cc4b1a62d0 tests/test-grep.out --- 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