Skip to content

Commit 2bc09ca

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 6948226 commit 2bc09ca

5 files changed

Lines changed: 90 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: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2817,6 +2817,21 @@ static int line_range_filter_diff(struct line_range_filter *filter,
28172817
return ret;
28182818
}
28192819

2820+
/*
2821+
* Expose the in-file line-range filter to callers outside diff.c (e.g.
2822+
* pickaxe -G); see xdiff-interface.h for the contract.
2823+
*/
2824+
int diff_emit_line_ranges(mmfile_t *one, mmfile_t *two,
2825+
const struct range_set *ranges,
2826+
xdiff_emit_line_fn line_fn, void *cb_data,
2827+
xpparam_t *xpp, xdemitconf_t *xecfg)
2828+
{
2829+
struct line_range_filter filter;
2830+
2831+
line_range_filter_init(&filter, ranges, line_fn, cb_data);
2832+
return line_range_filter_diff(&filter, one, two, xpp, xecfg);
2833+
}
2834+
28202835
static void pprint_rename(struct strbuf *name, const char *a, const char *b)
28212836
{
28222837
const char *old_name = a;

diffcore-pickaxe.c

Lines changed: 29 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,20 @@ 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.
58+
* -G needs only a hit/no-hit answer, so the line-number bookkeeping
59+
* the filter does for -L patch and check output is irrelevant here.
5560
*/
5661
memset(&xpp, 0, sizeof(xpp));
5762
memset(&xecfg, 0, sizeof(xecfg));
@@ -65,8 +70,12 @@ static int diff_grep(mmfile_t *one, mmfile_t *two,
6570
* An xdiff error might be our "data->hit" from above. See the
6671
* comment for xdiff_emit_line_fn in xdiff-interface.h
6772
*/
68-
ret = xdi_diff_outf(one, two, NULL, diffgrep_consume,
69-
&ecbdata, &xpp, &xecfg);
73+
if (ranges)
74+
ret = diff_emit_line_ranges(one, two, ranges, diffgrep_consume,
75+
&ecbdata, &xpp, &xecfg);
76+
else
77+
ret = xdi_diff_outf(one, two, NULL, diffgrep_consume,
78+
&ecbdata, &xpp, &xecfg);
7079
if (ecbdata.hit)
7180
return 1;
7281
if (ret)
@@ -119,8 +128,13 @@ static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws,
119128

120129
static int has_changes(mmfile_t *one, mmfile_t *two,
121130
struct diff_options *o UNUSED,
122-
regex_t *regexp, kwset_t kws)
131+
regex_t *regexp, kwset_t kws,
132+
const struct range_set *ranges UNUSED)
123133
{
134+
/*
135+
* -S counts needle occurrences in each whole blob. Scoping this to
136+
* a -L range is left to a follow-up; for now -S ignores the range.
137+
*/
124138
unsigned int c1 = one ? contains(one, regexp, kws, 0) : 0;
125139
unsigned int c2 = two ? contains(two, regexp, kws, c1 + 1) : 0;
126140
return c1 != c2;
@@ -132,6 +146,7 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
132146
struct userdiff_driver *textconv_one = NULL;
133147
struct userdiff_driver *textconv_two = NULL;
134148
mmfile_t mf1, mf2;
149+
const struct range_set *ranges;
135150
int ret;
136151

137152
/* ignore unmerged */
@@ -169,7 +184,13 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
169184
mf1.size = fill_textconv(o->repo, textconv_one, p->one, &mf1.ptr);
170185
mf2.size = fill_textconv(o->repo, textconv_two, p->two, &mf2.ptr);
171186

172-
ret = fn(&mf1, &mf2, o, regexp, kws);
187+
/*
188+
* -L scopes the search to the tracked range, but the range is in
189+
* original-file line coordinates that do not map onto textconv
190+
* output, so search the whole file when textconv is in play.
191+
*/
192+
ranges = (textconv_one || textconv_two) ? NULL : p->line_ranges;
193+
ret = fn(&mf1, &mf2, o, regexp, kws, ranges);
173194

174195
if (textconv_one)
175196
free(mf1.ptr);

t/t4211-line-log.sh

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

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

xdiff-interface.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,19 @@ int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
4646
xdiff_emit_line_fn line_fn,
4747
void *consume_callback_data,
4848
xpparam_t const *xpp, xdemitconf_t const *xecfg);
49+
50+
struct range_set;
51+
/*
52+
* Like xdi_diff_outf(), but forwards only the lines within the given
53+
* (post-image) line ranges to line_fn, as "git log -L" scopes its output.
54+
* Returns line_fn's latched return value (so a consumer can signal a hit
55+
* with a non-zero return), or non-zero on xdiff failure. Defined in
56+
* diff.c (it reuses the line-range filter there).
57+
*/
58+
int diff_emit_line_ranges(mmfile_t *mf1, mmfile_t *mf2,
59+
const struct range_set *ranges,
60+
xdiff_emit_line_fn line_fn, void *cb_data,
61+
xpparam_t *xpp, xdemitconf_t *xecfg);
4962
int read_mmfile(mmfile_t *ptr, const char *filename);
5063
void read_mmblob(mmfile_t *ptr, struct object_database *odb,
5164
const struct object_id *oid);

0 commit comments

Comments
 (0)