[PATCH 4 of 4] log: add -L/--line-range option to follow file history by line range

Denis Laxalde denis at laxalde.org
Wed Oct 4 11:04:02 EDT 2017


# HG changeset patch
# User Denis Laxalde <denis.laxalde at logilab.fr>
# Date 1507127733 -7200
#      Wed Oct 04 16:35:33 2017 +0200
# Node ID 0f2d8b304223a8d00163f917fdc18082a88bceae
# Parent  9274bdbba0f913672305ed3d91c7e05f38d1eab7
# Available At http://hg.logilab.org/users/dlaxalde/hg
#              hg pull http://hg.logilab.org/users/dlaxalde/hg -r 0f2d8b304223
# EXP-Topic followlines-cli
log: add -L/--line-range option to follow file history by line range

We add a -L/--line-range option to 'hg log' taking file patterns along with a
line range using the (new) FILE,FROMLINE-TOLINE syntax where FILE may be a
pattern (matching exactly one file). The resulting history is similar to what
the "followlines" revset except that, if --patch is specified, only diff hunks
within specified line range are shown (it's also more convenient to type).

Basically, this brings the CLI on par with what currently only exists in hgweb
through line selection in "file" and "annotate" views resulting in a file log
with filtered patch to only display followed line range.

The option may be specified multiple times and can be combined with --rev or
regular file pattern in a meaningful way. It also respects --follow semantics.
Only the --graph option is currently not supported.

Some debatable UI choices (I did not think too much about this for now).

*   "," as a separator between the FILE and line range information; the idea
    is to avoid confusion with file pattern syntax which uses ":".

*   "-" in the line range information may not be the best choice; in
    particular, we might want to add support for an offset +/- syntax.


The implementation spreads between commands.log() and cmdutil module.
In commands.log(), the main loop now works with tuples of (rev, matchfn,
lineranges), where 'rev' and 'matchfn' are similar to before except that
'matchfn' is now built out the loop. Without any -L option, "lineranges" is
None. With a -L option, 'matchfn' is a new matcher function built from files
specified in -L option(s) and any other regular FILE pattern.

The logic to build revisions from -L/--line-range options lives in
cmdutil.getloglineranges() and the resulting information is combined with
information from --rev option and file patterns in commands.log(). Options
--rev and file patterns act as restrictions to the revisions obtained by
following line-range history of files specified in -L/--line-range patterns.

.. feature::

   Add a -L/--line-range FILE,FROMLINE-TOLINE option to 'hg log' command to
   follow the history of files by line range. In combination with -p/--patch
   option, only diff hunks within specified line range will be displayed.

diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -26,6 +26,7 @@ from . import (
     changelog,
     copies,
     crecord as crecordmod,
+    dagop,
     dirstateguard,
     encoding,
     error,
@@ -2593,6 +2594,46 @@ 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 getloglineranges(repo, opts):
+    """Return a generator of tuples (rev, fpath, [line range]) obtained by
+    processing "line-range" log options and walking block ancestors of each
+    specified file/line-range.
+    """
+    wctx = repo[None]
+    follow = bool(opts.get('follow'))
+    # Two-levels map of "rev -> file path -> [line range]".
+    linerangesbyrev = {}
+    for fname, (fromline, toline) in _parselinerangelogopt(repo, opts):
+        fctx = wctx.filectx(fname)
+        for fctx, linerange in dagop.blockancestors(fctx, fromline, toline):
+            fpath = fctx.path()
+            if not follow and fpath != fname:
+                continue
+            linerangesbyrev.setdefault(
+                fctx.rev(), {}).setdefault(
+                    fpath, []).append(linerange)
+    return sorted(linerangesbyrev.iteritems(), reverse=follow)
+
 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
@@ -3230,6 +3230,8 @@ 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'),
+     _('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')),
@@ -3271,6 +3273,11 @@ 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.
 
+    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 can be specified multiple times.
+    This option is currently not compatible with --graph.
+
     .. note::
 
        :hg:`log --patch` may generate unexpected diff output for merge
@@ -3284,6 +3291,12 @@ def log(ui, repo, *pats, **opts):
        made on branches and will not show removals or mode changes. To
        see all such changes, use the --removed switch.
 
+    .. 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:
@@ -3332,6 +3345,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, shown in descending direction with patch::
+
+          hg log -L file.c,13-23 -L main.c,2-6 -p -f
+
     See :hg:`help dates` for a list of formats valid for -d/--date.
 
     See :hg:`help revisions` for more about specifying and ordering
@@ -3346,14 +3368,45 @@ def log(ui, repo, *pats, **opts):
 
     """
     opts = pycompat.byteskwargs(opts)
+    followlines = opts.get('line_range')
+    if followlines:
+        linerangesbyrev = cmdutil.getloglineranges(repo, opts)
+
     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 followlines:
+            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)
+
+    # Build an iterator of (rev, matcher, lineranges) tuples to be consumed
+    # later in the main loop.
+    if followlines:
+        # Filter revisions from --line-range patterns from those not in --rev
+        # option and combine any existing filematcher with the one built from
+        # "line range" patterns.
+        if filematcher is None:
+            def buildmatcher(files):
+                return scmutil.match(repo[None], files)
+        else:
+            def buildmatcher(files):
+                return scmutil.match(repo[None],
+                                     files + filematcher(None).files(),
+                                     default='path')
+        revsmatchandlineranges = (
+            (rev, buildmatcher(list(lrs)), lrs)
+            for rev, lrs in linerangesbyrev if rev in revs
+        )
+    else:
+        revsmatchandlineranges = (
+            (rev, filematcher(rev) if filematcher else None, None)
+            for rev in revs
+        )
+
     limit = cmdutil.loglimit(opts)
     count = 0
 
@@ -3366,7 +3419,7 @@ def log(ui, repo, *pats, **opts):
 
     ui.pager('log')
     displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
-    for rev in revs:
+    for rev, matchfn, lineranges in revsmatchandlineranges:
         if count == limit:
             break
         ctx = repo[rev]
@@ -3377,11 +3430,8 @@ def log(ui, repo, *pats, **opts):
                 rename = getrenamed(fn, rev)
                 if rename:
                     copies.append((fn, rename[0]))
-        if filematcher:
-            revmatchfn = filematcher(ctx.rev())
-        else:
-            revmatchfn = None
-        displayer.show(ctx, copies=copies, matchfn=revmatchfn)
+        displayer.show(ctx, copies=copies, matchfn=matchfn,
+                       lineranges=lineranges)
         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,848 @@
+  $ 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
+  $ hg ci -m "2 -> 2+"
+  $ 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
+  $ cat > bar << EOF
+  > a
+  > b
+  > c
+  > d
+  > e
+  > EOF
+  $ hg ci -Am '3 -> 3+; 11+ -> 11- and added bar'
+  adding bar
+(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 -L foo,5-7 -p
+  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
+  
+  changeset:   2:3aa051770d8a
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     2 -> 2+
+  
+  diff --git a/foo b/foo
+  --- a/foo
+  +++ b/foo
+  @@ -3,6 +3,6 @@
+   0
+   0
+   1
+  -2
+  +2+
+   3
+   4
+  
+  changeset:   4:d7650978f3c7
+  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:   5:b628de18978f
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     3 -> 3+; 11+ -> 11- and added bar
+  
+  diff --git a/foo b/foo
+  --- a/foo
+  +++ b/foo
+  @@ -4,7 +4,7 @@
+   0
+    1
+   2+
+  -3
+  +3+
+   4
+   5
+   6
+  
+
+With --follow.
+
+  $ hg log -L foo,5-7 -p -f
+  changeset:   5:b628de18978f
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     3 -> 3+; 11+ -> 11- and added bar
+  
+  diff --git a/foo b/foo
+  --- a/foo
+  +++ b/foo
+  @@ -4,7 +4,7 @@
+   0
+    1
+   2+
+  -3
+  +3+
+   4
+   5
+   6
+  
+  changeset:   4:d7650978f3c7
+  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:3aa051770d8a
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     2 -> 2+
+  
+  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 some white-space diff option, respective revisions are skipped.
+
+  $ hg log -L foo,5-7 -p -f --config diff.ignorews=true
+  changeset:   5:b628de18978f
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     3 -> 3+; 11+ -> 11- and added bar
+  
+  diff --git a/foo b/foo
+  --- a/foo
+  +++ b/foo
+  @@ -4,7 +4,7 @@
+   0
+    1
+   2+
+  -3
+  +3+
+   4
+   5
+   6
+  
+  changeset:   2:3aa051770d8a
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     2 -> 2+
+  
+  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 a regular file pattern, revision not touching this pattern are not shown.
+
+  $ sed 's/d/d+/' bar > bar.new
+  $ mv bar.new bar
+  $ hg ci -Am 'd -> d+'
+  $ hg log -L foo,5-7 -p bar
+  changeset:   5:b628de18978f
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     3 -> 3+; 11+ -> 11- and 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
+  @@ -4,7 +4,7 @@
+   0
+    1
+   2+
+  -3
+  +3+
+   4
+   5
+   6
+  
+
+Option --rev acts as a restriction as well.
+
+  $ hg log -L foo,5-7 -p -r 'desc(2)'
+  changeset:   2:3aa051770d8a
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     2 -> 2+
+  
+  diff --git a/foo b/foo
+  --- a/foo
+  +++ b/foo
+  @@ -3,6 +3,6 @@
+   0
+   0
+   1
+  -2
+  +2+
+   3
+   4
+  
+
+With several -L patterns, only changes touching both files in their respective
+line range are show.
+
+  $ hg log -L foo,5-7 -L bar,1-2 -p -f
+  changeset:   5:b628de18978f
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     3 -> 3+; 11+ -> 11- and 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
+  @@ -4,7 +4,7 @@
+   0
+    1
+   2+
+  -3
+  +3+
+   4
+   5
+   6
+  
+  changeset:   4:d7650978f3c7
+  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:3aa051770d8a
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     2 -> 2+
+  
+  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 -L foo,5-7 -L foo,14-15 -p -f
+  changeset:   5:b628de18978f
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     3 -> 3+; 11+ -> 11- and added bar
+  
+  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:d7650978f3c7
+  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:bf412c98f1f1
+  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:3aa051770d8a
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     2 -> 2+
+  
+  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 -L ba,z,1-2 -p
+  changeset:   7:61cf51d887ba
+  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
+  
+  changeset:   9:2d4ce1e2d65f
+  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
+  
+
+Exact prefix kinds work in -L options.
+
+  $ mkdir dir
+  $ cd dir
+  $ hg log -L path:foo,5-7 -p -f
+  changeset:   5:b628de18978f
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     3 -> 3+; 11+ -> 11- and added bar
+  
+  diff --git a/foo b/foo
+  --- a/foo
+  +++ b/foo
+  @@ -4,7 +4,7 @@
+   0
+    1
+   2+
+  -3
+  +3+
+   4
+   5
+   6
+  
+  changeset:   4:d7650978f3c7
+  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:3aa051770d8a
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     2 -> 2+
+  
+  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 according to --follow.
+
+  $ 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 -L relpath:baz,5-7 -p
+  changeset:   10:cb106a952b96
+  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
+  
+  $ hg log -L relpath:baz,5-7 -p --follow
+  changeset:   10:cb106a952b96
+  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:b628de18978f
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     3 -> 3+; 11+ -> 11- and added bar
+  
+  diff --git a/foo b/foo
+  --- a/foo
+  +++ b/foo
+  @@ -4,7 +4,7 @@
+   0
+    1
+   2+
+  -3
+  +3+
+   4
+   5
+   6
+  
+  changeset:   4:d7650978f3c7
+  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:3aa051770d8a
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     2 -> 2+
+  
+  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
+  
+
+Non-exact pattern kinds are not allowed.
+
+  $ cd ..
+  $ hg log -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 -L dir/baz,5-7 --graph
+  abort: graph not supported with line range patterns
+  [255]


More information about the Mercurial-devel mailing list