Skip to content

Commit cba5f13

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 c324b47 commit cba5f13

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;
@@ -376,6 +377,7 @@ struct adddel {
376377
};
377378

378379
struct file_item {
380+
size_t prefix_length;
379381
struct adddel index, worktree;
380382
};
381383

@@ -470,7 +472,7 @@ enum modified_files_filter {
470472

471473
static int get_modified_files(struct repository *r,
472474
enum modified_files_filter filter,
473-
struct string_list *files,
475+
struct prefix_item_list *files,
474476
const struct pathspec *ps)
475477
{
476478
struct object_id head_oid;
@@ -483,8 +485,8 @@ static int get_modified_files(struct repository *r,
483485
repo_read_index_preload(r, ps, 0) < 0)
484486
return error(_("could not read index"));
485487

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

490492
for (i = 0; i < 2; i++) {
@@ -520,7 +522,7 @@ static int get_modified_files(struct repository *r,
520522
hashmap_free_entries(&s.file_map, struct pathname_entry, ent);
521523

522524
/* While the diffs are ordered already, we ran *two* diffs... */
523-
string_list_sort(files);
525+
string_list_sort(&files->items);
524526

525527
return 0;
526528
}
@@ -555,43 +557,105 @@ static int is_valid_prefix(const char *prefix, size_t prefix_len)
555557
}
556558

557559
struct print_file_item_data {
558-
const char *modified_fmt;
559-
struct strbuf buf, index, worktree;
560+
const char *modified_fmt, *color, *reset;
561+
struct strbuf buf, name, index, worktree;
560562
};
561563

562564
static void print_file_item(int i, int selected, struct string_list_item *item,
563565
void *print_file_item_data)
564566
{
565567
struct file_item *c = item->util;
566568
struct print_file_item_data *d = print_file_item_data;
569+
const char *highlighted = NULL;
567570

568571
strbuf_reset(&d->index);
569572
strbuf_reset(&d->worktree);
570573
strbuf_reset(&d->buf);
571574

575+
/* Format the item with the prefix highlighted. */
576+
if (c->prefix_length > 0 &&
577+
is_valid_prefix(item->string, c->prefix_length)) {
578+
strbuf_reset(&d->name);
579+
strbuf_addf(&d->name, "%s%.*s%s%s", d->color,
580+
(int)c->prefix_length, item->string, d->reset,
581+
item->string + c->prefix_length);
582+
highlighted = d->name.buf;
583+
}
584+
572585
render_adddel(&d->worktree, &c->worktree, _("nothing"));
573586
render_adddel(&d->index, &c->index, _("unchanged"));
574-
strbuf_addf(&d->buf, d->modified_fmt,
575-
d->index.buf, d->worktree.buf, item->string);
587+
588+
strbuf_addf(&d->buf, d->modified_fmt, d->index.buf, d->worktree.buf,
589+
highlighted ? highlighted : item->string);
576590

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

580594
static int run_status(struct add_i_state *s, const struct pathspec *ps,
581-
struct string_list *files, struct list_options *opts)
595+
struct prefix_item_list *files,
596+
struct list_and_choose_options *opts)
582597
{
583598
if (get_modified_files(s->r, NO_FILTER, files, ps) < 0)
584599
return -1;
585600

586-
list(s, files, NULL, opts);
601+
list(s, &files->items, NULL, &opts->list_opts);
587602
putchar('\n');
588603

589604
return 0;
590605
}
591606

607+
static int run_update(struct add_i_state *s, const struct pathspec *ps,
608+
struct prefix_item_list *files,
609+
struct list_and_choose_options *opts)
610+
{
611+
int res = 0, fd;
612+
size_t count, i;
613+
struct lock_file index_lock;
614+
615+
if (get_modified_files(s->r, WORKTREE_ONLY, files, ps) < 0)
616+
return -1;
617+
618+
if (!files->items.nr) {
619+
putchar('\n');
620+
return 0;
621+
}
622+
623+
opts->prompt = N_("Update");
624+
count = list_and_choose(s, files, opts);
625+
if (count <= 0) {
626+
putchar('\n');
627+
return 0;
628+
}
629+
630+
fd = repo_hold_locked_index(s->r, &index_lock, LOCK_REPORT_ON_ERROR);
631+
if (fd < 0) {
632+
putchar('\n');
633+
return -1;
634+
}
635+
636+
for (i = 0; i < files->items.nr; i++) {
637+
const char *name = files->items.items[i].string;
638+
if (files->selected[i] &&
639+
add_file_to_index(s->r->index, name, 0) < 0) {
640+
res = error(_("could not stage '%s'"), name);
641+
break;
642+
}
643+
}
644+
645+
if (!res && write_locked_index(s->r->index, &index_lock, COMMIT_LOCK) < 0)
646+
res = error(_("could not write index"));
647+
648+
if (!res)
649+
printf(Q_("updated %d path\n",
650+
"updated %d paths\n", count), (int)count);
651+
652+
putchar('\n');
653+
return res;
654+
}
655+
592656
static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
593-
struct string_list *unused_files,
594-
struct list_options *unused_opts)
657+
struct prefix_item_list *unused_files,
658+
struct list_and_choose_options *unused_opts)
595659
{
596660
color_fprintf_ln(stdout, s->help_color, "status - %s",
597661
_("show paths with changes"));
@@ -609,9 +673,29 @@ static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
609673
return 0;
610674
}
611675

