diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py --- a/mercurial/cmdutil.py +++ b/mercurial/cmdutil.py @@ -26,12 +26,14 @@ from . import ( changelog, copies, crecord as crecordmod, + dagop, dirstateguard, encoding, error, formatter, graphmod, match as matchmod, + mdiff, obsolete, patch, pathutil, @@ -2585,6 +2587,87 @@ def getlogrevs(repo, pats, opts): return revs, expr, filematcher +def _parselinerangelogopt(repo, opts): + """Parse --line-range log option and return a list of tuples (filename, + (fromline, toline)). + """ + linerangebyfname = [] + for pat in opts.get('line_range', []): + try: + pat, linerange = pat.rsplit(',', 1) + except ValueError: + raise error.Abort(_('malformatted line-range pattern %s') % pat) + try: + fromline, toline = map(int, linerange.split('-')) + except ValueError: + raise error.Abort(_("invalid line range for %s") % pat) + msg = _("line range pattern '%s' must match exactly one file") % pat + fname = scmutil.parsefollowlinespattern(repo, None, pat, msg) + linerangebyfname.append( + (fname, util.processlinerange(fromline, toline))) + return linerangebyfname + +def getloglinerangerevs(repo, userrevs, opts): + """Return (revs, filematcher, hunksfilter). + + "revs" are revisions obtained by processing "line-range" log options and + walking block ancestors of each specified file/line-range. + + "filematcher(rev) -> match" is a factory function returning a match object + for a given revision for file patterns specified in --line-range option. + If neither --stat nor --patch options are passed, "filematcher" is None. + + "hunksfilter(rev) -> filterfn(fctx, hunks)" is a factory function + returning a hunks filtering function. + If neither --stat nor --patch options are passed, "filterhunks" is None. + """ + wctx = repo[None] + + # Two-levels map of "rev -> file ctx -> [line range]". + linerangesbyrev = {} + for fname, (fromline, toline) in _parselinerangelogopt(repo, opts): + fctx = wctx.filectx(fname) + for fctx, linerange in dagop.blockancestors(fctx, fromline, toline): + rev = fctx.introrev() + if rev not in userrevs: + continue + linerangesbyrev.setdefault( + rev, {}).setdefault( + fctx.path(), []).append(linerange) + + filematcher = None + hunksfilter = None + if opts.get('patch') or opts.get('stat'): + + def nofilterhunksfn(fctx, hunks): + return hunks + + def hunksfilter(rev): + fctxlineranges = linerangesbyrev.get(rev) + if fctxlineranges is None: + return nofilterhunksfn + + def filterfn(fctx, hunks): + lineranges = fctxlineranges.get(fctx.path()) + if lineranges is not None: + for hr, lines in hunks: + if any(mdiff.hunkinrange(hr[2:], lr) + for lr in lineranges): + yield hr, lines + else: + for hunk in hunks: + yield hunk + + return filterfn + + def filematcher(rev): + files = list(linerangesbyrev.get(rev, [])) + return scmutil.matchfiles(repo, files) + + revs = sorted(linerangesbyrev, reverse=True) + + return revs, filematcher, hunksfilter + def _graphnodeformatter(ui, displayer): spec = ui.config('ui', 'graphnodetemplate') if not spec: diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -3234,6 +3234,9 @@ def locate(ui, repo, *pats, **opts): ('k', 'keyword', [], _('do case-insensitive search for a given text'), _('TEXT')), ('r', 'rev', [], _('show the specified revision or revset'), _('REV')), + ('L', 'line-range', [], + _('follow line range of specified file (EXPERIMENTAL)'), + _('FILE,RANGE')), ('', 'removed', None, _('include revisions where files were removed')), ('m', 'only-merges', None, _('show only merges (DEPRECATED)')), ('u', 'user', [], _('revisions committed by user'), _('USER')), @@ -3275,6 +3278,14 @@ def log(ui, repo, *pats, **opts): Paths in the DAG are represented with '|', '/' and so forth. ':' in place of a '|' indicates one or more revisions in a path are omitted. + .. container:: verbose + + Use -L/--line-range FILE,M-N options to follow the history of lines + from M to N in FILE. With -p/--patch only diff hunks affecting + specified line range will be shown. This option requires --follow; + it can be specified multiple times. Currently, this option is not + compatible with --graph. This option is experimental. + .. note:: :hg:`log --patch` may generate unexpected diff output for merge @@ -3290,6 +3301,14 @@ def log(ui, repo, *pats, **opts): .. container:: verbose + .. note:: + + The history resulting from -L/--line-range options depends on diff + options; for instance if white-spaces are ignored, respective changes + with only white-spaces in specified line range will not be listed. + + .. container:: verbose + Some examples: - changesets with full descriptions and file lists:: @@ -3336,6 +3355,15 @@ def log(ui, repo, *pats, **opts): hg log -r "last(tagged())::" --template "{desc|firstline}\\n" + - changesets touching lines 13 to 23 for file.c:: + + hg log -L file.c,13-23 + + - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of + main.c with patch:: + + hg log -L file.c,13-23 -L main.c,2-6 -p + See :hg:`help dates` for a list of formats valid for -d/--date. See :hg:`help revisions` for more about specifying and ordering @@ -3350,14 +3378,38 @@ def log(ui, repo, *pats, **opts): """ opts = pycompat.byteskwargs(opts) + linerange = opts.get('line_range') + + if linerange and not opts.get('follow'): + raise error.Abort(_('--line-range requires --follow')) + if opts.get('follow') and opts.get('rev'): opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))] del opts['follow'] if opts.get('graph'): + if linerange: + raise error.Abort(_('graph not supported with line range patterns')) return cmdutil.graphlog(ui, repo, pats, opts) revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts) + hunksfilter = None + + if linerange: + revs, lrfilematcher, hunksfilter = cmdutil.getloglinerangerevs( + repo, revs, opts) + + if filematcher is not None and lrfilematcher is not None: + basefilematcher = filematcher + + def filematcher(rev): + files = (basefilematcher(rev).files() + + lrfilematcher(rev).files()) + return scmutil.matchfiles(repo, files) + + elif filematcher is None: + filematcher = lrfilematcher + limit = cmdutil.loglimit(opts) count = 0 @@ -3385,7 +3437,12 @@ def log(ui, repo, *pats, **opts): revmatchfn = filematcher(ctx.rev()) else: revmatchfn = None - displayer.show(ctx, copies=copies, matchfn=revmatchfn) + if hunksfilter: + revhunksfilter = hunksfilter(rev) + else: + revhunksfilter = None + displayer.show(ctx, copies=copies, matchfn=revmatchfn, + hunksfilterfn=revhunksfilter) if displayer.flush(ctx): count += 1 diff --git a/tests/test-completion.t b/tests/test-completion.t --- a/tests/test-completion.t +++ b/tests/test-completion.t @@ -225,7 +225,7 @@ Show all commands + options export: output, switch-parent, rev, text, git, binary, nodates forget: include, exclude init: ssh, remotecmd, insecure - log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude + log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude merge: force, rev, preview, tool pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure push: force, rev, bookmark, branch, new-branch, pushvars, ssh, remotecmd, insecure diff --git a/tests/test-log-linerange.t b/tests/test-log-linerange.t new file mode 100644 --- /dev/null +++ b/tests/test-log-linerange.t @@ -0,0 +1,869 @@ + $ cat >> $HGRCPATH << EOF + > [diff] + > git = true + > EOF + + $ hg init + $ cat > foo << EOF + > 0 + > 1 + > 2 + > 3 + > 4 + > EOF + $ hg ci -Am init + adding foo + $ cat > foo << EOF + > 0 + > 0 + > 0 + > 0 + > 1 + > 2 + > 3 + > 4 + > EOF + $ hg ci -m 'more 0' + $ sed 's/2/2+/' foo > foo.new + $ mv foo.new foo + $ cat > bar << EOF + > a + > b + > c + > d + > e + > EOF + $ hg add bar + $ hg ci -Am "2 -> 2+; added bar" + $ cat >> foo << EOF + > 5 + > 6 + > 7 + > 8 + > 9 + > 10 + > 11 + > EOF + $ hg ci -m "to 11" + +Add some changes with two diff hunks + + $ sed 's/^1$/ 1/' foo > foo.new + $ mv foo.new foo + $ sed 's/^11$/11+/' foo > foo.new + $ mv foo.new foo + $ hg ci -m '11 -> 11+; leading space before "1"' +(make sure there are two hunks in "foo") + $ hg diff -c . + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -2,7 +2,7 @@ + 0 + 0 + 0 + -1 + + 1 + 2+ + 3 + 4 + @@ -12,4 +12,4 @@ + 8 + 9 + 10 + -11 + +11+ + $ sed 's/3/3+/' foo > foo.new + $ mv foo.new foo + $ sed 's/^11+$/11-/' foo > foo.new + $ mv foo.new foo + $ sed 's/a/a+/' bar > bar.new + $ mv bar.new bar + $ hg ci -m 'foo: 3 -> 3+ and 11+ -> 11-; bar: a -> a+' +(make sure there are two hunks in "foo") + $ hg diff -c . foo + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -4,7 +4,7 @@ + 0 + 1 + 2+ + -3 + +3+ + 4 + 5 + 6 + @@ -12,4 +12,4 @@ + 8 + 9 + 10 + -11+ + +11- + + $ hg log -f -L foo,5-7 -p + changeset: 5:cfdf972b3971 + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: foo: 3 -> 3+ and 11+ -> 11-; bar: a -> a+ + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -4,7 +4,7 @@ + 0 + 1 + 2+ + -3 + +3+ + 4 + 5 + 6 + + changeset: 4:eaec41c1a0c9 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 11 -> 11+; leading space before "1" + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -2,7 +2,7 @@ + 0 + 0 + 0 + -1 + + 1 + 2+ + 3 + 4 + + changeset: 2:63a884426fd0 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 -> 2+; added bar + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -3,6 +3,6 @@ + 0 + 0 + 1 + -2 + +2+ + 3 + 4 + + changeset: 0:5ae1f82b9a00 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: init + + diff --git a/foo b/foo + new file mode 100644 + --- /dev/null + +++ b/foo + @@ -0,0 +1,5 @@ + +0 + +1 + +2 + +3 + +4 + + +With --template. + + $ hg log -f -L foo,5-7 -T '{rev}:{node|short} {desc|firstline}\n' + 5:cfdf972b3971 foo: 3 -> 3+ and 11+ -> 11-; bar: a -> a+ + 4:eaec41c1a0c9 11 -> 11+; leading space before "1" + 2:63a884426fd0 2 -> 2+; added bar + 0:5ae1f82b9a00 init + $ hg log -f -L foo,5-7 -T json + [ + { + "rev": 5, + "node": "cfdf972b3971a2a59638bf9583c0debbffee5404", + "branch": "default", + "phase": "draft", + "user": "test", + "date": [0, 0], + "desc": "foo: 3 -> 3+ and 11+ -> 11-; bar: a -> a+", + "bookmarks": [], + "tags": ["tip"], + "parents": ["eaec41c1a0c9ad0a5e999611d0149d171beffb8c"] + }, + { + "rev": 4, + "node": "eaec41c1a0c9ad0a5e999611d0149d171beffb8c", + "branch": "default", + "phase": "draft", + "user": "test", + "date": [0, 0], + "desc": "11 -> 11+; leading space before \"1\"", + "bookmarks": [], + "tags": [], + "parents": ["730a61fbaecf426c17c2c66bc42d195b5d5b0ba8"] + }, + { + "rev": 2, + "node": "63a884426fd0b277fcd55895bbb2f230434576eb", + "branch": "default", + "phase": "draft", + "user": "test", + "date": [0, 0], + "desc": "2 -> 2+; added bar", + "bookmarks": [], + "tags": [], + "parents": ["29a1e7c6b80024f63f310a2d71de979e9d2996d7"] + }, + { + "rev": 0, + "node": "5ae1f82b9a000ff1e0967d0dac1c58b9d796e1b4", + "branch": "default", + "phase": "draft", + "user": "test", + "date": [0, 0], + "desc": "init", + "bookmarks": [], + "tags": [], + "parents": ["0000000000000000000000000000000000000000"] + } + ] + +With some white-space diff option, respective revisions are skipped. + + $ hg log -f -L foo,5-7 -p --config diff.ignorews=true + changeset: 5:cfdf972b3971 + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: foo: 3 -> 3+ and 11+ -> 11-; bar: a -> a+ + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -4,7 +4,7 @@ + 0 + 1 + 2+ + -3 + +3+ + 4 + 5 + 6 + + changeset: 2:63a884426fd0 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 -> 2+; added bar + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -3,6 +3,6 @@ + 0 + 0 + 1 + -2 + +2+ + 3 + 4 + + changeset: 0:5ae1f82b9a00 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: init + + diff --git a/foo b/foo + new file mode 100644 + --- /dev/null + +++ b/foo + @@ -0,0 +1,5 @@ + +0 + +1 + +2 + +3 + +4 + + +Regular file patterns are allowed with -L and their diff shows all lines. + + $ hg log -f -L foo,5-7 -p bar + changeset: 5:cfdf972b3971 + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: foo: 3 -> 3+ and 11+ -> 11-; bar: a -> a+ + + diff --git a/bar b/bar + --- a/bar + +++ b/bar + @@ -1,4 +1,4 @@ + -a + +a+ + b + c + d + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -4,7 +4,7 @@ + 0 + 1 + 2+ + -3 + +3+ + 4 + 5 + 6 + + changeset: 2:63a884426fd0 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 -> 2+; added bar + + diff --git a/bar b/bar + new file mode 100644 + --- /dev/null + +++ b/bar + @@ -0,0 +1,5 @@ + +a + +b + +c + +d + +e + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -3,6 +3,6 @@ + 0 + 0 + 1 + -2 + +2+ + 3 + 4 + + +Option --rev acts as a restriction. + + $ hg log -f -L foo,5-7 -p -r 'desc(2)' + changeset: 2:63a884426fd0 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 -> 2+; added bar + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -3,6 +3,6 @@ + 0 + 0 + 1 + -2 + +2+ + 3 + 4 + + changeset: 0:5ae1f82b9a00 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: init + + diff --git a/foo b/foo + new file mode 100644 + --- /dev/null + +++ b/foo + @@ -0,0 +1,5 @@ + +0 + +1 + +2 + +3 + +4 + + +With several -L patterns, changes touching any files in their respective line +range are show. + + $ hg log -f -L foo,5-7 -L bar,1-2 -p + changeset: 5:cfdf972b3971 + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: foo: 3 -> 3+ and 11+ -> 11-; bar: a -> a+ + + diff --git a/bar b/bar + --- a/bar + +++ b/bar + @@ -1,4 +1,4 @@ + -a + +a+ + b + c + d + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -4,7 +4,7 @@ + 0 + 1 + 2+ + -3 + +3+ + 4 + 5 + 6 + + changeset: 4:eaec41c1a0c9 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 11 -> 11+; leading space before "1" + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -2,7 +2,7 @@ + 0 + 0 + 0 + -1 + + 1 + 2+ + 3 + 4 + + changeset: 2:63a884426fd0 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 -> 2+; added bar + + diff --git a/bar b/bar + new file mode 100644 + --- /dev/null + +++ b/bar + @@ -0,0 +1,5 @@ + +a + +b + +c + +d + +e + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -3,6 +3,6 @@ + 0 + 0 + 1 + -2 + +2+ + 3 + 4 + + changeset: 0:5ae1f82b9a00 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: init + + diff --git a/foo b/foo + new file mode 100644 + --- /dev/null + +++ b/foo + @@ -0,0 +1,5 @@ + +0 + +1 + +2 + +3 + +4 + + +Multiple -L options with the same file yields changes touching any of +specified line ranges. + + $ hg log -f -L foo,5-7 -L foo,14-15 -p + changeset: 5:cfdf972b3971 + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: foo: 3 -> 3+ and 11+ -> 11-; bar: a -> a+ + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -4,7 +4,7 @@ + 0 + 1 + 2+ + -3 + +3+ + 4 + 5 + 6 + @@ -12,4 +12,4 @@ + 8 + 9 + 10 + -11+ + +11- + + changeset: 4:eaec41c1a0c9 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 11 -> 11+; leading space before "1" + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -2,7 +2,7 @@ + 0 + 0 + 0 + -1 + + 1 + 2+ + 3 + 4 + @@ -12,4 +12,4 @@ + 8 + 9 + 10 + -11 + +11+ + + changeset: 3:730a61fbaecf + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: to 11 + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -6,3 +6,10 @@ + 2+ + 3 + 4 + +5 + +6 + +7 + +8 + +9 + +10 + +11 + + changeset: 2:63a884426fd0 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 -> 2+; added bar + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -3,6 +3,6 @@ + 0 + 0 + 1 + -2 + +2+ + 3 + 4 + + changeset: 0:5ae1f82b9a00 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: init + + diff --git a/foo b/foo + new file mode 100644 + --- /dev/null + +++ b/foo + @@ -0,0 +1,5 @@ + +0 + +1 + +2 + +3 + +4 + + +A file with a comma in its name. + + $ cat > ba,z << EOF + > q + > w + > e + > r + > t + > y + > EOF + $ hg ci -Am 'querty' + adding ba,z + $ cat >> ba,z << EOF + > u + > i + > o + > p + > EOF + $ hg ci -m 'more keys' + $ cat > ba,z << EOF + > a + > z + > e + > r + > t + > y + > u + > i + > o + > p + > EOF + $ hg ci -m 'azerty' + $ hg log -f -L ba,z,1-2 -p + changeset: 8:52373265138b + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: azerty + + diff --git a/ba,z b/ba,z + --- a/ba,z + +++ b/ba,z + @@ -1,5 +1,5 @@ + -q + -w + +a + +z + e + r + t + + changeset: 6:96ba8850f316 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: querty + + diff --git a/ba,z b/ba,z + new file mode 100644 + --- /dev/null + +++ b/ba,z + @@ -0,0 +1,6 @@ + +q + +w + +e + +r + +t + +y + + +Exact prefix kinds work in -L options. + + $ mkdir dir + $ cd dir + $ hg log -f -L path:foo,5-7 -p + changeset: 5:cfdf972b3971 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: foo: 3 -> 3+ and 11+ -> 11-; bar: a -> a+ + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -4,7 +4,7 @@ + 0 + 1 + 2+ + -3 + +3+ + 4 + 5 + 6 + + changeset: 4:eaec41c1a0c9 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 11 -> 11+; leading space before "1" + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -2,7 +2,7 @@ + 0 + 0 + 0 + -1 + + 1 + 2+ + 3 + 4 + + changeset: 2:63a884426fd0 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 -> 2+; added bar + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -3,6 +3,6 @@ + 0 + 0 + 1 + -2 + +2+ + 3 + 4 + + changeset: 0:5ae1f82b9a00 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: init + + diff --git a/foo b/foo + new file mode 100644 + --- /dev/null + +++ b/foo + @@ -0,0 +1,5 @@ + +0 + +1 + +2 + +3 + +4 + + +Renames are followed. + + $ hg mv ../foo baz + $ sed 's/1/1+/' baz > baz.new + $ mv baz.new baz + $ hg ci -m 'foo -> dir/baz; 1-1+' + $ hg diff -c . + diff --git a/foo b/dir/baz + rename from foo + rename to dir/baz + --- a/foo + +++ b/dir/baz + @@ -2,7 +2,7 @@ + 0 + 0 + 0 + - 1 + + 1+ + 2+ + 3+ + 4 + @@ -11,5 +11,5 @@ + 7 + 8 + 9 + -10 + -11- + +1+0 + +1+1- + $ hg log -f -L relpath:baz,5-7 -p + changeset: 9:6af29c3a778f + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: foo -> dir/baz; 1-1+ + + diff --git a/foo b/dir/baz + copy from foo + copy to dir/baz + --- a/foo + +++ b/dir/baz + @@ -2,7 +2,7 @@ + 0 + 0 + 0 + - 1 + + 1+ + 2+ + 3+ + 4 + + changeset: 5:cfdf972b3971 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: foo: 3 -> 3+ and 11+ -> 11-; bar: a -> a+ + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -4,7 +4,7 @@ + 0 + 1 + 2+ + -3 + +3+ + 4 + 5 + 6 + + changeset: 4:eaec41c1a0c9 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 11 -> 11+; leading space before "1" + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -2,7 +2,7 @@ + 0 + 0 + 0 + -1 + + 1 + 2+ + 3 + 4 + + changeset: 2:63a884426fd0 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 -> 2+; added bar + + diff --git a/foo b/foo + --- a/foo + +++ b/foo + @@ -3,6 +3,6 @@ + 0 + 0 + 1 + -2 + +2+ + 3 + 4 + + changeset: 0:5ae1f82b9a00 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: init + + diff --git a/foo b/foo + new file mode 100644 + --- /dev/null + +++ b/foo + @@ -0,0 +1,5 @@ + +0 + +1 + +2 + +3 + +4 + +Option --follow is required. + + $ hg log -L foo,5-7 + abort: --line-range requires --follow + [255] + +Non-exact pattern kinds are not allowed. + + $ cd .. + $ hg log -f -L glob:*a*,1-2 + hg: parse error: line range pattern 'glob:*a*' must match exactly one file + [255] + +Graph log does work yet. + + $ hg log -f -L dir/baz,5-7 --graph + abort: graph not supported with line range patterns + [255]