Skip to content

Commit a2caeb2

Browse files
committed
Merge branch 'jc/maint-clean-nested-worktree-in-subdir'
"git clean -d -f" (not "-d -f -f") is supposed to protect nested working trees of independent git repositories that exist in the current project working tree from getting removed, but the protection applied only to such working trees that are at the top-level of the current project by mistake. * jc/maint-clean-nested-worktree-in-subdir: clean: preserve nested git worktree in subdirectories
2 parents 27ed435 + ae2f203 commit a2caeb2

File tree

2 files changed

+43
-11
lines changed

2 files changed

+43
-11
lines changed

dir.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,23 +1172,27 @@ int is_empty_dir(const char *path)
11721172
return ret;
11731173
}
11741174

1175-
int remove_dir_recursively(struct strbuf *path, int flag)
1175+
static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
11761176
{
11771177
DIR *dir;
11781178
struct dirent *e;
1179-
int ret = 0, original_len = path->len, len;
1179+
int ret = 0, original_len = path->len, len, kept_down = 0;
11801180
int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY);
11811181
int keep_toplevel = (flag & REMOVE_DIR_KEEP_TOPLEVEL);
11821182
unsigned char submodule_head[20];
11831183

11841184
if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
1185-
!resolve_gitlink_ref(path->buf, "HEAD", submodule_head))
1185+
!resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) {
11861186
/* Do not descend and nuke a nested git work tree. */
1187+
if (kept_up)
1188+
*kept_up = 1;
11871189
return 0;
1190+
}
11881191

1189-
flag &= ~(REMOVE_DIR_KEEP_TOPLEVEL|REMOVE_DIR_KEEP_NESTED_GIT);
1192+
flag &= ~REMOVE_DIR_KEEP_TOPLEVEL;
11901193
dir = opendir(path->buf);
11911194
if (!dir) {
1195+
/* an empty dir could be removed even if it is unreadble */
11921196
if (!keep_toplevel)
11931197
return rmdir(path->buf);
11941198
else
@@ -1208,7 +1212,7 @@ int remove_dir_recursively(struct strbuf *path, int flag)
12081212
if (lstat(path->buf, &st))
12091213
; /* fall thru */
12101214
else if (S_ISDIR(st.st_mode)) {
1211-
if (!remove_dir_recursively(path, flag))
1215+
if (!remove_dir_recurse(path, flag, &kept_down))
12121216
continue; /* happy */
12131217
} else if (!only_empty && !unlink(path->buf))
12141218
continue; /* happy, too */
@@ -1220,11 +1224,22 @@ int remove_dir_recursively(struct strbuf *path, int flag)
12201224
closedir(dir);
12211225

12221226
strbuf_setlen(path, original_len);
1223-
if (!ret && !keep_toplevel)
1227+
if (!ret && !keep_toplevel && !kept_down)
12241228
ret = rmdir(path->buf);
1229+
else if (kept_up)
1230+
/*
1231+
* report the uplevel that it is not an error that we
1232+
* did not rmdir() our directory.
1233+
*/
1234+
*kept_up = !ret;
12251235
return ret;
12261236
}
12271237

1238+
int remove_dir_recursively(struct strbuf *path, int flag)
1239+
{
1240+
return remove_dir_recurse(path, flag, NULL);
1241+
}
1242+
12281243
void setup_standard_excludes(struct dir_struct *dir)
12291244
{
12301245
const char *path;

t/t7300-clean.sh

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -399,8 +399,8 @@ test_expect_success SANITY 'removal failure' '
399399
'
400400

401401
test_expect_success 'nested git work tree' '
402-
rm -fr foo bar &&
403-
mkdir foo bar &&
402+
rm -fr foo bar baz &&
403+
mkdir -p foo bar baz/boo &&
404404
(
405405
cd foo &&
406406
git init &&
@@ -412,15 +412,24 @@ test_expect_success 'nested git work tree' '
412412
cd bar &&
413413
>goodbye.people
414414
) &&
415+
(
416+
cd baz/boo &&
417+
git init &&
418+
>deeper.world
419+
git add . &&
420+
git commit -a -m deeply.nested
421+
) &&
415422
git clean -f -d &&
416423
test -f foo/.git/index &&
417424
test -f foo/hello.world &&
425+
test -f baz/boo/.git/index &&
426+
test -f baz/boo/deeper.world &&
418427
! test -d bar
419428
'
420429

421430
test_expect_success 'force removal of nested git work tree' '
422-
rm -fr foo bar &&
423-
mkdir foo bar &&
431+
rm -fr foo bar baz &&
432+
mkdir -p foo bar baz/boo &&
424433
(
425434
cd foo &&
426435
git init &&
@@ -432,9 +441,17 @@ test_expect_success 'force removal of nested git work tree' '
432441
cd bar &&
433442
>goodbye.people
434443
) &&
444+
(
445+
cd baz/boo &&
446+
git init &&
447+
>deeper.world
448+
git add . &&
449+
git commit -a -m deeply.nested
450+
) &&
435451
git clean -f -f -d &&
436452
! test -d foo &&
437-
! test -d bar
453+
! test -d bar &&
454+
! test -d baz
438455
'
439456

440457
test_expect_success 'git clean -e' '

0 commit comments

Comments
 (0)