]> git.ipfire.org Git - thirdparty/git.git/commitdiff
diff: fix --exit-code with external diff
authorRené Scharfe <l.s.r@web.de>
Sun, 5 May 2024 10:20:03 +0000 (12:20 +0200)
committerJunio C Hamano <gitster@pobox.com>
Mon, 6 May 2024 17:23:42 +0000 (10:23 -0700)
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 <gitster@pobox.com>
diff.c
t/t4020-diff-external.sh

diff --git a/diff.c b/diff.c
index 38773317a13a0920e2d455c7d4aecdd475833118..00382398d33b33fb77a5616c7787a309695b0a1e 100644 (file)
--- 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) {
                /*
index c1ac09ecc7140a3dcfcdf906bb4533ba131881de..b525d16c9062a3268fb22fb8dfae4614c8c7281a 100755 (executable)
@@ -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"' '