Skip to content

Commit b1c0a31

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 930c8c8 commit b1c0a31

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
@@ -568,6 +568,117 @@ static int run_update(struct add_i_state *s, const struct pathspec *ps,
568568
return res;
569569
}
570570

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

0 commit comments

Comments
 (0)