|  | #!/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 () { | 
|  | test_when_finished "git reset --hard" && | 
|  |  | 
|  | git checkout foo/bar/baz^{commit} && | 
|  | test_path_is_dir foo/bar && | 
|  |  | 
|  | ( | 
|  | cd foo && | 
|  | "$@" && | 
|  |  | 
|  | # Make sure foo still exists, and commands needing it work | 
|  | test-tool getcwd && | 
|  | git status --porcelain | 
|  | ) && | 
|  | test_path_is_missing foo/bar/baz && | 
|  | test_path_is_missing foo/bar && | 
|  |  | 
|  | test_path_is_dir foo | 
|  | } | 
|  |  | 
|  | test_required_dir_removal () { | 
|  | git checkout df_conflict^{commit} && | 
|  | test_when_finished "git clean -fdx" && | 
|  |  | 
|  | ( | 
|  | cd dirORfile && | 
|  |  | 
|  | # Ensure command refuses to run | 
|  | test_must_fail "$@" 2>../error && | 
|  | grep "Refusing to remove.*current working directory" ../error && | 
|  |  | 
|  | # ...and that the index and working tree are left clean | 
|  | git diff --exit-code HEAD && | 
|  |  | 
|  | # Ensure that getcwd and git status do not error out (which | 
|  | # they might if the current working directory had been removed) | 
|  | test-tool getcwd && | 
|  | git status --porcelain | 
|  | ) && | 
|  |  | 
|  | test_path_is_dir dirORfile | 
|  | } | 
|  |  | 
|  | test_expect_success 'checkout does not clean cwd incidentally' ' | 
|  | test_incidental_dir_removal git checkout init | 
|  | ' | 
|  |  | 
|  | test_expect_success 'checkout fails if cwd needs to be removed' ' | 
|  | test_required_dir_removal git checkout fd_conflict | 
|  | ' | 
|  |  | 
|  | test_expect_success 'reset --hard does not clean cwd incidentally' ' | 
|  | test_incidental_dir_removal git reset --hard init | 
|  | ' | 
|  |  | 
|  | test_expect_success 'reset --hard fails if cwd needs to be removed' ' | 
|  | test_required_dir_removal git reset --hard fd_conflict | 
|  | ' | 
|  |  | 
|  | test_expect_success 'merge does not clean cwd incidentally' ' | 
|  | test_incidental_dir_removal 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 && | 
|  |  | 
|  | test_must_fail git merge fd_conflict 2>../error | 
|  | ) && | 
|  |  | 
|  | test_path_is_dir dirORfile && | 
|  | grep "Refusing to remove the current working directory" error | 
|  | ' | 
|  |  | 
|  | GIT_TEST_MERGE_ALGORITHM=ort | 
|  |  | 
|  | test_expect_success 'merge fails if cwd needs to be removed' ' | 
|  | test_required_dir_removal git merge fd_conflict | 
|  | ' | 
|  |  | 
|  | test_expect_success 'cherry-pick does not clean cwd incidentally' ' | 
|  | test_incidental_dir_removal git cherry-pick reverted | 
|  | ' | 
|  |  | 
|  | test_expect_success 'cherry-pick fails if cwd needs to be removed' ' | 
|  | test_required_dir_removal git cherry-pick fd_conflict | 
|  | ' | 
|  |  | 
|  | test_expect_success 'rebase does not clean cwd incidentally' ' | 
|  | test_incidental_dir_removal git rebase reverted | 
|  | ' | 
|  |  | 
|  | test_expect_success 'rebase fails if cwd needs to be removed' ' | 
|  | test_required_dir_removal git rebase fd_conflict | 
|  | ' | 
|  |  | 
|  | test_expect_success 'revert does not clean cwd incidentally' ' | 
|  | test_incidental_dir_removal git revert HEAD | 
|  | ' | 
|  |  | 
|  | test_expect_success 'revert fails if cwd needs to be removed' ' | 
|  | test_required_dir_removal git revert undo_fd_conflict | 
|  | ' | 
|  |  | 
|  | test_expect_success 'rm does not clean cwd incidentally' ' | 
|  | test_incidental_dir_removal git rm bar/baz.t | 
|  | ' | 
|  |  | 
|  | test_expect_success 'apply does not remove cwd incidentally' ' | 
|  | git diff HEAD HEAD~1 >patch && | 
|  | test_incidental_dir_removal git apply ../patch | 
|  | ' | 
|  |  | 
|  | test_incidental_untracked_dir_removal () { | 
|  | test_when_finished "git reset --hard" && | 
|  |  | 
|  | git checkout foo/bar/baz^{commit} && | 
|  | mkdir -p untracked && | 
|  | mkdir empty | 
|  | >untracked/random && | 
|  |  | 
|  | ( | 
|  | cd untracked && | 
|  | "$@" && | 
|  |  | 
|  | # Make sure untracked still exists, and commands needing it work | 
|  | test-tool getcwd && | 
|  | git status --porcelain | 
|  | ) && | 
|  | test_path_is_missing empty && | 
|  | test_path_is_missing untracked/random && | 
|  |  | 
|  | test_path_is_dir untracked | 
|  | } | 
|  |  | 
|  | test_expect_success 'clean does not remove cwd incidentally' ' | 
|  | test_incidental_untracked_dir_removal \ | 
|  | git -C .. clean -fd -e warnings . >warnings && | 
|  | grep "Refusing to remove current working directory" warnings | 
|  | ' | 
|  |  | 
|  | test_expect_success 'stash does not remove cwd incidentally' ' | 
|  | test_incidental_untracked_dir_removal \ | 
|  | 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 && | 
|  | test_path_is_missing a/b/c && | 
|  | test_path_is_dir a/b | 
|  | ' | 
|  |  | 
|  | 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 | 
|  |  | 
|  | 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 |