Skip to content

Commit 5c31bbd

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 cba5f13 commit 5c31bbd

File tree

1 file changed

+109
-0
lines changed

1 file changed

+109
-0
lines changed

add-interactive.c

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,114 @@ static int run_update(struct add_i_state *s, const struct pathspec *ps,
653653
return res;
654654
}
655655

656+
static void revert_from_diff(struct diff_queue_struct *q,
657+
struct diff_options *opt, void *data)
658+
{
659+
int i, add_flags = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
660+
661+
for (i = 0; i < q->nr; i++) {
662+
struct diff_filespec *one = q->queue[i]->one;
663+
struct cache_entry *ce;
664+
665+
if (!(one->mode && !is_null_oid(&one->oid))) {
666+
remove_file_from_index(opt->repo->index, one->path);
667+
printf(_("note: %s is untracked now.\n"), one->path);
668+
} else {
669+
ce = make_cache_entry(opt->repo->index, one->mode,
670+
&one->oid, one->path, 0, 0);
671+
if (!ce)
672+
die(_("make_cache_entry failed for path '%s'"),
673+
one->path);
674+
add_index_entry(opt->repo->index, ce, add_flags);
675+
}
676+
}
677+
}
678+
679+
static int run_revert(struct add_i_state *s, const struct pathspec *ps,
680+
struct prefix_item_list *files,
681+
struct list_and_choose_options *opts)
682+
{
683+
int res = 0, fd;
684+
size_t count, i, j;
685+
686+
struct object_id oid;
687+
int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &oid,
688+
NULL);
689+
struct lock_file index_lock;
690+
const char **paths;
691+
struct tree *tree;
692+
struct diff_options diffopt = { NULL };
693+
694+
if (get_modified_files(s->r, INDEX_ONLY, files, ps) < 0)
695+
return -1;
696+
697+
if (!files->items.nr) {
698+
putchar('\n');
699+
return 0;
700+
}
701+
702+
opts->prompt = N_("Revert");
703+
count = list_and_choose(s, files, opts);
704+
if (count <= 0)
705+
goto finish_revert;
706+
707+
fd = repo_hold_locked_index(s->r, &index_lock, LOCK_REPORT_ON_ERROR);
708+
if (fd < 0) {
709+
res = -1;
710+
goto finish_revert;
711+
}
712+
713+
if (is_initial)
714+
oidcpy(&oid, s->r->hash_algo->empty_tree);
715+
else {
716+
tree = parse_tree_indirect(&oid);
717+
if (!tree) {
718+
res = error(_("Could not parse HEAD^{tree}"));
719+
goto finish_revert;
720+
}
721+
oidcpy(&oid, &tree->object.oid);
722+
}
723+
724+
ALLOC_ARRAY(paths, count + 1);
725+
for (i = j = 0; i < files->items.nr; i++)
726+
if (files->selected[i])
727+
paths[j++] = files->items.items[i].string;
728+
paths[j] = NULL;
729+
730+
parse_pathspec(&diffopt.pathspec, 0,
731+
PATHSPEC_PREFER_FULL | PATHSPEC_LITERAL_PATH,
732+
NULL, paths);
733+
734+
diffopt.output_format = DIFF_FORMAT_CALLBACK;
735+
diffopt.format_callback = revert_from_diff;
736+
diffopt.flags.override_submodule_config = 1;
737+
diffopt.repo = s->r;
738+
739+
if (do_diff_cache(&oid, &diffopt))
740+
res = -1;
741+
else {
742+
diffcore_std(&diffopt);
743+
diff_flush(&diffopt);
744+
}
745+
free(paths);
746+
clear_pathspec(&diffopt.pathspec);
747+
748+
if (!res && write_locked_index(s->r->index, &index_lock,
749+
COMMIT_LOCK) < 0)
750+
res = -1;
751+
else
752+
res = repo_refresh_and_write_index(s->r, REFRESH_QUIET, 0, 1,
753+
NULL, NULL, NULL);
754+
755+
if (!res)
756+
printf(Q_("reverted %d path\n",
757+
"reverted %d paths\n", count), (int)count);
758+
759+
finish_revert:
760+
putchar('\n');
761+
return res;
762+
}
763+
656764
static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
657765
struct prefix_item_list *unused_files,
658766
struct list_and_choose_options *unused_opts)
@@ -748,6 +856,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
748856
} command_list[] = {
749857
{ "status", run_status },
750858
{ "update", run_update },
859+
{ "revert", run_revert },
751860
{ "help", run_help },
752861
};
753862
struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT;

0 commit comments

Comments
 (0)