Merge branch 'nd/checkout-m'
[git] / builtin / clean.c
1 /*
2  * "git clean" builtin command
3  *
4  * Copyright (C) 2007 Shawn Bohrer
5  *
6  * Based on git-clean.sh by Pavel Roskin
7  */
8
9 #define USE_THE_INDEX_COMPATIBILITY_MACROS
10 #include "builtin.h"
11 #include "cache.h"
12 #include "config.h"
13 #include "dir.h"
14 #include "parse-options.h"
15 #include "string-list.h"
16 #include "quote.h"
17 #include "column.h"
18 #include "color.h"
19 #include "pathspec.h"
20 #include "help.h"
21
22 static int force = -1; /* unset */
23 static int interactive;
24 static struct string_list del_list = STRING_LIST_INIT_DUP;
25 static unsigned int colopts;
26
27 static const char *const builtin_clean_usage[] = {
28         N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
29         NULL
30 };
31
32 static const char *msg_remove = N_("Removing %s\n");
33 static const char *msg_would_remove = N_("Would remove %s\n");
34 static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
35 static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
36 static const char *msg_warn_remove_failed = N_("failed to remove %s");
37
38 enum color_clean {
39         CLEAN_COLOR_RESET = 0,
40         CLEAN_COLOR_PLAIN = 1,
41         CLEAN_COLOR_PROMPT = 2,
42         CLEAN_COLOR_HEADER = 3,
43         CLEAN_COLOR_HELP = 4,
44         CLEAN_COLOR_ERROR = 5
45 };
46
47 static const char *color_interactive_slots[] = {
48         [CLEAN_COLOR_ERROR]  = "error",
49         [CLEAN_COLOR_HEADER] = "header",
50         [CLEAN_COLOR_HELP]   = "help",
51         [CLEAN_COLOR_PLAIN]  = "plain",
52         [CLEAN_COLOR_PROMPT] = "prompt",
53         [CLEAN_COLOR_RESET]  = "reset",
54 };
55
56 static int clean_use_color = -1;
57 static char clean_colors[][COLOR_MAXLEN] = {
58         [CLEAN_COLOR_ERROR] = GIT_COLOR_BOLD_RED,
59         [CLEAN_COLOR_HEADER] = GIT_COLOR_BOLD,
60         [CLEAN_COLOR_HELP] = GIT_COLOR_BOLD_RED,
61         [CLEAN_COLOR_PLAIN] = GIT_COLOR_NORMAL,
62         [CLEAN_COLOR_PROMPT] = GIT_COLOR_BOLD_BLUE,
63         [CLEAN_COLOR_RESET] = GIT_COLOR_RESET,
64 };
65
66 #define MENU_OPTS_SINGLETON             01
67 #define MENU_OPTS_IMMEDIATE             02
68 #define MENU_OPTS_LIST_ONLY             04
69
70 struct menu_opts {
71         const char *header;
72         const char *prompt;
73         int flags;
74 };
75
76 #define MENU_RETURN_NO_LOOP             10
77
78 struct menu_item {
79         char hotkey;
80         const char *title;
81         int selected;
82         int (*fn)(void);
83 };
84
85 enum menu_stuff_type {
86         MENU_STUFF_TYPE_STRING_LIST = 1,
87         MENU_STUFF_TYPE_MENU_ITEM
88 };
89
90 struct menu_stuff {
91         enum menu_stuff_type type;
92         int nr;
93         void *stuff;
94 };
95
96 define_list_config_array(color_interactive_slots);
97
98 static int git_clean_config(const char *var, const char *value, void *cb)
99 {
100         const char *slot_name;
101
102         if (starts_with(var, "column."))
103                 return git_column_config(var, value, "clean", &colopts);
104
105         /* honors the color.interactive* config variables which also
106            applied in git-add--interactive and git-stash */
107         if (!strcmp(var, "color.interactive")) {
108                 clean_use_color = git_config_colorbool(var, value);
109                 return 0;
110         }
111         if (skip_prefix(var, "color.interactive.", &slot_name)) {
112                 int slot = LOOKUP_CONFIG(color_interactive_slots, slot_name);
113                 if (slot < 0)
114                         return 0;
115                 if (!value)
116                         return config_error_nonbool(var);
117                 return color_parse(value, clean_colors[slot]);
118         }
119
120         if (!strcmp(var, "clean.requireforce")) {
121                 force = !git_config_bool(var, value);
122                 return 0;
123         }
124
125         /* inspect the color.ui config variable and others */
126         return git_color_default_config(var, value, cb);
127 }
128
129 static const char *clean_get_color(enum color_clean ix)
130 {
131         if (want_color(clean_use_color))
132                 return clean_colors[ix];
133         return "";
134 }
135
136 static void clean_print_color(enum color_clean ix)
137 {
138         printf("%s", clean_get_color(ix));
139 }
140
141 static int exclude_cb(const struct option *opt, const char *arg, int unset)
142 {
143         struct string_list *exclude_list = opt->value;
144         BUG_ON_OPT_NEG(unset);
145         string_list_append(exclude_list, arg);
146         return 0;
147 }
148
149 static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
150                 int dry_run, int quiet, int *dir_gone)
151 {
152         DIR *dir;
153         struct strbuf quoted = STRBUF_INIT;
154         struct dirent *e;
155         int res = 0, ret = 0, gone = 1, original_len = path->len, len;
156         struct string_list dels = STRING_LIST_INIT_DUP;
157
158         *dir_gone = 1;
159
160         if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && is_nonbare_repository_dir(path)) {
161                 if (!quiet) {
162                         quote_path_relative(path->buf, prefix, &quoted);
163                         printf(dry_run ?  _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
164                                         quoted.buf);
165                 }
166
167                 *dir_gone = 0;
168                 goto out;
169         }
170
171         dir = opendir(path->buf);
172         if (!dir) {
173                 /* an empty dir could be removed even if it is unreadble */
174                 res = dry_run ? 0 : rmdir(path->buf);
175                 if (res) {
176                         int saved_errno = errno;
177                         quote_path_relative(path->buf, prefix, &quoted);
178                         errno = saved_errno;
179                         warning_errno(_(msg_warn_remove_failed), quoted.buf);
180                         *dir_gone = 0;
181                 }
182                 ret = res;
183                 goto out;
184         }
185
186         strbuf_complete(path, '/');
187
188         len = path->len;
189         while ((e = readdir(dir)) != NULL) {
190                 struct stat st;
191                 if (is_dot_or_dotdot(e->d_name))
192                         continue;
193
194                 strbuf_setlen(path, len);
195                 strbuf_addstr(path, e->d_name);
196                 if (lstat(path->buf, &st))
197                         ; /* fall thru */
198                 else if (S_ISDIR(st.st_mode)) {
199                         if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
200                                 ret = 1;
201                         if (gone) {
202                                 quote_path_relative(path->buf, prefix, &quoted);
203                                 string_list_append(&dels, quoted.buf);
204                         } else
205                                 *dir_gone = 0;
206                         continue;
207                 } else {
208                         res = dry_run ? 0 : unlink(path->buf);
209                         if (!res) {
210                                 quote_path_relative(path->buf, prefix, &quoted);
211                                 string_list_append(&dels, quoted.buf);
212                         } else {
213                                 int saved_errno = errno;
214                                 quote_path_relative(path->buf, prefix, &quoted);
215                                 errno = saved_errno;
216                                 warning_errno(_(msg_warn_remove_failed), quoted.buf);
217                                 *dir_gone = 0;
218                                 ret = 1;
219                         }
220                         continue;
221                 }
222
223                 /* path too long, stat fails, or non-directory still exists */
224                 *dir_gone = 0;
225                 ret = 1;
226                 break;
227         }
228         closedir(dir);
229
230         strbuf_setlen(path, original_len);
231
232         if (*dir_gone) {
233                 res = dry_run ? 0 : rmdir(path->buf);
234                 if (!res)
235                         *dir_gone = 1;
236                 else {
237                         int saved_errno = errno;
238                         quote_path_relative(path->buf, prefix, &quoted);
239                         errno = saved_errno;
240                         warning_errno(_(msg_warn_remove_failed), quoted.buf);
241                         *dir_gone = 0;
242                         ret = 1;
243                 }
244         }
245
246         if (!*dir_gone && !quiet) {
247                 int i;
248                 for (i = 0; i < dels.nr; i++)
249                         printf(dry_run ?  _(msg_would_remove) : _(msg_remove), dels.items[i].string);
250         }
251 out:
252         strbuf_release(&quoted);
253         string_list_clear(&dels, 0);
254         return ret;
255 }
256
257 static void pretty_print_dels(void)
258 {
259         struct string_list list = STRING_LIST_INIT_DUP;
260         struct string_list_item *item;
261         struct strbuf buf = STRBUF_INIT;
262         const char *qname;
263         struct column_options copts;
264
265         for_each_string_list_item(item, &del_list) {
266                 qname = quote_path_relative(item->string, NULL, &buf);
267                 string_list_append(&list, qname);
268         }
269
270         /*
271          * always enable column display, we only consult column.*
272          * about layout strategy and stuff
273          */
274         colopts = (colopts & ~COL_ENABLE_MASK) | COL_ENABLED;
275         memset(&copts, 0, sizeof(copts));
276         copts.indent = "  ";
277         copts.padding = 2;
278         print_columns(&list, colopts, &copts);
279         strbuf_release(&buf);
280         string_list_clear(&list, 0);
281 }
282
283 static void pretty_print_menus(struct string_list *menu_list)
284 {
285         unsigned int local_colopts = 0;
286         struct column_options copts;
287
288         local_colopts = COL_ENABLED | COL_ROW;
289         memset(&copts, 0, sizeof(copts));
290         copts.indent = "  ";
291         copts.padding = 2;
292         print_columns(menu_list, local_colopts, &copts);
293 }
294
295 static void prompt_help_cmd(int singleton)
296 {
297         clean_print_color(CLEAN_COLOR_HELP);
298         printf(singleton ?
299                   _("Prompt help:\n"
300                     "1          - select a numbered item\n"
301                     "foo        - select item based on unique prefix\n"
302                     "           - (empty) select nothing\n") :
303                   _("Prompt help:\n"
304                     "1          - select a single item\n"
305                     "3-5        - select a range of items\n"
306                     "2-3,6-9    - select multiple ranges\n"
307                     "foo        - select item based on unique prefix\n"
308                     "-...       - unselect specified items\n"
309                     "*          - choose all items\n"
310                     "           - (empty) finish selecting\n"));
311         clean_print_color(CLEAN_COLOR_RESET);
312 }
313
314 /*
315  * display menu stuff with number prefix and hotkey highlight
316  */
317 static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen)
318 {
319         struct string_list menu_list = STRING_LIST_INIT_DUP;
320         struct strbuf menu = STRBUF_INIT;
321         struct menu_item *menu_item;
322         struct string_list_item *string_list_item;
323         int i;
324
325         switch (stuff->type) {
326         default:
327                 die("Bad type of menu_stuff when print menu");
328         case MENU_STUFF_TYPE_MENU_ITEM:
329                 menu_item = (struct menu_item *)stuff->stuff;
330                 for (i = 0; i < stuff->nr; i++, menu_item++) {
331                         const char *p;
332                         int highlighted = 0;
333
334                         p = menu_item->title;
335                         if ((*chosen)[i] < 0)
336                                 (*chosen)[i] = menu_item->selected ? 1 : 0;
337                         strbuf_addf(&menu, "%s%2d: ", (*chosen)[i] ? "*" : " ", i+1);
338                         for (; *p; p++) {
339                                 if (!highlighted && *p == menu_item->hotkey) {
340                                         strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_PROMPT));
341                                         strbuf_addch(&menu, *p);
342                                         strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_RESET));
343                                         highlighted = 1;
344                                 } else {
345                                         strbuf_addch(&menu, *p);
346                                 }
347                         }
348                         string_list_append(&menu_list, menu.buf);
349                         strbuf_reset(&menu);
350                 }
351                 break;
352         case MENU_STUFF_TYPE_STRING_LIST:
353                 i = 0;
354                 for_each_string_list_item(string_list_item, (struct string_list *)stuff->stuff) {
355                         if ((*chosen)[i] < 0)
356                                 (*chosen)[i] = 0;
357                         strbuf_addf(&menu, "%s%2d: %s",
358                                     (*chosen)[i] ? "*" : " ", i+1, string_list_item->string);
359                         string_list_append(&menu_list, menu.buf);
360                         strbuf_reset(&menu);
361                         i++;
362                 }
363                 break;
364         }
365
366         pretty_print_menus(&menu_list);
367
368         strbuf_release(&menu);
369         string_list_clear(&menu_list, 0);
370 }
371
372 static int find_unique(const char *choice, struct menu_stuff *menu_stuff)
373 {
374         struct menu_item *menu_item;
375         struct string_list_item *string_list_item;
376         int i, len, found = 0;
377
378         len = strlen(choice);
379         switch (menu_stuff->type) {
380         default:
381                 die("Bad type of menu_stuff when parse choice");
382         case MENU_STUFF_TYPE_MENU_ITEM:
383
384                 menu_item = (struct menu_item *)menu_stuff->stuff;
385                 for (i = 0; i < menu_stuff->nr; i++, menu_item++) {
386                         if (len == 1 && *choice == menu_item->hotkey) {
387                                 found = i + 1;
388                                 break;
389                         }
390                         if (!strncasecmp(choice, menu_item->title, len)) {
391                                 if (found) {
392                                         if (len == 1) {
393                                                 /* continue for hotkey matching */
394                                                 found = -1;
395                                         } else {
396                                                 found = 0;
397                                                 break;
398                                         }
399                                 } else {
400                                         found = i + 1;
401                                 }
402                         }
403                 }
404                 break;
405         case MENU_STUFF_TYPE_STRING_LIST:
406                 string_list_item = ((struct string_list *)menu_stuff->stuff)->items;
407                 for (i = 0; i < menu_stuff->nr; i++, string_list_item++) {
408                         if (!strncasecmp(choice, string_list_item->string, len)) {
409                                 if (found) {
410                                         found = 0;
411                                         break;
412                                 }
413                                 found = i + 1;
414                         }
415                 }
416                 break;
417         }
418         return found;
419 }
420
421
422 /*
423  * Parse user input, and return choice(s) for menu (menu_stuff).
424  *
425  * Input
426  *     (for single choice)
427  *         1          - select a numbered item
428  *         foo        - select item based on menu title
429  *                    - (empty) select nothing
430  *
431  *     (for multiple choice)
432  *         1          - select a single item
433  *         3-5        - select a range of items
434  *         2-3,6-9    - select multiple ranges
435  *         foo        - select item based on menu title
436  *         -...       - unselect specified items
437  *         *          - choose all items
438  *                    - (empty) finish selecting
439  *
440  * The parse result will be saved in array **chosen, and
441  * return number of total selections.
442  */
443 static int parse_choice(struct menu_stuff *menu_stuff,
444                         int is_single,
445                         struct strbuf input,
446                         int **chosen)
447 {
448         struct strbuf **choice_list, **ptr;
449         int nr = 0;
450         int i;
451
452         if (is_single) {
453                 choice_list = strbuf_split_max(&input, '\n', 0);
454         } else {
455                 char *p = input.buf;
456                 do {
457                         if (*p == ',')
458                                 *p = ' ';
459                 } while (*p++);
460                 choice_list = strbuf_split_max(&input, ' ', 0);
461         }
462
463         for (ptr = choice_list; *ptr; ptr++) {
464                 char *p;
465                 int choose = 1;
466                 int bottom = 0, top = 0;
467                 int is_range, is_number;
468
469                 strbuf_trim(*ptr);
470                 if (!(*ptr)->len)
471                         continue;
472
473                 /* Input that begins with '-'; unchoose */
474                 if (*(*ptr)->buf == '-') {
475                         choose = 0;
476                         strbuf_remove((*ptr), 0, 1);
477                 }
478
479                 is_range = 0;
480                 is_number = 1;
481                 for (p = (*ptr)->buf; *p; p++) {
482                         if ('-' == *p) {
483                                 if (!is_range) {
484                                         is_range = 1;
485                                         is_number = 0;
486                                 } else {
487                                         is_number = 0;
488                                         is_range = 0;
489                                         break;
490                                 }
491                         } else if (!isdigit(*p)) {
492                                 is_number = 0;
493                                 is_range = 0;
494                                 break;
495                         }
496                 }
497
498                 if (is_number) {
499                         bottom = atoi((*ptr)->buf);
500                         top = bottom;
501                 } else if (is_range) {
502                         bottom = atoi((*ptr)->buf);
503                         /* a range can be specified like 5-7 or 5- */
504                         if (!*(strchr((*ptr)->buf, '-') + 1))
505                                 top = menu_stuff->nr;
506                         else
507                                 top = atoi(strchr((*ptr)->buf, '-') + 1);
508                 } else if (!strcmp((*ptr)->buf, "*")) {
509                         bottom = 1;
510                         top = menu_stuff->nr;
511                 } else {
512                         bottom = find_unique((*ptr)->buf, menu_stuff);
513                         top = bottom;
514                 }
515
516                 if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top ||
517                     (is_single && bottom != top)) {
518                         clean_print_color(CLEAN_COLOR_ERROR);
519                         printf(_("Huh (%s)?\n"), (*ptr)->buf);
520                         clean_print_color(CLEAN_COLOR_RESET);
521                         continue;
522                 }
523
524                 for (i = bottom; i <= top; i++)
525                         (*chosen)[i-1] = choose;
526         }
527
528         strbuf_list_free(choice_list);
529
530         for (i = 0; i < menu_stuff->nr; i++)
531                 nr += (*chosen)[i];
532         return nr;
533 }
534
535 /*
536  * Implement a git-add-interactive compatible UI, which is borrowed
537  * from git-add--interactive.perl.
538  *
539  * Return value:
540  *
541  *   - Return an array of integers
542  *   - , and it is up to you to free the allocated memory.
543  *   - The array ends with EOF.
544  *   - If user pressed CTRL-D (i.e. EOF), no selection returned.
545  */
546 static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
547 {
548         struct strbuf choice = STRBUF_INIT;
549         int *chosen, *result;
550         int nr = 0;
551         int eof = 0;
552         int i;
553
554         ALLOC_ARRAY(chosen, stuff->nr);
555         /* set chosen as uninitialized */
556         for (i = 0; i < stuff->nr; i++)
557                 chosen[i] = -1;
558
559         for (;;) {
560                 if (opts->header) {
561                         printf_ln("%s%s%s",
562                                   clean_get_color(CLEAN_COLOR_HEADER),
563                                   _(opts->header),
564                                   clean_get_color(CLEAN_COLOR_RESET));
565                 }
566
567                 /* chosen will be initialized by print_highlight_menu_stuff */
568                 print_highlight_menu_stuff(stuff, &chosen);
569
570                 if (opts->flags & MENU_OPTS_LIST_ONLY)
571                         break;
572
573                 if (opts->prompt) {
574                         printf("%s%s%s%s",
575                                clean_get_color(CLEAN_COLOR_PROMPT),
576                                _(opts->prompt),
577                                opts->flags & MENU_OPTS_SINGLETON ? "> " : ">> ",
578                                clean_get_color(CLEAN_COLOR_RESET));
579                 }
580
581                 if (strbuf_getline_lf(&choice, stdin) != EOF) {
582                         strbuf_trim(&choice);
583                 } else {
584                         eof = 1;
585                         break;
586                 }
587
588                 /* help for prompt */
589                 if (!strcmp(choice.buf, "?")) {
590                         prompt_help_cmd(opts->flags & MENU_OPTS_SINGLETON);
591                         continue;
592                 }
593
594                 /* for a multiple-choice menu, press ENTER (empty) will return back */
595                 if (!(opts->flags & MENU_OPTS_SINGLETON) && !choice.len)
596                         break;
597
598                 nr = parse_choice(stuff,
599                                   opts->flags & MENU_OPTS_SINGLETON,
600                                   choice,
601                                   &chosen);
602
603                 if (opts->flags & MENU_OPTS_SINGLETON) {
604                         if (nr)
605                                 break;
606                 } else if (opts->flags & MENU_OPTS_IMMEDIATE) {
607                         break;
608                 }
609         }
610
611         if (eof) {
612                 result = xmalloc(sizeof(int));
613                 *result = EOF;
614         } else {
615                 int j = 0;
616
617                 /*
618                  * recalculate nr, if return back from menu directly with
619                  * default selections.
620                  */
621                 if (!nr) {
622                         for (i = 0; i < stuff->nr; i++)
623                                 nr += chosen[i];
624                 }
625
626                 result = xcalloc(st_add(nr, 1), sizeof(int));
627                 for (i = 0; i < stuff->nr && j < nr; i++) {
628                         if (chosen[i])
629                                 result[j++] = i;
630                 }
631                 result[j] = EOF;
632         }
633
634         free(chosen);
635         strbuf_release(&choice);
636         return result;
637 }
638
639 static int clean_cmd(void)
640 {
641         return MENU_RETURN_NO_LOOP;
642 }
643
644 static int filter_by_patterns_cmd(void)
645 {
646         struct dir_struct dir;
647         struct strbuf confirm = STRBUF_INIT;
648         struct strbuf **ignore_list;
649         struct string_list_item *item;
650         struct exclude_list *el;
651         int changed = -1, i;
652
653         for (;;) {
654                 if (!del_list.nr)
655                         break;
656
657                 if (changed)
658                         pretty_print_dels();
659
660                 clean_print_color(CLEAN_COLOR_PROMPT);
661                 printf(_("Input ignore patterns>> "));
662                 clean_print_color(CLEAN_COLOR_RESET);
663                 if (strbuf_getline_lf(&confirm, stdin) != EOF)
664                         strbuf_trim(&confirm);
665                 else
666                         putchar('\n');
667
668                 /* quit filter_by_pattern mode if press ENTER or Ctrl-D */
669                 if (!confirm.len)
670                         break;
671
672                 memset(&dir, 0, sizeof(dir));
673                 el = add_exclude_list(&dir, EXC_CMDL, "manual exclude");
674                 ignore_list = strbuf_split_max(&confirm, ' ', 0);
675
676                 for (i = 0; ignore_list[i]; i++) {
677                         strbuf_trim(ignore_list[i]);
678                         if (!ignore_list[i]->len)
679                                 continue;
680
681                         add_exclude(ignore_list[i]->buf, "", 0, el, -(i+1));
682                 }
683
684                 changed = 0;
685                 for_each_string_list_item(item, &del_list) {
686                         int dtype = DT_UNKNOWN;
687
688                         if (is_excluded(&dir, &the_index, item->string, &dtype)) {
689                                 *item->string = '\0';
690                                 changed++;
691                         }
692                 }
693
694                 if (changed) {
695                         string_list_remove_empty_items(&del_list, 0);
696                 } else {
697                         clean_print_color(CLEAN_COLOR_ERROR);
698                         printf_ln(_("WARNING: Cannot find items matched by: %s"), confirm.buf);
699                         clean_print_color(CLEAN_COLOR_RESET);
700                 }
701
702                 strbuf_list_free(ignore_list);
703                 clear_directory(&dir);
704         }
705
706         strbuf_release(&confirm);
707         return 0;
708 }
709
710 static int select_by_numbers_cmd(void)
711 {
712         struct menu_opts menu_opts;
713         struct menu_stuff menu_stuff;
714         struct string_list_item *items;
715         int *chosen;
716         int i, j;
717
718         menu_opts.header = NULL;
719         menu_opts.prompt = N_("Select items to delete");
720         menu_opts.flags = 0;
721
722         menu_stuff.type = MENU_STUFF_TYPE_STRING_LIST;
723         menu_stuff.stuff = &del_list;
724         menu_stuff.nr = del_list.nr;
725
726         chosen = list_and_choose(&menu_opts, &menu_stuff);
727         items = del_list.items;
728         for (i = 0, j = 0; i < del_list.nr; i++) {
729                 if (i < chosen[j]) {
730                         *(items[i].string) = '\0';
731                 } else if (i == chosen[j]) {
732                         /* delete selected item */
733                         j++;
734                         continue;
735                 } else {
736                         /* end of chosen (chosen[j] == EOF), won't delete */
737                         *(items[i].string) = '\0';
738                 }
739         }
740
741         string_list_remove_empty_items(&del_list, 0);
742
743         free(chosen);
744         return 0;
745 }
746
747 static int ask_each_cmd(void)
748 {
749         struct strbuf confirm = STRBUF_INIT;
750         struct strbuf buf = STRBUF_INIT;
751         struct string_list_item *item;
752         const char *qname;
753         int changed = 0, eof = 0;
754
755         for_each_string_list_item(item, &del_list) {
756                 /* Ctrl-D should stop removing files */
757                 if (!eof) {
758                         qname = quote_path_relative(item->string, NULL, &buf);
759                         /* TRANSLATORS: Make sure to keep [y/N] as is */
760                         printf(_("Remove %s [y/N]? "), qname);
761                         if (strbuf_getline_lf(&confirm, stdin) != EOF) {
762                                 strbuf_trim(&confirm);
763                         } else {
764                                 putchar('\n');
765                                 eof = 1;
766                         }
767                 }
768                 if (!confirm.len || strncasecmp(confirm.buf, "yes", confirm.len)) {
769                         *item->string = '\0';
770                         changed++;
771                 }
772         }
773
774         if (changed)
775                 string_list_remove_empty_items(&del_list, 0);
776
777         strbuf_release(&buf);
778         strbuf_release(&confirm);
779         return MENU_RETURN_NO_LOOP;
780 }
781
782 static int quit_cmd(void)
783 {
784         string_list_clear(&del_list, 0);
785         printf(_("Bye.\n"));
786         return MENU_RETURN_NO_LOOP;
787 }
788
789 static int help_cmd(void)
790 {
791         clean_print_color(CLEAN_COLOR_HELP);
792         printf_ln(_(
793                     "clean               - start cleaning\n"
794                     "filter by pattern   - exclude items from deletion\n"
795                     "select by numbers   - select items to be deleted by numbers\n"
796                     "ask each            - confirm each deletion (like \"rm -i\")\n"
797                     "quit                - stop cleaning\n"
798                     "help                - this screen\n"
799                     "?                   - help for prompt selection"
800                    ));
801         clean_print_color(CLEAN_COLOR_RESET);
802         return 0;
803 }
804
805 static void interactive_main_loop(void)
806 {
807         while (del_list.nr) {
808                 struct menu_opts menu_opts;
809                 struct menu_stuff menu_stuff;
810                 struct menu_item menus[] = {
811                         {'c', "clean",                  0, clean_cmd},
812                         {'f', "filter by pattern",      0, filter_by_patterns_cmd},
813                         {'s', "select by numbers",      0, select_by_numbers_cmd},
814                         {'a', "ask each",               0, ask_each_cmd},
815                         {'q', "quit",                   0, quit_cmd},
816                         {'h', "help",                   0, help_cmd},
817                 };
818                 int *chosen;
819
820                 menu_opts.header = N_("*** Commands ***");
821                 menu_opts.prompt = N_("What now");
822                 menu_opts.flags = MENU_OPTS_SINGLETON;
823
824                 menu_stuff.type = MENU_STUFF_TYPE_MENU_ITEM;
825                 menu_stuff.stuff = menus;
826                 menu_stuff.nr = sizeof(menus) / sizeof(struct menu_item);
827
828                 clean_print_color(CLEAN_COLOR_HEADER);
829                 printf_ln(Q_("Would remove the following item:",
830                              "Would remove the following items:",
831                              del_list.nr));
832                 clean_print_color(CLEAN_COLOR_RESET);
833
834                 pretty_print_dels();
835
836                 chosen = list_and_choose(&menu_opts, &menu_stuff);
837
838                 if (*chosen != EOF) {
839                         int ret;
840                         ret = menus[*chosen].fn();
841                         if (ret != MENU_RETURN_NO_LOOP) {
842                                 FREE_AND_NULL(chosen);
843                                 if (!del_list.nr) {
844                                         clean_print_color(CLEAN_COLOR_ERROR);
845                                         printf_ln(_("No more files to clean, exiting."));
846                                         clean_print_color(CLEAN_COLOR_RESET);
847                                         break;
848                                 }
849                                 continue;
850                         }
851                 } else {
852                         quit_cmd();
853                 }
854
855                 FREE_AND_NULL(chosen);
856                 break;
857         }
858 }
859
860 static void correct_untracked_entries(struct dir_struct *dir)
861 {
862         int src, dst, ign;
863
864         for (src = dst = ign = 0; src < dir->nr; src++) {
865                 /* skip paths in ignored[] that cannot be inside entries[src] */
866                 while (ign < dir->ignored_nr &&
867                        0 <= cmp_dir_entry(&dir->entries[src], &dir->ignored[ign]))
868                         ign++;
869
870                 if (ign < dir->ignored_nr &&
871                     check_dir_entry_contains(dir->entries[src], dir->ignored[ign])) {
872                         /* entries[src] contains an ignored path, so we drop it */
873                         free(dir->entries[src]);
874                 } else {
875                         struct dir_entry *ent = dir->entries[src++];
876
877                         /* entries[src] does not contain an ignored path, so we keep it */
878                         dir->entries[dst++] = ent;
879
880                         /* then discard paths in entries[] contained inside entries[src] */
881                         while (src < dir->nr &&
882                                check_dir_entry_contains(ent, dir->entries[src]))
883                                 free(dir->entries[src++]);
884
885                         /* compensate for the outer loop's loop control */
886                         src--;
887                 }
888         }
889         dir->nr = dst;
890 }
891
892 int cmd_clean(int argc, const char **argv, const char *prefix)
893 {
894         int i, res;
895         int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
896         int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
897         int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
898         struct strbuf abs_path = STRBUF_INIT;
899         struct dir_struct dir;
900         struct pathspec pathspec;
901         struct strbuf buf = STRBUF_INIT;
902         struct string_list exclude_list = STRING_LIST_INIT_NODUP;
903         struct exclude_list *el;
904         struct string_list_item *item;
905         const char *qname;
906         struct option options[] = {
907                 OPT__QUIET(&quiet, N_("do not print names of files removed")),
908                 OPT__DRY_RUN(&dry_run, N_("dry run")),
909                 OPT__FORCE(&force, N_("force"), PARSE_OPT_NOCOMPLETE),
910                 OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")),
911                 OPT_BOOL('d', NULL, &remove_directories,
912                                 N_("remove whole directories")),
913                 { OPTION_CALLBACK, 'e', "exclude", &exclude_list, N_("pattern"),
914                   N_("add <pattern> to ignore rules"), PARSE_OPT_NONEG, exclude_cb },
915                 OPT_BOOL('x', NULL, &ignored, N_("remove ignored files, too")),
916                 OPT_BOOL('X', NULL, &ignored_only,
917                                 N_("remove only ignored files")),
918                 OPT_END()
919         };
920
921         git_config(git_clean_config, NULL);
922         if (force < 0)
923                 force = 0;
924         else
925                 config_set = 1;
926
927         argc = parse_options(argc, argv, prefix, options, builtin_clean_usage,
928                              0);
929
930         memset(&dir, 0, sizeof(dir));
931         if (ignored_only)
932                 dir.flags |= DIR_SHOW_IGNORED;
933
934         if (ignored && ignored_only)
935                 die(_("-x and -X cannot be used together"));
936
937         if (!interactive && !dry_run && !force) {
938                 if (config_set)
939                         die(_("clean.requireForce set to true and neither -i, -n, nor -f given; "
940                                   "refusing to clean"));
941                 else
942                         die(_("clean.requireForce defaults to true and neither -i, -n, nor -f given;"
943                                   " refusing to clean"));
944         }
945
946         if (force > 1)
947                 rm_flags = 0;
948
949         dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
950
951         if (remove_directories)
952                 dir.flags |= DIR_SHOW_IGNORED_TOO | DIR_KEEP_UNTRACKED_CONTENTS;
953
954         if (read_cache() < 0)
955                 die(_("index file corrupt"));
956
957         if (!ignored)
958                 setup_standard_excludes(&dir);
959
960         el = add_exclude_list(&dir, EXC_CMDL, "--exclude option");
961         for (i = 0; i < exclude_list.nr; i++)
962                 add_exclude(exclude_list.items[i].string, "", 0, el, -(i+1));
963
964         parse_pathspec(&pathspec, 0,
965                        PATHSPEC_PREFER_CWD,
966                        prefix, argv);
967
968         fill_directory(&dir, &the_index, &pathspec);
969         correct_untracked_entries(&dir);
970
971         for (i = 0; i < dir.nr; i++) {
972                 struct dir_entry *ent = dir.entries[i];
973                 int matches = 0;
974                 struct stat st;
975                 const char *rel;
976
977                 if (!cache_name_is_other(ent->name, ent->len))
978                         continue;
979
980                 if (pathspec.nr)
981                         matches = dir_path_match(&the_index, ent, &pathspec, 0, NULL);
982
983                 if (pathspec.nr && !matches)
984                         continue;
985
986                 if (lstat(ent->name, &st))
987                         die_errno("Cannot lstat '%s'", ent->name);
988
989                 if (S_ISDIR(st.st_mode) && !remove_directories &&
990                     matches != MATCHED_EXACTLY)
991                         continue;
992
993                 rel = relative_path(ent->name, prefix, &buf);
994                 string_list_append(&del_list, rel);
995         }
996
997         for (i = 0; i < dir.nr; i++)
998                 free(dir.entries[i]);
999
1000         for (i = 0; i < dir.ignored_nr; i++)
1001                 free(dir.ignored[i]);
1002
1003         if (interactive && del_list.nr > 0)
1004                 interactive_main_loop();
1005
1006         for_each_string_list_item(item, &del_list) {
1007                 struct stat st;
1008
1009                 if (prefix)
1010                         strbuf_addstr(&abs_path, prefix);
1011
1012                 strbuf_addstr(&abs_path, item->string);
1013
1014                 /*
1015                  * we might have removed this as part of earlier
1016                  * recursive directory removal, so lstat() here could
1017                  * fail with ENOENT.
1018                  */
1019                 if (lstat(abs_path.buf, &st))
1020                         continue;
1021
1022                 if (S_ISDIR(st.st_mode)) {
1023                         if (remove_dirs(&abs_path, prefix, rm_flags, dry_run, quiet, &gone))
1024                                 errors++;
1025                         if (gone && !quiet) {
1026                                 qname = quote_path_relative(item->string, NULL, &buf);
1027                                 printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
1028                         }
1029                 } else {
1030                         res = dry_run ? 0 : unlink(abs_path.buf);
1031                         if (res) {
1032                                 int saved_errno = errno;
1033                                 qname = quote_path_relative(item->string, NULL, &buf);
1034                                 errno = saved_errno;
1035                                 warning_errno(_(msg_warn_remove_failed), qname);
1036                                 errors++;
1037                         } else if (!quiet) {
1038                                 qname = quote_path_relative(item->string, NULL, &buf);
1039                                 printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
1040                         }
1041                 }
1042                 strbuf_reset(&abs_path);
1043         }
1044
1045         strbuf_release(&abs_path);
1046         strbuf_release(&buf);
1047         string_list_clear(&del_list, 0);
1048         string_list_clear(&exclude_list, 0);
1049         return (errors != 0);
1050 }