Skip to content

Commit b87e40e

Browse files
brandb97gitster
authored andcommitted
diff: ensure consistent diff behavior with ignore options
In git-diff, options like `-w` and `-I<regex>` require comparing file contents to determine whether two files are the same, even when their SHA values differ. For options like `--raw`, `--name-status`, and `--name-only`, git-diff deliberately compares only the SHA values to determine whether two files are the same, for performance reasons. As a result, a file shown in `git diff --name-status` may not appear in `git diff --patch`. To quickly determine whether two files are identical, Add helper function diff_flush_patch_quiet() in diff.c. Add `.diff_optimize` field in `struct diff_options`. When `.diff_optimize` is set to `DIFF_OPT_DRY_RUN`, builtin_diff() will return immediately upon detecting any change. Call diff_flush_patch_quiet() to determine if we should flush `--raw`, `--name-only` or `--name-status` output. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Lidong Yan <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 866e6a3 commit b87e40e

File tree

5 files changed

+74
-20
lines changed

5 files changed

+74
-20
lines changed

diff.c

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2444,6 +2444,15 @@ static int fn_out_consume(void *priv, char *line, unsigned long len)
24442444
return 0;
24452445
}
24462446

2447+
static int quick_consume(void *priv, char *line, unsigned long len)
2448+
{
2449+
struct emit_callback *ecbdata = priv;
2450+
struct diff_options *o = ecbdata->opt;
2451+
2452+
o->found_changes = 1;
2453+
return 1;
2454+
}
2455+
24472456
static void pprint_rename(struct strbuf *name, const char *a, const char *b)
24482457
{
24492458
const char *old_name = a;
@@ -3709,6 +3718,7 @@ static void builtin_diff(const char *name_a,
37093718
xdemitconf_t xecfg;
37103719
struct emit_callback ecbdata;
37113720
const struct userdiff_funcname *pe;
3721+
int dry_run = o->diff_optimize == DIFF_OPT_DRY_RUN;
37123722

37133723
if (must_show_header) {
37143724
emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
@@ -3741,16 +3751,17 @@ static void builtin_diff(const char *name_a,
37413751
xpp.ignore_regex_nr = o->ignore_regex_nr;
37423752
xpp.anchors = o->anchors;
37433753
xpp.anchors_nr = o->anchors_nr;
3744-
xecfg.ctxlen = o->context;
3745-
xecfg.interhunkctxlen = o->interhunkcontext;
3754+
xecfg.ctxlen = dry_run ? 0 : o->context;
3755+
xecfg.interhunkctxlen = dry_run ? 0 : o->interhunkcontext;
37463756
xecfg.flags = XDL_EMIT_FUNCNAMES;
37473757
if (o->flags.funccontext)
37483758
xecfg.flags |= XDL_EMIT_FUNCCONTEXT;
37493759
if (pe)
37503760
xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
37513761

37523762
diffopts = getenv("GIT_DIFF_OPTS");
3753-
if (!diffopts)
3763+
/* ignore ctxlen if we are in dry run mode */
3764+
if (!diffopts || dry_run)
37543765
;
37553766
else if (skip_prefix(diffopts, "--unified=", &v))
37563767
xecfg.ctxlen = strtoul(v, NULL, 10);
@@ -3759,8 +3770,11 @@ static void builtin_diff(const char *name_a,
37593770

37603771
if (o->word_diff)
37613772
init_diff_words_data(&ecbdata, o, one, two);
3762-
if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume,
3763-
&ecbdata, &xpp, &xecfg))
3773+
if (dry_run)
3774+
xdi_diff_outf(&mf1, &mf2, NULL, quick_consume,
3775+
&ecbdata, &xpp, &xecfg);
3776+
else if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume,
3777+
&ecbdata, &xpp, &xecfg))
37643778
die("unable to generate diff for %s", one->path);
37653779
if (o->word_diff)
37663780
free_diff_words_data(&ecbdata);
@@ -4988,6 +5002,8 @@ void diff_setup_done(struct diff_options *options)
49885002
options->filter = ~filter_bit[DIFF_STATUS_FILTER_AON];
49895003
options->filter &= ~options->filter_not;
49905004
}
5005+
5006+
options->diff_optimize = DIFF_OPT_NONE;
49915007
}
49925008

49935009
int parse_long_opt(const char *opt, const char **argv,
@@ -6150,6 +6166,22 @@ static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
61506166
run_diff(p, o);
61516167
}
61526168

