unpack-trees: refuse to remove startup_info->original_cwd

In the past, when a directory needs to be removed to make room for a file, we have always errored out when that directory contains any untracked (but not ignored) files. Add an extra condition on that: also error out if the directory is the current working directory we inherited from our parent process. Acked-by: Derrick Stolee <stolee@gmail.com> Acked-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Dec 9, 2021 at 05:08 UTC b817e545338cdb737b3deebf4917afb4a18ede57
3 files changed +21 -17
t/t2501-cwd-empty.sh
+7 -13
@@ -113,7 +113,7 @@ test_expect_success 'checkout does not clean cwd incidentally' '
113 '
114
115 test_expect_success 'checkout fails if cwd needs to be removed' '
116 - test_required_dir_removal failure git checkout fd_conflict
116 + test_required_dir_removal success git checkout fd_conflict
117 '
118
119 test_expect_success 'reset --hard does not clean cwd incidentally' '
@@ -144,23 +144,17 @@ test_expect_success 'merge fails if cwd needs to be removed; recursive friendly'
144 (
145 cd dirORfile &&
146
147 - # We would rather this failed, but we test for existing
148 - # rather than desired behavior
149 - git merge fd_conflict 2>../error
147 + test_must_fail git merge fd_conflict 2>../error
148 ) &&
149
152 - ## Here is the behavior we would rather have:
153 - #test_path_is_dir dirORfile &&
154 - #grep "Refusing to remove the current working directory" error
155 - ## But instead we test for existing behavior
156 - test_path_is_file dirORfile &&
157 - test_must_be_empty error
150 + test_path_is_dir dirORfile &&
151 + grep "Refusing to remove the current working directory" error
152 '
153
154 GIT_TEST_MERGE_ALGORITHM=ort
155
156 test_expect_success 'merge fails if cwd needs to be removed' '
163 - test_required_dir_removal failure git merge fd_conflict
157 + test_required_dir_removal success git merge fd_conflict
158 '
159
160 test_expect_success 'cherry-pick does not clean cwd incidentally' '
@@ -168,7 +162,7 @@ test_expect_success 'cherry-pick does not clean cwd incidentally' '
162 '
163
164 test_expect_success 'cherry-pick fails if cwd needs to be removed' '
171 - test_required_dir_removal failure git cherry-pick fd_conflict
165 + test_required_dir_removal success git cherry-pick fd_conflict
166 '
167
168 test_expect_success 'rebase does not clean cwd incidentally' '
@@ -184,7 +178,7 @@ test_expect_success 'revert does not clean cwd incidentally' '
178 '
179
180 test_expect_success 'revert fails if cwd needs to be removed' '
187 - test_required_dir_removal failure git revert undo_fd_conflict
181 + test_required_dir_removal success git revert undo_fd_conflict
182 '
183
184 test_expect_success 'rm does not clean cwd incidentally' '
unpack-trees.c
+13 -4
@@ -36,6 +36,9 @@ static const char *unpack_plumbing_errors[NB_UNPACK_TREES_WARNING_TYPES] = {
36 /* ERROR_NOT_UPTODATE_DIR */
37 "Updating '%s' would lose untracked files in it",
38
39 + /* ERROR_CWD_IN_THE_WAY */
40 + "Refusing to remove '%s' since it is the current working directory.",
41 +
42 /* ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN */
43 "Untracked working tree file '%s' would be overwritten by merge.",
44
@@ -131,6 +134,9 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
134 msgs[ERROR_NOT_UPTODATE_DIR] =
135 _("Updating the following directories would lose untracked files in them:\n%s");
136
137 + msgs[ERROR_CWD_IN_THE_WAY] =
138 + _("Refusing to remove the current working directory:\n%s");
139 +
140 if (!strcmp(cmd, "checkout"))
141 msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
142 ? _("The following untracked working tree files would be removed by checkout:\n%%s"
@@ -2146,10 +2152,7 @@ static int verify_clean_subdirectory(const struct cache_entry *ce,
2152 cnt++;
2153 }
2154
2149 - /*
2150 - * Then we need to make sure that we do not lose a locally
2151 - * present file that is not ignored.
2152 - */
2155 + /* Do not lose a locally present file that is not ignored. */
2156 pathbuf = xstrfmt("%.*s/", namelen, ce->name);
2157
2158 memset(&d, 0, sizeof(d));
@@ -2160,6 +2163,12 @@ static int verify_clean_subdirectory(const struct cache_entry *ce,
2163 free(pathbuf);
2164 if (i)
2165 return add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
2166 +
2167 + /* Do not lose startup_info->original_cwd */
2168 + if (startup_info->original_cwd &&
2169 + !strcmp(startup_info->original_cwd, ce->name))
2170 + return add_rejected_path(o, ERROR_CWD_IN_THE_WAY, ce->name);
2171 +
2172 return cnt;
2173 }
2174
unpack-trees.h
+1
@@ -19,6 +19,7 @@ enum unpack_trees_error_types {
19 ERROR_WOULD_OVERWRITE = 0,
20 ERROR_NOT_UPTODATE_FILE,
21 ERROR_NOT_UPTODATE_DIR,
22 + ERROR_CWD_IN_THE_WAY,
23 ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN,
24 ERROR_WOULD_LOSE_UNTRACKED_REMOVED,
25 ERROR_BIND_OVERLAP,