Skip to content

Commit

Permalink
Merge branch 'jc/maint-clean-nested-worktree-in-subdir' into maint
Browse files Browse the repository at this point in the history
"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
  • Loading branch information
Junio C Hamano committed Apr 26, 2012
2 parents 3f231e2 + ae2f203 commit 5d65c2e
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 11 deletions.
27 changes: 21 additions & 6 deletions dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -1172,23 +1172,27 @@ int is_empty_dir(const char *path)
return ret;
}

int remove_dir_recursively(struct strbuf *path, int flag)
static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
{
DIR *dir;
struct dirent *e;
int ret = 0, original_len = path->len, len;
int ret = 0, original_len = path->len, len, kept_down = 0;
int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY);
int keep_toplevel = (flag & REMOVE_DIR_KEEP_TOPLEVEL);
unsigned char submodule_head[20];

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

flag &= ~(REMOVE_DIR_KEEP_TOPLEVEL|REMOVE_DIR_KEEP_NESTED_GIT);
flag &= ~REMOVE_DIR_KEEP_TOPLEVEL;
dir = opendir(path->buf);
if (!dir) {
/* an empty dir could be removed even if it is unreadble */
if (!keep_toplevel)
return rmdir(path->buf);
else
Expand All @@ -1208,7 +1212,7 @@ int remove_dir_recursively(struct strbuf *path, int flag)
if (lstat(path->buf, &st))
; /* fall thru */
else if (S_ISDIR(st.st_mode)) {
if (!remove_dir_recursively(path, flag))
if (!remove_dir_recurse(path, flag, &kept_down))
continue; /* happy */
} else if (!only_empty && !unlink(path->buf))
continue; /* happy, too */
Expand All @@ -1220,11 +1224,22 @@ int remove_dir_recursively(struct strbuf *path, int flag)
closedir(dir);

strbuf_setlen(path, original_len);
if (!ret && !keep_toplevel)
if (!ret && !keep_toplevel && !kept_down)
ret = rmdir(path->buf);
else if (kept_up)
/*
* report the uplevel that it is not an error that we
* did not rmdir() our directory.
*/
*kept_up = !ret;
return ret;
}

int remove_dir_recursively(struct strbuf *path, int flag)
{
return remove_dir_recurse(path, flag, NULL);
}

void setup_standard_excludes(struct dir_struct *dir)
{
const char *path;
Expand Down
27 changes: 22 additions & 5 deletions t/t7300-clean.sh
Original file line number Diff line number Diff line change
Expand Up @@ -399,8 +399,8 @@ test_expect_success SANITY 'removal failure' '
'

test_expect_success 'nested git work tree' '
rm -fr foo bar &&
mkdir foo bar &&
rm -fr foo bar baz &&
mkdir -p foo bar baz/boo &&
(
cd foo &&
git init &&
Expand All @@ -412,15 +412,24 @@ test_expect_success 'nested git work tree' '
cd bar &&
>goodbye.people
) &&
(
cd baz/boo &&
git init &&
>deeper.world
git add . &&
git commit -a -m deeply.nested
) &&
git clean -f -d &&
test -f foo/.git/index &&
test -f foo/hello.world &&
test -f baz/boo/.git/index &&
test -f baz/boo/deeper.world &&
! test -d bar
'

test_expect_success 'force removal of nested git work tree' '
rm -fr foo bar &&
mkdir foo bar &&
rm -fr foo bar baz &&
mkdir -p foo bar baz/boo &&
(
cd foo &&
git init &&
Expand All @@ -432,9 +441,17 @@ test_expect_success 'force removal of nested git work tree' '
cd bar &&
>goodbye.people
) &&
(
cd baz/boo &&
git init &&
>deeper.world
git add . &&
git commit -a -m deeply.nested
) &&
git clean -f -f -d &&
! test -d foo &&
! test -d bar
! test -d bar &&
! test -d baz
'

test_expect_success 'git clean -e' '
Expand Down

0 comments on commit 5d65c2e

Please sign in to comment.