Skip to content

Commit 11be65c

Browse files
rscharfegitster
authored andcommitted
diff: fix --exit-code with external diff
You can ask the diff machinery to let the exit code indicate whether there are changes, e.g. with --exit-code. It as two ways to calculate that bit: The quick one assumes blobs with different hashes have different content, and the more elaborate way actually compares the contents, possibly applying transformations like ignoring whitespace. Always use the slower path by setting the flag diff_from_contents, because any of the files could have an external diff driver set via an attribute, which might consider binary differences irrelevant, like e.g. Signed-off-by: Junio C Hamano <[email protected]>
1 parent 7b30c3a commit 11be65c

File tree

2 files changed

+38
-3
lines changed

2 files changed

+38
-3
lines changed

diff.c

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "setup.h"
4141
#include "strmap.h"
4242
#include "ws.h"
43+
#include "write-or-die.h"
4344

4445
#ifdef NO_FAST_WORKING_DIRECTORY
4546
#define FAST_WORKING_DIRECTORY 0
@@ -4396,8 +4397,33 @@ static void run_external_diff(const char *pgm,
43964397
diff_free_filespec_data(one);
43974398
diff_free_filespec_data(two);
43984399
cmd.use_shell = 1;
4399-
if (run_command(&cmd))
4400-
die(_("external diff died, stopping at %s"), name);
4400+
if (o->flags.diff_from_contents) {
4401+
int got_output = 0;
4402+
cmd.out = -1;
4403+
if (start_command(&cmd))
4404+
die(_("external diff died, stopping at %s"), name);
4405+
for (;;) {
4406+
char buffer[8192];
4407+
ssize_t len = xread(cmd.out, buffer, sizeof(buffer));
4408+
if (!len)
4409+
break;
4410+
if (len < 0)
4411+
die(_("unable to read from external diff,"
4412+
" stopping at %s"), name);
4413+
got_output = 1;
4414+
if (write_in_full(1, buffer, len) < 0)
4415+
die(_("unable to write output of external diff,"
4416+
" stopping at %s"), name);
4417+
}
4418+
close(cmd.out);
4419+
if (finish_command(&cmd))
4420+
die(_("external diff died, stopping at %s"), name);
4421+
if (got_output)
4422+
o->found_changes = 1;
4423+
} else {
4424+
if (run_command(&cmd))
4425+
die(_("external diff died, stopping at %s"), name);
4426+
}
44014427

44024428
remove_tempfile();
44034429
}
@@ -4844,6 +4870,7 @@ void diff_setup_done(struct diff_options *options)
48444870
*/
48454871

48464872
if ((options->xdl_opts & XDF_WHITESPACE_FLAGS) ||
4873+
options->flags.exit_with_status ||
48474874
options->ignore_regex_nr)
48484875
options->flags.diff_from_contents = 1;
48494876
else
@@ -6732,7 +6759,7 @@ void diff_flush(struct diff_options *options)
67326759
if (output_format & DIFF_FORMAT_CALLBACK)
67336760
options->format_callback(q, options, options->format_callback_data);
67346761

6735-
if (output_format & DIFF_FORMAT_NO_OUTPUT &&
6762+
if ((!output_format || output_format & DIFF_FORMAT_NO_OUTPUT) &&
67366763
options->flags.exit_with_status &&
67376764
options->flags.diff_from_contents) {
67386765
/*

t/t4020-diff-external.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,14 @@ test_expect_success 'no diff with -diff' '
172172
grep Binary out
173173
'
174174

175+
test_expect_success 'diff.external and --exit-code with output' '
176+
test_expect_code 1 git -c diff.external=echo diff --exit-code
177+
'
178+
179+
test_expect_success 'diff.external and --exit-code without output' '
180+
git -c diff.external=true diff --exit-code
181+
'
182+
175183
echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file
176184

177185
test_expect_success 'force diff with "diff"' '

0 commit comments

Comments
 (0)