676+
static void choose_prompt_help(struct add_i_state *s)
677+
{
678+
color_fprintf_ln(stdout, s->help_color, "%s",
679+
_("Prompt help:"));
680+
color_fprintf_ln(stdout, s->help_color, "1 - %s",
681+
_("select a single item"));
682+
color_fprintf_ln(stdout, s->help_color, "3-5 - %s",
683+
_("select a range of items"));
684+
color_fprintf_ln(stdout, s->help_color, "2-3,6-9 - %s",
685+
_("select multiple ranges"));
686+
color_fprintf_ln(stdout, s->help_color, "foo - %s",
687+
_("select item based on unique prefix"));
688+
color_fprintf_ln(stdout, s->help_color, "-... - %s",
689+
_("unselect specified items"));
690+
color_fprintf_ln(stdout, s->help_color, "* - %s",
691+
_("choose all items"));
692+
color_fprintf_ln(stdout, s->help_color, " - %s",
693+
_("(empty) finish selecting"));
694+
}
695+
612696
typedef int (*command_t)(struct add_i_state *s, const struct pathspec *ps,
613-
struct string_list *files,
614-
struct list_options *opts);
697+
struct prefix_item_list *files,
698+
struct list_and_choose_options *opts);
615699

616700
struct command_item {
617701
size_t prefix_length;
@@ -663,18 +747,21 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
663747
command_t command;
664748
} command_list[] = {
665749
{ "status", run_status },
750+
{ "update", run_update },
666751
{ "help", run_help },
667752
};
668753
struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT;
669754

670755
struct print_file_item_data print_file_item_data = {
671-
"%12s %12s %s", STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
756+
"%12s %12s %s", NULL, NULL,
757+
STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
672758
};
673-
struct list_options opts = {
674-
0, NULL, print_file_item, &print_file_item_data
759+
struct list_and_choose_options opts = {
760+
{ 0, NULL, print_file_item, &print_file_item_data },
761+
NULL, 0, choose_prompt_help
675762
};
676763
struct strbuf header = STRBUF_INIT;
677-
struct string_list files = STRING_LIST_INIT_DUP;
764+
struct prefix_item_list files = PREFIX_ITEM_LIST_INIT;
678765
ssize_t i;
679766
int res = 0;
680767

@@ -695,11 +782,13 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
695782
data.color = s.prompt_color;
696783
data.reset = s.reset_color;
697784
}
785+
print_file_item_data.color = data.color;
786+
print_file_item_data.reset = data.reset;
698787

699788
strbuf_addstr(&header, " ");
700789
strbuf_addf(&header, print_file_item_data.modified_fmt,
701790
_("staged"), _("unstaged"), _("path"));
702-
opts.header = header.buf;
791+
opts.list_opts.header = header.buf;
703792

704793
if (discard_index(r->index) < 0 ||
705794
repo_read_index(r) < 0 ||
@@ -723,8 +812,9 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
723812
}
724813
}
725814

726-
string_list_clear(&files, 1);
815+
prefix_item_list_clear(&files);
727816
strbuf_release(&print_file_item_data.buf);
817+
strbuf_release(&print_file_item_data.name);
728818
strbuf_release(&print_file_item_data.index);
729819
strbuf_release(&print_file_item_data.worktree);
730820
strbuf_release(&header);

0 commit comments

Comments
 (0)