2
* "git clean" builtin command
4
* Copyright (C) 2007 Shawn Bohrer
6
* Based on git-clean.sh by Pavel Roskin
14
#include "parse-options.h"
16
#include "read-cache-ll.h"
17
#include "repository.h"
19
#include "string-list.h"
27
static int require_force = -1; /* unset */
28
static int interactive;
29
static struct string_list del_list = STRING_LIST_INIT_DUP;
30
static unsigned int colopts;
32
static const char *const builtin_clean_usage[] = {
33
N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] [<pathspec>...]"),
37
static const char *msg_remove = N_("Removing %s\n");
38
static const char *msg_would_remove = N_("Would remove %s\n");
39
static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
40
static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
41
static const char *msg_warn_remove_failed = N_("failed to remove %s");
42
static const char *msg_warn_lstat_failed = N_("could not lstat %s\n");
43
static const char *msg_skip_cwd = N_("Refusing to remove current working directory\n");
44
static const char *msg_would_skip_cwd = N_("Would refuse to remove current working directory\n");
47
CLEAN_COLOR_RESET = 0,
48
CLEAN_COLOR_PLAIN = 1,
49
CLEAN_COLOR_PROMPT = 2,
50
CLEAN_COLOR_HEADER = 3,
55
static const char *color_interactive_slots[] = {
56
[CLEAN_COLOR_ERROR] = "error",
57
[CLEAN_COLOR_HEADER] = "header",
58
[CLEAN_COLOR_HELP] = "help",
59
[CLEAN_COLOR_PLAIN] = "plain",
60
[CLEAN_COLOR_PROMPT] = "prompt",
61
[CLEAN_COLOR_RESET] = "reset",
64
static int clean_use_color = -1;
65
static char clean_colors[][COLOR_MAXLEN] = {
66
[CLEAN_COLOR_ERROR] = GIT_COLOR_BOLD_RED,
67
[CLEAN_COLOR_HEADER] = GIT_COLOR_BOLD,
68
[CLEAN_COLOR_HELP] = GIT_COLOR_BOLD_RED,
69
[CLEAN_COLOR_PLAIN] = GIT_COLOR_NORMAL,
70
[CLEAN_COLOR_PROMPT] = GIT_COLOR_BOLD_BLUE,
71
[CLEAN_COLOR_RESET] = GIT_COLOR_RESET,
74
#define MENU_OPTS_SINGLETON 01
75
#define MENU_OPTS_IMMEDIATE 02
76
#define MENU_OPTS_LIST_ONLY 04
84
#define MENU_RETURN_NO_LOOP 10
94
MENU_STUFF_TYPE_STRING_LIST = 1,
95
MENU_STUFF_TYPE_MENU_ITEM
99
enum menu_stuff_type type;
104
define_list_config_array(color_interactive_slots);
106
static int git_clean_config(const char *var, const char *value,
107
const struct config_context *ctx, void *cb)
109
const char *slot_name;
111
if (starts_with(var, "column."))
112
return git_column_config(var, value, "clean", &colopts);
114
/* honors the color.interactive* config variables which also
115
applied in git-add--interactive and git-stash */
116
if (!strcmp(var, "color.interactive")) {
117
clean_use_color = git_config_colorbool(var, value);
120
if (skip_prefix(var, "color.interactive.", &slot_name)) {
121
int slot = LOOKUP_CONFIG(color_interactive_slots, slot_name);
125
return config_error_nonbool(var);
126
return color_parse(value, clean_colors[slot]);
129
if (!strcmp(var, "clean.requireforce")) {
130
require_force = git_config_bool(var, value);
134
if (git_color_config(var, value, cb) < 0)
137
return git_default_config(var, value, ctx, cb);
140
static const char *clean_get_color(enum color_clean ix)
142
if (want_color(clean_use_color))
143
return clean_colors[ix];
147
static void clean_print_color(enum color_clean ix)
149
printf("%s", clean_get_color(ix));
152
static int exclude_cb(const struct option *opt, const char *arg, int unset)
154
struct string_list *exclude_list = opt->value;
155
BUG_ON_OPT_NEG(unset);
156
string_list_append(exclude_list, arg);
160
static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
161
int dry_run, int quiet, int *dir_gone)
164
struct strbuf quoted = STRBUF_INIT;
165
struct strbuf realpath = STRBUF_INIT;
166
struct strbuf real_ocwd = STRBUF_INIT;
168
int res = 0, ret = 0, gone = 1, original_len = path->len, len;
169
struct string_list dels = STRING_LIST_INIT_DUP;
173
if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
174
is_nonbare_repository_dir(path)) {
176
quote_path(path->buf, prefix, "ed, 0);
177
printf(dry_run ? _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
185
dir = opendir(path->buf);
187
/* an empty dir could be removed even if it is unreadble */
188
res = dry_run ? 0 : rmdir(path->buf);
190
int saved_errno = errno;
191
quote_path(path->buf, prefix, "ed, 0);
193
warning_errno(_(msg_warn_remove_failed), quoted.buf);
200
strbuf_complete(path, '/');
203
while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL) {
206
strbuf_setlen(path, len);
207
strbuf_addstr(path, e->d_name);
208
if (lstat(path->buf, &st))
209
warning_errno(_(msg_warn_lstat_failed), path->buf);
210
else if (S_ISDIR(st.st_mode)) {
211
if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
214
quote_path(path->buf, prefix, "ed, 0);
215
string_list_append(&dels, quoted.buf);
220
res = dry_run ? 0 : unlink(path->buf);
222
quote_path(path->buf, prefix, "ed, 0);
223
string_list_append(&dels, quoted.buf);
225
int saved_errno = errno;
226
quote_path(path->buf, prefix, "ed, 0);
228
warning_errno(_(msg_warn_remove_failed), quoted.buf);
235
/* path too long, stat fails, or non-directory still exists */
242
strbuf_setlen(path, original_len);
246
* Normalize path components in path->buf, e.g. change '\' to
249
strbuf_realpath(&realpath, path->buf, 1);
252
* path and realpath are absolute; for comparison, we would
253
* like to transform startup_info->original_cwd to an absolute
256
if (startup_info->original_cwd)
257
strbuf_realpath(&real_ocwd,
258
startup_info->original_cwd, 1);
260
if (!strbuf_cmp(&realpath, &real_ocwd)) {
261
printf("%s", dry_run ? _(msg_would_skip_cwd) : _(msg_skip_cwd));
264
res = dry_run ? 0 : rmdir(path->buf);
268
int saved_errno = errno;
269
quote_path(path->buf, prefix, "ed, 0);
271
warning_errno(_(msg_warn_remove_failed), quoted.buf);
278
if (!*dir_gone && !quiet) {
280
for (i = 0; i < dels.nr; i++)
281
printf(dry_run ? _(msg_would_remove) : _(msg_remove), dels.items[i].string);
284
strbuf_release(&realpath);
285
strbuf_release(&real_ocwd);
286
strbuf_release("ed);
287
string_list_clear(&dels, 0);
291
static void pretty_print_dels(void)
293
struct string_list list = STRING_LIST_INIT_DUP;
294
struct string_list_item *item;
295
struct strbuf buf = STRBUF_INIT;
297
struct column_options copts;
299
for_each_string_list_item(item, &del_list) {
300
qname = quote_path(item->string, NULL, &buf, 0);
301
string_list_append(&list, qname);
305
* always enable column display, we only consult column.*
306
* about layout strategy and stuff
308
colopts = (colopts & ~COL_ENABLE_MASK) | COL_ENABLED;
309
memset(&copts, 0, sizeof(copts));
312
print_columns(&list, colopts, &copts);
313
strbuf_release(&buf);
314
string_list_clear(&list, 0);
317
static void pretty_print_menus(struct string_list *menu_list)
319
unsigned int local_colopts = 0;
320
struct column_options copts;
322
local_colopts = COL_ENABLED | COL_ROW;
323
memset(&copts, 0, sizeof(copts));
326
print_columns(menu_list, local_colopts, &copts);
329
static void prompt_help_cmd(int singleton)
331
clean_print_color(CLEAN_COLOR_HELP);
334
"1 - select a numbered item\n"
335
"foo - select item based on unique prefix\n"
336
" - (empty) select nothing\n") :
338
"1 - select a single item\n"
339
"3-5 - select a range of items\n"
340
"2-3,6-9 - select multiple ranges\n"
341
"foo - select item based on unique prefix\n"
342
"-... - unselect specified items\n"
343
"* - choose all items\n"
344
" - (empty) finish selecting\n"));
345
clean_print_color(CLEAN_COLOR_RESET);
349
* display menu stuff with number prefix and hotkey highlight
351
static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen)
353
struct string_list menu_list = STRING_LIST_INIT_DUP;
354
struct strbuf menu = STRBUF_INIT;
355
struct menu_item *menu_item;
356
struct string_list_item *string_list_item;
359
switch (stuff->type) {
361
die("Bad type of menu_stuff when print menu");
362
case MENU_STUFF_TYPE_MENU_ITEM:
363
menu_item = (struct menu_item *)stuff->stuff;
364
for (i = 0; i < stuff->nr; i++, menu_item++) {
368
p = menu_item->title;
369
if ((*chosen)[i] < 0)
370
(*chosen)[i] = menu_item->selected ? 1 : 0;
371
strbuf_addf(&menu, "%s%2d: ", (*chosen)[i] ? "*" : " ", i+1);
373
if (!highlighted && *p == menu_item->hotkey) {
374
strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_PROMPT));
375
strbuf_addch(&menu, *p);
376
strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_RESET));
379
strbuf_addch(&menu, *p);
382
string_list_append(&menu_list, menu.buf);
386
case MENU_STUFF_TYPE_STRING_LIST:
388
for_each_string_list_item(string_list_item, (struct string_list *)stuff->stuff) {
389
if ((*chosen)[i] < 0)
391
strbuf_addf(&menu, "%s%2d: %s",
392
(*chosen)[i] ? "*" : " ", i+1, string_list_item->string);
393
string_list_append(&menu_list, menu.buf);
400
pretty_print_menus(&menu_list);
402
strbuf_release(&menu);
403
string_list_clear(&menu_list, 0);
406
static int find_unique(const char *choice, struct menu_stuff *menu_stuff)
408
struct menu_item *menu_item;
409
struct string_list_item *string_list_item;
410
int i, len, found = 0;
412
len = strlen(choice);
413
switch (menu_stuff->type) {
415
die("Bad type of menu_stuff when parse choice");
416
case MENU_STUFF_TYPE_MENU_ITEM:
418
menu_item = (struct menu_item *)menu_stuff->stuff;
419
for (i = 0; i < menu_stuff->nr; i++, menu_item++) {
420
if (len == 1 && *choice == menu_item->hotkey) {
424
if (!strncasecmp(choice, menu_item->title, len)) {
427
/* continue for hotkey matching */
439
case MENU_STUFF_TYPE_STRING_LIST:
440
string_list_item = ((struct string_list *)menu_stuff->stuff)->items;
441
for (i = 0; i < menu_stuff->nr; i++, string_list_item++) {
442
if (!strncasecmp(choice, string_list_item->string, len)) {
456
* Parse user input, and return choice(s) for menu (menu_stuff).
459
* (for single choice)
460
* 1 - select a numbered item
461
* foo - select item based on menu title
462
* - (empty) select nothing
464
* (for multiple choice)
465
* 1 - select a single item
466
* 3-5 - select a range of items
467
* 2-3,6-9 - select multiple ranges
468
* foo - select item based on menu title
469
* -... - unselect specified items
470
* * - choose all items
471
* - (empty) finish selecting
473
* The parse result will be saved in array **chosen, and
474
* return number of total selections.
476
static int parse_choice(struct menu_stuff *menu_stuff,
481
struct strbuf **choice_list, **ptr;
486
choice_list = strbuf_split_max(&input, '\n', 0);
493
choice_list = strbuf_split_max(&input, ' ', 0);
496
for (ptr = choice_list; *ptr; ptr++) {
499
int bottom = 0, top = 0;
500
int is_range, is_number;
506
/* Input that begins with '-'; unchoose */
507
if (*(*ptr)->buf == '-') {
509
strbuf_remove((*ptr), 0, 1);
514
for (p = (*ptr)->buf; *p; p++) {
524
} else if (!isdigit(*p)) {
532
bottom = atoi((*ptr)->buf);
534
} else if (is_range) {
535
bottom = atoi((*ptr)->buf);
536
/* a range can be specified like 5-7 or 5- */
537
if (!*(strchr((*ptr)->buf, '-') + 1))
538
top = menu_stuff->nr;
540
top = atoi(strchr((*ptr)->buf, '-') + 1);
541
} else if (!strcmp((*ptr)->buf, "*")) {
543
top = menu_stuff->nr;
545
bottom = find_unique((*ptr)->buf, menu_stuff);
549
if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top ||
550
(is_single && bottom != top)) {
551
clean_print_color(CLEAN_COLOR_ERROR);
552
printf(_("Huh (%s)?\n"), (*ptr)->buf);
553
clean_print_color(CLEAN_COLOR_RESET);
557
for (i = bottom; i <= top; i++)
558
(*chosen)[i-1] = choose;
561
strbuf_list_free(choice_list);
563
for (i = 0; i < menu_stuff->nr; i++)
569
* Implement a git-add-interactive compatible UI, which is borrowed
570
* from add-interactive.c.
574
* - Return an array of integers
575
* - , and it is up to you to free the allocated memory.
576
* - The array ends with EOF.
577
* - If user pressed CTRL-D (i.e. EOF), no selection returned.
579
static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
581
struct strbuf choice = STRBUF_INIT;
582
int *chosen, *result;
587
ALLOC_ARRAY(chosen, stuff->nr);
588
/* set chosen as uninitialized */
589
for (i = 0; i < stuff->nr; i++)
595
clean_get_color(CLEAN_COLOR_HEADER),
597
clean_get_color(CLEAN_COLOR_RESET));
600
/* chosen will be initialized by print_highlight_menu_stuff */
601
print_highlight_menu_stuff(stuff, &chosen);
603
if (opts->flags & MENU_OPTS_LIST_ONLY)
608
clean_get_color(CLEAN_COLOR_PROMPT),
610
opts->flags & MENU_OPTS_SINGLETON ? "> " : ">> ",
611
clean_get_color(CLEAN_COLOR_RESET));
614
if (git_read_line_interactively(&choice) == EOF) {
619
/* help for prompt */
620
if (!strcmp(choice.buf, "?")) {
621
prompt_help_cmd(opts->flags & MENU_OPTS_SINGLETON);
625
/* for a multiple-choice menu, press ENTER (empty) will return back */
626
if (!(opts->flags & MENU_OPTS_SINGLETON) && !choice.len)
629
nr = parse_choice(stuff,
630
opts->flags & MENU_OPTS_SINGLETON,
634
if (opts->flags & MENU_OPTS_SINGLETON) {
637
} else if (opts->flags & MENU_OPTS_IMMEDIATE) {
643
result = xmalloc(sizeof(int));
649
* recalculate nr, if return back from menu directly with
650
* default selections.
653
for (i = 0; i < stuff->nr; i++)
657
CALLOC_ARRAY(result, st_add(nr, 1));
658
for (i = 0; i < stuff->nr && j < nr; i++) {
666
strbuf_release(&choice);
670
static int clean_cmd(void)
672
return MENU_RETURN_NO_LOOP;
675
static int filter_by_patterns_cmd(void)
677
struct dir_struct dir = DIR_INIT;
678
struct strbuf confirm = STRBUF_INIT;
679
struct strbuf **ignore_list;
680
struct string_list_item *item;
681
struct pattern_list *pl;
691
clean_print_color(CLEAN_COLOR_PROMPT);
692
printf(_("Input ignore patterns>> "));
693
clean_print_color(CLEAN_COLOR_RESET);
694
if (git_read_line_interactively(&confirm) == EOF)
697
/* quit filter_by_pattern mode if press ENTER or Ctrl-D */
701
pl = add_pattern_list(&dir, EXC_CMDL, "manual exclude");
702
ignore_list = strbuf_split_max(&confirm, ' ', 0);
704
for (i = 0; ignore_list[i]; i++) {
705
strbuf_trim(ignore_list[i]);
706
if (!ignore_list[i]->len)
709
add_pattern(ignore_list[i]->buf, "", 0, pl, -(i+1));
713
for_each_string_list_item(item, &del_list) {
714
int dtype = DT_UNKNOWN;
716
if (is_excluded(&dir, the_repository->index, item->string, &dtype)) {
717
*item->string = '\0';
723
string_list_remove_empty_items(&del_list, 0);
725
clean_print_color(CLEAN_COLOR_ERROR);
726
printf_ln(_("WARNING: Cannot find items matched by: %s"), confirm.buf);
727
clean_print_color(CLEAN_COLOR_RESET);
730
strbuf_list_free(ignore_list);
734
strbuf_release(&confirm);
738
static int select_by_numbers_cmd(void)
740
struct menu_opts menu_opts;
741
struct menu_stuff menu_stuff;
742
struct string_list_item *items;
746
menu_opts.header = NULL;
747
menu_opts.prompt = N_("Select items to delete");
750
menu_stuff.type = MENU_STUFF_TYPE_STRING_LIST;
751
menu_stuff.stuff = &del_list;
752
menu_stuff.nr = del_list.nr;
754
chosen = list_and_choose(&menu_opts, &menu_stuff);
755
items = del_list.items;
756
for (i = 0, j = 0; i < del_list.nr; i++) {
758
*(items[i].string) = '\0';
759
} else if (i == chosen[j]) {
760
/* delete selected item */
764
/* end of chosen (chosen[j] == EOF), won't delete */
765
*(items[i].string) = '\0';
769
string_list_remove_empty_items(&del_list, 0);
775
static int ask_each_cmd(void)
777
struct strbuf confirm = STRBUF_INIT;
778
struct strbuf buf = STRBUF_INIT;
779
struct string_list_item *item;
781
int changed = 0, eof = 0;
783
for_each_string_list_item(item, &del_list) {
784
/* Ctrl-D should stop removing files */
786
qname = quote_path(item->string, NULL, &buf, 0);
787
/* TRANSLATORS: Make sure to keep [y/N] as is */
788
printf(_("Remove %s [y/N]? "), qname);
789
if (git_read_line_interactively(&confirm) == EOF) {
794
if (!confirm.len || strncasecmp(confirm.buf, "yes", confirm.len)) {
795
*item->string = '\0';
801
string_list_remove_empty_items(&del_list, 0);
803
strbuf_release(&buf);
804
strbuf_release(&confirm);
805
return MENU_RETURN_NO_LOOP;
808
static int quit_cmd(void)
810
string_list_clear(&del_list, 0);
812
return MENU_RETURN_NO_LOOP;
815
static int help_cmd(void)
817
clean_print_color(CLEAN_COLOR_HELP);
819
"clean - start cleaning\n"
820
"filter by pattern - exclude items from deletion\n"
821
"select by numbers - select items to be deleted by numbers\n"
822
"ask each - confirm each deletion (like \"rm -i\")\n"
823
"quit - stop cleaning\n"
824
"help - this screen\n"
825
"? - help for prompt selection"
827
clean_print_color(CLEAN_COLOR_RESET);
831
static void interactive_main_loop(void)
833
while (del_list.nr) {
834
struct menu_opts menu_opts;
835
struct menu_stuff menu_stuff;
836
struct menu_item menus[] = {
837
{'c', "clean", 0, clean_cmd},
838
{'f', "filter by pattern", 0, filter_by_patterns_cmd},
839
{'s', "select by numbers", 0, select_by_numbers_cmd},
840
{'a', "ask each", 0, ask_each_cmd},
841
{'q', "quit", 0, quit_cmd},
842
{'h', "help", 0, help_cmd},
846
menu_opts.header = N_("*** Commands ***");
847
menu_opts.prompt = N_("What now");
848
menu_opts.flags = MENU_OPTS_SINGLETON;
850
menu_stuff.type = MENU_STUFF_TYPE_MENU_ITEM;
851
menu_stuff.stuff = menus;
852
menu_stuff.nr = sizeof(menus) / sizeof(struct menu_item);
854
clean_print_color(CLEAN_COLOR_HEADER);
855
printf_ln(Q_("Would remove the following item:",
856
"Would remove the following items:",
858
clean_print_color(CLEAN_COLOR_RESET);
862
chosen = list_and_choose(&menu_opts, &menu_stuff);
864
if (*chosen != EOF) {
866
ret = menus[*chosen].fn();
867
if (ret != MENU_RETURN_NO_LOOP) {
868
FREE_AND_NULL(chosen);
870
clean_print_color(CLEAN_COLOR_ERROR);
871
printf_ln(_("No more files to clean, exiting."));
872
clean_print_color(CLEAN_COLOR_RESET);
881
FREE_AND_NULL(chosen);
886
static void correct_untracked_entries(struct dir_struct *dir)
890
for (src = dst = ign = 0; src < dir->nr; src++) {
891
/* skip paths in ignored[] that cannot be inside entries[src] */
892
while (ign < dir->ignored_nr &&
893
0 <= cmp_dir_entry(&dir->entries[src], &dir->ignored[ign]))
896
if (ign < dir->ignored_nr &&
897
check_dir_entry_contains(dir->entries[src], dir->ignored[ign])) {
898
/* entries[src] contains an ignored path, so we drop it */
899
free(dir->entries[src]);
901
struct dir_entry *ent = dir->entries[src++];
903
/* entries[src] does not contain an ignored path, so we keep it */
904
dir->entries[dst++] = ent;
906
/* then discard paths in entries[] contained inside entries[src] */
907
while (src < dir->nr &&
908
check_dir_entry_contains(ent, dir->entries[src]))
909
free(dir->entries[src++]);
911
/* compensate for the outer loop's loop control */
918
int cmd_clean(int argc, const char **argv, const char *prefix)
921
int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
922
int ignored_only = 0, force = 0, errors = 0, gone = 1;
923
int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
924
struct strbuf abs_path = STRBUF_INIT;
925
struct dir_struct dir = DIR_INIT;
926
struct pathspec pathspec;
927
struct strbuf buf = STRBUF_INIT;
928
struct string_list exclude_list = STRING_LIST_INIT_NODUP;
929
struct pattern_list *pl;
930
struct string_list_item *item;
932
struct option options[] = {
933
OPT__QUIET(&quiet, N_("do not print names of files removed")),
934
OPT__DRY_RUN(&dry_run, N_("dry run")),
935
OPT__FORCE(&force, N_("force"), PARSE_OPT_NOCOMPLETE),
936
OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")),
937
OPT_BOOL('d', NULL, &remove_directories,
938
N_("remove whole directories")),
939
OPT_CALLBACK_F('e', "exclude", &exclude_list, N_("pattern"),
940
N_("add <pattern> to ignore rules"), PARSE_OPT_NONEG, exclude_cb),
941
OPT_BOOL('x', NULL, &ignored, N_("remove ignored files, too")),
942
OPT_BOOL('X', NULL, &ignored_only,
943
N_("remove only ignored files")),
947
git_config(git_clean_config, NULL);
949
argc = parse_options(argc, argv, prefix, options, builtin_clean_usage,
952
if (require_force != 0 && !force && !interactive && !dry_run)
953
die(_("clean.requireForce is true and -f not given: refusing to clean"));
958
dir.flags |= DIR_SKIP_NESTED_GIT;
960
dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
962
if (ignored && ignored_only)
963
die(_("options '%s' and '%s' cannot be used together"), "-x", "-X");
965
setup_standard_excludes(&dir);
967
dir.flags |= DIR_SHOW_IGNORED;
971
* Remaining args implies pathspecs specified, and we should
972
* recurse within those.
974
remove_directories = 1;
977
if (remove_directories && !ignored_only) {
979
* We need to know about ignored files too:
981
* If (ignored), then we will delete ignored files as well.
983
* If (!ignored), then even though we not are doing
984
* anything with ignored files, we need to know about them
985
* so that we can avoid deleting a directory of untracked
986
* files that also contains an ignored file within it.
988
* For the (!ignored) case, since we only need to avoid
989
* deleting ignored files, we can set
990
* DIR_SHOW_IGNORED_TOO_MODE_MATCHING in order to avoid
991
* recursing into a directory which is itself ignored.
993
dir.flags |= DIR_SHOW_IGNORED_TOO;
995
dir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;
998
* Let the fill_directory() machinery know that we aren't
999
* just recursing to collect the ignored files; we want all
1000
* the untracked ones so that we can delete them. (Note:
1001
* we could also set DIR_KEEP_UNTRACKED_CONTENTS when
1002
* ignored_only is true, since DIR_KEEP_UNTRACKED_CONTENTS
1003
* only has effect in combination with DIR_SHOW_IGNORED_TOO. It makes
1004
* the code clearer to exclude it, though.
1006
dir.flags |= DIR_KEEP_UNTRACKED_CONTENTS;
1009
prepare_repo_settings(the_repository);
1010
the_repository->settings.command_requires_full_index = 0;
1012
if (repo_read_index(the_repository) < 0)
1013
die(_("index file corrupt"));
1015
pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option");
1016
for (i = 0; i < exclude_list.nr; i++)
1017
add_pattern(exclude_list.items[i].string, "", 0, pl, -(i+1));
1019
parse_pathspec(&pathspec, 0,
1020
PATHSPEC_PREFER_CWD,
1023
fill_directory(&dir, the_repository->index, &pathspec);
1024
correct_untracked_entries(&dir);
1026
for (i = 0; i < dir.nr; i++) {
1027
struct dir_entry *ent = dir.entries[i];
1031
if (!index_name_is_other(the_repository->index, ent->name, ent->len))
1034
if (lstat(ent->name, &st))
1035
die_errno("Cannot lstat '%s'", ent->name);
1037
if (S_ISDIR(st.st_mode) && !remove_directories)
1040
rel = relative_path(ent->name, prefix, &buf);
1041
string_list_append(&del_list, rel);
1046
if (interactive && del_list.nr > 0)
1047
interactive_main_loop();
1049
for_each_string_list_item(item, &del_list) {
1052
strbuf_reset(&abs_path);
1054
strbuf_addstr(&abs_path, prefix);
1056
strbuf_addstr(&abs_path, item->string);
1059
* we might have removed this as part of earlier
1060
* recursive directory removal, so lstat() here could
1063
if (lstat(abs_path.buf, &st))
1066
if (S_ISDIR(st.st_mode)) {
1067
if (remove_dirs(&abs_path, prefix, rm_flags, dry_run, quiet, &gone))
1069
if (gone && !quiet) {
1070
qname = quote_path(item->string, NULL, &buf, 0);
1071
printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
1074
res = dry_run ? 0 : unlink(abs_path.buf);
1076
int saved_errno = errno;
1077
qname = quote_path(item->string, NULL, &buf, 0);
1078
errno = saved_errno;
1079
warning_errno(_(msg_warn_remove_failed), qname);
1081
} else if (!quiet) {
1082
qname = quote_path(item->string, NULL, &buf, 0);
1083
printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
1088
strbuf_release(&abs_path);
1089
strbuf_release(&buf);
1090
string_list_clear(&del_list, 0);
1091
string_list_clear(&exclude_list, 0);
1092
clear_pathspec(&pathspec);
1093
return (errors != 0);