pickaxe -G: terminate early on matching lines

Solve a long-standing item for "git log -Grx" of us e.g. finding "+ str" in the diff context and noting that we had a "hit", but xdiff diligently continuing to generate and spew the rest of the diff at us. This makes use of a new "early return" xdiff interface added by preceding commits. The TODO item (or, the NEEDSWORK comment) has been there since "git log -G" was implemented. See f506b8e8b5f (git log/diff: add -G<regexp> that greps in the patch text, 2010-08-23). But now with the support added in the preceding changes to the xdiff-interface we can return early. Let's assert the behavior of that new early-return xdiff-interface by having a BUG() call here to die if it ever starts handing us needless work again. Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ævar Arnfjörð Bjarmason committed Apr 12, 2021 at 19:15 UTC fa59e7beb2a61d9cd1312b212075edf12935fdc6
2 files changed +23 -11
diffcore-pickaxe.c
+19 -11
@@ -27,13 +27,12 @@ static int diffgrep_consume(void *priv, char *line, unsigned long len)
27 if (line[0] != '+' && line[0] != '-')
28 return 0;
29 if (data->hit)
30 - /*
31 - * NEEDSWORK: we should have a way to terminate the
32 - * caller early.
33 - */
34 - return 0;
35 - data->hit = !regexec_buf(data->regexp, line + 1, len - 1, 1,
36 - &regmatch, 0);
30 + BUG("Already matched in diffgrep_consume! Broken xdiff_emit_line_fn?");
31 + if (!regexec_buf(data->regexp, line + 1, len - 1, 1,
32 + &regmatch, 0)) {
33 + data->hit = 1;
34 + return 1;
35 + }
36 return 0;
37 }
38
@@ -45,6 +44,7 @@ static int diff_grep(mmfile_t *one, mmfile_t *two,
44 struct diffgrep_cb ecbdata;
45 xpparam_t xpp;
46 xdemitconf_t xecfg;
47 + int ret;
48
49 if (!one)
50 return !regexec_buf(regexp, two->ptr, two->size,
@@ -63,10 +63,18 @@ static int diff_grep(mmfile_t *one, mmfile_t *two,
63 ecbdata.hit = 0;
64 xecfg.ctxlen = o->context;
65 xecfg.interhunkctxlen = o->interhunkcontext;
66 - if (xdi_diff_outf(one, two, discard_hunk_line, diffgrep_consume,
67 - &ecbdata, &xpp, &xecfg))
68 - return 0;
69 - return ecbdata.hit;
66 +
67 + /*
68 + * An xdiff error might be our "data->hit" from above. See the
69 + * comment for xdiff_emit_line_fn in xdiff-interface.h
70 + */
71 + ret = xdi_diff_outf(one, two, discard_hunk_line, diffgrep_consume,
72 + &ecbdata, &xpp, &xecfg);
73 + if (ecbdata.hit)
74 + return 1;
75 + if (ret)
76 + return ret;
77 + return 0;
78 }
79
80 static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws,
xdiff-interface.h
+4
@@ -27,6 +27,10 @@
27 * doing so will currently make your early return indistinguishable
28 * from an error internal to xdiff, xdiff itself will see that
29 * non-zero return and translate it to -1.
30 + *
31 + * See "diff_grep" in diffcore-pickaxe.c for a trick to work around
32 + * this, i.e. using the "consume_callback_data" to note the desired
33 + * early return.
34 */
35 typedef int (*xdiff_emit_line_fn)(void *, char *, unsigned long);
36 typedef void (*xdiff_emit_hunk_fn)(void *data,