Merge branch 'jc/maint-clean-nested-dir-safety'
* jc/maint-clean-nested-dir-safety: clean: require double -f options to nuke nested git repository and work tree
This commit is contained in:
commit
14683af812
@ -27,6 +27,9 @@ OPTIONS
|
|||||||
-------
|
-------
|
||||||
-d::
|
-d::
|
||||||
Remove untracked directories in addition to untracked files.
|
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::
|
-f::
|
||||||
If the git configuration specifies clean.requireForce as true,
|
If the git configuration specifies clean.requireForce as true,
|
||||||
|
@ -31,6 +31,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
|||||||
int i;
|
int i;
|
||||||
int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
|
int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
|
||||||
int ignored_only = 0, baselen = 0, config_set = 0, errors = 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 strbuf directory = STRBUF_INIT;
|
||||||
struct dir_struct dir;
|
struct dir_struct dir;
|
||||||
static const char **pathspec;
|
static const char **pathspec;
|
||||||
@ -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; "
|
die("clean.requireForce%s set and -n or -f not given; "
|
||||||
"refusing to clean", config_set ? "" : " not");
|
"refusing to clean", config_set ? "" : " not");
|
||||||
|
|
||||||
|
if (force > 1)
|
||||||
|
rm_flags = 0;
|
||||||
|
|
||||||
dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
|
dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
|
||||||
|
|
||||||
if (!ignored)
|
if (!ignored)
|
||||||
@ -131,7 +135,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
|||||||
(matches == MATCHED_EXACTLY)) {
|
(matches == MATCHED_EXACTLY)) {
|
||||||
if (!quiet)
|
if (!quiet)
|
||||||
printf("Removing %s\n", qname);
|
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);
|
warning("failed to remove '%s'", qname);
|
||||||
errors++;
|
errors++;
|
||||||
}
|
}
|
||||||
|
12
dir.c
12
dir.c
@ -861,12 +861,20 @@ int is_empty_dir(const char *path)
|
|||||||
return ret;
|
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;
|
struct dirent *e;
|
||||||
int ret = 0, original_len = path->len, len;
|
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)
|
if (!dir)
|
||||||
return -1;
|
return -1;
|
||||||
if (path->buf[original_len - 1] != '/')
|
if (path->buf[original_len - 1] != '/')
|
||||||
|
5
dir.h
5
dir.h
@ -88,7 +88,10 @@ static inline int is_dot_or_dotdot(const char *name)
|
|||||||
extern int is_empty_dir(const char *dir);
|
extern int is_empty_dir(const char *dir);
|
||||||
|
|
||||||
extern void setup_standard_excludes(struct dir_struct *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 */
|
/* tries to remove the path with empty directories along it, ignores ENOENT */
|
||||||
extern int remove_path(const char *path);
|
extern int remove_path(const char *path);
|
||||||
|
2
refs.c
2
refs.c
@ -821,7 +821,7 @@ static int remove_empty_directories(const char *file)
|
|||||||
strbuf_init(&path, 20);
|
strbuf_init(&path, 20);
|
||||||
strbuf_addstr(&path, file);
|
strbuf_addstr(&path, file);
|
||||||
|
|
||||||
result = remove_dir_recursively(&path, 1);
|
result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY);
|
||||||
|
|
||||||
strbuf_release(&path);
|
strbuf_release(&path);
|
||||||
|
|
||||||
|
@ -380,4 +380,43 @@ test_expect_success 'removal failure' '
|
|||||||
'
|
'
|
||||||
chmod 755 foo
|
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
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user