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).
--- 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):
--- 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