diff --git a/add-interactive.c b/add-interactive.c index ea406e903b..1e34e88069 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -6,6 +6,7 @@ #include "revision.h" #include "refs.h" #include "string-list.h" +#include "lockfile.h" struct add_i_state { struct repository *r; @@ -377,6 +378,7 @@ struct adddel { }; struct file_item { + size_t prefix_length; struct adddel index, worktree; }; @@ -471,7 +473,7 @@ enum modified_files_filter { static int get_modified_files(struct repository *r, enum modified_files_filter filter, - struct string_list *files, + struct prefix_item_list *files, const struct pathspec *ps) { struct object_id head_oid; @@ -484,8 +486,8 @@ static int get_modified_files(struct repository *r, repo_read_index_preload(r, ps, 0) < 0) return error(_("could not read index")); - string_list_clear(files, 1); - s.files = files; + prefix_item_list_clear(files); + s.files = &files->items; hashmap_init(&s.file_map, pathname_entry_cmp, NULL, 0); for (i = 0; i < 2; i++) { @@ -524,7 +526,7 @@ static int get_modified_files(struct repository *r, hashmap_free_entries(&s.file_map, struct pathname_entry, ent); /* While the diffs are ordered already, we ran *two* diffs... */ - string_list_sort(files); + string_list_sort(&files->items); return 0; } @@ -559,8 +561,8 @@ static int is_valid_prefix(const char *prefix, size_t prefix_len) } struct print_file_item_data { - const char *modified_fmt; - struct strbuf buf, index, worktree; + const char *modified_fmt, *color, *reset; + struct strbuf buf, name, index, worktree; }; static void print_file_item(int i, int selected, struct string_list_item *item, @@ -568,34 +570,96 @@ static void print_file_item(int i, int selected, struct string_list_item *item, { struct file_item *c = item->util; struct print_file_item_data *d = print_file_item_data; + const char *highlighted = NULL; strbuf_reset(&d->index); strbuf_reset(&d->worktree); strbuf_reset(&d->buf); + /* Format the item with the prefix highlighted. */ + if (c->prefix_length > 0 && + is_valid_prefix(item->string, c->prefix_length)) { + strbuf_reset(&d->name); + strbuf_addf(&d->name, "%s%.*s%s%s", d->color, + (int)c->prefix_length, item->string, d->reset, + item->string + c->prefix_length); + highlighted = d->name.buf; + } + render_adddel(&d->worktree, &c->worktree, _("nothing")); render_adddel(&d->index, &c->index, _("unchanged")); - strbuf_addf(&d->buf, d->modified_fmt, - d->index.buf, d->worktree.buf, item->string); + + strbuf_addf(&d->buf, d->modified_fmt, d->index.buf, d->worktree.buf, + highlighted ? highlighted : item->string); printf("%c%2d: %s", selected ? '*' : ' ', i + 1, d->buf.buf); } static int run_status(struct add_i_state *s, const struct pathspec *ps, - struct string_list *files, struct list_options *opts) + struct prefix_item_list *files, + struct list_and_choose_options *opts) { if (get_modified_files(s->r, NO_FILTER, files, ps) < 0) return -1; - list(s, files, NULL, opts); + list(s, &files->items, NULL, &opts->list_opts); putchar('\n'); return 0; } +static int run_update(struct add_i_state *s, const struct pathspec *ps, + struct prefix_item_list *files, + struct list_and_choose_options *opts) +{ + int res = 0, fd; + size_t count, i; + struct lock_file index_lock; + + if (get_modified_files(s->r, WORKTREE_ONLY, files, ps) < 0) + return -1; + + if (!files->items.nr) { + putchar('\n'); + return 0; + } + + opts->prompt = N_("Update"); + count = list_and_choose(s, files, opts); + if (count <= 0) { + putchar('\n'); + return 0; + } + + fd = repo_hold_locked_index(s->r, &index_lock, LOCK_REPORT_ON_ERROR); + if (fd < 0) { + putchar('\n'); + return -1; + } + + for (i = 0; i < files->items.nr; i++) { + const char *name = files->items.items[i].string; + if (files->selected[i] && + add_file_to_index(s->r->index, name, 0) < 0) { + res = error(_("could not stage '%s'"), name); + break; + } + } + + if (!res && write_locked_index(s->r->index, &index_lock, COMMIT_LOCK) < 0) + res = error(_("could not write index")); + + if (!res) + printf(Q_("updated %d path\n", + "updated %d paths\n", count), (int)count); + + putchar('\n'); + return res; +} + static int run_help(struct add_i_state *s, const struct pathspec *unused_ps, - struct string_list *unused_files, - struct list_options *unused_opts) + struct prefix_item_list *unused_files, + struct list_and_choose_options *unused_opts) { color_fprintf_ln(stdout, s->help_color, "status - %s", _("show paths with changes")); @@ -613,9 +677,29 @@ static int run_help(struct add_i_state *s, const struct pathspec *unused_ps, return 0; } +static void choose_prompt_help(struct add_i_state *s) +{ + color_fprintf_ln(stdout, s->help_color, "%s", + _("Prompt help:")); + color_fprintf_ln(stdout, s->help_color, "1 - %s", + _("select a single item")); + color_fprintf_ln(stdout, s->help_color, "3-5 - %s", + _("select a range of items")); + color_fprintf_ln(stdout, s->help_color, "2-3,6-9 - %s", + _("select multiple ranges")); + color_fprintf_ln(stdout, s->help_color, "foo - %s", + _("select item based on unique prefix")); + color_fprintf_ln(stdout, s->help_color, "-... - %s", + _("unselect specified items")); + color_fprintf_ln(stdout, s->help_color, "* - %s", + _("choose all items")); + color_fprintf_ln(stdout, s->help_color, " - %s", + _("(empty) finish selecting")); +} + typedef int (*command_t)(struct add_i_state *s, const struct pathspec *ps, - struct string_list *files, - struct list_options *opts); + struct prefix_item_list *files, + struct list_and_choose_options *opts); struct command_item { size_t prefix_length; @@ -667,18 +751,21 @@ int run_add_i(struct repository *r, const struct pathspec *ps) command_t command; } command_list[] = { { "status", run_status }, + { "update", run_update }, { "help", run_help }, }; struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT; struct print_file_item_data print_file_item_data = { - "%12s %12s %s", STRBUF_INIT, STRBUF_INIT, STRBUF_INIT + "%12s %12s %s", NULL, NULL, + STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; - struct list_options opts = { - 0, NULL, print_file_item, &print_file_item_data + struct list_and_choose_options opts = { + { 0, NULL, print_file_item, &print_file_item_data }, + NULL, 0, choose_prompt_help }; struct strbuf header = STRBUF_INIT; - struct string_list files = STRING_LIST_INIT_DUP; + struct prefix_item_list files = PREFIX_ITEM_LIST_INIT; ssize_t i; int res = 0; @@ -699,11 +786,13 @@ int run_add_i(struct repository *r, const struct pathspec *ps) data.color = s.prompt_color; data.reset = s.reset_color; } + print_file_item_data.color = data.color; + print_file_item_data.reset = data.reset; strbuf_addstr(&header, " "); strbuf_addf(&header, print_file_item_data.modified_fmt, _("staged"), _("unstaged"), _("path")); - opts.header = header.buf; + opts.list_opts.header = header.buf; if (discard_index(r->index) < 0 || repo_read_index(r) < 0 || @@ -727,8 +816,9 @@ int run_add_i(struct repository *r, const struct pathspec *ps) } } - string_list_clear(&files, 1); + prefix_item_list_clear(&files); strbuf_release(&print_file_item_data.buf); + strbuf_release(&print_file_item_data.name); strbuf_release(&print_file_item_data.index); strbuf_release(&print_file_item_data.worktree); strbuf_release(&header);