git/t/t4053-diff-no-index.sh
Johannes Sixt ffd04e92e2 diff: don't attempt to strip prefix from absolute Windows paths
git diff can be invoked with absolute paths. Typically, this triggers
the --no-index case. Then the absolute paths remain in the file names
that are printed in the output.

There is one peculiarity, though: When the command is invoked from a
a sub-directory in a repository, then it is attempted to strip the
sub-directory from the beginning of relative paths. Yet, to detect a
relative path the code just checks for an initial forward slash.
This mistakes a Windows style path like "D:/base" as a relative path
and the output looks like this, for example:

  D:\dir\test\one>git -P diff --numstat D:\dir\base D:\dir\diff
  1       1       ir/{base => diff}/1.txt

where the correct output should be

  D:\dir\test\one>git -P diff --numstat D:\dir\base D:\dir\diff
  1       1       D:/dir/{base => diff}/1.txt

If the sub-directory where 'git diff' is invoked is sufficiently deep
that the prefix becomes longer than the path to be printed, then the
subsequent code accesses the path out of bounds.

Use is_absolute_path() to detect Windows style absolute paths.

One might wonder whether the check for a directory separator that
is visible in the patch context should be changed from == '/' to
is_dir_sep() or not. It turns out not to be necessary. That code
only ever investigates paths that have undergone pathspec
normalization, after which there are only forward slashes even on
Windows.

Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-22 10:17:50 +09:00

140 lines
3.7 KiB
Bash
Executable file

#!/bin/sh
test_description='diff --no-index'
. ./test-lib.sh
test_expect_success 'setup' '
mkdir a &&
mkdir b &&
echo 1 >a/1 &&
echo 2 >a/2 &&
git init repo &&
echo 1 >repo/a &&
mkdir -p non/git &&
echo 1 >non/git/a &&
echo 1 >non/git/b
'
test_expect_success 'git diff --no-index directories' '
test_expect_code 1 git diff --no-index a b >cnt &&
test_line_count = 14 cnt
'
test_expect_success 'git diff --no-index relative path outside repo' '
(
cd repo &&
test_expect_code 0 git diff --no-index a ../non/git/a &&
test_expect_code 0 git diff --no-index ../non/git/a ../non/git/b
)
'
test_expect_success 'git diff --no-index with broken index' '
(
cd repo &&
echo broken >.git/index &&
git diff --no-index a ../non/git/a
)
'
test_expect_success 'git diff outside repo with broken index' '
(
cd repo &&
git diff ../non/git/a ../non/git/b
)
'
test_expect_success 'git diff --no-index executed outside repo gives correct error message' '
(
GIT_CEILING_DIRECTORIES=$TRASH_DIRECTORY/non &&
export GIT_CEILING_DIRECTORIES &&
cd non/git &&
test_must_fail git diff --no-index a 2>actual.err &&
echo "usage: git diff --no-index <path> <path>" >expect.err &&
test_cmp expect.err actual.err
)
'
test_expect_success 'diff D F and diff F D' '
(
cd repo &&
echo in-repo >a &&
echo non-repo >../non/git/a &&
mkdir sub &&
echo sub-repo >sub/a &&
test_must_fail git diff --no-index sub/a ../non/git/a >expect &&
test_must_fail git diff --no-index sub/a ../non/git/ >actual &&
test_cmp expect actual &&
test_must_fail git diff --no-index a ../non/git/a >expect &&
test_must_fail git diff --no-index a ../non/git/ >actual &&
test_cmp expect actual &&
test_must_fail git diff --no-index ../non/git/a a >expect &&
test_must_fail git diff --no-index ../non/git a >actual &&
test_cmp expect actual
)
'
test_expect_success 'turning a file into a directory' '
(
cd non/git &&
mkdir d e e/sub &&
echo 1 >d/sub &&
echo 2 >e/sub/file &&
printf "D\td/sub\nA\te/sub/file\n" >expect &&
test_must_fail git diff --no-index --name-status d e >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 \
git -C repo/sub \
diff --no-index ../../non/git/a ../../non/git/b >actual &&
head -n 1 <actual >actual.head &&
test_cmp expect actual.head
'
test_expect_success 'diff from repo subdir shows real paths (implicit)' '
echo "diff --git a/../../non/git/a b/../../non/git/b" >expect &&
test_expect_code 1 \
git -C repo/sub \
diff ../../non/git/a ../../non/git/b >actual &&
head -n 1 <actual >actual.head &&
test_cmp expect actual.head
'
test_expect_success 'diff --no-index from repo subdir respects config (explicit)' '
echo "diff --git ../../non/git/a ../../non/git/b" >expect &&
test_config -C repo diff.noprefix true &&
test_expect_code 1 \
git -C repo/sub \
diff --no-index ../../non/git/a ../../non/git/b >actual &&
head -n 1 <actual >actual.head &&
test_cmp expect actual.head
'
test_expect_success 'diff --no-index from repo subdir respects config (implicit)' '
echo "diff --git ../../non/git/a ../../non/git/b" >expect &&
test_config -C repo diff.noprefix true &&
test_expect_code 1 \
git -C repo/sub \
diff ../../non/git/a ../../non/git/b >actual &&
head -n 1 <actual >actual.head &&
test_cmp expect actual.head
'
test_expect_success 'diff --no-index from repo subdir with absolute paths' '
cat <<-EOF >expect &&
1 1 $(pwd)/non/git/{a => b}
EOF
test_expect_code 1 \
git -C repo/sub diff --numstat \
"$(pwd)/non/git/a" "$(pwd)/non/git/b" >actual &&
test_cmp expect actual
'
test_done