+static int run_add_untracked(struct add_i_state *s, const struct pathspec *ps,
+ struct prefix_item_list *files,
+ struct list_and_choose_options *opts)
+{
+ struct print_file_item_data *d = opts->list_opts.print_item_data;
+ int res = 0, fd;
+ size_t count, i;
+ struct lock_file index_lock;
+
+ if (get_untracked_files(s->r, files, ps) < 0)
+ return -1;
+
+ if (!files->items.nr) {
+ printf(_("No untracked files.\n"));
+ goto finish_add_untracked;
+ }
+
+ opts->prompt = N_("Add untracked");
+ d->only_names = 1;
+ count = list_and_choose(s, files, opts);
+ d->only_names = 0;
+ if (count <= 0)
+ goto finish_add_untracked;
+
+ fd = repo_hold_locked_index(s->r, &index_lock, LOCK_REPORT_ON_ERROR);
+ if (fd < 0) {
+ res = -1;
+ goto finish_add_untracked;
+ }
+
+ for (i = 0; i < files->items.nr; i++) {
+ const char *name = files->items.items[i].string;
+ if (files->selected[i] &&
+ add_file_to_index(s->r->index, name, 0) < 0) {
+ res = error(_("could not stage '%s'"), name);
+ break;
+ }
+ }
+
+ if (!res &&
+ write_locked_index(s->r->index, &index_lock, COMMIT_LOCK) < 0)
+ res = error(_("could not write index"));
+
+ if (!res)
+ printf(Q_("added %d path\n",
+ "added %d paths\n", count), (int)count);
+
+finish_add_untracked:
+ putchar('\n');
+ return res;
+}
+
+static int run_patch(struct add_i_state *s, const struct pathspec *ps,
+ struct prefix_item_list *files,
+ struct list_and_choose_options *opts)
+{
+ int res = 0;
+ ssize_t count, i, j;
+ size_t unmerged_count = 0, binary_count = 0;
+
+ if (get_modified_files(s->r, WORKTREE_ONLY, files, ps,
+ &unmerged_count, &binary_count) < 0)
+ return -1;
+
+ if (unmerged_count || binary_count) {
+ for (i = j = 0; i < files->items.nr; i++) {
+ struct file_item *item = files->items.items[i].util;
+
+ if (item->index.binary || item->worktree.binary) {
+ free(item);
+ free(files->items.items[i].string);
+ } else if (item->index.unmerged ||
+ item->worktree.unmerged) {
+ color_fprintf_ln(stderr, s->error_color,
+ _("ignoring unmerged: %s"),
+ files->items.items[i].string);
+ free(item);
+ free(files->items.items[i].string);
+ } else
+ files->items.items[j++] = files->items.items[i];
+ }
+ files->items.nr = j;
+ }
+
+ if (!files->items.nr) {
+ if (binary_count)
+ fprintf(stderr, _("Only binary files changed.\n"));
+ else
+ fprintf(stderr, _("No changes.\n"));
+ return 0;
+ }
+
+ opts->prompt = N_("Patch update");
+ count = list_and_choose(s, files, opts);
+ if (count >= 0) {
+ struct argv_array args = ARGV_ARRAY_INIT;
+ struct pathspec ps_selected = { 0 };
+
+ for (i = 0; i < files->items.nr; i++)
+ if (files->selected[i])
+ argv_array_push(&args,
+ files->items.items[i].string);
+ parse_pathspec(&ps_selected,
+ PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
+ PATHSPEC_LITERAL_PATH, "", args.argv);
+ res = run_add_p(s->r, &ps_selected);
+ argv_array_clear(&args);
+ clear_pathspec(&ps_selected);
+ }
+
+ return res;
+}
+
+static int run_diff(struct add_i_state *s, const struct pathspec *ps,
+ struct prefix_item_list *files,
+ struct list_and_choose_options *opts)
+{
+ int res = 0;
+ ssize_t count, i;
+
+ struct object_id oid;
+ int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &oid,
+ NULL);
+ if (get_modified_files(s->r, INDEX_ONLY, files, ps, NULL, NULL) < 0)
+ return -1;
+
+ if (!files->items.nr) {
+ putchar('\n');
+ return 0;
+ }
+
+ opts->prompt = N_("Review diff");
+ opts->flags = IMMEDIATE;
+ count = list_and_choose(s, files, opts);
+ opts->flags = 0;
+ if (count >= 0) {
+ struct argv_array args = ARGV_ARRAY_INIT;
+
+ argv_array_pushl(&args, "git", "diff", "-p", "--cached",
+ oid_to_hex(!is_initial ? &oid :
+ s->r->hash_algo->empty_tree),
+ "--", NULL);
+ for (i = 0; i < files->items.nr; i++)
+ if (files->selected[i])
+ argv_array_push(&args,
+ files->items.items[i].string);
+ res = run_command_v_opt(args.argv, 0);
+ argv_array_clear(&args);
+ }
+
+ putchar('\n');
+ return res;
+}
+