git-clean: refactor git-clean into two phases

Before introducing interactive git-clean, refactor git-clean operations
into two phases:

 * hold cleaning items in del_list,
 * and remove them in a separate loop at the end.

We will introduce interactive git-clean between the two phases. The
interactive git-clean will show what would be done and must confirm
before do real cleaning.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Jiang Xin 2013-06-25 23:53:47 +08:00 committed by Junio C Hamano
parent e9a820cefd
commit 396049e5fb

View File

@ -15,6 +15,7 @@
#include "quote.h"
static int force = -1; /* unset */
static struct string_list del_list = STRING_LIST_INIT_DUP;
static const char *const builtin_clean_usage[] = {
N_("git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
@ -148,12 +149,13 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
struct strbuf directory = STRBUF_INIT;
struct strbuf abs_path = STRBUF_INIT;
struct dir_struct dir;
static const char **pathspec;
struct strbuf buf = STRBUF_INIT;
struct string_list exclude_list = STRING_LIST_INIT_NODUP;
struct exclude_list *el;
struct string_list_item *item;
const char *qname;
char *seen = NULL;
struct option options[] = {
@ -223,6 +225,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
int matches = 0;
struct cache_entry *ce;
struct stat st;
const char *rel;
/*
* Remove the '/' at the end that directory
@ -242,13 +245,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
continue; /* Yup, this one exists unmerged */
}
/*
* we might have removed this as part of earlier
* recursive directory removal, so lstat() here could
* fail with ENOENT.
*/
if (lstat(ent->name, &st))
continue;
die_errno("Cannot lstat '%s'", ent->name);
if (pathspec) {
memset(seen, 0, argc > 0 ? argc : 1);
@ -257,33 +255,61 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
}
if (S_ISDIR(st.st_mode)) {
strbuf_addstr(&directory, ent->name);
if (remove_directories || (matches == MATCHED_EXACTLY)) {
if (remove_dirs(&directory, prefix, rm_flags, dry_run, quiet, &gone))
errors++;
if (gone && !quiet) {
qname = quote_path_relative(directory.buf, prefix, &buf);
printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
rel = relative_path(ent->name, prefix, &buf);
string_list_append(&del_list, rel);
}
}
strbuf_reset(&directory);
} else {
if (pathspec && !matches)
continue;
res = dry_run ? 0 : unlink(ent->name);
rel = relative_path(ent->name, prefix, &buf);
string_list_append(&del_list, rel);
}
}
/* TODO: do interactive git-clean here, which will modify del_list */
for_each_string_list_item(item, &del_list) {
struct stat st;
if (prefix)
strbuf_addstr(&abs_path, prefix);
strbuf_addstr(&abs_path, item->string);
/*
* we might have removed this as part of earlier
* recursive directory removal, so lstat() here could
* fail with ENOENT.
*/
if (lstat(abs_path.buf, &st))
continue;
if (S_ISDIR(st.st_mode)) {
if (remove_dirs(&abs_path, prefix, rm_flags, dry_run, quiet, &gone))
errors++;
if (gone && !quiet) {
qname = quote_path_relative(item->string, NULL, &buf);
printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
}
} else {
res = dry_run ? 0 : unlink(abs_path.buf);
if (res) {
qname = quote_path_relative(ent->name, prefix, &buf);
qname = quote_path_relative(item->string, NULL, &buf);
warning(_(msg_warn_remove_failed), qname);
errors++;
} else if (!quiet) {
qname = quote_path_relative(ent->name, prefix, &buf);
qname = quote_path_relative(item->string, NULL, &buf);
printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
}
}
strbuf_reset(&abs_path);
}
free(seen);
strbuf_release(&directory);
strbuf_release(&abs_path);
strbuf_release(&buf);
string_list_clear(&del_list, 0);
string_list_clear(&exclude_list, 0);
return (errors != 0);
}