git-clean: add support for -i/--interactive
Show what would be done and the user must confirm before actually cleaning. Would remove ... Would remove ... Would remove ... Remove [y/n]? Press "y" to start cleaning, and press "n" if you want to abort. Signed-off-by: Jiang Xin <worldhello.net@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
parent
396049e5fb
commit
1769600208
|
@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
[verse]
|
[verse]
|
||||||
'git clean' [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
|
'git clean' [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
@ -34,7 +34,13 @@ OPTIONS
|
||||||
-f::
|
-f::
|
||||||
--force::
|
--force::
|
||||||
If the Git configuration variable clean.requireForce is not set
|
If the Git configuration variable clean.requireForce is not set
|
||||||
to false, 'git clean' will refuse to run unless given -f or -n.
|
to false, 'git clean' will refuse to run unless given -f, -n or
|
||||||
|
-i.
|
||||||
|
|
||||||
|
-i::
|
||||||
|
--interactive::
|
||||||
|
Show what would be done and the user must confirm before actually
|
||||||
|
cleaning.
|
||||||
|
|
||||||
-n::
|
-n::
|
||||||
--dry-run::
|
--dry-run::
|
||||||
|
|
|
@ -15,10 +15,11 @@
|
||||||
#include "quote.h"
|
#include "quote.h"
|
||||||
|
|
||||||
static int force = -1; /* unset */
|
static int force = -1; /* unset */
|
||||||
|
static int interactive;
|
||||||
static struct string_list del_list = STRING_LIST_INIT_DUP;
|
static struct string_list del_list = STRING_LIST_INIT_DUP;
|
||||||
|
|
||||||
static const char *const builtin_clean_usage[] = {
|
static const char *const builtin_clean_usage[] = {
|
||||||
N_("git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
|
N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -143,6 +144,50 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void interactive_main_loop(void)
|
||||||
|
{
|
||||||
|
struct strbuf confirm = STRBUF_INIT;
|
||||||
|
struct strbuf buf = STRBUF_INIT;
|
||||||
|
struct string_list_item *item;
|
||||||
|
const char *qname;
|
||||||
|
|
||||||
|
while (del_list.nr) {
|
||||||
|
putchar('\n');
|
||||||
|
for_each_string_list_item(item, &del_list) {
|
||||||
|
qname = quote_path_relative(item->string, NULL, &buf);
|
||||||
|
printf(_(msg_would_remove), qname);
|
||||||
|
}
|
||||||
|
putchar('\n');
|
||||||
|
|
||||||
|
printf(_("Remove [y/n]? "));
|
||||||
|
if (strbuf_getline(&confirm, stdin, '\n') != EOF) {
|
||||||
|
strbuf_trim(&confirm);
|
||||||
|
} else {
|
||||||
|
/* Ctrl-D is the same as "quit" */
|
||||||
|
string_list_clear(&del_list, 0);
|
||||||
|
putchar('\n');
|
||||||
|
printf_ln("Bye.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (confirm.len) {
|
||||||
|
if (!strncasecmp(confirm.buf, "yes", confirm.len)) {
|
||||||
|
break;
|
||||||
|
} else if (!strncasecmp(confirm.buf, "no", confirm.len) ||
|
||||||
|
!strncasecmp(confirm.buf, "quit", confirm.len)) {
|
||||||
|
string_list_clear(&del_list, 0);
|
||||||
|
printf_ln("Bye.");
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strbuf_release(&buf);
|
||||||
|
strbuf_release(&confirm);
|
||||||
|
}
|
||||||
|
|
||||||
int cmd_clean(int argc, const char **argv, const char *prefix)
|
int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
int i, res;
|
int i, res;
|
||||||
|
@ -162,6 +207,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||||
OPT__QUIET(&quiet, N_("do not print names of files removed")),
|
OPT__QUIET(&quiet, N_("do not print names of files removed")),
|
||||||
OPT__DRY_RUN(&dry_run, N_("dry run")),
|
OPT__DRY_RUN(&dry_run, N_("dry run")),
|
||||||
OPT__FORCE(&force, N_("force")),
|
OPT__FORCE(&force, N_("force")),
|
||||||
|
OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")),
|
||||||
OPT_BOOLEAN('d', NULL, &remove_directories,
|
OPT_BOOLEAN('d', NULL, &remove_directories,
|
||||||
N_("remove whole directories")),
|
N_("remove whole directories")),
|
||||||
{ OPTION_CALLBACK, 'e', "exclude", &exclude_list, N_("pattern"),
|
{ OPTION_CALLBACK, 'e', "exclude", &exclude_list, N_("pattern"),
|
||||||
|
@ -188,12 +234,12 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||||
if (ignored && ignored_only)
|
if (ignored && ignored_only)
|
||||||
die(_("-x and -X cannot be used together"));
|
die(_("-x and -X cannot be used together"));
|
||||||
|
|
||||||
if (!dry_run && !force) {
|
if (!interactive && !dry_run && !force) {
|
||||||
if (config_set)
|
if (config_set)
|
||||||
die(_("clean.requireForce set to true and neither -n nor -f given; "
|
die(_("clean.requireForce set to true and neither -i, -n nor -f given; "
|
||||||
"refusing to clean"));
|
"refusing to clean"));
|
||||||
else
|
else
|
||||||
die(_("clean.requireForce defaults to true and neither -n nor -f given; "
|
die(_("clean.requireForce defaults to true and neither -i, -n nor -f given; "
|
||||||
"refusing to clean"));
|
"refusing to clean"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +313,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: do interactive git-clean here, which will modify del_list */
|
if (interactive && del_list.nr > 0)
|
||||||
|
interactive_main_loop();
|
||||||
|
|
||||||
for_each_string_list_item(item, &del_list) {
|
for_each_string_list_item(item, &del_list) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
Loading…
Reference in New Issue