Patchwork [7,of,7] revset: add diff(pattern) predicate for "grep --diff"

login
register
mail settings
Submitter Yuya Nishihara
Date Oct. 14, 2020, 2:13 p.m.
Message ID <86c34dc393abb0cd636e.1602684817@mimosa>
Download mbox | patch
Permalink /patch/47462/
State Accepted
Headers show

Comments

Yuya Nishihara - Oct. 14, 2020, 2:13 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1599556584 -32400
#      Tue Sep 08 18:16:24 2020 +0900
# Node ID 86c34dc393abb0cd636ec03a2e81a26f4b0694d5
# Parent  c71d4b4018af62936283b28ce4ed70cc9ea33840
revset: add diff(pattern) predicate for "grep --diff"

I find this is useful in GUI log viewer since the tool only needs to support
"log -rREV" command.

This is basic implementation. Windowed search is not implemented since it
wouldn't work pretty well with the smartset API. And filename matcher is
not supported because the syntax isn't determined. My idea is to add handling
of diff(pattern, file(..)) and diff(pattern, follow(..)), which will then be
evolved to a full revset+matcher combinator support:

  x & diff(pattern, y & z)
                    =====
                    y & z builds (revs(y) & revs(z), matcher(y) & matcher(z))
                    pair, and narrows the search space of diff()
      ====================
      diff() returns matched (revs, matcher) pair
  ========================
  revs and matcher will be combined respectively by &-operator, and the matcher
  will optionally be used to filter "hg log -p" output

The predicate name "diff()" wouldn't be great, but grep() is already used.
Another options I can think of are "grepdiff()" and "containsdiff()".
Naming suggestions are welcome.
Pulkit Goyal - Oct. 15, 2020, 12:12 p.m.
On Wed, Oct 14, 2020 at 8:00 PM Yuya Nishihara <yuya@tcha.org> wrote:
>
> # HG changeset patch
> # User Yuya Nishihara <yuya@tcha.org>
> # Date 1599556584 -32400
> #      Tue Sep 08 18:16:24 2020 +0900
> # Node ID 86c34dc393abb0cd636ec03a2e81a26f4b0694d5
> # Parent  c71d4b4018af62936283b28ce4ed70cc9ea33840
> revset: add diff(pattern) predicate for "grep --diff"

I am not sure whether release-notes script will pick this up. It will
be nice if you followup with an entry in `relnotes/next` about it.
Yuya Nishihara - Oct. 16, 2020, 10:59 a.m.
On Thu, 15 Oct 2020 17:42:48 +0530, Pulkit Goyal wrote:
> On Wed, Oct 14, 2020 at 8:00 PM Yuya Nishihara <yuya@tcha.org> wrote:
> > # HG changeset patch
> > # User Yuya Nishihara <yuya@tcha.org>
> > # Date 1599556584 -32400
> > #      Tue Sep 08 18:16:24 2020 +0900
> > # Node ID 86c34dc393abb0cd636ec03a2e81a26f4b0694d5
> > # Parent  c71d4b4018af62936283b28ce4ed70cc9ea33840
> > revset: add diff(pattern) predicate for "grep --diff"
> 
> I am not sure whether release-notes script will pick this up. It will
> be nice if you followup with an entry in `relnotes/next` about it.

Sure.

Btw, which name do you prefer?

 a. diff(pattern)
 b. diffcontains(pattern), by augie
 c. ...
Pulkit Goyal - Oct. 16, 2020, 1:55 p.m.
On Fri, 16 Oct, 2020, 16:30 Yuya Nishihara, <yuya@tcha.org> wrote:

