diff --no-index: fix -R with file/directory conflicts

When a path is a file on one side and a directory on the other,
queue_diff() queues the file separately before recursing into the
directory. This early queue entry bypasses the reverse_diff handling
used for ordinary files.

As a result, comparing directories d and e where d/sub is a file and
e/sub/file is another file reports both paths as deleted with -R.
Reversing the operands reports both paths as added instead.

Swap the filespecs of the early queue entry when reverse_diff is set,
so that -R reverses the file change as well as the directory contents.
Add regression tests for both directions.

Signed-off-by: Haokai Ding <hk_ding@outlook.com>
Reviewed-by: Jacob Keller <jacob.keller@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
main
Haokai Ding 2026-09-18 07:19:06 +00:00 committed by Junio C Hamano
parent e9019fcafe
commit 9a13b3742b
2 changed files with 20 additions and 0 deletions

View File

@ -188,6 +188,8 @@ static int queue_diff(struct diff_options *o, const struct git_hash_algo *algop,
mode1 = 0;
}
/* emit that file */
if (o->flags.reverse_diff)
SWAP(d1, d2);
diff_queue(&diff_queued_diff, d1, d2);

/* and then let the entire directory be created or deleted */

View File

@ -120,6 +120,24 @@ test_expect_success 'turning a file into a directory' '
)
'

test_expect_success 'reverse diff when turning a file into a directory' '
(
cd non/git &&
printf "A\td/sub\nD\te/sub/file\n" >expect &&
test_expect_code 1 git diff --no-index -R --name-status d e >actual &&
test_cmp expect actual
)
'

test_expect_success 'reverse diff when turning a directory into a file' '
(
cd non/git &&
printf "D\td/sub\nA\te/sub/file\n" >expect &&
test_expect_code 1 git diff --no-index -R --name-status e d >actual &&
test_cmp expect actual
)
'

test_expect_success 'diff from repo subdir shows real paths (explicit)' '
echo "diff --git a/../../non/git/a b/../../non/git/b" >expect &&
test_expect_code 1 \