git/t/t2021-checkout-overwrite.sh
Matheus Tavares fab78a0c3d checkout: don't follow symlinks when removing entries
At 1d718a5108 ("do not overwrite untracked symlinks", 2011-02-20),
symlink.c:check_leading_path() started returning different codes for
FL_ENOENT and FL_SYMLINK. But one of its callers, unlink_entry(), was
not adjusted for this change, so it started to follow symlinks on the
leading path of to-be-removed entries. Fix that and add a regression
test.

Note that since 1d718a5108 check_leading_path() no longer differentiates
the case where it found a symlink in the path's leading components from
the cases where it found a regular file or failed to lstat() the
component. So, a side effect of this current patch is that
unlink_entry() now returns early in all of these three cases. And
because we no longer try to unlink such paths, we also don't get the
warning from remove_or_warn().

For the regular file and symlink cases, it's questionable whether the
warning was useful in the first place: unlink_entry() removes tracked
paths that should no longer be present in the state we are checking out
to. If the path had its leading dir replaced by another file, it means
that the basename already doesn't exist, so there is no need for a
warning. Sure, we are leaving a regular file or symlink behind at the
path's dirname, but this file is either untracked now (so again, no
need to warn), or it will be replaced by a tracked file during the next
phase of this checkout operation.

As for failing to lstat() one of the leading components, the basename
might still exist only we cannot unlink it (e.g. due to the lack of the
required permissions). Since the user expect it to be removed
(especially with checkout's --no-overlay option), add back the warning
in this more relevant case.

Signed-off-by: Matheus Tavares <matheus.bernardino@usp.br>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-03-18 12:58:10 -07:00

67 lines
1.4 KiB
Bash
Executable File

#!/bin/sh
test_description='checkout must not overwrite an untracked objects'
. ./test-lib.sh
test_expect_success 'setup' '
mkdir -p a/b/c &&
>a/b/c/d &&
git add -A &&
git commit -m base &&
git tag start
'
test_expect_success 'create a commit where dir a/b changed to file' '
git checkout -b file &&
rm -rf a/b &&
>a/b &&
git add -A &&
git commit -m "dir to file"
'
test_expect_success 'checkout commit with dir must not remove untracked a/b' '
git rm --cached a/b &&
git commit -m "un-track the file" &&
test_must_fail git checkout start &&
test -f a/b
'
test_expect_success 'create a commit where dir a/b changed to symlink' '
rm -rf a/b && # cleanup if previous test failed
git checkout -f -b symlink start &&
rm -rf a/b &&
git add -A &&
test_ln_s_add foo a/b &&
git commit -m "dir to symlink"
'
test_expect_success 'checkout commit with dir must not remove untracked a/b' '
git rm --cached a/b &&
git commit -m "un-track the symlink" &&
test_must_fail git checkout start
'
test_expect_success SYMLINKS 'the symlink remained' '
test -h a/b
'
test_expect_success SYMLINKS 'checkout -f must not follow symlinks when removing entries' '
git checkout -f start &&
mkdir dir &&
>dir/f &&
git add dir/f &&
git commit -m "add dir/f" &&
mv dir untracked &&
ln -s untracked dir &&
git checkout -f HEAD~ &&
test_path_is_file untracked/f
'
test_done