Skip to content

Commit b813234

Browse files
committed
Merge branch 'jc/maint-clean-nested-dir-safety' into maint
* jc/maint-clean-nested-dir-safety: clean: require double -f options to nuke nested git repository and work tree
2 parents bd30037 + a0f4afb commit b813234

File tree

6 files changed

+63
-5
lines changed

6 files changed

+63
-5
lines changed

Documentation/git-clean.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ OPTIONS
2727
-------
2828
-d::
2929
Remove untracked directories in addition to untracked files.
30+
If an untracked directory is managed by a different git
31+
repository, it is not removed by default. Use -f option twice
32+
if you really want to remove such a directory.
3033

3134
-f::
3235
If the git configuration specifies clean.requireForce as true,

builtin-clean.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
3131
int i;
3232
int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
3333
int ignored_only = 0, baselen = 0, config_set = 0, errors = 0;
34+
int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
3435
struct strbuf directory = STRBUF_INIT;
3536
struct dir_struct dir;
3637
static const char **pathspec;
@@ -69,6 +70,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
6970
die("clean.requireForce%s set and -n or -f not given; "
7071
"refusing to clean", config_set ? "" : " not");
7172

73+
if (force > 1)
74+
rm_flags = 0;
75+
7276
dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
7377

7478
if (!ignored)
@@ -131,7 +135,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
131135
(matches == MATCHED_EXACTLY)) {
132136
if (!quiet)
133137
printf("Removing %s\n", qname);
134-
if (remove_dir_recursively(&directory, 0) != 0) {
138+
if (remove_dir_recursively(&directory,
139+
rm_flags) != 0) {
135140
warning("failed to remove '%s'", qname);
136141
errors++;
137142
}

dir.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -861,12 +861,20 @@ int is_empty_dir(const char *path)
861861
return ret;
862862
}
863863

864-
int remove_dir_recursively(struct strbuf *path, int only_empty)
864+
int remove_dir_recursively(struct strbuf *path, int flag)
865865
{
866-
DIR *dir = opendir(path->buf);
866+
DIR *dir;
867867
struct dirent *e;
868868
int ret = 0, original_len = path->len, len;
869+
int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY);
870+
unsigned char submodule_head[20];
869871

872+
if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
873+
!resolve_gitlink_ref(path->buf, "HEAD", submodule_head))
874+
/* Do not descend and nuke a nested git work tree. */
875+
return 0;
876+
877+
dir = opendir(path->buf);
870878
if (!dir)
871879
return -1;
872880
if (path->buf[original_len - 1] != '/')

dir.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,10 @@ static inline int is_dot_or_dotdot(const char *name)
8888
extern int is_empty_dir(const char *dir);
8989

9090
extern void setup_standard_excludes(struct dir_struct *dir);
91-
extern int remove_dir_recursively(struct strbuf *path, int only_empty);
91+
92+
#define REMOVE_DIR_EMPTY_ONLY 01
93+
#define REMOVE_DIR_KEEP_NESTED_GIT 02
94+
extern int remove_dir_recursively(struct strbuf *path, int flag);
9295

9396
/* tries to remove the path with empty directories along it, ignores ENOENT */
9497
extern int remove_path(const char *path);

refs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ static int remove_empty_directories(const char *file)
821821
strbuf_init(&path, 20);
822822
strbuf_addstr(&path, file);
823823

824-
result = remove_dir_recursively(&path, 1);
824+
result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY);
825825

826826
strbuf_release(&path);
827827

t/t7300-clean.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,4 +380,43 @@ test_expect_success 'removal failure' '
380380
'
381381
chmod 755 foo
382382

383+
test_expect_success 'nested git work tree' '
384+
rm -fr foo bar &&
385+
mkdir foo bar &&
386+
(
387+
cd foo &&
388+
git init &&
389+
>hello.world
390+
git add . &&
391+
git commit -a -m nested
392+
) &&
393+
(
394+
cd bar &&
395+
>goodbye.people
396+
) &&
397+
git clean -f -d &&
398+
test -f foo/.git/index &&
399+
test -f foo/hello.world &&
400+
! test -d bar
401+
'
402+
403+
test_expect_success 'force removal of nested git work tree' '
404+
rm -fr foo bar &&
405+
mkdir foo bar &&
406+
(
407+
cd foo &&
408+
git init &&
409+
>hello.world
410+
git add . &&
411+
git commit -a -m nested
412+
) &&
413+
(
414+
cd bar &&
415+
>goodbye.people
416+
) &&
417+
git clean -f -f -d &&
418+
! test -d foo &&
419+
! test -d bar
420+
'
421+
383422
test_done

0 commit comments

Comments
 (0)