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