diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt index 3ab749b921..ba31d8d166 100644 --- a/Documentation/git-clean.txt +++ b/Documentation/git-clean.txt @@ -37,9 +37,9 @@ OPTIONS --force:: If the Git configuration variable clean.requireForce is not set to false, 'git clean' will refuse to delete files or directories - unless given -f or -i. Git will refuse to delete directories - with .git sub directory or file unless a second -f - is given. + unless given -f or -i. Git will refuse to modify untracked + nested git repositories (directories with a .git subdirectory) + unless a second -f is given. -i:: --interactive:: diff --git a/builtin/clean.c b/builtin/clean.c index 68d70e41c0..3a7a63ae71 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -946,6 +946,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix) if (force > 1) rm_flags = 0; + else + dir.flags |= DIR_SKIP_NESTED_GIT; dir.flags |= DIR_SHOW_OTHER_DIRECTORIES; diff --git a/dir.c b/dir.c index 3b2fe1701c..7ff79170fc 100644 --- a/dir.c +++ b/dir.c @@ -1451,6 +1451,16 @@ static enum path_treatment treat_directory(struct dir_struct *dir, return path_none; case index_nonexistent: + if (dir->flags & DIR_SKIP_NESTED_GIT) { + int nested_repo; + struct strbuf sb = STRBUF_INIT; + strbuf_addstr(&sb, dirname); + nested_repo = is_nonbare_repository_dir(&sb); + strbuf_release(&sb); + if (nested_repo) + return path_none; + } + if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES) break; if (exclude && diff --git a/dir.h b/dir.h index 46c238ab49..739aea7c96 100644 --- a/dir.h +++ b/dir.h @@ -156,7 +156,8 @@ struct dir_struct { DIR_SHOW_IGNORED_TOO = 1<<5, DIR_COLLECT_KILLED_ONLY = 1<<6, DIR_KEEP_UNTRACKED_CONTENTS = 1<<7, - DIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8 + DIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8, + DIR_SKIP_NESTED_GIT = 1<<9 } flags; struct dir_entry **entries; struct dir_entry **ignored; diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index 530dfdab34..6e6d24c1c3 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -549,7 +549,7 @@ test_expect_failure 'nested (non-empty) bare repositories should be cleaned even test_path_is_missing strange_bare ' -test_expect_success 'giving path in nested git work tree will remove it' ' +test_expect_success 'giving path in nested git work tree will NOT remove it' ' rm -fr repo && mkdir repo && ( @@ -561,7 +561,7 @@ test_expect_success 'giving path in nested git work tree will remove it' ' git clean -f -d repo/bar/baz && test_path_is_file repo/.git/HEAD && test_path_is_dir repo/bar/ && - test_path_is_missing repo/bar/baz + test_path_is_file repo/bar/baz/hello.world ' test_expect_success 'giving path to nested .git will not remove it' ' @@ -579,7 +579,7 @@ test_expect_success 'giving path to nested .git will not remove it' ' test_path_is_dir untracked/ ' -test_expect_success 'giving path to nested .git/ will remove contents' ' +test_expect_success 'giving path to nested .git/ will NOT remove contents' ' rm -fr repo untracked && mkdir repo untracked && ( @@ -589,7 +589,7 @@ test_expect_success 'giving path to nested .git/ will remove contents' ' ) && git clean -f -d repo/.git/ && test_path_is_dir repo/.git && - test_dir_is_empty repo/.git && + test_path_is_file repo/.git/HEAD && test_path_is_dir untracked/ ' @@ -671,7 +671,7 @@ test_expect_success 'git clean -d skips untracked dirs containing ignored files' test_path_is_missing foo/b/bb ' -test_expect_failure 'git clean -d skips nested repo containing ignored files' ' +test_expect_success 'git clean -d skips nested repo containing ignored files' ' test_when_finished "rm -rf nested-repo-with-ignored-file" && git init nested-repo-with-ignored-file &&