Skip to content

Commit

Permalink
Merge branch 'jc/maint-clean-nested-dir-safety'
Browse files Browse the repository at this point in the history
* jc/maint-clean-nested-dir-safety:
  clean: require double -f options to nuke nested git repository and work tree
  • Loading branch information
Junio C Hamano committed Aug 16, 2009
2 parents b21f9e7 + a0f4afb commit 14683af
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 5 deletions.
3 changes: 3 additions & 0 deletions Documentation/git-clean.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ OPTIONS
-------
-d::
Remove untracked directories in addition to untracked files.
If an untracked directory is managed by a different git
repository, it is not removed by default. Use -f option twice
if you really want to remove such a directory.

-f::
If the git configuration specifies clean.requireForce as true,
Expand Down
7 changes: 6 additions & 1 deletion builtin-clean.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
int i;
int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
int ignored_only = 0, baselen = 0, config_set = 0, errors = 0;
int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
struct strbuf directory = STRBUF_INIT;
struct dir_struct dir;
static const char **pathspec;
Expand Down Expand Up @@ -69,6 +70,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
die("clean.requireForce%s set and -n or -f not given; "
"refusing to clean", config_set ? "" : " not");

if (force > 1)
rm_flags = 0;

dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;

if (!ignored)
Expand Down Expand Up @@ -131,7 +135,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
(matches == MATCHED_EXACTLY)) {
if (!quiet)
printf("Removing %s\n", qname);
if (remove_dir_recursively(&directory, 0) != 0) {
if (remove_dir_recursively(&directory,
rm_flags) != 0) {
warning("failed to remove '%s'", qname);
errors++;
}
Expand Down
12 changes: 10 additions & 2 deletions dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -861,12 +861,20 @@ int is_empty_dir(const char *path)
return ret;
}

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

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

dir = opendir(path->buf);
if (!dir)
return -1;
if (path->buf[original_len - 1] != '/')
Expand Down
5 changes: 4 additions & 1 deletion dir.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ static inline int is_dot_or_dotdot(const char *name)
extern int is_empty_dir(const char *dir);

extern void setup_standard_excludes(struct dir_struct *dir);
extern int remove_dir_recursively(struct strbuf *path, int only_empty);

#define REMOVE_DIR_EMPTY_ONLY 01
#define REMOVE_DIR_KEEP_NESTED_GIT 02
extern int remove_dir_recursively(struct strbuf *path, int flag);

/* tries to remove the path with empty directories along it, ignores ENOENT */
extern int remove_path(const char *path);
Expand Down
2 changes: 1 addition & 1 deletion refs.c
Original file line number Diff line number Diff line change
Expand Up @@ -821,7 +821,7 @@ static int remove_empty_directories(const char *file)
strbuf_init(&path, 20);
strbuf_addstr(&path, file);

result = remove_dir_recursively(&path, 1);
result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY);

strbuf_release(&path);

Expand Down
39 changes: 39 additions & 0 deletions t/t7300-clean.sh
Original file line number Diff line number Diff line change
Expand Up @@ -380,4 +380,43 @@ test_expect_success 'removal failure' '
'
chmod 755 foo

test_expect_success 'nested git work tree' '
rm -fr foo bar &&
mkdir foo bar &&
(
cd foo &&
git init &&
>hello.world
git add . &&
git commit -a -m nested
) &&
(
cd bar &&
>goodbye.people
) &&
git clean -f -d &&
test -f foo/.git/index &&
test -f foo/hello.world &&
! test -d bar
'

test_expect_success 'force removal of nested git work tree' '
rm -fr foo bar &&
mkdir foo bar &&
(
cd foo &&
git init &&
>hello.world
git add . &&
git commit -a -m nested
) &&
(
cd bar &&
>goodbye.people
) &&
git clean -f -f -d &&
! test -d foo &&
! test -d bar
'

test_done

0 comments on commit 14683af

Please sign in to comment.