clean: do not attempt to remove startup_info->original_cwd

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 c65744e7d7f512f3666da5964f0ace90325dd94a
2 files changed +38 -11
builtin/clean.c
+35 -9
@@ -36,6 +36,8 @@ static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
36 static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
37 static const char *msg_warn_remove_failed = N_("failed to remove %s");
38 static const char *msg_warn_lstat_failed = N_("could not lstat %s\n");
39 +static const char *msg_skip_cwd = N_("Refusing to remove current working directory\n");
40 +static const char *msg_would_skip_cwd = N_("Would refuse to remove current working directory\n");
41
42 enum color_clean {
43 CLEAN_COLOR_RESET = 0,
@@ -153,6 +155,8 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
155 {
156 DIR *dir;
157 struct strbuf quoted = STRBUF_INIT;
158 + struct strbuf realpath = STRBUF_INIT;
159 + struct strbuf real_ocwd = STRBUF_INIT;
160 struct dirent *e;
161 int res = 0, ret = 0, gone = 1, original_len = path->len, len;
162 struct string_list dels = STRING_LIST_INIT_DUP;
@@ -231,16 +235,36 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
235 strbuf_setlen(path, original_len);
236
237 if (*dir_gone) {
234 - res = dry_run ? 0 : rmdir(path->buf);
235 - if (!res)
236 - *dir_gone = 1;
237 - else {
238 - int saved_errno = errno;
239 - quote_path(path->buf, prefix, &quoted, 0);
240 - errno = saved_errno;
241 - warning_errno(_(msg_warn_remove_failed), quoted.buf);
238 + /*
239 + * Normalize path components in path->buf, e.g. change '\' to
240 + * '/' on Windows.
241 + */
242 + strbuf_realpath(&realpath, path->buf, 1);
243 +
244 + /*
245 + * path and realpath are absolute; for comparison, we would
246 + * like to transform startup_info->original_cwd to an absolute
247 + * path too.
248 + */
249 + if (startup_info->original_cwd)
250 + strbuf_realpath(&real_ocwd,
251 + startup_info->original_cwd, 1);
252 +
253 + if (!strbuf_cmp(&realpath, &real_ocwd)) {
254 + printf("%s", dry_run ? _(msg_would_skip_cwd) : _(msg_skip_cwd));
255 *dir_gone = 0;
243 - ret = 1;
256 + } else {
257 + res = dry_run ? 0 : rmdir(path->buf);
258 + if (!res)
259 + *dir_gone = 1;
260 + else {
261 + int saved_errno = errno;
262 + quote_path(path->buf, prefix, &quoted, 0);
263 + errno = saved_errno;
264 + warning_errno(_(msg_warn_remove_failed), quoted.buf);
265 + *dir_gone = 0;
266 + ret = 1;
267 + }
268 }
269 }
270
@@ -250,6 +274,8 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
274 printf(dry_run ? _(msg_would_remove) : _(msg_remove), dels.items[i].string);
275 }
276 out:
277 + strbuf_release(&realpath);
278 + strbuf_release(&real_ocwd);
279 strbuf_release(&quoted);
280 string_list_clear(&dels, 0);
281 return ret;
t/t2501-cwd-empty.sh
+3 -2
@@ -230,8 +230,9 @@ test_incidental_untracked_dir_removal () {
230 }
231
232 test_expect_success 'clean does not remove cwd incidentally' '
233 - test_incidental_untracked_dir_removal failure \
234 - git -C .. clean -fd -e warnings . >warnings
233 + test_incidental_untracked_dir_removal success \
234 + git -C .. clean -fd -e warnings . >warnings &&
235 + grep "Refusing to remove current working directory" warnings
236 '
237
238 test_expect_success 'stash does not remove cwd incidentally' '