6169+
/* return 1 if any change is found; otherwise, return 0 */
6170+
static int diff_flush_patch_quiet(struct diff_filepair *p, struct diff_options *o)
6171+
{
6172+
int diff_opt = o->diff_optimize;
6173+
int found_changes = o->found_changes;
6174+
int ret;
6175+
6176+
o->diff_optimize = DIFF_OPT_DRY_RUN;
6177+
o->found_changes = 0;
6178+
diff_flush_patch(p, o);
6179+
ret = o->found_changes;
6180+
o->diff_optimize = diff_opt;
6181+
o->found_changes |= found_changes;
6182+
return ret;
6183+
}
6184+
61536185
static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o,
61546186
struct diffstat_t *diffstat)
61556187
{
@@ -6778,7 +6810,19 @@ void diff_flush(struct diff_options *options)
67786810
DIFF_FORMAT_CHECKDIFF)) {
67796811
for (i = 0; i < q->nr; i++) {
67806812
struct diff_filepair *p = q->queue[i];
6781-
if (check_pair_status(p))
6813+
int need_flush = 1;
6814+
6815+
if (!check_pair_status(p))
6816+
continue;
6817+
6818+
if (options->flags.diff_from_contents) {
6819+
if (diff_flush_patch_quiet(p, options))
6820+
need_flush = 1;
6821+
else
6822+
need_flush = 0;
6823+
}
6824+
6825+
if (need_flush)
67826826
flush_one_pair(p, options);
67836827
}
67846828
separator++;
@@ -6831,19 +6875,10 @@ void diff_flush(struct diff_options *options)
68316875
if (output_format & DIFF_FORMAT_NO_OUTPUT &&
68326876
options->flags.exit_with_status &&
68336877
options->flags.diff_from_contents) {
6834-
/*
6835-
* run diff_flush_patch for the exit status. setting
6836-
* options->file to /dev/null should be safe, because we
6837-
* aren't supposed to produce any output anyway.
6838-
*/
6839-
diff_free_file(options);
6840-
options->file = xfopen("/dev/null", "w");
6841-
options->close_file = 1;
6842-
options->color_moved = 0;
68436878
for (i = 0; i < q->nr; i++) {
68446879
struct diff_filepair *p = q->queue[i];
68456880
if (check_pair_status(p))
6846-
diff_flush_patch(p, options);
6881+
diff_flush_patch_quiet(p, options);
68476882
if (options->found_changes)
68486883
break;
68496884
}

diff.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,11 @@ struct diff_options {
400400
#define COLOR_MOVED_WS_ERROR (1<<0)
401401
unsigned color_moved_ws_handling;
402402

403+
enum {
404+
DIFF_OPT_NONE = 0,
405+
DIFF_OPT_DRY_RUN = 1,
406+
} diff_optimize;
407+
403408
struct repository *repo;
404409
struct strmap *additional_path_headers;
405410

t/t4013-diff-various.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,20 @@ test_expect_success 'diff -I<regex>: detect malformed regex' '
648648
test_grep "invalid regex given to -I: " error
649649
'
650650

651+
test_expect_success 'diff -I<regex>: ignore matching file' '
652+
test_seq 50 >file1 &&
653+
git add file1 &&
654+
test_seq 50 | sed -e "s/13/ten and three/" -e "s/^[124-9].*/& /" >file1 &&
655+
656+
: >actual &&
657+
git diff --raw --ignore-blank-lines -I"ten.*e" -I"^[124-9]" >>actual &&
658+
git diff --name-only --ignore-blank-lines -I"ten.*e" -I"^[124-9]" >>actual &&
659+
git diff --name-status --ignore-blank-lines -I"ten.*e" -I"^[124-9]" >>actual &&
660+
! grep "file1" actual &&
661+
662+
git rm -f file1
663+
'
664+
651665
# check_prefix <patch> <src> <dst>
652666
# check only lines with paths to avoid dependency on exact oid/contents
653667
check_prefix () {

t/t4015-diff-whitespace.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ test_description='Test special whitespace in diff engine.
1111
. "$TEST_DIRECTORY"/lib-diff.sh
1212

1313
for opt_res in --patch --quiet -s --stat --shortstat --dirstat=lines \
14-
--raw! --name-only! --name-status!
14+
--raw --name-only --name-status
1515
do
1616
opts=${opt_res%!} expect_failure=
1717
test "$opts" = "$opt_res" ||

xdiff-interface.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
* from an error internal to xdiff, xdiff itself will see that
2929
* non-zero return and translate it to -1.
3030
*
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.
31+
* See "diff_grep" in diffcore-pickaxe.c and "quick_consume" in diff.c
32+
* for a trick to work around this, i.e. using the "consume_callback_data"
33+
* to note the desired early return.
3434
*/
3535
typedef int (*xdiff_emit_line_fn)(void *, char *, unsigned long);
3636
typedef void (*xdiff_emit_hunk_fn)(void *data,

0 commit comments

Comments
 (0)