| #!/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 |