While the sequencer infrastructure knows to rewind "HEAD" to whatever it was pointing to before a rebase, it doesn't do the same for non-rebase operations like cherry-picks. This is because the expectation is that the user directly picks commits on top of whatever "HEAD" points to, and we advance the reference pointed to by "HEAD" instead of updating it directly. We're about to introduce a new command though that needs to detach "HEAD" while being more similar to git-cherry-pick(1) rathen than to git-rebase(1). As such, we'll want to restore "HEAD" to point to the branch that we started on while not using the more heavy-weight rebase machinery. Introduce a new option `restore_head_target` to do so. Persist the option into the sequencer configuration so that it persists across different processes, e.g. when we need to stop due to a merge conflict. Signed-off-by: Patrick Steinhardt <ps@xxxxxx> --- sequencer.c | 27 ++++++++++++++++++++++++++- sequencer.h | 3 +++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/sequencer.c b/sequencer.c index 7066cdc939..bff181df76 100644 --- a/sequencer.c +++ b/sequencer.c @@ -413,6 +413,7 @@ void replay_opts_release(struct replay_opts *opts) struct replay_ctx *ctx = opts->ctx; free(opts->gpg_sign); + free(opts->restore_head_target); free(opts->reflog_action); free(opts->default_strategy); free(opts->strategy); @@ -3142,6 +3143,8 @@ static int populate_opts_cb(const char *key, const char *value, } else if (!strcmp(key, "options.skip-commit-summary")) { opts->skip_commit_summary = git_config_bool_or_int(key, value, ctx->kvi, &error_flag); + } else if (!strcmp(key, "options.restore-head-target")) { + git_config_string_dup(&opts->restore_head_target, key, value); } else { return error(_("invalid key: %s"), key); } @@ -3709,6 +3712,10 @@ static int save_opts(struct replay_opts *opts) if (opts->skip_commit_summary) res |= repo_config_set_in_file_gently(the_repository, opts_file, "options.skip-commit-summary", NULL, "true"); + if (opts->restore_head_target) + res |= repo_config_set_in_file_gently(the_repository, opts_file, + "options.restore-head-target", NULL, opts->restore_head_target); + return res; } @@ -5177,6 +5184,23 @@ static int pick_commits(struct repository *r, return -1; } + if (opts->restore_head_target) { + struct reset_head_opts reset_opts = { 0 }; + const char *msg; + + msg = reflog_message(opts, "finish", "returning to %s", opts->restore_head_target); + + reset_opts.branch = opts->restore_head_target; + reset_opts.flags = RESET_HEAD_REFS_ONLY; + reset_opts.branch_msg = msg; + reset_opts.head_msg = msg; + + if (reset_head(r, &reset_opts)) { + error(_("could not switch HEAD back to %s"), opts->restore_head_target); + return -1; + } + } + /* * Sequence of picks finished successfully; cleanup by * removing the .git/sequencer directory @@ -5533,7 +5557,8 @@ int sequencer_pick_revisions(struct repository *r, if (opts->revs->cmdline.nr == 1 && opts->revs->cmdline.rev->whence == REV_CMD_REV && opts->revs->no_walk && - !opts->revs->cmdline.rev->flags) { + !opts->revs->cmdline.rev->flags && + !opts->restore_head_target) { struct commit *cmit; if (prepare_revision_walk(opts->revs)) { diff --git a/sequencer.h b/sequencer.h index 1767fd737e..a905f6afc7 100644 --- a/sequencer.h +++ b/sequencer.h @@ -72,6 +72,9 @@ struct replay_opts { /* Reflog */ char *reflog_action; + /* Reference to which HEAD shall be reset to after the operation. */ + char *restore_head_target; + /* placeholder commit for -i --root */ struct object_id squash_onto; int have_squash_onto; -- 2.51.0.308.g032396e0da.dirty