Raw
1 #define DISABLE_SIGN_COMPARE_WARNINGS
2
3 #include "git-compat-util.h"
4 #include "add-interactive.h"
5 #include "color.h"
6 #include "diffcore.h"
7 #include "gettext.h"
8 #include "hash.h"
9 #include "hex.h"
10 #include "preload-index.h"
11 #include "read-cache-ll.h"
12 #include "repository.h"
13 #include "revision.h"
14 #include "refs.h"
15 #include "string-list.h"
16 #include "lockfile.h"
17 #include "dir.h"
18 #include "run-command.h"
19 #include "prompt.h"
20 #include "tree.h"
21
22 void init_add_i_state(struct add_i_state *s, struct repository *r,
23 struct interactive_options *opts)
24 {
25 s->r = r;
26 interactive_config_init(&s->cfg, r, opts);
27 }
28
29 void clear_add_i_state(struct add_i_state *s)
30 {
31 interactive_config_clear(&s->cfg);
32 memset(s, 0, sizeof(*s));
33 interactive_config_clear(&s->cfg);
34 }
35
36 /*
37 * A "prefix item list" is a list of items that are identified by a string, and
38 * a unique prefix (if any) is determined for each item.
39 *
40 * It is implemented in the form of a pair of `string_list`s, the first one
41 * duplicating the strings, with the `util` field pointing at a structure whose
42 * first field must be `size_t prefix_length`.
43 *
44 * That `prefix_length` field will be computed by `find_unique_prefixes()`; It
45 * will be set to zero if no valid, unique prefix could be found.
46 *
47 * The second `string_list` is called `sorted` and does _not_ duplicate the
48 * strings but simply reuses the first one's, with the `util` field pointing at
49 * the `string_item_list` of the first `string_list`. It will be populated and
50 * sorted by `find_unique_prefixes()`.
51 */
52 struct prefix_item_list {
53 struct string_list items;
54 struct string_list sorted;
55 int *selected; /* for multi-selections */
56 size_t min_length, max_length;
57 };
58 #define PREFIX_ITEM_LIST_INIT { \
59 .items = STRING_LIST_INIT_DUP, \
60 .sorted = STRING_LIST_INIT_NODUP, \
61 .min_length = 1, \
62 .max_length = 4, \
63 }
64
65 static void prefix_item_list_clear(struct prefix_item_list *list)
66 {
67 string_list_clear(&list->items, 1);
68 string_list_clear(&list->sorted, 0);
69 FREE_AND_NULL(list->selected);
70 }
71
72 static void extend_prefix_length(struct string_list_item *p,
73 const char *other_string, size_t max_length)
74 {
75 size_t *len = p->util;
76
77 if (!*len || memcmp(p->string, other_string, *len))
78 return;
79
80 for (;;) {
81 char c = p->string[*len];
82
83 /*
84 * Is `p` a strict prefix of `other`? Or have we exhausted the
85 * maximal length of the prefix? Or is the current character a
86 * multi-byte UTF-8 one? If so, there is no valid, unique
87 * prefix.
88 */
89 if (!c || ++*len > max_length || !isascii(c)) {
90 *len = 0;
91 break;
92 }
93
94 if (c != other_string[*len - 1])
95 break;
96 }
97 }
98
99 static void find_unique_prefixes(struct prefix_item_list *list)
100 {
101 size_t i;
102
103 if (list->sorted.nr == list->items.nr)
104 return;
105
106 string_list_clear(&list->sorted, 0);
107 /* Avoid reallocating incrementally */
108 list->sorted.items = xmalloc(st_mult(sizeof(*list->sorted.items),
109 list->items.nr));
110 list->sorted.nr = list->sorted.alloc = list->items.nr;
111
112 for (i = 0; i < list->items.nr; i++) {
113 list->sorted.items[i].string = list->items.items[i].string;
114 list->sorted.items[i].util = list->items.items + i;
115 }
116
117 string_list_sort(&list->sorted);
118
119 for (i = 0; i < list->sorted.nr; i++) {
120 struct string_list_item *sorted_item = list->sorted.items + i;
121 struct string_list_item *item = sorted_item->util;
122 size_t *len = item->util;
123
124 *len = 0;
125 while (*len < list->min_length) {
126 char c = item->string[(*len)++];
127
128 if (!c || !isascii(c)) {
129 *len = 0;
130 break;
131 }
132 }
133
134 if (i > 0)
135 extend_prefix_length(item, sorted_item[-1].string,
136 list->max_length);
137 if (i + 1 < list->sorted.nr)
138 extend_prefix_length(item, sorted_item[1].string,
139 list->max_length);
140 }
141 }
142
143 static ssize_t find_unique(const char *string, struct prefix_item_list *list)
144 {
145 bool exact_match;
146 size_t index = string_list_find_insert_index(&list->sorted, string, &exact_match);
147 struct string_list_item *item;
148
149 if (list->items.nr != list->sorted.nr)
150 BUG("prefix_item_list in inconsistent state (%"PRIuMAX
151 " vs %"PRIuMAX")",
152 (uintmax_t)list->items.nr, (uintmax_t)list->sorted.nr);
153
154 if (exact_match)
155 item = list->sorted.items[index].util;
156 else if (index > 0 &&
157 starts_with(list->sorted.items[index - 1].string, string))
158 return -1;
159 else if (index + 1 < list->sorted.nr &&
160 starts_with(list->sorted.items[index + 1].string, string))
161 return -1;
162 else if (index < list->sorted.nr &&
163 starts_with(list->sorted.items[index].string, string))
164 item = list->sorted.items[index].util;
165 else
166 return -1;
167 return item - list->items.items;
168 }
169
170 struct list_options {
171 int columns;
172 const char *header;
173 void (*print_item)(int i, int selected, struct string_list_item *item,
174 void *print_item_data);
175 void *print_item_data;
176 };
177
178 static void list(struct add_i_state *s, struct string_list *list, int *selected,
179 struct list_options *opts)
180 {
181 int i, last_lf = 0;
182
183 if (!list->nr)
184 return;
185
186 if (opts->header)
187 color_fprintf_ln(stdout, s->cfg.header_color,
188 "%s", opts->header);
189
190 for (i = 0; i < list->nr; i++) {
191 opts->print_item(i, selected ? selected[i] : 0, list->items + i,
192 opts->print_item_data);
193
194 if ((opts->columns) && ((i + 1) % (opts->columns))) {
195 putchar('\t');
196 last_lf = 0;
197 }
198 else {
199 putchar('\n');
200 last_lf = 1;
201 }
202 }
203
204 if (!last_lf)
205 putchar('\n');
206 }
207 struct list_and_choose_options {
208 struct list_options list_opts;
209
210 const char *prompt;
211 enum {
212 SINGLETON = (1<<0),
213 IMMEDIATE = (1<<1),
214 } flags;
215 void (*print_help)(struct add_i_state *s);
216 };
217
218 #define LIST_AND_CHOOSE_ERROR (-1)
219 #define LIST_AND_CHOOSE_QUIT (-2)
220
221 /*
222 * Returns the selected index in singleton mode, the number of selected items
223 * otherwise.
224 *
225 * If an error occurred, returns `LIST_AND_CHOOSE_ERROR`. Upon EOF,
226 * `LIST_AND_CHOOSE_QUIT` is returned.
227 */
228 static ssize_t list_and_choose(struct add_i_state *s,
229 struct prefix_item_list *items,
230 struct list_and_choose_options *opts)
231 {
232 int singleton = opts->flags & SINGLETON;
233 int immediate = opts->flags & IMMEDIATE;
234
235 struct strbuf input = STRBUF_INIT;
236 ssize_t res = singleton ? LIST_AND_CHOOSE_ERROR : 0;
237
238 if (!singleton) {
239 free(items->selected);
240 CALLOC_ARRAY(items->selected, items->items.nr);
241 }
242
243 if (singleton && !immediate)
244 BUG("singleton requires immediate");
245
246 find_unique_prefixes(items);
247
248 for (;;) {
249 char *p;
250
251 strbuf_reset(&input);
252
253 list(s, &items->items, items->selected, &opts->list_opts);
254
255 color_fprintf(stdout, s->cfg.prompt_color, "%s", opts->prompt);
256 fputs(singleton ? "> " : ">> ", stdout);
257 fflush(stdout);
258
259 if (git_read_line_interactively(&input) == EOF) {
260 putchar('\n');
261 if (immediate)
262 res = LIST_AND_CHOOSE_QUIT;
263 break;
264 }
265
266 if (!input.len)
267 break;
268
269 if (!strcmp(input.buf, "?")) {
270 opts->print_help(s);
271 continue;
272 }
273
274 p = input.buf;
275 for (;;) {
276 size_t sep = strcspn(p, " \t\r\n,");
277 int choose = 1;
278 /* `from` is inclusive, `to` is exclusive */
279 ssize_t from = -1, to = -1;
280
281 if (!sep) {
282 if (!*p)
283 break;
284 p++;
285 continue;
286 }
287
288 /* Input that begins with '-'; de-select */
289 if (*p == '-') {
290 choose = 0;
291 p++;
292 sep--;
293 }
294
295 if (sep == 1 && *p == '*') {
296 from = 0;
297 to = items->items.nr;
298 } else if (isdigit(*p)) {
299 char *endp;
300 /*
301 * A range can be specified like 5-7 or 5-.
302 *
303 * Note: `from` is 0-based while the user input
304 * is 1-based, hence we have to decrement by
305 * one. We do not have to decrement `to` even
306 * if it is 0-based because it is an exclusive
307 * boundary.
308 */
309 from = strtoul(p, &endp, 10) - 1;
310 if (endp == p + sep)
311 to = from + 1;
312 else if (*endp == '-') {
313 if (isdigit(*(++endp)))
314 to = strtoul(endp, &endp, 10);
315 else
316 to = items->items.nr;
317 /* extra characters after the range? */
318 if (endp != p + sep)
319 from = -1;
320 }
321 }
322
323 if (p[sep])
324 p[sep++] = '\0';
325 if (from < 0) {
326 from = find_unique(p, items);
327 if (from >= 0)
328 to = from + 1;
329 }
330
331 if (from < 0 || from >= items->items.nr ||
332 (singleton && from + 1 != to)) {
333 color_fprintf_ln(stderr, s->cfg.error_color,
334 _("Huh (%s)?"), p);
335 break;
336 } else if (singleton) {
337 res = from;
338 break;
339 }
340
341 if (to > items->items.nr)
342 to = items->items.nr;
343
344 for (; from < to; from++)
345 if (items->selected[from] != choose) {
346 items->selected[from] = choose;
347 res += choose ? +1 : -1;
348 }
349
350 p += sep;
351 }
352
353 if ((immediate && res != LIST_AND_CHOOSE_ERROR) ||
354 !strcmp(input.buf, "*"))
355 break;
356 }
357
358 strbuf_release(&input);
359 return res;
360 }
361
362 struct adddel {
363 uintmax_t add, del;
364 unsigned seen:1, unmerged:1, binary:1;
365 };
366
367 struct file_item {
368 size_t prefix_length;
369 struct adddel index, worktree;
370 };
371
372 static void add_file_item(struct string_list *files, const char *name)
373 {
374 struct file_item *item = xcalloc(1, sizeof(*item));
375
376 string_list_append(files, name)->util = item;
377 }
378
379 struct pathname_entry {
380 struct hashmap_entry ent;
381 const char *name;
382 struct file_item *item;
383 };
384
385 static int pathname_entry_cmp(const void *cmp_data UNUSED,
386 const struct hashmap_entry *he1,
387 const struct hashmap_entry *he2,
388 const void *name)
389 {
390 const struct pathname_entry *e1 =
391 container_of(he1, const struct pathname_entry, ent);
392 const struct pathname_entry *e2 =
393 container_of(he2, const struct pathname_entry, ent);
394
395 return strcmp(e1->name, name ? (const char *)name : e2->name);
396 }
397
398 struct collection_status {
399 enum { FROM_WORKTREE = 0, FROM_INDEX = 1 } mode;
400
401 const char *reference;
402
403 unsigned skip_unseen:1;
404 size_t unmerged_count, binary_count;
405 struct string_list *files;
406 struct hashmap file_map;
407 };
408
409 static void collect_changes_cb(struct diff_queue_struct *q,
410 struct diff_options *options,
411 void *data)
412 {
413 struct collection_status *s = data;
414 struct diffstat_t stat = { 0 };
415 int i;
416
417 if (!q->nr)
418 return;
419
420 compute_diffstat(options, &stat, q);
421
422 for (i = 0; i < stat.nr; i++) {
423 const char *name = stat.files[i]->name;
424 int hash = strhash(name);
425 struct pathname_entry *entry;
426 struct file_item *file_item;
427 struct adddel *adddel, *other_adddel;
428
429 entry = hashmap_get_entry_from_hash(&s->file_map, hash, name,
430 struct pathname_entry, ent);
431 if (!entry) {
432 if (s->skip_unseen)
433 continue;
434
435 add_file_item(s->files, name);
436
437 CALLOC_ARRAY(entry, 1);
438 hashmap_entry_init(&entry->ent, hash);
439 entry->name = s->files->items[s->files->nr - 1].string;
440 entry->item = s->files->items[s->files->nr - 1].util;
441 hashmap_add(&s->file_map, &entry->ent);
442 }
443
444 file_item = entry->item;
445 adddel = s->mode == FROM_INDEX ?
446 &file_item->index : &file_item->worktree;
447 other_adddel = s->mode == FROM_INDEX ?
448 &file_item->worktree : &file_item->index;
449 adddel->seen = 1;
450 adddel->add = stat.files[i]->added;
451 adddel->del = stat.files[i]->deleted;
452 if (stat.files[i]->is_binary) {
453 if (!other_adddel->binary)
454 s->binary_count++;
455 adddel->binary = 1;
456 }
457 if (stat.files[i]->is_unmerged) {
458 if (!other_adddel->unmerged)
459 s->unmerged_count++;
460 adddel->unmerged = 1;
461 }
462 }
463 free_diffstat_info(&stat);
464 }
465
466 enum modified_files_filter {
467 NO_FILTER = 0,
468 WORKTREE_ONLY = 1,
469 INDEX_ONLY = 2,
470 };
471
472 static int get_modified_files(struct repository *r,
473 enum modified_files_filter filter,
474 struct prefix_item_list *files,
475 const struct pathspec *ps,
476 size_t *unmerged_count,
477 size_t *binary_count)
478 {
479 struct object_id head_oid;
480 int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(r),
481 "HEAD", RESOLVE_REF_READING,
482 &head_oid, NULL);
483 struct collection_status s = { 0 };
484 int i;
485
486 discard_index(r->index);
487 if (repo_read_index_preload(r, ps, 0) < 0)
488 return error(_("could not read index"));
489
490 prefix_item_list_clear(files);
491 s.files = &files->items;
492 hashmap_init(&s.file_map, pathname_entry_cmp, NULL, 0);
493
494 for (i = 0; i < 2; i++) {
495 struct rev_info rev;
496 struct setup_revision_opt opt = { 0 };
497
498 if (filter == INDEX_ONLY)
499 s.mode = (i == 0) ? FROM_INDEX : FROM_WORKTREE;
500 else
501 s.mode = (i == 0) ? FROM_WORKTREE : FROM_INDEX;
502 s.skip_unseen = filter && i;
503
504 opt.def = is_initial ?
505 empty_tree_oid_hex(r->hash_algo) : oid_to_hex(&head_oid);
506
507 repo_init_revisions(r, &rev, NULL);
508 setup_revisions(0, NULL, &rev, &opt);
509
510 rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
511 rev.diffopt.format_callback = collect_changes_cb;
512 rev.diffopt.format_callback_data = &s;
513
514 if (ps)
515 copy_pathspec(&rev.prune_data, ps);
516
517 if (s.mode == FROM_INDEX)
518 run_diff_index(&rev, DIFF_INDEX_CACHED);
519 else {
520 rev.diffopt.flags.ignore_dirty_submodules = 1;
521 run_diff_files(&rev, 0);
522 }
523
524 release_revisions(&rev);
525 }
526 hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);
527 if (unmerged_count)
528 *unmerged_count = s.unmerged_count;
529 if (binary_count)
530 *binary_count = s.binary_count;
531
532 /* While the diffs are ordered already, we ran *two* diffs... */
533 string_list_sort(&files->items);
534
535 return 0;
536 }
537
538 static void render_adddel(struct strbuf *buf,
539 struct adddel *ad, const char *no_changes)
540 {
541 if (ad->binary)
542 strbuf_addstr(buf, _("binary"));
543 else if (ad->seen)
544 strbuf_addf(buf, "+%"PRIuMAX"/-%"PRIuMAX,
545 (uintmax_t)ad->add, (uintmax_t)ad->del);
546 else
547 strbuf_addstr(buf, no_changes);
548 }
549
550 /* filters out prefixes which have special meaning to list_and_choose() */
551 static int is_valid_prefix(const char *prefix, size_t prefix_len)
552 {
553 return prefix_len && prefix &&
554 /*
555 * We expect `prefix` to be NUL terminated, therefore this
556 * `strcspn()` call is okay, even if it might do much more
557 * work than strictly necessary.
558 */
559 strcspn(prefix, " \t\r\n,") >= prefix_len && /* separators */
560 *prefix != '-' && /* deselection */
561 !isdigit(*prefix) && /* selection */
562 (prefix_len != 1 ||
563 (*prefix != '*' && /* "all" wildcard */
564 *prefix != '?')); /* prompt help */
565 }
566
567 struct print_file_item_data {
568 const char *modified_fmt, *color, *reset;
569 struct strbuf buf, name, index, worktree;
570 unsigned only_names:1;
571 };
572
573 static void print_file_item(int i, int selected, struct string_list_item *item,
574 void *print_file_item_data)
575 {
576 struct file_item *c = item->util;
577 struct print_file_item_data *d = print_file_item_data;
578 const char *highlighted = NULL;
579
580 strbuf_reset(&d->index);
581 strbuf_reset(&d->worktree);
582 strbuf_reset(&d->buf);
583
584 /* Format the item with the prefix highlighted. */
585 if (c->prefix_length > 0 &&
586 is_valid_prefix(item->string, c->prefix_length)) {
587 strbuf_reset(&d->name);
588 strbuf_addf(&d->name, "%s%.*s%s%s", d->color,
589 (int)c->prefix_length, item->string, d->reset,
590 item->string + c->prefix_length);
591 highlighted = d->name.buf;
592 }
593
594 if (d->only_names) {
595 printf("%c%2d: %s", selected ? '*' : ' ', i + 1,
596 highlighted ? highlighted : item->string);
597 return;
598 }
599
600 render_adddel(&d->worktree, &c->worktree, _("nothing"));
601 render_adddel(&d->index, &c->index, _("unchanged"));
602
603 strbuf_addf(&d->buf, d->modified_fmt, d->index.buf, d->worktree.buf,
604 highlighted ? highlighted : item->string);
605
606 printf("%c%2d: %s", selected ? '*' : ' ', i + 1, d->buf.buf);
607 }
608
609 static int run_status(struct add_i_state *s, const struct pathspec *ps,
610 struct prefix_item_list *files,
611 struct list_and_choose_options *opts)
612 {
613 if (get_modified_files(s->r, NO_FILTER, files, ps, NULL, NULL) < 0)
614 return -1;
615
616 list(s, &files->items, NULL, &opts->list_opts);
617 putchar('\n');
618
619 return 0;
620 }
621
622 static int run_update(struct add_i_state *s, const struct pathspec *ps,
623 struct prefix_item_list *files,
624 struct list_and_choose_options *opts)
625 {
626 int res = 0, fd;
627 size_t count, i;
628 struct lock_file index_lock;
629
630 if (get_modified_files(s->r, WORKTREE_ONLY, files, ps, NULL, NULL) < 0)
631 return -1;
632
633 if (!files->items.nr) {
634 putchar('\n');
635 return 0;
636 }
637
638 opts->prompt = N_("Update");
639 count = list_and_choose(s, files, opts);
640 if (count <= 0) {
641 putchar('\n');
642 return 0;
643 }
644
645 fd = repo_hold_locked_index(s->r, &index_lock, LOCK_REPORT_ON_ERROR);
646 if (fd < 0) {
647 putchar('\n');
648 return -1;
649 }
650
651 for (i = 0; i < files->items.nr; i++) {
652 const char *name = files->items.items[i].string;
653 struct stat st;
654
655 if (!files->selected[i])
656 continue;
657 if (lstat(name, &st) && is_missing_file_error(errno)) {
658 if (remove_file_from_index(s->r->index, name) < 0) {
659 res = error(_("could not stage '%s'"), name);
660 break;
661 }
662 } else if (add_file_to_index(s->r->index, name, 0) < 0) {
663 res = error(_("could not stage '%s'"), name);
664 break;
665 }
666 }
667
668 if (!res && write_locked_index(s->r->index, &index_lock, COMMIT_LOCK) < 0)
669 res = error(_("could not write index"));
670
671 if (!res)
672 printf(Q_("updated %d path\n",
673 "updated %d paths\n", count), (int)count);
674
675 putchar('\n');
676 return res;
677 }
678
679 static void revert_from_diff(struct diff_queue_struct *q,
680 struct diff_options *opt, void *data UNUSED)
681 {
682 int i, add_flags = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
683
684 for (i = 0; i < q->nr; i++) {
685 struct diff_filespec *one = q->queue[i]->one;
686 struct cache_entry *ce;
687
688 if (!(one->mode && !is_null_oid(&one->oid))) {
689 remove_file_from_index(opt->repo->index, one->path);
690 printf(_("note: %s is untracked now.\n"), one->path);
691 } else {
692 ce = make_cache_entry(opt->repo->index, one->mode,
693 &one->oid, one->path, 0, 0);
694 if (!ce)
695 die(_("make_cache_entry failed for path '%s'"),
696 one->path);
697 add_index_entry(opt->repo->index, ce, add_flags);
698 }
699 }
700 }
701
702 static int run_revert(struct add_i_state *s, const struct pathspec *ps,
703 struct prefix_item_list *files,
704 struct list_and_choose_options *opts)
705 {
706 int res = 0, fd;
707 size_t count, i, j;
708
709 struct object_id oid;
710 int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(s->r),
711 "HEAD", RESOLVE_REF_READING,
712 &oid,
713 NULL);
714 struct lock_file index_lock;
715 const char **paths;
716 struct tree *tree;
717 struct diff_options diffopt = { NULL };
718
719 if (get_modified_files(s->r, INDEX_ONLY, files, ps, NULL, NULL) < 0)
720 return -1;
721
722 if (!files->items.nr) {
723 putchar('\n');
724 return 0;
725 }
726
727 opts->prompt = N_("Revert");
728 count = list_and_choose(s, files, opts);
729 if (count <= 0)
730 goto finish_revert;
731
732 fd = repo_hold_locked_index(s->r, &index_lock, LOCK_REPORT_ON_ERROR);
733 if (fd < 0) {
734 res = -1;
735 goto finish_revert;
736 }
737
738 if (is_initial)
739 oidcpy(&oid, s->r->hash_algo->empty_tree);
740 else {
741 tree = repo_parse_tree_indirect(s->r, &oid);
742 if (!tree) {
743 res = error(_("Could not parse HEAD^{tree}"));
744 goto finish_revert;
745 }
746 oidcpy(&oid, &tree->object.oid);
747 }
748
749 ALLOC_ARRAY(paths, count + 1);
750 for (i = j = 0; i < files->items.nr; i++)
751 if (files->selected[i])
752 paths[j++] = files->items.items[i].string;
753 paths[j] = NULL;
754
755 parse_pathspec(&diffopt.pathspec, 0,
756 PATHSPEC_PREFER_FULL | PATHSPEC_LITERAL_PATH,
757 NULL, paths);
758
759 diffopt.output_format = DIFF_FORMAT_CALLBACK;
760 diffopt.format_callback = revert_from_diff;
761 diffopt.flags.override_submodule_config = 1;
762 diffopt.repo = s->r;
763
764 if (do_diff_cache(&oid, &diffopt)) {
765 diff_free(&diffopt);
766 res = -1;
767 } else {
768 diffcore_std(&diffopt);
769 diff_flush(&diffopt);
770 }
771 free(paths);
772
773 if (!res && write_locked_index(s->r->index, &index_lock,
774 COMMIT_LOCK) < 0)
775 res = -1;
776 else
777 res = repo_refresh_and_write_index(s->r, REFRESH_QUIET, 0, 1,
778 NULL, NULL, NULL);
779
780 if (!res)
781 printf(Q_("reverted %d path\n",
782 "reverted %d paths\n", count), (int)count);
783
784 finish_revert:
785 putchar('\n');
786 return res;
787 }
788
789 static int get_untracked_files(struct repository *r,
790 struct prefix_item_list *files,
791 const struct pathspec *ps)
792 {
793 struct dir_struct dir = { 0 };
794 size_t i;
795 struct strbuf buf = STRBUF_INIT;
796
797 if (repo_read_index(r) < 0)
798 return error(_("could not read index"));
799
800 prefix_item_list_clear(files);
801 setup_standard_excludes(&dir);
802 add_pattern_list(&dir, EXC_CMDL, "--exclude option");
803 fill_directory(&dir, r->index, ps);
804
805 for (i = 0; i < dir.nr; i++) {
806 struct dir_entry *ent = dir.entries[i];
807
808 if (index_name_is_other(r->index, ent->name, ent->len)) {
809 strbuf_reset(&buf);
810 strbuf_add(&buf, ent->name, ent->len);
811 add_file_item(&files->items, buf.buf);
812 }
813 }
814
815 strbuf_release(&buf);
816 dir_clear(&dir);
817 return 0;
818 }
819
820 static int run_add_untracked(struct add_i_state *s, const struct pathspec *ps,
821 struct prefix_item_list *files,
822 struct list_and_choose_options *opts)
823 {
824 struct print_file_item_data *d = opts->list_opts.print_item_data;
825 int res = 0, fd;
826 size_t count, i;
827 struct lock_file index_lock;
828
829 if (get_untracked_files(s->r, files, ps) < 0)
830 return -1;
831
832 if (!files->items.nr) {
833 printf(_("No untracked files.\n"));
834 goto finish_add_untracked;
835 }
836
837 opts->prompt = N_("Add untracked");
838 d->only_names = 1;
839 count = list_and_choose(s, files, opts);
840 d->only_names = 0;
841 if (count <= 0)
842 goto finish_add_untracked;
843
844 fd = repo_hold_locked_index(s->r, &index_lock, LOCK_REPORT_ON_ERROR);
845 if (fd < 0) {
846 res = -1;
847 goto finish_add_untracked;
848 }
849
850 for (i = 0; i < files->items.nr; i++) {
851 const char *name = files->items.items[i].string;
852 if (files->selected[i] &&
853 add_file_to_index(s->r->index, name, 0) < 0) {
854 res = error(_("could not stage '%s'"), name);
855 break;
856 }
857 }
858
859 if (!res &&
860 write_locked_index(s->r->index, &index_lock, COMMIT_LOCK) < 0)
861 res = error(_("could not write index"));
862
863 if (!res)
864 printf(Q_("added %d path\n",
865 "added %d paths\n", count), (int)count);
866
867 finish_add_untracked:
868 putchar('\n');
869 return res;
870 }
871
872 static int run_patch(struct add_i_state *s, const struct pathspec *ps,
873 struct prefix_item_list *files,
874 struct list_and_choose_options *opts)
875 {
876 int res = 0;
877 ssize_t count, i, j;
878 size_t unmerged_count = 0, binary_count = 0;
879
880 if (get_modified_files(s->r, WORKTREE_ONLY, files, ps,
881 &unmerged_count, &binary_count) < 0)
882 return -1;
883
884 if (unmerged_count || binary_count) {
885 for (i = j = 0; i < files->items.nr; i++) {
886 struct file_item *item = files->items.items[i].util;
887
888 if (item->index.binary || item->worktree.binary) {
889 free(item);
890 free(files->items.items[i].string);
891 } else if (item->index.unmerged ||
892 item->worktree.unmerged) {
893 color_fprintf_ln(stderr, s->cfg.error_color,
894 _("ignoring unmerged: %s"),
895 files->items.items[i].string);
896 free(item);
897 free(files->items.items[i].string);
898 } else
899 files->items.items[j++] = files->items.items[i];
900 }
901 files->items.nr = j;
902 }
903
904 if (!files->items.nr) {
905 if (binary_count)
906 fprintf(stderr, _("Only binary files changed.\n"));
907 else
908 fprintf(stderr, _("No changes.\n"));
909 return 0;
910 }
911
912 opts->prompt = N_("Patch update");
913 count = list_and_choose(s, files, opts);
914 if (count > 0) {
915 struct interactive_options opts = {
916 .context = s->cfg.context,
917 .interhunkcontext = s->cfg.interhunkcontext,
918 .auto_advance = s->cfg.auto_advance,
919 };
920 struct strvec args = STRVEC_INIT;
921 struct pathspec ps_selected = { 0 };
922
923 for (i = 0; i < files->items.nr; i++)
924 if (files->selected[i])
925 strvec_push(&args,
926 files->items.items[i].string);
927 parse_pathspec(&ps_selected,
928 PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
929 PATHSPEC_LITERAL_PATH, "", args.v);
930 res = run_add_p(s->r, ADD_P_ADD, &opts, NULL, &ps_selected, 0);
931 strvec_clear(&args);
932 clear_pathspec(&ps_selected);
933 }
934
935 return res;
936 }
937
938 static int run_diff(struct add_i_state *s, const struct pathspec *ps,
939 struct prefix_item_list *files,
940 struct list_and_choose_options *opts)
941 {
942 int res = 0;
943 ssize_t count, i;
944
945 struct object_id oid;
946 int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(s->r),
947 "HEAD", RESOLVE_REF_READING,
948 &oid,
949 NULL);
950 if (get_modified_files(s->r, INDEX_ONLY, files, ps, NULL, NULL) < 0)
951 return -1;
952
953 if (!files->items.nr) {
954 putchar('\n');
955 return 0;
956 }
957
958 opts->prompt = N_("Review diff");
959 opts->flags = IMMEDIATE;
960 count = list_and_choose(s, files, opts);
961 opts->flags = 0;
962 if (count > 0) {
963 struct child_process cmd = CHILD_PROCESS_INIT;
964
965 strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", NULL);
966 if (s->cfg.context != -1)
967 strvec_pushf(&cmd.args, "--unified=%i", s->cfg.context);
968 if (s->cfg.interhunkcontext != -1)
969 strvec_pushf(&cmd.args, "--inter-hunk-context=%i", s->cfg.interhunkcontext);
970 strvec_pushl(&cmd.args, oid_to_hex(!is_initial ? &oid :
971 s->r->hash_algo->empty_tree), "--", NULL);
972 for (i = 0; i < files->items.nr; i++)
973 if (files->selected[i])
974 strvec_push(&cmd.args,
975 files->items.items[i].string);
976 res = run_command(&cmd);
977 }
978
979 putchar('\n');
980 return res;
981 }
982
983 static int run_help(struct add_i_state *s, const struct pathspec *ps UNUSED,
984 struct prefix_item_list *files UNUSED,
985 struct list_and_choose_options *opts UNUSED)
986 {
987 color_fprintf_ln(stdout, s->cfg.help_color, "status - %s",
988 _("show paths with changes"));
989 color_fprintf_ln(stdout, s->cfg.help_color, "update - %s",
990 _("add working tree state to the staged set of changes"));
991 color_fprintf_ln(stdout, s->cfg.help_color, "revert - %s",
992 _("revert staged set of changes back to the HEAD version"));
993 color_fprintf_ln(stdout, s->cfg.help_color, "patch - %s",
994 _("pick hunks and update selectively"));
995 color_fprintf_ln(stdout, s->cfg.help_color, "diff - %s",
996 _("view diff between HEAD and index"));
997 color_fprintf_ln(stdout, s->cfg.help_color, "add untracked - %s",
998 _("add contents of untracked files to the staged set of changes"));
999
1000 return 0;
1001 }
1002
1003 static void choose_prompt_help(struct add_i_state *s)
1004 {
1005 color_fprintf_ln(stdout, s->cfg.help_color, "%s",
1006 _("Prompt help:"));
1007 color_fprintf_ln(stdout, s->cfg.help_color, "1 - %s",
1008 _("select a single item"));
1009 color_fprintf_ln(stdout, s->cfg.help_color, "3-5 - %s",
1010 _("select a range of items"));
1011 color_fprintf_ln(stdout, s->cfg.help_color, "2-3,6-9 - %s",
1012 _("select multiple ranges"));
1013 color_fprintf_ln(stdout, s->cfg.help_color, "foo - %s",
1014 _("select item based on unique prefix"));
1015 color_fprintf_ln(stdout, s->cfg.help_color, "-... - %s",
1016 _("unselect specified items"));
1017 color_fprintf_ln(stdout, s->cfg.help_color, "* - %s",
1018 _("choose all items"));
1019 color_fprintf_ln(stdout, s->cfg.help_color, " - %s",
1020 _("(empty) finish selecting"));
1021 }
1022
1023 typedef int (*command_t)(struct add_i_state *s, const struct pathspec *ps,
1024 struct prefix_item_list *files,
1025 struct list_and_choose_options *opts);
1026
1027 struct command_item {
1028 size_t prefix_length;
1029 command_t command;
1030 };
1031
1032 struct print_command_item_data {
1033 const char *color, *reset;
1034 };
1035
1036 static void print_command_item(int i, int selected UNUSED,
1037 struct string_list_item *item,
1038 void *print_command_item_data)
1039 {
1040 struct print_command_item_data *d = print_command_item_data;
1041 struct command_item *util = item->util;
1042
1043 if (!util->prefix_length ||
1044 !is_valid_prefix(item->string, util->prefix_length))
1045 printf(" %2d: %s", i + 1, item->string);
1046 else
1047 printf(" %2d: %s%.*s%s%s", i + 1,
1048 d->color, (int)util->prefix_length, item->string,
1049 d->reset, item->string + util->prefix_length);
1050 }
1051
1052 static void command_prompt_help(struct add_i_state *s)
1053 {
1054 const char *help_color = s->cfg.help_color;
1055 color_fprintf_ln(stdout, help_color, "%s", _("Prompt help:"));
1056 color_fprintf_ln(stdout, help_color, "1 - %s",
1057 _("select a numbered item"));
1058 color_fprintf_ln(stdout, help_color, "foo - %s",
1059 _("select item based on unique prefix"));
1060 color_fprintf_ln(stdout, help_color, " - %s",
1061 _("(empty) select nothing"));
1062 }
1063
1064 int run_add_i(struct repository *r, const struct pathspec *ps,
1065 struct interactive_options *interactive_opts)
1066 {
1067 struct add_i_state s = { NULL };
1068 struct print_command_item_data data = { "[", "]" };
1069 struct list_and_choose_options main_loop_opts = {
1070 { 4, N_("*** Commands ***"), print_command_item, &data },
1071 N_("What now"), SINGLETON | IMMEDIATE, command_prompt_help
1072 };
1073 struct {
1074 const char *string;
1075 command_t command;
1076 } command_list[] = {
1077 { "status", run_status },
1078 { "update", run_update },
1079 { "revert", run_revert },
1080 { "add untracked", run_add_untracked },
1081 { "patch", run_patch },
1082 { "diff", run_diff },
1083 { "quit", NULL },
1084 { "help", run_help },
1085 };
1086 struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT;
1087
1088 struct print_file_item_data print_file_item_data = {
1089 "%12s %12s %s", NULL, NULL,
1090 STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
1091 };
1092 struct list_and_choose_options opts = {
1093 { 0, NULL, print_file_item, &print_file_item_data },
1094 NULL, 0, choose_prompt_help
1095 };
1096 struct strbuf header = STRBUF_INIT;
1097 struct prefix_item_list files = PREFIX_ITEM_LIST_INIT;
1098 ssize_t i;
1099 int res = 0;
1100
1101 for (i = 0; i < ARRAY_SIZE(command_list); i++) {
1102 struct command_item *util = xcalloc(1, sizeof(*util));
1103 util->command = command_list[i].command;
1104 string_list_append(&commands.items, command_list[i].string)
1105 ->util = util;
1106 }
1107
1108 init_add_i_state(&s, r, interactive_opts);
1109
1110 /*
1111 * When color was asked for, use the prompt color for
1112 * highlighting, otherwise use square brackets.
1113 */
1114 if (want_color(s.cfg.use_color_interactive)) {
1115 data.color = s.cfg.prompt_color;
1116 data.reset = s.cfg.reset_color_interactive;
1117 }
1118 print_file_item_data.color = data.color;
1119 print_file_item_data.reset = data.reset;
1120
1121 strbuf_addstr(&header, " ");
1122 strbuf_addf(&header, print_file_item_data.modified_fmt,
1123 _("staged"), _("unstaged"), _("path"));
1124 opts.list_opts.header = header.buf;
1125
1126 discard_index(r->index);
1127 if (repo_read_index(r) < 0 ||
1128 repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
1129 NULL, NULL, NULL) < 0)
1130 warning(_("could not refresh index"));
1131
1132 res = run_status(&s, ps, &files, &opts);
1133
1134 for (;;) {
1135 struct command_item *util;
1136
1137 i = list_and_choose(&s, &commands, &main_loop_opts);
1138 if (i < 0 || i >= commands.items.nr)
1139 util = NULL;
1140 else
1141 util = commands.items.items[i].util;
1142
1143 if (i == LIST_AND_CHOOSE_QUIT || (util && !util->command)) {
1144 printf(_("Bye.\n"));
1145 res = 0;
1146 break;
1147 }
1148
1149 if (util)
1150 res = util->command(&s, ps, &files, &opts);
1151 }
1152
1153 prefix_item_list_clear(&files);
1154 strbuf_release(&print_file_item_data.buf);
1155 strbuf_release(&print_file_item_data.name);
1156 strbuf_release(&print_file_item_data.index);
1157 strbuf_release(&print_file_item_data.worktree);
1158 strbuf_release(&header);
1159 prefix_item_list_clear(&commands);
1160 clear_add_i_state(&s);
1161
1162 return res;
1163 }