Skip to content

Commit a1b3ff0

Browse files
committed
diffcore-pickaxe: scope -G to the -L tracked range
git log -L scopes its diff output to the tracked range, but pickaxe (-S, -G) still runs in diffcore over the whole-file change, so -L -G selects a commit whenever the pattern appears in any added or removed line of the file, even outside the tracked range. Teach -G to honor the range. diff_grep() already runs an xdiff pass and greps the +/- lines; route that pass through the line-range filter so only the tracked range's lines are grepped. Expose the filter as diff_emit_line_ranges(), a line-range-scoped xdi_diff_outf(), thread the filepair's line_ranges through the pickaxe callback, and pass it from pickaxe_match(). Skip scoping under textconv, whose output is not in the original file's line coordinates. -G needs only a hit/no-hit answer, so the line-number concerns the filter handles for patch and check output do not apply here. -S is left matching the whole file: it counts needle occurrences per blob rather than grepping the diff, so scoping it needs a different approach, left to a follow-up. has_changes() takes the range parameter but ignores it for now. Document the resulting -L pickaxe scoping: -G is range-scoped, while -S still matches the whole file. Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
1 parent dfb6a0b commit a1b3ff0

4 files changed

Lines changed: 80 additions & 9 deletions

File tree

Documentation/line-range-options.adoc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
+
2121
Patch formatting options such as `--word-diff`, `--color-moved`,
2222
`--no-prefix`, and whitespace options (`-w`, `-b`) are supported,
23-
as are pickaxe options (`-S`, `-G`) and `--diff-filter`.
23+
as are pickaxe options (`-S`, `-G`) and `--diff-filter`. `-G` is
24+
scoped to the tracked range; `-S` is still evaluated over the whole
25+
file, so an `-S` query may select a commit for a change outside the
26+
range.
2427
+
2528
include::line-range-format.adoc[]

diff.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2812,6 +2812,27 @@ static int line_range_filter_diff(struct line_range_filter *filter,
28122812
return ret;
28132813
}
28142814

2815+
/*
2816+
* Public entry point: run an xdiff of (one, two) but forward only the lines
2817+
* inside the given (post-image) line ranges to line_fn, the way "git log -L"
2818+
* scopes its output. Lets a diff-driven operation outside diff.c (e.g.
2819+
* pickaxe -G) restrict itself to a tracked range. Returns the latched return
2820+
* value of line_fn (so a consumer that signals a hit via a non-zero return
2821+
* works), or non-zero on xdiff failure.
2822+
*/
2823+
int diff_emit_line_ranges(mmfile_t *one, mmfile_t *two,
2824+
const struct range_set *ranges,
2825+
xdiff_emit_line_fn line_fn, void *cb_data,
2826+
xpparam_t *xpp, xdemitconf_t *xecfg)
2827+
{
2828+
struct line_range_filter filter;
2829+
2830+
/* the filter tracks the post-image position from hunk headers */
2831+
xecfg->flags &= ~XDL_EMIT_NO_HUNK_HDR;
2832+
line_range_filter_init(&filter, ranges, line_fn, cb_data);
2833+
return line_range_filter_diff(&filter, one, two, xpp, xecfg);
2834+
}
2835+
28152836
static void pprint_rename(struct strbuf *name, const char *a, const char *b)
28162837
{
28172838
const char *old_name = a;

diffcore-pickaxe.c

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616

1717
typedef int (*pickaxe_fn)(mmfile_t *one, mmfile_t *two,
1818
struct diff_options *o,
19-
regex_t *regexp, kwset_t kws);
19+
regex_t *regexp, kwset_t kws,
20+
const struct range_set *ranges);
2021

