From 7b30c3ad2da76bde9f49b049890acac0943d4c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 5 May 2024 12:19:56 +0200 Subject: [PATCH 1/2] diff: report unmerged paths as changes in run_diff_cmd() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit You can ask the diff machinery to let the exit code indicate whether there are changes, e.g. with --quiet. 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. The quick way considers an unmerged file to be a change and reports exit code 1, which makes sense. The slower path uses the struct diff_options member found_changes to indicate whether the blobs differ even with the transformations applied. It's not set for unmerged files, though, resulting in exit code 0. Set found_changes in run_diff_cmd() for unmerged files, for a consistent exit code of 1 if there's an unmerged file, regardless of whether whitespace is ignored. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- diff.c | 1 + t/t4046-diff-unmerged.sh | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/diff.c b/diff.c index e50def4538..38773317a1 100644 --- a/diff.c +++ b/diff.c @@ -4547,6 +4547,7 @@ static void run_diff_cmd(const char *pgm, o, complete_rewrite); } else { fprintf(o->file, "* Unmerged path %s\n", name); + o->found_changes = 1; } } diff --git a/t/t4046-diff-unmerged.sh b/t/t4046-diff-unmerged.sh index ffaf69335f..c606ee4c40 100755 --- a/t/t4046-diff-unmerged.sh +++ b/t/t4046-diff-unmerged.sh @@ -96,4 +96,12 @@ test_expect_success 'diff --stat' ' test_cmp diff-stat.expect diff-stat.actual ' +test_expect_success 'diff --quiet' ' + test_expect_code 1 git diff --cached --quiet +' + +test_expect_success 'diff --quiet --ignore-all-space' ' + test_expect_code 1 git diff --cached --quiet --ignore-all-space +' + test_done From 11be65cfa43416219e85384a3a80d672b65b76ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 5 May 2024 12:20:03 +0200 Subject: [PATCH 2/2] 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 --- diff.c | 33 ++++++++++++++++++++++++++++++--- t/t4020-diff-external.sh | 8 ++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/diff.c b/diff.c index 38773317a1..00382398d3 100644 --- a/diff.c +++ b/diff.c @@ -40,6 +40,7 @@ #include "setup.h" #include "strmap.h" #include "ws.h" +#include "write-or-die.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -4396,8 +4397,33 @@ static void run_external_diff(const char *pgm, diff_free_filespec_data(one); diff_free_filespec_data(two); cmd.use_shell = 1; - if (run_command(&cmd)) - die(_("external diff died, stopping at %s"), name); + if (o->flags.diff_from_contents) { + int got_output = 0; + cmd.out = -1; + if (start_command(&cmd)) + die(_("external diff died, stopping at %s"), name); + for (;;) { + char buffer[8192]; + ssize_t len = xread(cmd.out, buffer, sizeof(buffer)); + if (!len) + break; + if (len < 0) + die(_("unable to read from external diff," + " stopping at %s"), name); + got_output = 1; + if (write_in_full(1, buffer, len) < 0) + die(_("unable to write output of external diff," + " stopping at %s"), name); + } + close(cmd.out); + if (finish_command(&cmd)) + die(_("external diff died, stopping at %s"), name); + if (got_output) + o->found_changes = 1; + } else { + if (run_command(&cmd)) + die(_("external diff died, stopping at %s"), name); + } remove_tempfile(); } @@ -4844,6 +4870,7 @@ void diff_setup_done(struct diff_options *options) */ if ((options->xdl_opts & XDF_WHITESPACE_FLAGS) || + options->flags.exit_with_status || options->ignore_regex_nr) options->flags.diff_from_contents = 1; else @@ -6732,7 +6759,7 @@ void diff_flush(struct diff_options *options) if (output_format & DIFF_FORMAT_CALLBACK) options->format_callback(q, options, options->format_callback_data); - if (output_format & DIFF_FORMAT_NO_OUTPUT && + if ((!output_format || output_format & DIFF_FORMAT_NO_OUTPUT) && options->flags.exit_with_status && options->flags.diff_from_contents) { /* diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index c1ac09ecc7..b525d16c90 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -172,6 +172,14 @@ test_expect_success 'no diff with -diff' ' grep Binary out ' +test_expect_success 'diff.external and --exit-code with output' ' + test_expect_code 1 git -c diff.external=echo diff --exit-code +' + +test_expect_success 'diff.external and --exit-code without output' ' + git -c diff.external=true diff --exit-code +' + echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file test_expect_success 'force diff with "diff"' '