Skip to content

Commit c6ba59e

Browse files
committed
built-in add -i: re-implement revert in C
This is a relatively straight-forward port from the Perl version, with the notable exception that we imitate `git reset -- <paths>` in the C version rather than the convoluted `git ls-tree HEAD -- <paths> | git update-index --index-info` followed by `git update-index --force-remove -- <paths>` for the missed ones. While at it, we fix the pretty obvious bug where the `revert` command offers to unstage files that do not have staged changes. Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 41e36a5 commit c6ba59e

File tree

1 file changed

+113
-1
lines changed

1 file changed

+113
-1
lines changed

add-interactive.c

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,117 @@ static int run_update(struct add_i_state *s, const struct pathspec *ps,
561561
return res;
562562
}
563563

564+
static void revert_from_diff(struct diff_queue_struct *q,
565+
struct diff_options *opt, void *data)
566+
{
567+
int i, add_flags = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
568+
569+
for (i = 0; i < q->nr; i++) {
570+
struct diff_filespec *one = q->queue[i]->one;
571+
struct cache_entry *ce;
572+
573+
if (!(one->mode && !is_null_oid(&one->oid))) {
574+
remove_file_from_index(opt->repo->index, one->path);
575+
printf(_("note: %s is untracked now.\n"), one->path);
576+
} else {
577+
ce = make_cache_entry(opt->repo->index, one->mode,
578+
&one->oid, one->path, 0, 0);
579+
if (!ce)
580+
die(_("make_cache_entry failed for path '%s'"),
581+
one->path);
582+
add_index_entry(opt->repo->index, ce, add_flags);
583+
}
584+
}
585+
}
586+
587+
static int run_revert(struct add_i_state *s, const struct pathspec *ps,
588+
struct file_list *files,
589+
struct list_and_choose_options *opts)
590+
{
591+
int res = 0, fd, *selected = NULL;
592+
size_t count, i, j;
593+
594+
struct object_id oid;
595+
int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &oid,
596+
NULL);
597+
struct lock_file index_lock;
598+
const char **paths;
599+
struct tree *tree;
600+
struct diff_options diffopt = { NULL };
601+
602+
reset_file_list(files);
603+
if (get_modified_files(s->r, INDEX_ONLY, files, ps) < 0)
604+
return -1;
605+
606+
if (!files->nr) {
607+
putchar('\n');
608+
return 0;
609+
}
610+
611+
opts->prompt = N_("Revert");
612+
CALLOC_ARRAY(selected, files->nr);
613+
614+
count = list_and_choose((struct prefix_item **)files->file,
615+
selected, files->nr, s, opts);
616+
if (count <= 0)
617+
goto finish_revert;
618+
619+
fd = repo_hold_locked_index(s->r, &index_lock, LOCK_REPORT_ON_ERROR);
620+
if (fd < 0) {
621+
res = -1;
622+
goto finish_revert;
623+
}
624+
625+
if (is_initial)
626+
oidcpy(&oid, s->r->hash_algo->empty_tree);
627+
else {
628+
tree = parse_tree_indirect(&oid);
629+
if (!tree) {
630+
res = error(_("Could not parse HEAD^{tree}"));
631+
goto finish_revert;
632+
}
633+
oidcpy(&oid, &tree->object.oid);
634+
}
635+
636+
ALLOC_ARRAY(paths, count + 1);
637+
for (i = j = 0; i < files->nr; i++)
638+
if (selected[i])
639+
paths[j++] = files->file[i]->item.name;
640+
paths[j] = NULL;
641+
642+
parse_pathspec(&diffopt.pathspec, 0,
643+
PATHSPEC_PREFER_FULL | PATHSPEC_LITERAL_PATH,
644+
NULL, paths);
645+
646+
diffopt.output_format = DIFF_FORMAT_CALLBACK;
647+
diffopt.format_callback = revert_from_diff;
648+
diffopt.flags.override_submodule_config = 1;
649+
diffopt.repo = s->r;
650+
651+
if (do_diff_cache(&oid, &diffopt))
652+
res = -1;
653+
else {
654+
diffcore_std(&diffopt);
655+
diff_flush(&diffopt);
656+
}
657+
free(paths);
658+
clear_pathspec(&diffopt.pathspec);
659+
660+
if (!res && write_locked_index(s->r->index, &index_lock,
661+
COMMIT_LOCK) < 0)
662+
res = -1;
663+
else
664+
res = repo_refresh_and_write_index(s->r, REFRESH_QUIET, 1);
665+
if (!res)
666+
printf(Q_("reverted %d path\n",
667+
"reverted %d paths\n", count), (int)count);
668+
669+
finish_revert:
670+
putchar('\n');
671+
free(selected);
672+
return res;
673+
}
674+
564675
static int run_help(struct add_i_state *s, const struct pathspec *ps,
565676
struct file_list *files,
566677
struct list_and_choose_options *opts)
@@ -652,9 +763,10 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
652763
struct command_item
653764
status = { { "status" }, run_status },
654765
update = { { "update" }, run_update },
766+
revert = { { "revert" }, run_revert },
655767
help = { { "help" }, run_help };
656768
struct command_item *commands[] = {
657-
&status, &update,
769+
&status, &update, &revert,
658770
&help
659771
};
660772

0 commit comments

Comments
 (0)