2122
struct diffgrep_cb {
2223
regex_t *regexp;
@@ -42,16 +43,18 @@ static int diffgrep_consume(void *priv, char *line, unsigned long len)
4243

4344
static int diff_grep(mmfile_t *one, mmfile_t *two,
4445
struct diff_options *o,
45-
regex_t *regexp, kwset_t kws UNUSED)
46+
regex_t *regexp, kwset_t kws UNUSED,
47+
const struct range_set *ranges)
4648
{
4749
struct diffgrep_cb ecbdata;
4850
xpparam_t xpp;
4951
xdemitconf_t xecfg;
5052
int ret;
5153

5254
/*
53-
* We have both sides; need to run textual diff and see if
54-
* the pattern appears on added/deleted lines.
55+
* We have both sides; need to run textual diff and see if the
56+
* pattern appears on added/deleted lines. Under -L (ranges set),
57+
* forward only the tracked range's lines so the match is scoped.
5558
*/
5659
memset(&xpp, 0, sizeof(xpp));
5760
memset(&xecfg, 0, sizeof(xecfg));
@@ -65,8 +68,12 @@ static int diff_grep(mmfile_t *one, mmfile_t *two,
6568
* An xdiff error might be our "data->hit" from above. See the
6669
* comment for xdiff_emit_line_fn in xdiff-interface.h
6770
*/
68-
ret = xdi_diff_outf(one, two, NULL, diffgrep_consume,
69-
&ecbdata, &xpp, &xecfg);
71+
if (ranges)
72+
ret = diff_emit_line_ranges(one, two, ranges, diffgrep_consume,
73+
&ecbdata, &xpp, &xecfg);
74+
else
75+
ret = xdi_diff_outf(one, two, NULL, diffgrep_consume,
76+
&ecbdata, &xpp, &xecfg);
7077
if (ecbdata.hit)
7178
return 1;
7279
if (ret)
@@ -119,8 +126,13 @@ static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws,
119126

120127
static int has_changes(mmfile_t *one, mmfile_t *two,
121128
struct diff_options *o UNUSED,
122-
regex_t *regexp, kwset_t kws)
129+
regex_t *regexp, kwset_t kws,
130+
const struct range_set *ranges UNUSED)
123131
{
132+
/*
133+
* -S counts needle occurrences in each whole blob. Scoping this to
134+
* a -L range is left to a follow-up; for now -S ignores the range.
135+
*/
124136
unsigned int c1 = one ? contains(one, regexp, kws, 0) : 0;
125137
unsigned int c2 = two ? contains(two, regexp, kws, c1 + 1) : 0;
126138
return c1 != c2;
@@ -169,7 +181,13 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
169181
mf1.size = fill_textconv(o->repo, textconv_one, p->one, &mf1.ptr);
170182
mf2.size = fill_textconv(o->repo, textconv_two, p->two, &mf2.ptr);
171183

172-
ret = fn(&mf1, &mf2, o, regexp, kws);
184+
/*
185+
* -L scopes the search to the tracked range. Skip that under
186+
* textconv: the range is in original-file line coordinates, which do
187+
* not map onto textconv output, so fall back to the whole file.
188+
*/
189+
ret = fn(&mf1, &mf2, o, regexp, kws,
190+
(textconv_one || textconv_two) ? NULL : p->line_ranges);
173191

174192
if (textconv_one)
175193
free(mf1.ptr);

t/t4211-line-log.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,4 +1078,33 @@ test_expect_success '--check does not report blank-at-eof outside the range' '
10781078
test_grep ! "blank line at EOF" actual
10791079
'
10801080

1081+
test_expect_success '-L -G is scoped to the tracked range' '
1082+
git checkout --orphan grep-scope &&
1083+
git reset --hard &&
1084+
cat >gp.c <<-\EOF &&
1085+
int func1()
1086+
{
1087+
return ALPHA;
1088+
}
1089+
1090+
int func2()
1091+
{
1092+
return BETA;
1093+
}
1094+
EOF
1095+
git add gp.c &&
1096+
test_tick &&
1097+
git commit -m "add gp.c" &&
1098+
sed -e "s/ALPHA/ALPHA2/" -e "s/BETA/BETA2/" gp.c >tmp &&
1099+
mv tmp gp.c &&
1100+
git commit -a -m "touch both functions" &&
1101+
# The commit changes ALPHA (func1) and BETA (func2). Tracking func2,
1102+
# -G BETA matches its in-range change; -G ALPHA must not, since ALPHA
1103+
# changes only outside the tracked range.
1104+
git log -L:func2:gp.c -G BETA --format=%s >actual &&
1105+
test_grep "touch both functions" actual &&
1106+
git log -L:func2:gp.c -G ALPHA --format=%s >actual &&
1107+
test_grep ! "touch both functions" actual
1108+
'
1109+
10811110
test_done

0 commit comments

Comments
 (0)