[PATCH v2] diff: stop output garbled message in dry run mode
- From
Lidong Yan <yldhome2d2@gmail.com>
- Date
- Oct 18, 2025, 09:48 UTC
- Message-ID
- <20251018094823.31173-1-yldhome2d2@gmail.com>
- In-Reply-To
- <pull.2071.git.git.1760671049113.gitgitgadget@gmail.com>
Earlier, b55e6d36 (diff: ensure consistent diff behavior with ignore options, 2025-08-08) introduced "dry-run" mode to the diff machinery so that content-based diff filtering (like ignoring space changes or those that match -I<regex>) can first try to produce a patch without emitting any output to see if under the given diff filtering condition we would get any output lines, and a new helper function diff_flush_patch_quietly() was introduced to use the mode to see an individual filepair needs to be shown.
However, the solution was not complete. When files are deleted, file modes change, or there are unmerged entries in the index, dry-run mode still produces output because we overlooked these conditions, and as a result, dry-run mode was not quiet.
Since dry-run mode is only set in diff_flush_patch_quietly(), setting the output file to "/dev/null" within diff_flush_patch_quietly() ensures no output is emitted in dry-run mode. To improve performance of dry-run mode, add a check before outputting to determine if we should exit early to avoid unnecessary output processing.
Signed-off-by: Lidong Yan <yldhome2d2@gmail.com> --- I copied Peff's code from https://lore.kernel.org/git/20251017083641.GB4073661@coredump.intra.peff.net/
diff.c | 20 ++++++++++++++++++-- t/t4013-diff-various.sh | 37 +++++++++++++++++++++++++++++++++++++ t/t4035-diff-quiet.sh | 4 ++++ 3 files changed, 59 insertions(+), 2 deletions(-)
diff --git a/diff.c b/diff.c index 87fa16b730..ec05ac565b 100644 --- a/diff.c +++ b/diff.c @@ -1351,6 +1351,9 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, int len = eds->len; unsigned flags = eds->flags; + if (o->dry_run) + return; + switch (s) { case DIFF_SYMBOL_NO_LF_EOF: context = diff_get_color_opt(o, DIFF_CONTEXT); @@ -4420,7 +4423,7 @@ static void run_external_diff(const struct external_diff *pgm, { struct child_process cmd = CHILD_PROCESS_INIT; struct diff_queue_struct *q = &diff_queued_diff; - int quiet = !(o->output_format & DIFF_FORMAT_PATCH); + int quiet = !(o->output_format & DIFF_FORMAT_PATCH) || o->dry_run; int rc; /* @@ -4615,7 +4618,8 @@ static void run_diff_cmd(const struct external_diff *pgm, p->status == DIFF_STATUS_RENAMED) o->found_changes = 1; } else { - fprintf(o->file, "* Unmerged path %s\n", name); + if (!o->dry_run) + fprintf(o->file, "* Unmerged path %s\n", name); o->found_changes = 1; } } @@ -6194,14 +6198,26 @@ static int diff_flush_patch_quietly(struct diff_filepair *p, struct diff_options { int saved_dry_run = o->dry_run; int saved_found_changes = o->found_changes; + int saved_color_moved = o->color_moved; + int saved_close_file = o->close_file; + FILE *saved_file = o->file; int ret; o->dry_run = 1; o->found_changes = 0; + o->color_moved = 0; + o->close_file = 1; + o->file = xfopen("/dev/null", "w"); diff_flush_patch(p, o); ret = o->found_changes; + if (o->file) + fclose(o->file); + o->dry_run = saved_dry_run; o->found_changes |= saved_found_changes; + o->color_moved = saved_color_moved; + o->close_file = saved_close_file; + o->file = saved_file; return ret; } diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 55a06eadb3..2f8fe191b8 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -661,6 +661,43 @@ test_expect_success 'diff -I<regex>: ignore matching file' ' test_grep ! "file1" actual ' +test_expect_success 'diff -I<regex>: ignore all content changes' ' + test_when_finished "git rm -f file1 file2 file3" && + : >file1 && + git add file1 && + : >file2 && + git add file2 && + : >file3 && + git add file3 && + + echo "A" >file3 && + A_hash=$(git hash-object -w file3) && + echo "B" >file3 && + B_hash=$(git hash-object -w file3) && + cat <<-EOF | git update-index --index-info && + 100644 $A_hash 1 file3 + 100644 $B_hash 2 file3 + EOF + + rm -f file1 file2 && + mkdir file2 && + test_diff_no_content_changes () { + git diff $1 --ignore-blank-lines -I".*" >actual && + test_line_count = 3 actual && + test_grep "file1" actual && + test_grep "file2" actual && + test_grep "file3" actual && + test_grep ! "diff --git" actual + } && + test_diff_no_content_changes "--raw" && + test_diff_no_content_changes "--name-only" && + test_diff_no_content_changes "--name-status" && + + : >actual && + test_must_fail git diff --quiet -I".*" >actual && + test_must_be_empty actual +' + # check_prefix <patch> <src> <dst> # check only lines with paths to avoid dependency on exact oid/contents check_prefix () { diff --git a/t/t4035-diff-quiet.sh b/t/t4035-diff-quiet.sh index 0352bf81a9..35eaf0855f 100755 --- a/t/t4035-diff-quiet.sh +++ b/t/t4035-diff-quiet.sh @@ -50,6 +50,10 @@ test_expect_success 'git diff-tree HEAD HEAD' ' test_expect_code 0 git diff-tree --quiet HEAD HEAD >cnt && test_line_count = 0 cnt ' +test_expect_success 'git diff-tree -w HEAD^ HEAD' ' + test_expect_code 1 git diff-tree --quiet -w HEAD^ HEAD >cnt && + test_line_count = 0 cnt +' test_expect_success 'git diff-files' ' test_expect_code 0 git diff-files --quiet >cnt && test_line_count = 0 cnt
-- 2.50.1 (Apple Git-155)