@@ -901,17 +901,22 @@ def _followfirst(repo, subset, x):
# 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 @@ def followlines(repo, subset, x):
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):
@@ -484,7 +484,9 @@ annotate removed file
$ 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 @@ Test followlines() revset
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 @@ Test followlines() revset
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 @@ Test followlines() revset
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 @@ renames are followed
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 @@ merge
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