Skip to content

Commit

Permalink
built-in add -i: implement the update command
Browse files Browse the repository at this point in the history
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]>
Signed-off-by: Junio C Hamano <[email protected]>
  • Loading branch information
dscho authored and gitster committed Dec 1, 2019
1 parent f37c226 commit a8c45be
Showing 1 changed file with 110 additions and 20 deletions.
130 changes: 110 additions & 20 deletions add-interactive.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "revision.h"
#include "refs.h"
#include "string-list.h"
#include "lockfile.h"

struct add_i_state {
struct repository *r;
Expand Down Expand Up @@ -377,6 +378,7 @@ struct adddel {
};

struct file_item {
size_t prefix_length;
struct adddel index, worktree;
};

Expand Down Expand Up @@ -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;
Expand All @@ -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++) {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -559,43 +561,105 @@ 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,
void *print_file_item_data)
{
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"));
Expand All @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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 ||
Expand All @@ -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);
Expand Down

0 comments on commit a8c45be

Please sign in to comment.