summaryrefslogtreecommitdiff
path: root/builtin/clean.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/clean.c')
-rw-r--r--builtin/clean.c138
1 files changed, 93 insertions, 45 deletions
diff --git a/builtin/clean.c b/builtin/clean.c
index 0371010afb..8d9a7dc206 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -8,6 +8,7 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "dir.h"
#include "parse-options.h"
#include "string-list.h"
@@ -15,6 +16,7 @@
#include "column.h"
#include "color.h"
#include "pathspec.h"
+#include "help.h"
static int force = -1; /* unset */
static int interactive;
@@ -32,15 +34,6 @@ static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
static const char *msg_warn_remove_failed = N_("failed to remove %s");
-static int clean_use_color = -1;
-static char clean_colors[][COLOR_MAXLEN] = {
- GIT_COLOR_RESET,
- GIT_COLOR_NORMAL, /* PLAIN */
- GIT_COLOR_BOLD_BLUE, /* PROMPT */
- GIT_COLOR_BOLD, /* HEADER */
- GIT_COLOR_BOLD_RED, /* HELP */
- GIT_COLOR_BOLD_RED, /* ERROR */
-};
enum color_clean {
CLEAN_COLOR_RESET = 0,
CLEAN_COLOR_PLAIN = 1,
@@ -50,6 +43,25 @@ enum color_clean {
CLEAN_COLOR_ERROR = 5
};
+static const char *color_interactive_slots[] = {
+ [CLEAN_COLOR_ERROR] = "error",
+ [CLEAN_COLOR_HEADER] = "header",
+ [CLEAN_COLOR_HELP] = "help",
+ [CLEAN_COLOR_PLAIN] = "plain",
+ [CLEAN_COLOR_PROMPT] = "prompt",
+ [CLEAN_COLOR_RESET] = "reset",
+};
+
+static int clean_use_color = -1;
+static char clean_colors[][COLOR_MAXLEN] = {
+ [CLEAN_COLOR_ERROR] = GIT_COLOR_BOLD_RED,
+ [CLEAN_COLOR_HEADER] = GIT_COLOR_BOLD,
+ [CLEAN_COLOR_HELP] = GIT_COLOR_BOLD_RED,
+ [CLEAN_COLOR_PLAIN] = GIT_COLOR_NORMAL,
+ [CLEAN_COLOR_PROMPT] = GIT_COLOR_BOLD_BLUE,
+ [CLEAN_COLOR_RESET] = GIT_COLOR_RESET,
+};
+
#define MENU_OPTS_SINGLETON 01
#define MENU_OPTS_IMMEDIATE 02
#define MENU_OPTS_LIST_ONLY 04
@@ -80,22 +92,7 @@ struct menu_stuff {
void *stuff;
};
-static int parse_clean_color_slot(const char *var)
-{
- if (!strcasecmp(var, "reset"))
- return CLEAN_COLOR_RESET;
- if (!strcasecmp(var, "plain"))
- return CLEAN_COLOR_PLAIN;
- if (!strcasecmp(var, "prompt"))
- return CLEAN_COLOR_PROMPT;
- if (!strcasecmp(var, "header"))
- return CLEAN_COLOR_HEADER;
- if (!strcasecmp(var, "help"))
- return CLEAN_COLOR_HELP;
- if (!strcasecmp(var, "error"))
- return CLEAN_COLOR_ERROR;
- return -1;
-}
+define_list_config_array(color_interactive_slots);
static int git_clean_config(const char *var, const char *value, void *cb)
{
@@ -111,7 +108,7 @@ static int git_clean_config(const char *var, const char *value, void *cb)
return 0;
}
if (skip_prefix(var, "color.interactive.", &slot_name)) {
- int slot = parse_clean_color_slot(slot_name);
+ int slot = LOOKUP_CONFIG(color_interactive_slots, slot_name);
if (slot < 0)
return 0;
if (!value)
@@ -166,7 +163,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
}
*dir_gone = 0;
- return 0;
+ goto out;
}
dir = opendir(path->buf);
@@ -174,11 +171,14 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
/* an empty dir could be removed even if it is unreadble */
res = dry_run ? 0 : rmdir(path->buf);
if (res) {
+ int saved_errno = errno;
quote_path_relative(path->buf, prefix, &quoted);
- warning(_(msg_warn_remove_failed), quoted.buf);
+ errno = saved_errno;
+ warning_errno(_(msg_warn_remove_failed), quoted.buf);
*dir_gone = 0;
}
- return res;
+ ret = res;
+ goto out;
}
strbuf_complete(path, '/');
@@ -208,8 +208,10 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
quote_path_relative(path->buf, prefix, &quoted);
string_list_append(&dels, quoted.buf);
} else {
+ int saved_errno = errno;
quote_path_relative(path->buf, prefix, &quoted);
- warning(_(msg_warn_remove_failed), quoted.buf);
+ errno = saved_errno;
+ warning_errno(_(msg_warn_remove_failed), quoted.buf);
*dir_gone = 0;
ret = 1;
}
@@ -230,8 +232,10 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
if (!res)
*dir_gone = 1;
else {
+ int saved_errno = errno;
quote_path_relative(path->buf, prefix, &quoted);
- warning(_(msg_warn_remove_failed), quoted.buf);
+ errno = saved_errno;
+ warning_errno(_(msg_warn_remove_failed), quoted.buf);
*dir_gone = 0;
ret = 1;
}
@@ -242,6 +246,8 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
for (i = 0; i < dels.nr; i++)
printf(dry_run ? _(msg_would_remove) : _(msg_remove), dels.items[i].string);
}
+out:
+ strbuf_release(&quoted);
string_list_clear(&dels, 0);
return ret;
}
@@ -287,11 +293,11 @@ static void pretty_print_menus(struct string_list *menu_list)
static void prompt_help_cmd(int singleton)
{
clean_print_color(CLEAN_COLOR_HELP);
- printf_ln(singleton ?
+ printf(singleton ?
_("Prompt help:\n"
"1 - select a numbered item\n"
"foo - select item based on unique prefix\n"
- " - (empty) select nothing") :
+ " - (empty) select nothing\n") :
_("Prompt help:\n"
"1 - select a single item\n"
"3-5 - select a range of items\n"
@@ -299,7 +305,7 @@ static void prompt_help_cmd(int singleton)
"foo - select item based on unique prefix\n"
"-... - unselect specified items\n"
"* - choose all items\n"
- " - (empty) finish selecting"));
+ " - (empty) finish selecting\n"));
clean_print_color(CLEAN_COLOR_RESET);
}
@@ -508,7 +514,7 @@ static int parse_choice(struct menu_stuff *menu_stuff,
if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top ||
(is_single && bottom != top)) {
clean_print_color(CLEAN_COLOR_ERROR);
- printf_ln(_("Huh (%s)?"), (*ptr)->buf);
+ printf(_("Huh (%s)?\n"), (*ptr)->buf);
clean_print_color(CLEAN_COLOR_RESET);
continue;
}
@@ -677,7 +683,7 @@ static int filter_by_patterns_cmd(void)
for_each_string_list_item(item, &del_list) {
int dtype = DT_UNKNOWN;
- if (is_excluded(&dir, item->string, &dtype)) {
+ if (is_excluded(&dir, &the_index, item->string, &dtype)) {
*item->string = '\0';
changed++;
}
@@ -774,7 +780,7 @@ static int ask_each_cmd(void)
static int quit_cmd(void)
{
string_list_clear(&del_list, 0);
- printf_ln(_("Bye."));
+ printf(_("Bye.\n"));
return MENU_RETURN_NO_LOOP;
}
@@ -831,8 +837,7 @@ static void interactive_main_loop(void)
int ret;
ret = menus[*chosen].fn();
if (ret != MENU_RETURN_NO_LOOP) {
- free(chosen);
- chosen = NULL;
+ FREE_AND_NULL(chosen);
if (!del_list.nr) {
clean_print_color(CLEAN_COLOR_ERROR);
printf_ln(_("No more files to clean, exiting."));
@@ -845,12 +850,43 @@ static void interactive_main_loop(void)
quit_cmd();
}
- free(chosen);
- chosen = NULL;
+ FREE_AND_NULL(chosen);
break;
}
}
+static void correct_untracked_entries(struct dir_struct *dir)
+{
+ int src, dst, ign;
+
+ for (src = dst = ign = 0; src < dir->nr; src++) {
+ /* skip paths in ignored[] that cannot be inside entries[src] */
+ while (ign < dir->ignored_nr &&
+ 0 <= cmp_dir_entry(&dir->entries[src], &dir->ignored[ign]))
+ ign++;
+
+ if (ign < dir->ignored_nr &&
+ check_dir_entry_contains(dir->entries[src], dir->ignored[ign])) {
+ /* entries[src] contains an ignored path, so we drop it */
+ free(dir->entries[src]);
+ } else {
+ struct dir_entry *ent = dir->entries[src++];
+
+ /* entries[src] does not contain an ignored path, so we keep it */
+ dir->entries[dst++] = ent;
+
+ /* then discard paths in entries[] contained inside entries[src] */
+ while (src < dir->nr &&
+ check_dir_entry_contains(ent, dir->entries[src]))
+ free(dir->entries[src++]);
+
+ /* compensate for the outer loop's loop control */
+ src--;
+ }
+ }
+ dir->nr = dst;
+}
+
int cmd_clean(int argc, const char **argv, const char *prefix)
{
int i, res;
@@ -868,7 +904,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT__QUIET(&quiet, N_("do not print names of files removed")),
OPT__DRY_RUN(&dry_run, N_("dry run")),
- OPT__FORCE(&force, N_("force")),
+ OPT__FORCE(&force, N_("force"), PARSE_OPT_NOCOMPLETE),
OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")),
OPT_BOOL('d', NULL, &remove_directories,
N_("remove whole directories")),
@@ -910,6 +946,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
+ if (remove_directories)
+ dir.flags |= DIR_SHOW_IGNORED_TOO | DIR_KEEP_UNTRACKED_CONTENTS;
+
if (read_cache() < 0)
die(_("index file corrupt"));
@@ -924,7 +963,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
PATHSPEC_PREFER_CWD,
prefix, argv);
- fill_directory(&dir, &pathspec);
+ fill_directory(&dir, &the_index, &pathspec);
+ correct_untracked_entries(&dir);
for (i = 0; i < dir.nr; i++) {
struct dir_entry *ent = dir.entries[i];
@@ -936,7 +976,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
continue;
if (pathspec.nr)
- matches = dir_path_match(ent, &pathspec, 0, NULL);
+ matches = dir_path_match(&the_index, ent, &pathspec, 0, NULL);
if (pathspec.nr && !matches)
continue;
@@ -952,6 +992,12 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
string_list_append(&del_list, rel);
}
+ for (i = 0; i < dir.nr; i++)
+ free(dir.entries[i]);
+
+ for (i = 0; i < dir.ignored_nr; i++)
+ free(dir.ignored[i]);
+
if (interactive && del_list.nr > 0)
interactive_main_loop();
@@ -981,8 +1027,10 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
} else {
res = dry_run ? 0 : unlink(abs_path.buf);
if (res) {
+ int saved_errno = errno;
qname = quote_path_relative(item->string, NULL, &buf);
- warning(_(msg_warn_remove_failed), qname);
+ errno = saved_errno;
+ warning_errno(_(msg_warn_remove_failed), qname);
errors++;
} else if (!quiet) {
qname = quote_path_relative(item->string, NULL, &buf);