To abort, perform a "rerere clear" and "reset --hard" to the ref specified by the HEAD file introduced earlier in the series. Signed-off-by: Ramkumar Ramachandra <artagnon@xxxxxxxxx> --- I want some feedback on this -- am I doing this right? Should I be re-implementing "reset --hard" like this? builtin/revert.c | 84 +++++++++++++++++++++++++++++++++++++-- t/t3510-cherry-pick-sequence.sh | 27 ++++++++++++ 2 files changed, 107 insertions(+), 4 deletions(-) diff --git a/builtin/revert.c b/builtin/revert.c index 8eee4d5..ae85df7 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -12,6 +12,7 @@ #include "revision.h" #include "rerere.h" #include "merge-recursive.h" +#include "unpack-trees.h" #include "refs.h" #include "dir.h" @@ -208,8 +209,6 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts) NULL); /* Remove these when the options are actually implemented */ - if (opts->abort_oper) - die("--abort is not implemented yet"); if (opts->skip_oper) die("--skip is not implemented yet"); if (opts->continue_oper) @@ -647,6 +646,44 @@ static void read_and_refresh_cache(const char *me, struct replay_opts *opts) rollback_lock_file(&index_lock); } +static int hard_reset_to_ref(unsigned char *sha1) +{ + struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); + struct string_list merge_rr = STRING_LIST_INIT_DUP; + struct unpack_trees_options unpack_tree_opts; + struct tree_desc desc; + int fd; + + /* rerere clear */ + setup_rerere(&merge_rr, 0); + rerere_clear(&merge_rr); + + /* Clean worktree, index */ + read_cache_unmerged(); + + memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts)); + unpack_tree_opts.head_idx = 1; + unpack_tree_opts.src_index = &the_index; + unpack_tree_opts.dst_index = &the_index; + unpack_tree_opts.fn = oneway_merge; + unpack_tree_opts.merge = 1; + unpack_tree_opts.update = 1; + unpack_tree_opts.reset = 1; + + fd = hold_locked_index(lock, 1); + fill_tree_descriptor(&desc, sha1); + if (unpack_trees(1, &desc, &unpack_tree_opts) < 0) + return error (_("Failed to unpack trees.")); + if (write_index(&the_index, fd) || + commit_locked_index(lock)) + return error(_("Could not write index.")); + + /* Update the HEAD ref */ + if (update_ref("updating HEAD", "HEAD", sha1, NULL, 0, MSG_ON_ERR)) + return error(_("Unable to update HEAD")); + return 0; +} + static void format_todo(struct strbuf *buf, struct commit_list *list, struct replay_opts *opts) { @@ -743,6 +780,45 @@ static int pick_commits(struct replay_opts *opts) return cleanup_sequencer_dir(); } +static int process_continuation(struct replay_opts *opts) +{ + unsigned char sha1[20]; + char head[40]; + int fd; + + if (opts->abort_oper) { + /* First, read the HEAD_FILE */ + if (!file_exists(HEAD_FILE)) + goto error; + fd = open(HEAD_FILE, O_RDONLY, 0666); + if (fd < 0) + return error(_("Could not open '%s' for reading: %s"), + HEAD_FILE, strerror(errno)); + if (read_in_full(fd, head, 40) < 40 || get_sha1_hex(head, sha1) < 0) { + close(fd); + return error(_("Corrupt '%s': %s"), HEAD_FILE, strerror(errno)); + } + close(fd); + + if (hard_reset_to_ref(sha1) < 0) + /* Error already reported */ + return -1; + return cleanup_sequencer_dir(); + } + else if (opts->skip_oper) { + if (!file_exists(TODO_FILE)) + goto error; + } + else if (opts->continue_oper) { + if (!file_exists(TODO_FILE)) + goto error; + } + + return pick_commits(opts); +error: + return error(_("No %s in progress"), me); +} + int cmd_revert(int argc, const char **argv, const char *prefix) { int res; @@ -755,7 +831,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix) git_config(git_default_config, NULL); me = "revert"; parse_args(argc, argv, &opts); - res = pick_commits(&opts); + res = process_continuation(&opts); if (res > 0) /* Exit status from conflict */ return res; @@ -775,7 +851,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix) git_config(git_default_config, NULL); me = "cherry-pick"; parse_args(argc, argv, &opts); - res = pick_commits(&opts); + res = process_continuation(&opts); if (res > 0) return res; if (res < 0) diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh index a2e1888..7e22898 100644 --- a/t/t3510-cherry-pick-sequence.sh +++ b/t/t3510-cherry-pick-sequence.sh @@ -36,4 +36,31 @@ test_expect_success 'cherry-pick cleans up sequencer directory upon success' ' test_must_fail test -d .git/sequencer ' +test_expect_success '--abort complains when no cherry-pick is in progress' ' + pristine_detach initial && + test_must_fail git cherry-pick --abort >actual 2>&1 && + echo "error: No cherry-pick in progress" >expect && + test_i18ncmp expect actual +' + +test_expect_success '--abort restores HEAD after failed cherry-pick' ' + pristine_detach initial && + head=$(git rev-parse HEAD) && + test_must_fail git cherry-pick base..picked && + git cherry-pick --abort && + newhead=$(git rev-parse HEAD) && + test "$head" = "$newhead" +' + +test_expect_success '--abort resets worktree and index after failed cherry-pick' ' + pristine_detach initial && + head=$(git rev-parse HEAD) && + test_must_fail git cherry-pick base..picked && + git add foo && + git cherry-pick --abort && + git update-index -q --ignore-submodules --refresh && + git diff-files --quiet --ignore-submodules && + git diff-index --cached --quiet --ignore-submodules HEAD -- +' + test_done -- 1.7.5.GIT -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html