# HG changeset patch # User Denis Laxalde # Date 1484555087 -3600 # Node ID 5e3b49defbff8f82ce8d906147a144e3c678937d # Parent 826e600605f60f2027cea3aa239b4bc0ea2abc84 revset: add a 'descend' argument to followlines to return descendants This is useful to follow changes in a block of lines forward in the history (for instance, when one wants to find out how a function evolved from a point in history). We added a 'descend' parameter to followlines(), which defaults to False. If True, followlines() returns descendants of startrev. Because context.blockdescendants() does not follow renames, these are not followed by the revset either, so history will end when a rename occurs (as can be seen in tests). diff -r 826e600605f6 -r 5e3b49defbff mercurial/revset.py --- a/mercurial/revset.py Mon Apr 10 15:11:36 2017 +0200 +++ b/mercurial/revset.py Mon Jan 16 09:24:47 2017 +0100 @@ -901,17 +901,22 @@ # of every revisions or files revisions. return _follow(repo, subset, x, '_followfirst', followfirst=True) -@predicate('followlines(file, fromline:toline[, startrev=.])', safe=True) +@predicate('followlines(file, fromline:toline[, startrev=., descend=False])', + safe=True) def followlines(repo, subset, x): """Changesets modifying `file` in line range ('fromline', 'toline'). Line range corresponds to 'file' content at 'startrev' and should hence be consistent with file size. If startrev is not specified, working directory's parent is used. + + By default, ancestors of 'startrev' are returned. If 'descend' is True, + descendants of 'startrev' are returned though renames are (currently) not + followed in this direction. """ from . import context # avoid circular import issues - args = getargsdict(x, 'followlines', 'file *lines startrev') + args = getargsdict(x, 'followlines', 'file *lines startrev descend') if len(args['lines']) != 1: raise error.ParseError(_("followlines requires a line range")) @@ -939,9 +944,17 @@ fromline, toline = util.processlinerange(fromline, toline) fctx = repo[rev].filectx(fname) - revs = (c.rev() for c, _linerange - in context.blockancestors(fctx, fromline, toline)) - return subset & generatorset(revs, iterasc=False) + if args.get('descend', False): + rs = generatorset( + (c.rev() for c, _linerange + in context.blockdescendants(fctx, fromline, toline)), + iterasc=True) + else: + rs = generatorset( + (c.rev() for c, _linerange + in context.blockancestors(fctx, fromline, toline)), + iterasc=False) + return subset & rs @predicate('all()', safe=True) def getall(repo, subset, x): diff -r 826e600605f6 -r 5e3b49defbff tests/test-annotate.t --- a/tests/test-annotate.t Mon Apr 10 15:11:36 2017 +0200 +++ b/tests/test-annotate.t Mon Jan 16 09:24:47 2017 +0100 @@ -484,7 +484,9 @@ $ hg id -n 20 -Test followlines() revset +Test followlines() revset; we usually check both followlines(pat, range) and +followlines(pat, range, descend=True) to make sure both give the same result +when they should. $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5)' 16: baz:0 @@ -494,9 +496,11 @@ 16: baz:0 19: baz:3 20: baz:4 - $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=.^)' + $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=19)' 16: baz:0 19: baz:3 + $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=19, descend=True)' + 20: baz:4 $ printf "0\n0\n" | cat - baz > baz1 $ mv baz1 baz $ hg ci -m 'added two lines with 0' @@ -504,12 +508,16 @@ 16: baz:0 19: baz:3 20: baz:4 + $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, descend=True, startrev=19)' + 20: baz:4 $ echo 6 >> baz $ hg ci -m 'added line 8' $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7)' 16: baz:0 19: baz:3 20: baz:4 + $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=19, descend=True)' + 20: baz:4 $ sed 's/3/3+/' baz > baz.new $ mv baz.new baz $ hg ci -m 'baz:3->3+' @@ -518,6 +526,9 @@ 19: baz:3 20: baz:4 23: baz:3->3+ + $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=19, descend=True)' + 20: baz:4 + 23: baz:3->3+ $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 1:2)' 21: added two lines with 0 @@ -536,9 +547,13 @@ 20: baz:4 23: baz:3->3+ 24: qux:4->4+ - $ hg up 23 --quiet + +but are missed when following children + $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7, startrev=22, descend=True)' + 23: baz:3->3+ merge + $ hg up 23 --quiet $ echo 7 >> baz $ hg ci -m 'one more line, out of line range' created new head @@ -581,6 +596,10 @@ 28: merge from other side $ hg up 23 --quiet +we are missing the branch with rename when following children + $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7, startrev=25, descend=True)' + 26: baz:3+->3- + check error cases $ hg log -r 'followlines()' hg: parse error: followlines takes at least 1 positional arguments