summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xt/t2501-cwd-empty.sh342
1 files changed, 342 insertions, 0 deletions
diff --git a/t/t2501-cwd-empty.sh b/t/t2501-cwd-empty.sh
new file mode 100755
index 0000000000..a05abd1818
--- /dev/null
+++ b/t/t2501-cwd-empty.sh
@@ -0,0 +1,342 @@
+#!/bin/sh
+
+test_description='Test handling of the current working directory becoming empty'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit init &&
+
+ git branch fd_conflict &&
+
+ mkdir -p foo/bar &&
+ test_commit foo/bar/baz &&
+
+ git revert HEAD &&
+ git tag reverted &&
+
+ git checkout fd_conflict &&
+ mkdir dirORfile &&
+ test_commit dirORfile/foo &&
+
+ git rm -r dirORfile &&
+ echo not-a-directory >dirORfile &&
+ git add dirORfile &&
+ git commit -m dirORfile &&
+
+ git switch -c df_conflict HEAD~1 &&
+ test_commit random_file &&
+
+ git switch -c undo_fd_conflict fd_conflict &&
+ git revert HEAD
+'
+
+test_incidental_dir_removal () {
+ works=$1 &&
+ shift &&
+
+ test_when_finished "git reset --hard" &&
+
+ git checkout foo/bar/baz^{commit} &&
+ test_path_is_dir foo/bar &&
+
+ (
+ cd foo &&
+ "$@" &&
+
+ # Although we want pwd & git status to pass, test for existing
+ # rather than desired behavior.
+ if test "$works" = "success"
+ then
+ test-tool getcwd &&
+ git status --porcelain
+ else
+ ! test-tool getcwd &&
+ test_might_fail git status --porcelain
+ fi
+ ) &&
+ test_path_is_missing foo/bar/baz &&
+ test_path_is_missing foo/bar &&
+
+ # Although we want dir to be present, test for existing rather
+ # than desired behavior.
+ if test "$works" = "success"
+ then
+ test_path_is_dir foo
+ else
+ test_path_is_missing foo
+ fi
+}
+
+test_required_dir_removal () {
+ works=$1 &&
+ shift &&
+
+ git checkout df_conflict^{commit} &&
+ test_when_finished "git clean -fdx" &&
+
+ (
+ cd dirORfile &&
+
+ # We'd like for the command to fail (much as it would if there
+ # was an untracked file there), and for the index and worktree
+ # to be left clean with pwd and git status working afterwards.
+ # But test for existing rather than desired behavior.
+ if test "$works" = "success"
+ then
+ test_must_fail "$@" 2>../error &&
+ grep "Refusing to remove.*current working directory" ../error &&
+
+ git diff --exit-code HEAD &&
+
+ test-tool getcwd &&
+ git status --porcelain
+ else
+ "$@" &&
+ ! test-tool getcwd &&
+ test_might_fail git status --porcelain
+ fi
+ ) &&
+
+ # Although we want dirORfile to be present, test for existing rather
+ # than desired behavior.
+ if test "$works" = "success"
+ then
+ test_path_is_dir dirORfile
+ else
+ test_path_is_file dirORfile
+ fi
+}
+
+test_expect_success 'checkout does not clean cwd incidentally' '
+ test_incidental_dir_removal failure git checkout init
+'
+
+test_expect_success 'checkout fails if cwd needs to be removed' '
+ test_required_dir_removal failure git checkout fd_conflict
+'
+
+test_expect_success 'reset --hard does not clean cwd incidentally' '
+ test_incidental_dir_removal failure git reset --hard init
+'
+
+test_expect_success 'reset --hard fails if cwd needs to be removed' '
+ test_required_dir_removal failure git reset --hard fd_conflict
+'
+
+test_expect_success 'merge does not clean cwd incidentally' '
+ test_incidental_dir_removal failure git merge reverted
+'
+
+# This file uses some simple merges where
+# Base: 'dirORfile/' exists
+# Side1: random other file changed
+# Side2: 'dirORfile/' removed, 'dirORfile' added
+# this should resolve cleanly, but merge-recursive throws merge conflicts
+# because it's dumb. Add a special test for checking merge-recursive (and
+# merge-ort), then after this just hard require ort for all remaining tests.
+#
+test_expect_success 'merge fails if cwd needs to be removed; recursive friendly' '
+ git checkout foo/bar/baz &&
+ test_when_finished "git clean -fdx" &&
+
+ mkdir dirORfile &&
+ (
+ cd dirORfile &&
+
+ # We would rather this failed, but we test for existing
+ # rather than desired behavior
+ git merge fd_conflict 2>../error
+ ) &&
+
+ ## Here is the behavior we would rather have:
+ #test_path_is_dir dirORfile &&
+ #grep "Refusing to remove the current working directory" error
+ ## But instead we test for existing behavior
+ test_path_is_file dirORfile &&
+ test_must_be_empty error
+'
+
+GIT_TEST_MERGE_ALGORITHM=ort
+
+test_expect_success 'merge fails if cwd needs to be removed' '
+ test_required_dir_removal failure git merge fd_conflict
+'
+
+test_expect_success 'cherry-pick does not clean cwd incidentally' '
+ test_incidental_dir_removal failure git cherry-pick reverted
+'
+
+test_expect_success 'cherry-pick fails if cwd needs to be removed' '
+ test_required_dir_removal failure git cherry-pick fd_conflict
+'
+
+test_expect_success 'rebase does not clean cwd incidentally' '
+ test_incidental_dir_removal failure git rebase reverted
+'
+
+test_expect_success 'rebase fails if cwd needs to be removed' '
+ test_required_dir_removal failure git rebase fd_conflict
+'
+
+test_expect_success 'revert does not clean cwd incidentally' '
+ test_incidental_dir_removal failure git revert HEAD
+'
+
+test_expect_success 'revert fails if cwd needs to be removed' '
+ test_required_dir_removal failure git revert undo_fd_conflict
+'
+
+test_expect_success 'rm does not clean cwd incidentally' '
+ test_incidental_dir_removal failure git rm bar/baz.t
+'
+
+test_expect_success 'apply does not remove cwd incidentally' '
+ git diff HEAD HEAD~1 >patch &&
+ test_incidental_dir_removal failure git apply ../patch
+'
+
+test_incidental_untracked_dir_removal () {
+ works=$1 &&
+ shift &&
+
+ test_when_finished "git reset --hard" &&
+
+ git checkout foo/bar/baz^{commit} &&
+ mkdir -p untracked &&
+ mkdir empty
+ >untracked/random &&
+
+ (
+ cd untracked &&
+ "$@" &&
+
+ # Although we want pwd & git status to pass, test for existing
+ # rather than desired behavior.
+ if test "$works" = "success"
+ then
+ test-tool getcwd &&
+ git status --porcelain
+ else
+ ! test-tool getcwd &&
+ test_might_fail git status --porcelain
+ fi
+ ) &&
+ test_path_is_missing empty &&
+ test_path_is_missing untracked/random &&
+
+ # Although we want dir to be present, test for existing rather
+ # than desired behavior.
+ if test "$works" = "success"
+ then
+ test_path_is_dir untracked
+ else
+ test_path_is_missing untracked
+ fi
+}
+
+test_expect_success 'clean does not remove cwd incidentally' '
+ test_incidental_untracked_dir_removal failure \
+ git -C .. clean -fd -e warnings . >warnings
+'
+
+test_expect_success 'stash does not remove cwd incidentally' '
+ test_incidental_untracked_dir_removal failure \
+ git stash --include-untracked
+'
+
+test_expect_success '`rm -rf dir` only removes a subset of dir' '
+ test_when_finished "rm -rf a/" &&
+
+ mkdir -p a/b/c &&
+ >a/b/c/untracked &&
+ >a/b/c/tracked &&
+ git add a/b/c/tracked &&
+
+ (
+ cd a/b &&
+ git rm -rf ../b
+ ) &&
+
+ test_path_is_dir a/b &&
+ test_path_is_missing a/b/c/tracked &&
+ test_path_is_file a/b/c/untracked
+'
+
+test_expect_success '`rm -rf dir` even with only tracked files will remove something else' '
+ test_when_finished "rm -rf a/" &&
+
+ mkdir -p a/b/c &&
+ >a/b/c/tracked &&
+ git add a/b/c/tracked &&
+
+ (
+ cd a/b &&
+ git rm -rf ../b
+ ) &&
+
+ test_path_is_missing a/b/c/tracked &&
+ ## We would prefer if a/b was still present, though empty, since it
+ ## was the current working directory
+ #test_path_is_dir a/b
+ ## But the current behavior is that it not only deletes the directory
+ ## a/b as requested, but also goes and deletes a
+ test_path_is_missing a
+'
+
+test_expect_success 'git version continues working from a deleted dir' '
+ mkdir tmp &&
+ (
+ cd tmp &&
+ rm -rf ../tmp &&
+ git version
+ )
+'
+
+test_submodule_removal () {
+ path_status=$1 &&
+ shift &&
+
+ test_status=
+ test "$path_status" = dir && test_status=test_must_fail
+
+ # Actually, while path_status=dir && test_status=test_must_fail
+ # reflect our desired behavior, current behavior is:
+ path_status=missing
+ test_status=
+
+ test_when_finished "git reset --hard HEAD~1" &&
+ test_when_finished "rm -rf .git/modules/my_submodule" &&
+
+ git checkout foo/bar/baz &&
+
+ git init my_submodule &&
+ touch my_submodule/file &&
+ git -C my_submodule add file &&
+ git -C my_submodule commit -m "initial commit" &&
+ git submodule add ./my_submodule &&
+ git commit -m "Add the submodule" &&
+
+ (
+ cd my_submodule &&
+ $test_status "$@"
+ ) &&
+
+ test_path_is_${path_status} my_submodule
+}
+
+test_expect_success 'rm -r with -C leaves submodule if cwd inside' '
+ test_submodule_removal dir git -C .. rm -r my_submodule/
+'
+
+test_expect_success 'rm -r leaves submodule if cwd inside' '
+ test_submodule_removal dir \
+ git --git-dir=../.git --work-tree=.. rm -r ../my_submodule/
+'
+
+test_expect_success 'rm -rf removes submodule even if cwd inside' '
+ test_submodule_removal missing \
+ git --git-dir=../.git --work-tree=.. rm -rf ../my_submodule/
+'
+
+test_done