To abort, simply reset the HEAD to the location specified by the HEAD_FILE written earlier, without touching the worktree or index. It is upto the user to execute "rerere clear", "reset --hard", and "reset --merge" as approprite. Helped-by: Jonathan Nieder <jrnieder@xxxxxxxxx> Helped-by: Junio C Hamano <gitster@xxxxxxxxx> Helped-by: Jeff King <peff@xxxxxxxx> Signed-off-by: Ramkumar Ramachandra <artagnon@xxxxxxxxx> --- I've persisted the TODO early, and made it complain when an existing cherry-pick/ revert is in progress. Further, as Junio suggested, I've made no attempt to touch the index or the worktree during the --abort operation: I simply update HEAD and leave the user to do the rest. Should we print some hints about this? However, by making it complain everytime a cherry-pick/ revert is in progress, I've essentially broken the entire testsuite -- everytime a test or even a script (see git-rebase.sh) calls cherry-pick or revert, I must call '--abort' to clean up afterwards. Fixing this will require me to touch many seemingly unrelated files. Also, note that I'm depending on the "libify reset" patch I posted a few minutes ago to print_new_head_line exactly the way reset does. In other news, I've started writing '--continue' and '--skip' operations, but the limiting factor is the instruction sheet's format: I'm still wondering if there's any way to avoid being verbose/ ugly and flexible (by allowing command-line options for each instruction) at the same time. Will post RFC patches asking more directed questions soon. Thanks. builtin/revert.c | 85 +++++++++++++++++++++++++++++++++++--- t/t3510-cherry-pick-sequence.sh | 16 +++++++ 2 files changed, 94 insertions(+), 7 deletions(-) diff --git a/builtin/revert.c b/builtin/revert.c index e1a05a3..efcc01f 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -14,6 +14,7 @@ #include "merge-recursive.h" #include "refs.h" #include "dir.h" +#include "reset.h" /* * This implements the builtins revert and cherry-pick. @@ -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) @@ -719,6 +718,7 @@ static int pick_commits(struct replay_opts *opts) struct commit *commit; unsigned char sha1[20]; const char *head; + int res; setenv(GIT_REFLOG_ACTION, me, 0); @@ -730,11 +730,30 @@ static int pick_commits(struct replay_opts *opts) persist_head(head); prepare_revs(&revs, opts); + /* Prepare todo_list and persist it early before picking */ + struct commit_list *todo_list = NULL; + struct commit_list *cur = NULL; + struct commit_list *new_item = NULL; + + /* Insert into todo_list in the same order */ while ((commit = get_revision(&revs))) { - int res = do_pick_commit(commit, opts); + new_item = xmalloc(sizeof(struct commit_list)); + new_item->item = commit; + if (cur) + cur->next = new_item; + else + todo_list = new_item; + cur = new_item; + } + cur->next = NULL; + + persist_todo(todo_list, opts); + + for (cur = todo_list; cur; cur = cur->next) { + res = do_pick_commit(cur->item, opts); if (res) { - commit_list_insert(commit, &revs.commits); - persist_todo(revs.commits, opts); + commit_list_insert(cur->item, &todo_list); + persist_todo(todo_list, opts); return res; } } @@ -743,6 +762,58 @@ static int pick_commits(struct replay_opts *opts) return cleanup_sequencer_dir(); } +static int process_continuation(struct replay_opts *opts) +{ + unsigned char sha1[20]; + struct commit *commit; + char head_hex[40]; + char msg[1024]; + 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_hex, 40) < 40 || get_sha1_hex(head_hex, sha1) < 0) { + close(fd); + return error(_("Corrupt '%s': %s"), HEAD_FILE, strerror(errno)); + } + close(fd); + + /* Update the HEAD ref */ + if (snprintf(msg, sizeof(msg), "%s: updating HEAD", me) >= sizeof(msg)) + warning(_("Reflog action message too long: %.*s..."), 50, msg); + if (update_ref(msg, "HEAD", sha1, NULL, 0, MSG_ON_ERR)) + return -1; + commit = lookup_commit_reference(sha1); + print_new_head_line(commit); + + 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; + } + else if (file_exists(HEAD_FILE)) { + error(_("A %s is already in progress"), me); + advise(_("Use %s --continue to continue the operation"), me); + advise(_("or %s --abort to start afresh"), me); + return -1; + } + + 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 +826,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 +846,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..c6ace35 100644 --- a/t/t3510-cherry-pick-sequence.sh +++ b/t/t3510-cherry-pick-sequence.sh @@ -36,4 +36,20 @@ 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_done -- 1.7.4.1 -- 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