Merge branch 'tr/log-full-diff-keep-true-parents'
[gitweb.git] / builtin / clean.c
index 363690299ccdd5e872c45f718d4596a29427c87e..4b6fd42be7aeae55c71c7b79ee45fb73973ec0d5 100644 (file)
@@ -365,6 +365,56 @@ static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen)
        string_list_clear(&menu_list, 0);
 }
 
+static int find_unique(const char *choice, struct menu_stuff *menu_stuff)
+{
+       struct menu_item *menu_item;
+       struct string_list_item *string_list_item;
+       int i, len, found = 0;
+
+       len = strlen(choice);
+       switch (menu_stuff->type) {
+       default:
+               die("Bad type of menu_stuff when parse choice");
+       case MENU_STUFF_TYPE_MENU_ITEM:
+
+               menu_item = (struct menu_item *)menu_stuff->stuff;
+               for (i = 0; i < menu_stuff->nr; i++, menu_item++) {
+                       if (len == 1 && *choice == menu_item->hotkey) {
+                               found = i + 1;
+                               break;
+                       }
+                       if (!strncasecmp(choice, menu_item->title, len)) {
+                               if (found) {
+                                       if (len == 1) {
+                                               /* continue for hotkey matching */
+                                               found = -1;
+                                       } else {
+                                               found = 0;
+                                               break;
+                                       }
+                               } else {
+                                       found = i + 1;
+                               }
+                       }
+               }
+               break;
+       case MENU_STUFF_TYPE_STRING_LIST:
+               string_list_item = ((struct string_list *)menu_stuff->stuff)->items;
+               for (i = 0; i < menu_stuff->nr; i++, string_list_item++) {
+                       if (!strncasecmp(choice, string_list_item->string, len)) {
+                               if (found) {
+                                       found = 0;
+                                       break;
+                               }
+                               found = i + 1;
+                       }
+               }
+               break;
+       }
+       return found;
+}
+
+
 /*
  * Parse user input, and return choice(s) for menu (menu_stuff).
  *
@@ -392,8 +442,6 @@ static int parse_choice(struct menu_stuff *menu_stuff,
                        int **chosen)
 {
        struct strbuf **choice_list, **ptr;
-       struct menu_item *menu_item;
-       struct string_list_item *string_list_item;
        int nr = 0;
        int i;
 
@@ -457,32 +505,8 @@ static int parse_choice(struct menu_stuff *menu_stuff,
                        bottom = 1;
                        top = menu_stuff->nr;
                } else {
-                       switch (menu_stuff->type) {
-                       default:
-                               die("Bad type of menu_stuff when parse choice");
-                       case MENU_STUFF_TYPE_MENU_ITEM:
-                               menu_item = (struct menu_item *)menu_stuff->stuff;
-                               for (i = 0; i < menu_stuff->nr; i++, menu_item++) {
-                                       if (((*ptr)->len == 1 &&
-                                            *(*ptr)->buf == menu_item->hotkey) ||
-                                           !strcasecmp((*ptr)->buf, menu_item->title)) {
-                                               bottom = i + 1;
-                                               top = bottom;
-                                               break;
-                                       }
-                               }
-                               break;
-                       case MENU_STUFF_TYPE_STRING_LIST:
-                               string_list_item = ((struct string_list *)menu_stuff->stuff)->items;
-                               for (i = 0; i < menu_stuff->nr; i++, string_list_item++) {
-                                       if (!strcasecmp((*ptr)->buf, string_list_item->string)) {
-                                               bottom = i + 1;
-                                               top = bottom;
-                                               break;
-                                       }
-                               }
-                               break;
-                       }
+                       bottom = find_unique((*ptr)->buf, menu_stuff);
+                       top = bottom;
                }
 
                if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top ||
@@ -680,6 +704,77 @@ static int filter_by_patterns_cmd(void)
        return 0;
 }
 
+static int select_by_numbers_cmd(void)
+{
+       struct menu_opts menu_opts;
+       struct menu_stuff menu_stuff;
+       struct string_list_item *items;
+       int *chosen;
+       int i, j;
+
+       menu_opts.header = NULL;
+       menu_opts.prompt = N_("Select items to delete");
+       menu_opts.flags = 0;
+
+       menu_stuff.type = MENU_STUFF_TYPE_STRING_LIST;
+       menu_stuff.stuff = &del_list;
+       menu_stuff.nr = del_list.nr;
+
+       chosen = list_and_choose(&menu_opts, &menu_stuff);
+       items = del_list.items;
+       for (i = 0, j = 0; i < del_list.nr; i++) {
+               if (i < chosen[j]) {
+                       *(items[i].string) = '\0';
+               } else if (i == chosen[j]) {
+                       /* delete selected item */
+                       j++;
+                       continue;
+               } else {
+                       /* end of chosen (chosen[j] == EOF), won't delete */
+                       *(items[i].string) = '\0';
+               }
+       }
+
+       string_list_remove_empty_items(&del_list, 0);
+
+       free(chosen);
+       return 0;
+}
+
+static int ask_each_cmd(void)
+{
+       struct strbuf confirm = STRBUF_INIT;
+       struct strbuf buf = STRBUF_INIT;
+       struct string_list_item *item;
+       const char *qname;
+       int changed = 0, eof = 0;
+
+       for_each_string_list_item(item, &del_list) {
+               /* Ctrl-D should stop removing files */
+               if (!eof) {
+                       qname = quote_path_relative(item->string, NULL, &buf);
+                       printf(_("remove %s? "), qname);
+                       if (strbuf_getline(&confirm, stdin, '\n') != EOF) {
+                               strbuf_trim(&confirm);
+                       } else {
+                               putchar('\n');
+                               eof = 1;
+                       }
+               }
+               if (!confirm.len || strncasecmp(confirm.buf, "yes", confirm.len)) {
+                       *item->string = '\0';
+                       changed++;
+               }
+       }
+
+       if (changed)
+               string_list_remove_empty_items(&del_list, 0);
+
+       strbuf_release(&buf);
+       strbuf_release(&confirm);
+       return MENU_RETURN_NO_LOOP;
+}
+
 static int quit_cmd(void)
 {
        string_list_clear(&del_list, 0);
@@ -693,6 +788,8 @@ static int help_cmd(void)
        printf_ln(_(
                    "clean               - start cleaning\n"
                    "filter by pattern   - exclude items from deletion\n"
+                   "select by numbers   - select items to be deleted by numbers\n"
+                   "ask each            - confirm each deletion (like \"rm -i\")\n"
                    "quit                - stop cleaning\n"
                    "help                - this screen\n"
                    "?                   - help for prompt selection"
@@ -709,6 +806,8 @@ static void interactive_main_loop(void)
                struct menu_item menus[] = {
                        {'c', "clean",                  0, clean_cmd},
                        {'f', "filter by pattern",      0, filter_by_patterns_cmd},
+                       {'s', "select by numbers",      0, select_by_numbers_cmd},
+                       {'a', "ask each",               0, ask_each_cmd},
                        {'q', "quit",                   0, quit_cmd},
                        {'h', "help",                   0, help_cmd},
                };
@@ -776,12 +875,12 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                OPT__DRY_RUN(&dry_run, N_("dry run")),
                OPT__FORCE(&force, N_("force")),
                OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")),
-               OPT_BOOLEAN('d', NULL, &remove_directories,
+               OPT_BOOL('d', NULL, &remove_directories,
                                N_("remove whole directories")),
                { OPTION_CALLBACK, 'e', "exclude", &exclude_list, N_("pattern"),
                  N_("add <pattern> to ignore rules"), PARSE_OPT_NONEG, exclude_cb },
-               OPT_BOOLEAN('x', NULL, &ignored, N_("remove ignored files, too")),
-               OPT_BOOLEAN('X', NULL, &ignored_only,
+               OPT_BOOL('x', NULL, &ignored, N_("remove ignored files, too")),
+               OPT_BOOL('X', NULL, &ignored_only,
                                N_("remove only ignored files")),
                OPT_END()
        };
@@ -837,7 +936,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                struct dir_entry *ent = dir.entries[i];
                int len, pos;
                int matches = 0;
-               struct cache_entry *ce;
+               const struct cache_entry *ce;
                struct stat st;
                const char *rel;