From: Junio C Hamano Date: Tue, 11 Nov 2014 18:20:13 +0000 (-0800) Subject: Merge branch 'rs/clean-menu-item-defn' into maint X-Git-Tag: v2.2.0-rc2~4^2 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/caea1a2bb52262c64fdef545578ee7c5ceb113c5?ds=inline;hp=-c Merge branch 'rs/clean-menu-item-defn' into maint * rs/clean-menu-item-defn: clean: use f(void) instead of f() to declare a pointer to a function without arguments --- caea1a2bb52262c64fdef545578ee7c5ceb113c5 diff --combined builtin/clean.c index 1032563e5f,95b41279c0..3beeea6ec0 --- a/builtin/clean.c +++ b/builtin/clean.c @@@ -15,7 -15,6 +15,7 @@@ #include "quote.h" #include "column.h" #include "color.h" +#include "pathspec.h" static int force = -1; /* unset */ static int interactive; @@@ -48,7 -47,7 +48,7 @@@ enum color_clean CLEAN_COLOR_PROMPT = 2, CLEAN_COLOR_HEADER = 3, CLEAN_COLOR_HELP = 4, - CLEAN_COLOR_ERROR = 5, + CLEAN_COLOR_ERROR = 5 }; #define MENU_OPTS_SINGLETON 01 @@@ -67,7 -66,7 +67,7 @@@ struct menu_item char hotkey; const char *title; int selected; - int (*fn)(); + int (*fn)(void); }; enum menu_stuff_type { @@@ -100,7 -99,7 +100,7 @@@ static int parse_clean_color_slot(cons static int git_clean_config(const char *var, const char *value, void *cb) { - if (!prefixcmp(var, "column.")) + if (starts_with(var, "column.")) return git_column_config(var, value, "clean", &colopts); /* honors the color.interactive* config variables which also @@@ -109,7 -108,7 +109,7 @@@ clean_use_color = git_config_colorbool(var, value); return 0; } - if (!prefixcmp(var, "color.interactive.")) { + if (starts_with(var, "color.interactive.")) { int slot = parse_clean_color_slot(var + strlen("color.interactive.")); if (slot < 0) @@@ -154,7 -153,7 +154,7 @@@ static int remove_dirs(struct strbuf *p DIR *dir; struct strbuf quoted = STRBUF_INIT; struct dirent *e; - int res = 0, ret = 0, gone = 1, original_len = path->len, len, i; + int res = 0, ret = 0, gone = 1, original_len = path->len, len; unsigned char submodule_head[20]; struct string_list dels = STRING_LIST_INIT_DUP; @@@ -242,7 -241,6 +242,7 @@@ } if (!*dir_gone && !quiet) { + int i; for (i = 0; i < dels.nr; i++) printf(dry_run ? _(msg_would_remove) : _(msg_remove), dels.items[i].string); } @@@ -367,56 -365,6 +367,56 @@@ static void print_highlight_menu_stuff( 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). * @@@ -444,6 -392,8 +444,6 @@@ static int parse_choice(struct menu_stu int **chosen) { struct strbuf **choice_list, **ptr; - struct menu_item *menu_item; - struct string_list_item *string_list_item; int nr = 0; int i; @@@ -507,8 -457,32 +507,8 @@@ 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 || @@@ -621,7 -595,8 +621,7 @@@ static int *list_and_choose(struct menu nr += chosen[i]; } - result = xmalloc(sizeof(int) * (nr + 1)); - memset(result, 0, sizeof(int) * (nr + 1)); + result = xcalloc(nr + 1, sizeof(int)); for (i = 0; i < stuff->nr && j < nr; i++) { if (chosen[i]) result[j++] = i; @@@ -639,143 -614,6 +639,143 @@@ static int clean_cmd(void return MENU_RETURN_NO_LOOP; } +static int filter_by_patterns_cmd(void) +{ + struct dir_struct dir; + struct strbuf confirm = STRBUF_INIT; + struct strbuf **ignore_list; + struct string_list_item *item; + struct exclude_list *el; + int changed = -1, i; + + for (;;) { + if (!del_list.nr) + break; + + if (changed) + pretty_print_dels(); + + clean_print_color(CLEAN_COLOR_PROMPT); + printf(_("Input ignore patterns>> ")); + clean_print_color(CLEAN_COLOR_RESET); + if (strbuf_getline(&confirm, stdin, '\n') != EOF) + strbuf_trim(&confirm); + else + putchar('\n'); + + /* quit filter_by_pattern mode if press ENTER or Ctrl-D */ + if (!confirm.len) + break; + + memset(&dir, 0, sizeof(dir)); + el = add_exclude_list(&dir, EXC_CMDL, "manual exclude"); + ignore_list = strbuf_split_max(&confirm, ' ', 0); + + for (i = 0; ignore_list[i]; i++) { + strbuf_trim(ignore_list[i]); + if (!ignore_list[i]->len) + continue; + + add_exclude(ignore_list[i]->buf, "", 0, el, -(i+1)); + } + + changed = 0; + for_each_string_list_item(item, &del_list) { + int dtype = DT_UNKNOWN; + + if (is_excluded(&dir, item->string, &dtype)) { + *item->string = '\0'; + changed++; + } + } + + if (changed) { + string_list_remove_empty_items(&del_list, 0); + } else { + clean_print_color(CLEAN_COLOR_ERROR); + printf_ln(_("WARNING: Cannot find items matched by: %s"), confirm.buf); + clean_print_color(CLEAN_COLOR_RESET); + } + + strbuf_list_free(ignore_list); + clear_directory(&dir); + } + + strbuf_release(&confirm); + 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); @@@ -788,9 -626,6 +788,9 @@@ static int help_cmd(void clean_print_color(CLEAN_COLOR_HELP); 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" @@@ -806,9 -641,6 +806,9 @@@ static void interactive_main_loop(void struct menu_stuff menu_stuff; 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}, }; @@@ -864,23 -696,24 +864,23 @@@ int cmd_clean(int argc, const char **ar int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT; struct strbuf abs_path = STRBUF_INIT; struct dir_struct dir; - static const char **pathspec; + struct pathspec pathspec; struct strbuf buf = STRBUF_INIT; struct string_list exclude_list = STRING_LIST_INIT_NODUP; struct exclude_list *el; struct string_list_item *item; const char *qname; - char *seen = NULL; 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_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 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() }; @@@ -903,11 -736,11 +903,11 @@@ if (!interactive && !dry_run && !force) { if (config_set) - die(_("clean.requireForce set to true and neither -i, -n nor -f given; " + die(_("clean.requireForce set to true and neither -i, -n, nor -f given; " "refusing to clean")); else - die(_("clean.requireForce defaults to true and neither -i, -n nor -f given; " - "refusing to clean")); + die(_("clean.requireForce defaults to true and neither -i, -n, nor -f given;" + " refusing to clean")); } if (force > 1) @@@ -925,36 -758,59 +925,36 @@@ for (i = 0; i < exclude_list.nr; i++) add_exclude(exclude_list.items[i].string, "", 0, el, -(i+1)); - pathspec = get_pathspec(prefix, argv); - - fill_directory(&dir, pathspec); + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_CWD, + prefix, argv); - if (pathspec) - seen = xmalloc(argc > 0 ? argc : 1); + fill_directory(&dir, &pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; - int len, pos; int matches = 0; - struct cache_entry *ce; struct stat st; const char *rel; - /* - * Remove the '/' at the end that directory - * walking adds for directory entries. - */ - len = ent->len; - if (len && ent->name[len-1] == '/') - len--; - pos = cache_name_pos(ent->name, len); - if (0 <= pos) - continue; /* exact match */ - pos = -pos - 1; - if (pos < active_nr) { - ce = active_cache[pos]; - if (ce_namelen(ce) == len && - !memcmp(ce->name, ent->name, len)) - continue; /* Yup, this one exists unmerged */ - } + if (!cache_name_is_other(ent->name, ent->len)) + continue; if (lstat(ent->name, &st)) die_errno("Cannot lstat '%s'", ent->name); - if (pathspec) { - memset(seen, 0, argc > 0 ? argc : 1); - matches = match_pathspec(pathspec, ent->name, len, - 0, seen); - } + if (pathspec.nr) + matches = dir_path_match(ent, &pathspec, 0, NULL); - if (S_ISDIR(st.st_mode)) { - if (remove_directories || (matches == MATCHED_EXACTLY)) { - rel = relative_path(ent->name, prefix, &buf); - string_list_append(&del_list, rel); - } - } else { - if (pathspec && !matches) - continue; - rel = relative_path(ent->name, prefix, &buf); - string_list_append(&del_list, rel); - } + if (pathspec.nr && !matches) + continue; + + if (S_ISDIR(st.st_mode) && !remove_directories && + matches != MATCHED_EXACTLY) + continue; + + rel = relative_path(ent->name, prefix, &buf); + string_list_append(&del_list, rel); } if (interactive && del_list.nr > 0) @@@ -996,6 -852,7 +996,6 @@@ } strbuf_reset(&abs_path); } - free(seen); strbuf_release(&abs_path); strbuf_release(&buf);