Skip to content

Commit

Permalink
clean: require double -f options to nuke nested git repository and wo…
Browse files Browse the repository at this point in the history
…rk tree

When you have an embedded git work tree in your work tree (be it
an orphaned submodule, or an independent checkout of an unrelated
project), "git clean -d -f" blindly descended into it and removed
everything.  This is rarely what the user wants.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
gitster committed Jul 29, 2009
1 parent 0a53e9d commit a0f4afb
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 a0f4afb

Please sign in to comment.