@matheusbsbs / matheus-bagliano / commits / c54ef5e424
Fork 0

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;