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