@matheusbsbs / matheus-bagliano / commits / a8c45be939
Fork 0

built-in add -i: implement the `update` command

After `status` and `help`, it is now time to port the `update` command to C, the second command that is shown in the main loop menu of `git add -i`. This `git add -i` command is the first one which lets the user choose a subset of a list of files, and as such, this patch lays the groundwork for the other commands of that category: - It teaches the `print_file_item()` function to show a unique prefix if we found any (the code to find it had been added already in the previous patch where we colored the unique prefixes of the main loop commands, but that patch uses the `print_command_item()` function to display the menu items). - This patch also adds the help text that is shown when the user input to select items from the shown list could not be parsed. - As `get_modified_files()` clears the list of files, it now has to take care of clearing the _full_ `prefix_item_list` lest the `sorted` and `selected` fields go stale and inconsistent. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Nov 29, 2019 at 21:11 UTC a8c45be939d88c2d1df88daf7958da2ee5dce170
1 file changed +110 -20
add-interactive.c
+110 -20
@@ -6,6 +6,7 @@
6 #include "revision.h"
7 #include "refs.h"
8 #include "string-list.h"
9 +#include "lockfile.h"
10
11 struct add_i_state {
12 struct repository *r;
@@ -377,6 +378,7 @@ struct adddel {
378 };
379
380 struct file_item {
381 + size_t prefix_length;
382 struct adddel index, worktree;
383 };
384
@@ -471,7 +473,7 @@ enum modified_files_filter {
473
474 static int get_modified_files(struct repository *r,
475 enum modified_files_filter filter,
474 - struct string_list *files,
476 + struct prefix_item_list *files,
477 const struct pathspec *ps)
478 {
479 struct object_id head_oid;
@@ -484,8 +486,8 @@ static int get_modified_files(struct repository *r,
486 repo_read_index_preload(r, ps, 0) < 0)
487 return error(_("could not read index"));
488
487 - string_list_clear(files, 1);
488 - s.files = files;
489 + prefix_item_list_clear(files);
490 + s.files = &files->items;
491 hashmap_init(&s.file_map, pathname_entry_cmp, NULL, 0);
492
493 for (i = 0; i < 2; i++) {
@@ -524,7 +526,7 @@ static int get_modified_files(struct repository *r,
526 hashmap_free_entries(&s.file_map, struct pathname_entry, ent);
527
528 /* While the diffs are ordered already, we ran *two* diffs... */
527 - string_list_sort(files);
529 + string_list_sort(&files->items);
530
531 return 0;
532 }
@@ -559,8 +561,8 @@ static int is_valid_prefix(const char *prefix, size_t prefix_len)
561 }
562
563 struct print_file_item_data {
562 - const char *modified_fmt;
563 - struct strbuf buf, index, worktree;
564 + const char *modified_fmt, *color, *reset;
565 + struct strbuf buf, name, index, worktree;
566 };
567
568 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,
570 {
571 struct file_item *c = item->util;
572 struct print_file_item_data *d = print_file_item_data;
573 + const char *highlighted = NULL;
574
575 strbuf_reset(&d->index);
576 strbuf_reset(&d->worktree);
577 strbuf_reset(&d->buf);
578
579 + /* Format the item with the prefix highlighted. */
580 + if (c->prefix_length > 0 &&
581 + is_valid_prefix(item->string, c->prefix_length)) {
582 + strbuf_reset(&d->name);
583 + strbuf_addf(&d->name, "%s%.*s%s%s", d->color,
584 + (int)c->prefix_length, item->string, d->reset,
585 + item->string + c->prefix_length);
586 + highlighted = d->name.buf;
587 + }
588 +
589 render_adddel(&d->worktree, &c->worktree, _("nothing"));
590 render_adddel(&d->index, &c->index, _("unchanged"));
578 - strbuf_addf(&d->buf, d->modified_fmt,
579 - d->index.buf, d->worktree.buf, item->string);
591 +
592 + strbuf_addf(&d->buf, d->modified_fmt, d->index.buf, d->worktree.buf,
593 + highlighted ? highlighted : item->string);
594
595 printf("%c%2d: %s", selected ? '*' : ' ', i + 1, d->buf.buf);
596 }
597
598 static int run_status(struct add_i_state *s, const struct pathspec *ps,
585 - struct string_list *files, struct list_options *opts)
599 + struct prefix_item_list *files,
600 + struct list_and_choose_options *opts)
601 {
602 if (get_modified_files(s->r, NO_FILTER, files, ps) < 0)
603 return -1;
604
590 - list(s, files, NULL, opts);
605 + list(s, &files->items, NULL, &opts->list_opts);
606 putchar('\n');
607
608 return 0;
609 }
610
611 +static int run_update(struct add_i_state *s, const struct pathspec *ps,
612 + struct prefix_item_list *files,
613 + struct list_and_choose_options *opts)
614 +{
615 + int res = 0, fd;
616 + size_t count, i;
617 + struct lock_file index_lock;
618 +
619 + if (get_modified_files(s->r, WORKTREE_ONLY, files, ps) < 0)
620 + return -1;
621 +
622 + if (!files->items.nr) {
623 + putchar('\n');
624 + return 0;
625 + }
626 +
627 + opts->prompt = N_("Update");
628 + count = list_and_choose(s, files, opts);
629 + if (count <= 0) {
630 + putchar('\n');
631 + return 0;
632 + }
633 +
634 + fd = repo_hold_locked_index(s->r, &index_lock, LOCK_REPORT_ON_ERROR);
635 + if (fd < 0) {
636 + putchar('\n');
637 + return -1;
638 + }
639 +
640 + for (i = 0; i < files->items.nr; i++) {
641 + const char *name = files->items.items[i].string;
642 + if (files->selected[i] &&
643 + add_file_to_index(s->r->index, name, 0) < 0) {
644 + res = error(_("could not stage '%s'"), name);
645 + break;
646 + }
647 + }
648 +
649 + if (!res && write_locked_index(s->r->index, &index_lock, COMMIT_LOCK) < 0)
650 + res = error(_("could not write index"));
651 +
652 + if (!res)
653 + printf(Q_("updated %d path\n",
654 + "updated %d paths\n", count), (int)count);
655 +
656 + putchar('\n');
657 + return res;
658 +}
659 +
660 static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
597 - struct string_list *unused_files,
598 - struct list_options *unused_opts)
661 + struct prefix_item_list *unused_files,
662 + struct list_and_choose_options *unused_opts)
663 {
664 color_fprintf_ln(stdout, s->help_color, "status - %s",
665 _("show paths with changes"));
@@ -613,9 +677,29 @@ static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
677 return 0;
678 }
679
680 +static void choose_prompt_help(struct add_i_state *s)
681 +{
682 + color_fprintf_ln(stdout, s->help_color, "%s",
683 + _("Prompt help:"));
684 + color_fprintf_ln(stdout, s->help_color, "1 - %s",
685 + _("select a single item"));
686 + color_fprintf_ln(stdout, s->help_color, "3-5 - %s",
687 + _("select a range of items"));
688 + color_fprintf_ln(stdout, s->help_color, "2-3,6-9 - %s",
689 + _("select multiple ranges"));
690 + color_fprintf_ln(stdout, s->help_color, "foo - %s",
691 + _("select item based on unique prefix"));
692 + color_fprintf_ln(stdout, s->help_color, "-... - %s",
693 + _("unselect specified items"));
694 + color_fprintf_ln(stdout, s->help_color, "* - %s",
695 + _("choose all items"));
696 + color_fprintf_ln(stdout, s->help_color, " - %s",
697 + _("(empty) finish selecting"));
698 +}
699 +
700 typedef int (*command_t)(struct add_i_state *s, const struct pathspec *ps,
617 - struct string_list *files,
618 - struct list_options *opts);
701 + struct prefix_item_list *files,
702 + struct list_and_choose_options *opts);
703
704 struct command_item {
705 size_t prefix_length;
@@ -667,18 +751,21 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
751 command_t command;
752 } command_list[] = {
753 { "status", run_status },
754 + { "update", run_update },
755 { "help", run_help },
756 };
757 struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT;
758
759 struct print_file_item_data print_file_item_data = {
675 - "%12s %12s %s", STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
760 + "%12s %12s %s", NULL, NULL,
761 + STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
762 };
677 - struct list_options opts = {
678 - 0, NULL, print_file_item, &print_file_item_data
763 + struct list_and_choose_options opts = {
764 + { 0, NULL, print_file_item, &print_file_item_data },
765 + NULL, 0, choose_prompt_help
766 };
767 struct strbuf header = STRBUF_INIT;
681 - struct string_list files = STRING_LIST_INIT_DUP;
768 + struct prefix_item_list files = PREFIX_ITEM_LIST_INIT;
769 ssize_t i;
770 int res = 0;
771
@@ -699,11 +786,13 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
786 data.color = s.prompt_color;
787 data.reset = s.reset_color;
788 }
789 + print_file_item_data.color = data.color;
790 + print_file_item_data.reset = data.reset;
791
792 strbuf_addstr(&header, " ");
793 strbuf_addf(&header, print_file_item_data.modified_fmt,
794 _("staged"), _("unstaged"), _("path"));
706 - opts.header = header.buf;
795 + opts.list_opts.header = header.buf;
796
797 if (discard_index(r->index) < 0 ||
798 repo_read_index(r) < 0 ||
@@ -727,8 +816,9 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
816 }
817 }
818
730 - string_list_clear(&files, 1);
819 + prefix_item_list_clear(&files);
820 strbuf_release(&print_file_item_data.buf);
821 + strbuf_release(&print_file_item_data.name);
822 strbuf_release(&print_file_item_data.index);
823 strbuf_release(&print_file_item_data.worktree);
824 strbuf_release(&header);