Merge branch 'sg/unpack-progress-throughput'
[git] / add-interactive.c
1 #include "cache.h"
2 #include "add-interactive.h"
3 #include "color.h"
4 #include "config.h"
5 #include "diffcore.h"
6 #include "revision.h"
7 #include "refs.h"
8 #include "string-list.h"
9
10 struct add_i_state {
11         struct repository *r;
12         int use_color;
13         char header_color[COLOR_MAXLEN];
14         char help_color[COLOR_MAXLEN];
15         char prompt_color[COLOR_MAXLEN];
16         char error_color[COLOR_MAXLEN];
17         char reset_color[COLOR_MAXLEN];
18 };
19
20 static void init_color(struct repository *r, struct add_i_state *s,
21                        const char *slot_name, char *dst,
22                        const char *default_color)
23 {
24         char *key = xstrfmt("color.interactive.%s", slot_name);
25         const char *value;
26
27         if (!s->use_color)
28                 dst[0] = '\0';
29         else if (repo_config_get_value(r, key, &value) ||
30                  color_parse(value, dst))
31                 strlcpy(dst, default_color, COLOR_MAXLEN);
32
33         free(key);
34 }
35
36 static void init_add_i_state(struct add_i_state *s, struct repository *r)
37 {
38         const char *value;
39
40         s->r = r;
41
42         if (repo_config_get_value(r, "color.interactive", &value))
43                 s->use_color = -1;
44         else
45                 s->use_color =
46                         git_config_colorbool("color.interactive", value);
47         s->use_color = want_color(s->use_color);
48
49         init_color(r, s, "header", s->header_color, GIT_COLOR_BOLD);
50         init_color(r, s, "help", s->help_color, GIT_COLOR_BOLD_RED);
51         init_color(r, s, "prompt", s->prompt_color, GIT_COLOR_BOLD_BLUE);
52         init_color(r, s, "error", s->error_color, GIT_COLOR_BOLD_RED);
53         init_color(r, s, "reset", s->reset_color, GIT_COLOR_RESET);
54 }
55
56 /*
57  * A "prefix item list" is a list of items that are identified by a string, and
58  * a unique prefix (if any) is determined for each item.
59  *
60  * It is implemented in the form of a pair of `string_list`s, the first one
61  * duplicating the strings, with the `util` field pointing at a structure whose
62  * first field must be `size_t prefix_length`.
63  *
64  * That `prefix_length` field will be computed by `find_unique_prefixes()`; It
65  * will be set to zero if no valid, unique prefix could be found.
66  *
67  * The second `string_list` is called `sorted` and does _not_ duplicate the
68  * strings but simply reuses the first one's, with the `util` field pointing at
69  * the `string_item_list` of the first `string_list`. It  will be populated and
70  * sorted by `find_unique_prefixes()`.
71  */
72 struct prefix_item_list {
73         struct string_list items;
74         struct string_list sorted;
75         size_t min_length, max_length;
76 };
77 #define PREFIX_ITEM_LIST_INIT \
78         { STRING_LIST_INIT_DUP, STRING_LIST_INIT_NODUP, 1, 4 }
79
80 static void prefix_item_list_clear(struct prefix_item_list *list)
81 {
82         string_list_clear(&list->items, 1);
83         string_list_clear(&list->sorted, 0);
84 }
85
86 static void extend_prefix_length(struct string_list_item *p,
87                                  const char *other_string, size_t max_length)
88 {
89         size_t *len = p->util;
90
91         if (!*len || memcmp(p->string, other_string, *len))
92                 return;
93
94         for (;;) {
95                 char c = p->string[*len];
96
97                 /*
98                  * Is `p` a strict prefix of `other`? Or have we exhausted the
99                  * maximal length of the prefix? Or is the current character a
100                  * multi-byte UTF-8 one? If so, there is no valid, unique
101                  * prefix.
102                  */
103                 if (!c || ++*len > max_length || !isascii(c)) {
104                         *len = 0;
105                         break;
106                 }
107
108                 if (c != other_string[*len - 1])
109                         break;
110         }
111 }
112
113 static void find_unique_prefixes(struct prefix_item_list *list)
114 {
115         size_t i;
116
117         if (list->sorted.nr == list->items.nr)
118                 return;
119
120         string_list_clear(&list->sorted, 0);
121         /* Avoid reallocating incrementally */
122         list->sorted.items = xmalloc(st_mult(sizeof(*list->sorted.items),
123                                              list->items.nr));
124         list->sorted.nr = list->sorted.alloc = list->items.nr;
125
126         for (i = 0; i < list->items.nr; i++) {
127                 list->sorted.items[i].string = list->items.items[i].string;
128                 list->sorted.items[i].util = list->items.items + i;
129         }
130
131         string_list_sort(&list->sorted);
132
133         for (i = 0; i < list->sorted.nr; i++) {
134                 struct string_list_item *sorted_item = list->sorted.items + i;
135                 struct string_list_item *item = sorted_item->util;
136                 size_t *len = item->util;
137
138                 *len = 0;
139                 while (*len < list->min_length) {
140                         char c = item->string[(*len)++];
141
142                         if (!c || !isascii(c)) {
143                                 *len = 0;
144                                 break;
145                         }
146                 }
147
148                 if (i > 0)
149                         extend_prefix_length(item, sorted_item[-1].string,
150                                              list->max_length);
151                 if (i + 1 < list->sorted.nr)
152                         extend_prefix_length(item, sorted_item[1].string,
153                                              list->max_length);
154         }
155 }
156
157 static ssize_t find_unique(const char *string, struct prefix_item_list *list)
158 {
159         int index = string_list_find_insert_index(&list->sorted, string, 1);
160         struct string_list_item *item;
161
162         if (list->items.nr != list->sorted.nr)
163                 BUG("prefix_item_list in inconsistent state (%"PRIuMAX
164                     " vs %"PRIuMAX")",
165                     (uintmax_t)list->items.nr, (uintmax_t)list->sorted.nr);
166
167         if (index < 0)
168                 item = list->sorted.items[-1 - index].util;
169         else if (index > 0 &&
170                  starts_with(list->sorted.items[index - 1].string, string))
171                 return -1;
172         else if (index + 1 < list->sorted.nr &&
173                  starts_with(list->sorted.items[index + 1].string, string))
174                 return -1;
175         else if (index < list->sorted.nr)
176                 item = list->sorted.items[index].util;
177         else
178                 return -1;
179         return item - list->items.items;
180 }
181
182 struct list_options {
183         int columns;
184         const char *header;
185         void (*print_item)(int i, struct string_list_item *item, void *print_item_data);
186         void *print_item_data;
187 };
188
189 static void list(struct add_i_state *s, struct string_list *list,
190                  struct list_options *opts)
191 {
192         int i, last_lf = 0;
193
194         if (!list->nr)
195                 return;
196
197         if (opts->header)
198                 color_fprintf_ln(stdout, s->header_color,
199                                  "%s", opts->header);
200
201         for (i = 0; i < list->nr; i++) {
202                 opts->print_item(i, list->items + i, opts->print_item_data);
203
204                 if ((opts->columns) && ((i + 1) % (opts->columns))) {
205                         putchar('\t');
206                         last_lf = 0;
207                 }
208                 else {
209                         putchar('\n');
210                         last_lf = 1;
211                 }
212         }
213
214         if (!last_lf)
215                 putchar('\n');
216 }
217 struct list_and_choose_options {
218         struct list_options list_opts;
219
220         const char *prompt;
221         void (*print_help)(struct add_i_state *s);
222 };
223
224 #define LIST_AND_CHOOSE_ERROR (-1)
225 #define LIST_AND_CHOOSE_QUIT  (-2)
226
227 /*
228  * Returns the selected index.
229  *
230  * If an error occurred, returns `LIST_AND_CHOOSE_ERROR`. Upon EOF,
231  * `LIST_AND_CHOOSE_QUIT` is returned.
232  */
233 static ssize_t list_and_choose(struct add_i_state *s,
234                                struct prefix_item_list *items,
235                                struct list_and_choose_options *opts)
236 {
237         struct strbuf input = STRBUF_INIT;
238         ssize_t res = LIST_AND_CHOOSE_ERROR;
239
240         find_unique_prefixes(items);
241
242         for (;;) {
243                 char *p;
244
245                 strbuf_reset(&input);
246
247                 list(s, &items->items, &opts->list_opts);
248
249                 color_fprintf(stdout, s->prompt_color, "%s", opts->prompt);
250                 fputs("> ", stdout);
251                 fflush(stdout);
252
253                 if (strbuf_getline(&input, stdin) == EOF) {
254                         putchar('\n');
255                         res = LIST_AND_CHOOSE_QUIT;
256                         break;
257                 }
258                 strbuf_trim(&input);
259
260                 if (!input.len)
261                         break;
262
263                 if (!strcmp(input.buf, "?")) {
264                         opts->print_help(s);
265                         continue;
266                 }
267
268                 p = input.buf;
269                 for (;;) {
270                         size_t sep = strcspn(p, " \t\r\n,");
271                         ssize_t index = -1;
272
273                         if (!sep) {
274                                 if (!*p)
275                                         break;
276                                 p++;
277                                 continue;
278                         }
279
280                         if (isdigit(*p)) {
281                                 char *endp;
282                                 index = strtoul(p, &endp, 10) - 1;
283                                 if (endp != p + sep)
284                                         index = -1;
285                         }
286
287                         if (p[sep])
288                                 p[sep++] = '\0';
289                         if (index < 0)
290                                 index = find_unique(p, items);
291
292                         if (index < 0 || index >= items->items.nr)
293                                 color_fprintf_ln(stdout, s->error_color,
294                                                  _("Huh (%s)?"), p);
295                         else {
296                                 res = index;
297                                 break;
298                         }
299
300                         p += sep;
301                 }
302
303                 if (res != LIST_AND_CHOOSE_ERROR)
304                         break;
305         }
306
307         strbuf_release(&input);
308         return res;
309 }
310
311 struct adddel {
312         uintmax_t add, del;
313         unsigned seen:1, binary:1;
314 };
315
316 struct file_item {
317         struct adddel index, worktree;
318 };
319
320 static void add_file_item(struct string_list *files, const char *name)
321 {
322         struct file_item *item = xcalloc(sizeof(*item), 1);
323
324         string_list_append(files, name)->util = item;
325 }
326
327 struct pathname_entry {
328         struct hashmap_entry ent;
329         const char *name;
330         struct file_item *item;
331 };
332
333 static int pathname_entry_cmp(const void *unused_cmp_data,
334                               const struct hashmap_entry *he1,
335                               const struct hashmap_entry *he2,
336                               const void *name)
337 {
338         const struct pathname_entry *e1 =
339                 container_of(he1, const struct pathname_entry, ent);
340         const struct pathname_entry *e2 =
341                 container_of(he2, const struct pathname_entry, ent);
342
343         return strcmp(e1->name, name ? (const char *)name : e2->name);
344 }
345
346 struct collection_status {
347         enum { FROM_WORKTREE = 0, FROM_INDEX = 1 } phase;
348
349         const char *reference;
350
351         struct string_list *files;
352         struct hashmap file_map;
353 };
354
355 static void collect_changes_cb(struct diff_queue_struct *q,
356                                struct diff_options *options,
357                                void *data)
358 {
359         struct collection_status *s = data;
360         struct diffstat_t stat = { 0 };
361         int i;
362
363         if (!q->nr)
364                 return;
365
366         compute_diffstat(options, &stat, q);
367
368         for (i = 0; i < stat.nr; i++) {
369                 const char *name = stat.files[i]->name;
370                 int hash = strhash(name);
371                 struct pathname_entry *entry;
372                 struct file_item *file_item;
373                 struct adddel *adddel;
374
375                 entry = hashmap_get_entry_from_hash(&s->file_map, hash, name,
376                                                     struct pathname_entry, ent);
377                 if (!entry) {
378                         add_file_item(s->files, name);
379
380                         entry = xcalloc(sizeof(*entry), 1);
381                         hashmap_entry_init(&entry->ent, hash);
382                         entry->name = s->files->items[s->files->nr - 1].string;
383                         entry->item = s->files->items[s->files->nr - 1].util;
384                         hashmap_add(&s->file_map, &entry->ent);
385                 }
386
387                 file_item = entry->item;
388                 adddel = s->phase == FROM_INDEX ?
389                         &file_item->index : &file_item->worktree;
390                 adddel->seen = 1;
391                 adddel->add = stat.files[i]->added;
392                 adddel->del = stat.files[i]->deleted;
393                 if (stat.files[i]->is_binary)
394                         adddel->binary = 1;
395         }
396         free_diffstat_info(&stat);
397 }
398
399 static int get_modified_files(struct repository *r, struct string_list *files,
400                               const struct pathspec *ps)
401 {
402         struct object_id head_oid;
403         int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
404                                              &head_oid, NULL);
405         struct collection_status s = { FROM_WORKTREE };
406
407         if (discard_index(r->index) < 0 ||
408             repo_read_index_preload(r, ps, 0) < 0)
409                 return error(_("could not read index"));
410
411         string_list_clear(files, 1);
412         s.files = files;
413         hashmap_init(&s.file_map, pathname_entry_cmp, NULL, 0);
414
415         for (s.phase = FROM_WORKTREE; s.phase <= FROM_INDEX; s.phase++) {
416                 struct rev_info rev;
417                 struct setup_revision_opt opt = { 0 };
418
419                 opt.def = is_initial ?
420                         empty_tree_oid_hex() : oid_to_hex(&head_oid);
421
422                 init_revisions(&rev, NULL);
423                 setup_revisions(0, NULL, &rev, &opt);
424
425                 rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
426                 rev.diffopt.format_callback = collect_changes_cb;
427                 rev.diffopt.format_callback_data = &s;
428
429                 if (ps)
430                         copy_pathspec(&rev.prune_data, ps);
431
432                 if (s.phase == FROM_INDEX)
433                         run_diff_index(&rev, 1);
434                 else {
435                         rev.diffopt.flags.ignore_dirty_submodules = 1;
436                         run_diff_files(&rev, 0);
437                 }
438         }
439         hashmap_free_entries(&s.file_map, struct pathname_entry, ent);
440
441         /* While the diffs are ordered already, we ran *two* diffs... */
442         string_list_sort(files);
443
444         return 0;
445 }
446
447 static void render_adddel(struct strbuf *buf,
448                                 struct adddel *ad, const char *no_changes)
449 {
450         if (ad->binary)
451                 strbuf_addstr(buf, _("binary"));
452         else if (ad->seen)
453                 strbuf_addf(buf, "+%"PRIuMAX"/-%"PRIuMAX,
454                             (uintmax_t)ad->add, (uintmax_t)ad->del);
455         else
456                 strbuf_addstr(buf, no_changes);
457 }
458
459 /* filters out prefixes which have special meaning to list_and_choose() */
460 static int is_valid_prefix(const char *prefix, size_t prefix_len)
461 {
462         return prefix_len && prefix &&
463                 /*
464                  * We expect `prefix` to be NUL terminated, therefore this
465                  * `strcspn()` call is okay, even if it might do much more
466                  * work than strictly necessary.
467                  */
468                 strcspn(prefix, " \t\r\n,") >= prefix_len &&    /* separators */
469                 *prefix != '-' &&                               /* deselection */
470                 !isdigit(*prefix) &&                            /* selection */
471                 (prefix_len != 1 ||
472                  (*prefix != '*' &&                             /* "all" wildcard */
473                   *prefix != '?'));                             /* prompt help */
474 }
475
476 struct print_file_item_data {
477         const char *modified_fmt;
478         struct strbuf buf, index, worktree;
479 };
480
481 static void print_file_item(int i, struct string_list_item *item,
482                             void *print_file_item_data)
483 {
484         struct file_item *c = item->util;
485         struct print_file_item_data *d = print_file_item_data;
486
487         strbuf_reset(&d->index);
488         strbuf_reset(&d->worktree);
489         strbuf_reset(&d->buf);
490
491         render_adddel(&d->worktree, &c->worktree, _("nothing"));
492         render_adddel(&d->index, &c->index, _("unchanged"));
493         strbuf_addf(&d->buf, d->modified_fmt,
494                     d->index.buf, d->worktree.buf, item->string);
495
496         printf(" %2d: %s", i + 1, d->buf.buf);
497 }
498
499 static int run_status(struct add_i_state *s, const struct pathspec *ps,
500                       struct string_list *files, struct list_options *opts)
501 {
502         if (get_modified_files(s->r, files, ps) < 0)
503                 return -1;
504
505         list(s, files, opts);
506         putchar('\n');
507
508         return 0;
509 }
510
511 static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
512                     struct string_list *unused_files,
513                     struct list_options *unused_opts)
514 {
515         color_fprintf_ln(stdout, s->help_color, "status        - %s",
516                          _("show paths with changes"));
517         color_fprintf_ln(stdout, s->help_color, "update        - %s",
518                          _("add working tree state to the staged set of changes"));
519         color_fprintf_ln(stdout, s->help_color, "revert        - %s",
520                          _("revert staged set of changes back to the HEAD version"));
521         color_fprintf_ln(stdout, s->help_color, "patch         - %s",
522                          _("pick hunks and update selectively"));
523         color_fprintf_ln(stdout, s->help_color, "diff          - %s",
524                          _("view diff between HEAD and index"));
525         color_fprintf_ln(stdout, s->help_color, "add untracked - %s",
526                          _("add contents of untracked files to the staged set of changes"));
527
528         return 0;
529 }
530
531 typedef int (*command_t)(struct add_i_state *s, const struct pathspec *ps,
532                          struct string_list *files,
533                          struct list_options *opts);
534
535 struct command_item {
536         size_t prefix_length;
537         command_t command;
538 };
539
540 struct print_command_item_data {
541         const char *color, *reset;
542 };
543
544 static void print_command_item(int i, struct string_list_item *item,
545                                void *print_command_item_data)
546 {
547         struct print_command_item_data *d = print_command_item_data;
548         struct command_item *util = item->util;
549
550         if (!util->prefix_length ||
551             !is_valid_prefix(item->string, util->prefix_length))
552                 printf(" %2d: %s", i + 1, item->string);
553         else
554                 printf(" %2d: %s%.*s%s%s", i + 1,
555                        d->color, (int)util->prefix_length, item->string,
556                        d->reset, item->string + util->prefix_length);
557 }
558
559 static void command_prompt_help(struct add_i_state *s)
560 {
561         const char *help_color = s->help_color;
562         color_fprintf_ln(stdout, help_color, "%s", _("Prompt help:"));
563         color_fprintf_ln(stdout, help_color, "1          - %s",
564                          _("select a numbered item"));
565         color_fprintf_ln(stdout, help_color, "foo        - %s",
566                          _("select item based on unique prefix"));
567         color_fprintf_ln(stdout, help_color, "           - %s",
568                          _("(empty) select nothing"));
569 }
570
571 int run_add_i(struct repository *r, const struct pathspec *ps)
572 {
573         struct add_i_state s = { NULL };
574         struct print_command_item_data data = { "[", "]" };
575         struct list_and_choose_options main_loop_opts = {
576                 { 4, N_("*** Commands ***"), print_command_item, &data },
577                 N_("What now"), command_prompt_help
578         };
579         struct {
580                 const char *string;
581                 command_t command;
582         } command_list[] = {
583                 { "status", run_status },
584                 { "help", run_help },
585         };
586         struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT;
587
588         struct print_file_item_data print_file_item_data = {
589                 "%12s %12s %s", STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
590         };
591         struct list_options opts = {
592                 0, NULL, print_file_item, &print_file_item_data
593         };
594         struct strbuf header = STRBUF_INIT;
595         struct string_list files = STRING_LIST_INIT_DUP;
596         ssize_t i;
597         int res = 0;
598
599         for (i = 0; i < ARRAY_SIZE(command_list); i++) {
600                 struct command_item *util = xcalloc(sizeof(*util), 1);
601                 util->command = command_list[i].command;
602                 string_list_append(&commands.items, command_list[i].string)
603                         ->util = util;
604         }
605
606         init_add_i_state(&s, r);
607
608         /*
609          * When color was asked for, use the prompt color for
610          * highlighting, otherwise use square brackets.
611          */
612         if (s.use_color) {
613                 data.color = s.prompt_color;
614                 data.reset = s.reset_color;
615         }
616
617         strbuf_addstr(&header, "      ");
618         strbuf_addf(&header, print_file_item_data.modified_fmt,
619                     _("staged"), _("unstaged"), _("path"));
620         opts.header = header.buf;
621
622         if (discard_index(r->index) < 0 ||
623             repo_read_index(r) < 0 ||
624             repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
625                                          NULL, NULL, NULL) < 0)
626                 warning(_("could not refresh index"));
627
628         res = run_status(&s, ps, &files, &opts);
629
630         for (;;) {
631                 i = list_and_choose(&s, &commands, &main_loop_opts);
632                 if (i == LIST_AND_CHOOSE_QUIT) {
633                         printf(_("Bye.\n"));
634                         res = 0;
635                         break;
636                 }
637                 if (i != LIST_AND_CHOOSE_ERROR) {
638                         struct command_item *util =
639                                 commands.items.items[i].util;
640                         res = util->command(&s, ps, &files, &opts);
641                 }
642         }
643
644         string_list_clear(&files, 1);
645         strbuf_release(&print_file_item_data.buf);
646         strbuf_release(&print_file_item_data.index);
647         strbuf_release(&print_file_item_data.worktree);
648         strbuf_release(&header);
649         prefix_item_list_clear(&commands);
650
651         return res;
652 }