> On Thu, 15 Oct 2020 17:42:48 +0530, Pulkit Goyal wrote:
> > On Wed, Oct 14, 2020 at 8:00 PM Yuya Nishihara <yuya@tcha.org> wrote:
> > > # HG changeset patch
> > > # User Yuya Nishihara <yuya@tcha.org>
> > > # Date 1599556584 -32400
> > > #      Tue Sep 08 18:16:24 2020 +0900
> > > # Node ID 86c34dc393abb0cd636ec03a2e81a26f4b0694d5
> > > # Parent  c71d4b4018af62936283b28ce4ed70cc9ea33840
> > > revset: add diff(pattern) predicate for "grep --diff"
> >
> > I am not sure whether release-notes script will pick this up. It will
> > be nice if you followup with an entry in `relnotes/next` about it.
>
> Sure.
>
> Btw, which name do you prefer?
>
>  a. diff(pattern)
>  b. diffcontains(pattern), by augie
>

+1 for diffcontains()

 c. ...

Patch

diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -17,6 +17,7 @@  from . import (
     diffutil,
     encoding,
     error,
+    grep as grepmod,
     hbisect,
     match as matchmod,
     node,
@@ -993,6 +994,45 @@  def destination(repo, subset, x):
     )
 
 
+@predicate(b'diff(pattern)', weight=110)
+def diff(repo, subset, x):
+    """Search revision differences for when the pattern was added or removed.
+
+    The pattern may be a substring literal or a regular expression. See
+    :hg:`help revisions.patterns`.
+    """
+    args = getargsdict(x, b'diff', b'pattern')
+    if b'pattern' not in args:
+        # i18n: "diff" is a keyword
+        raise error.ParseError(_(b'diff takes at least 1 argument'))
+
+    pattern = getstring(args[b'pattern'], _(b'diff requires a string pattern'))
+    regexp = stringutil.substringregexp(pattern, re.M)
+
+    # TODO: add support for file pattern and --follow. For example,
+    # diff(pattern[, set]) where set may be file(pattern) or follow(pattern),
+    # and we'll eventually add a support for narrowing files by revset?
+    fmatch = matchmod.always()
+
+    def makefilematcher(ctx):
+        return fmatch
+
+    # TODO: search in a windowed way
+    searcher = grepmod.grepsearcher(repo.ui, repo, regexp, diff=True)
+
+    def testdiff(rev):
+        # consume the generator to discard revfiles/matches cache
+        found = False
+        for fn, ctx, pstates, states in searcher.searchfiles(
+            baseset([rev]), makefilematcher
+        ):
+            if next(grepmod.difflinestates(pstates, states), None):
+                found = True
+        return found
+
+    return subset.filter(testdiff, condrepr=(b'<diff %r>', pattern))
+
+
 @predicate(b'contentdivergent()', safe=True)
 def contentdivergent(repo, subset, x):
     """
diff --git a/tests/test-grep.t b/tests/test-grep.t
--- a/tests/test-grep.t
+++ b/tests/test-grep.t
@@ -21,6 +21,18 @@  pattern error
   grep: invalid match pattern: nothing to repeat* (glob)
   [1]
 
+invalid revset syntax
+
+  $ hg log -r 'diff()'
+  hg: parse error: diff takes at least 1 argument
+  [255]
+  $ hg log -r 'diff(:)'
+  hg: parse error: diff requires a string pattern
+  [255]
+  $ hg log -r 'diff("re:**test**")'
+  hg: parse error: invalid regular expression: nothing to repeat* (glob)
+  [255]
+
 simple
 
   $ hg grep -r tip:0 '.*'
@@ -553,6 +565,18 @@  Test wdir
   color:2:-:orange
   color:1:+:orange
 
+revset predicate for "grep --diff"
+
+  $ hg log -qr 'diff("re:^bl...$")'
+  0:203191eb5e21
+  $ hg log -qr 'diff("orange")'
+  1:7c585a21e0d1
+  2:11bd8bc8d653
+  3:e0116d3829f8
+  $ hg log -qr '2:0 & diff("orange")'
+  2:11bd8bc8d653
+  1:7c585a21e0d1
+
 test substring match: '^' should only match at the beginning
 
   $ hg grep -r tip:0 '^.' --config extensions.color= --color debug