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'', 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