Skip to content

Commit c311a29

Browse files
committed
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 <[email protected]>
1 parent 58a581f commit c311a29

File tree

1 file changed

+110
-20
lines changed

1 file changed

+110
-20
lines changed

add-interactive.c

Lines changed: 110 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "revision.h"
77
#include "refs.h"
88
#include "string-list.h"
9+
#include "lockfile.h"
910

1011
struct add_i_state {
1112
struct repository *r;
@@ -377,6 +378,7 @@ struct adddel {
377378
};
378379

379380
struct file_item {
381+
size_t prefix_length;
380382
struct adddel index, worktree;
381383
};
382384

@@ -471,7 +473,7 @@ enum modified_files_filter {
471473

472474
static int get_modified_files(struct repository *r,
473475
enum modified_files_filter filter,
474-
struct string_list *files,
476+
struct prefix_item_list *files,
475477
const struct pathspec *ps)
476478
{
477479
struct object_id head_oid;
@@ -484,8 +486,8 @@ static int get_modified_files(struct repository *r,
484486
repo_read_index_preload(r, ps, 0) < 0)
485487
return error(_("could not read index"));
486488

487-
string_list_clear(files, 1);
488-
s.files = files;
489+
prefix_item_list_clear(files);
490+
s.files = &files->items;
489491
hashmap_init(&s.file_map, pathname_entry_cmp, NULL, 0);
490492

491493
for (i = 0; i < 2; i++) {
@@ -524,7 +526,7 @@ static int get_modified_files(struct repository *r,
524526
hashmap_free_entries(&s.file_map, struct pathname_entry, ent);
525527

526528
/* While the diffs are ordered already, we ran *two* diffs... */
527-
string_list_sort(files);
529+
string_list_sort(&files->items);
528530

529531
return 0;
530532
}
@@ -559,43 +561,105 @@ static int is_valid_prefix(const char *prefix, size_t prefix_len)
559561
}
560562

561563
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;
564566
};
565567

566568
static void print_file_item(int i, int selected, struct string_list_item *item,
567569
void *print_file_item_data)
568570
{
569571
struct file_item *c = item->util;
570572
struct print_file_item_data *d = print_file_item_data;
573+
const char *highlighted = NULL;
571574

572575
strbuf_reset(&d->index);
573576
strbuf_reset(&d->worktree);
574577
strbuf_reset(&d->buf);
575578

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+
576589
render_adddel(&d->worktree, &c->worktree, _("nothing"));
577590
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);
580594

581595
printf("%c%2d: %s", selected ? '*' : ' ', i + 1, d->buf.buf);
582596
}
583597

584598
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)
586601
{
587602
if (get_modified_files(s->r, NO_FILTER, files, ps) < 0)
588603
return -1;
589604

590-
list(s, files, NULL, opts);
605+
list(s, &files->items, NULL, &opts->list_opts);
591606
putchar('\n');
592607

593608
return 0;
594609
}
595610

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+
596660
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)
599663
{
600664
color_fprintf_ln(stdout, s->help_color, "status - %s",
601665
_("show paths with changes"));
@@ -613,9 +677,29 @@ static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
613677
return 0;
614678
}
615679

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+
616700
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);
619703

620704
struct command_item {
621705
size_t prefix_length;
@@ -667,18 +751,21 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
667751
command_t command;
668752
} command_list[] = {
669753
{ "status", run_status },
754+
{ "update", run_update },
670755
{ "help", run_help },
671756
};
672757
struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT;
673758

674759
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
676762
};
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
679766
};
680767
struct strbuf header = STRBUF_INIT;
681-
struct string_list files = STRING_LIST_INIT_DUP;
768+
struct prefix_item_list files = PREFIX_ITEM_LIST_INIT;
682769
ssize_t i;
683770
int res = 0;
684771

@@ -699,11 +786,13 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
699786
data.color = s.prompt_color;
700787
data.reset = s.reset_color;
701788
}
789+
print_file_item_data.color = data.color;
790+
print_file_item_data.reset = data.reset;
702791

703792
strbuf_addstr(&header, " ");
704793
strbuf_addf(&header, print_file_item_data.modified_fmt,
705794
_("staged"), _("unstaged"), _("path"));
706-
opts.header = header.buf;
795+
opts.list_opts.header = header.buf;
707796

708797
if (discard_index(r->index) < 0 ||
709798
repo_read_index(r) < 0 ||
@@ -727,8 +816,9 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
727816
}
728817
}
729818

730-
string_list_clear(&files, 1);
819+
prefix_item_list_clear(&files);
731820
strbuf_release(&print_file_item_data.buf);
821+
strbuf_release(&print_file_item_data.name);
732822
strbuf_release(&print_file_item_data.index);
733823
strbuf_release(&print_file_item_data.worktree);
734824
strbuf_release(&header);

0 commit comments

Comments
 (0)