From 1946d45844c65ede4e3a514a5decf16612ad79f0 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Wed, 26 Jan 2022 13:05:41 +0000 Subject: reset_head(): remove action parameter The only use of the action parameter is to setup the error messages for unpack_trees(). All but two cases pass either "checkout" or "reset". The case that passes "reset --hard" would be better passing "reset" so that the error messages match the builtin reset command like all the other callers that are doing a reset. The case that passes "Fast-forwarded" is only updating HEAD and so the parameter is unused in that case as it does not call unpack_trees(). The value to pass to setup_unpack_trees_porcelain() can be determined by checking whether flags contains RESET_HEAD_HARD without the caller having to specify it. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano --- sequencer.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sequencer.c') diff --git a/sequencer.c b/sequencer.c index b4135a78c9..e18329a399 100644 --- a/sequencer.c +++ b/sequencer.c @@ -4137,8 +4137,7 @@ void create_autostash(struct repository *r, const char *path, path); write_file(path, "%s", oid_to_hex(&oid)); printf(_("Created autostash: %s\n"), buf.buf); - if (reset_head(r, NULL, "reset --hard", - NULL, RESET_HEAD_HARD, NULL, NULL, + if (reset_head(r, NULL, NULL, RESET_HEAD_HARD, NULL, NULL, default_reflog_action) < 0) die(_("could not reset --hard")); -- cgit v1.3 From b7de153bd9332a992baa6f937372f0b1833f61e5 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Wed, 26 Jan 2022 13:05:44 +0000 Subject: create_autostash(): remove unneeded parameter The default_reflog parameter of create_autostash() is passed to reset_head(). However as creating a stash does not involve updating any refs the parameter is not used by reset_head(). Removing the parameter from create_autostash() simplifies the callers. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano --- builtin/merge.c | 6 ++---- builtin/rebase.c | 8 ++++---- sequencer.c | 5 ++--- sequencer.h | 3 +-- 4 files changed, 9 insertions(+), 13 deletions(-) (limited to 'sequencer.c') diff --git a/builtin/merge.c b/builtin/merge.c index ea3112e0c0..cb0e4e2225 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -1565,8 +1565,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (autostash) create_autostash(the_repository, - git_path_merge_autostash(the_repository), - "merge"); + git_path_merge_autostash(the_repository)); if (checkout_fast_forward(the_repository, &head_commit->object.oid, &commit->object.oid, @@ -1637,8 +1636,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (autostash) create_autostash(the_repository, - git_path_merge_autostash(the_repository), - "merge"); + git_path_merge_autostash(the_repository)); /* We are going to make a new commit. */ git_committer_info(IDENT_STRICT); diff --git a/builtin/rebase.c b/builtin/rebase.c index 82be965915..3d78b5c8be 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -1657,10 +1657,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (repo_read_index(the_repository) < 0) die(_("could not read index")); - if (options.autostash) { - create_autostash(the_repository, state_dir_path("autostash", &options), - DEFAULT_REFLOG_ACTION); - } + if (options.autostash) + create_autostash(the_repository, + state_dir_path("autostash", &options)); + if (require_clean_work_tree(the_repository, "rebase", _("Please commit or stash them."), 1, 1)) { diff --git a/sequencer.c b/sequencer.c index e18329a399..119564f435 100644 --- a/sequencer.c +++ b/sequencer.c @@ -4100,8 +4100,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset) return -1; } -void create_autostash(struct repository *r, const char *path, - const char *default_reflog_action) +void create_autostash(struct repository *r, const char *path) { struct strbuf buf = STRBUF_INIT; struct lock_file lock_file = LOCK_INIT; @@ -4138,7 +4137,7 @@ void create_autostash(struct repository *r, const char *path, write_file(path, "%s", oid_to_hex(&oid)); printf(_("Created autostash: %s\n"), buf.buf); if (reset_head(r, NULL, NULL, RESET_HEAD_HARD, NULL, NULL, - default_reflog_action) < 0) + NULL) < 0) die(_("could not reset --hard")); if (discard_index(r->index) < 0 || diff --git a/sequencer.h b/sequencer.h index 05a7d2ba6b..da64473636 100644 --- a/sequencer.h +++ b/sequencer.h @@ -197,8 +197,7 @@ void commit_post_rewrite(struct repository *r, const struct commit *current_head, const struct object_id *new_head); -void create_autostash(struct repository *r, const char *path, - const char *default_reflog_action); +void create_autostash(struct repository *r, const char *path); int save_autostash(const char *path); int apply_autostash(const char *path); int apply_autostash_oid(const char *stash_oid); -- cgit v1.3 From 6ae8086161d81a707ff36dfdc07f57e4f473e0fd Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Wed, 26 Jan 2022 13:05:46 +0000 Subject: reset_head(): take struct rebase_head_opts This function takes a confusingly large number of parameters which makes it difficult to remember which order to pass them in. The following commits will add a couple more parameters which makes the problem worse. To address this change the function to take a struct of options. Using a struct means that it is no longer necessary to remember which order to pass the parameters in and anyone reading the code can easily see which value is passed to each parameter. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano --- builtin/rebase.c | 57 +++++++++++++++++++++++++++++++++++--------------------- reset.c | 38 ++++++++++++++++++------------------- reset.h | 40 +++++++++++++++++++++++++++++++++++---- sequencer.c | 5 ++--- 4 files changed, 92 insertions(+), 48 deletions(-) (limited to 'sequencer.c') diff --git a/builtin/rebase.c b/builtin/rebase.c index fdd822c470..ecc368dd4f 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -571,6 +571,7 @@ static int finish_rebase(struct rebase_options *opts) static int move_to_original_branch(struct rebase_options *opts) { struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT; + struct reset_head_opts ropts = { 0 }; int ret; if (!opts->head_name) @@ -583,9 +584,11 @@ static int move_to_original_branch(struct rebase_options *opts) opts->head_name, oid_to_hex(&opts->onto->object.oid)); strbuf_addf(&head_reflog, "rebase finished: returning to %s", opts->head_name); - ret = reset_head(the_repository, NULL, opts->head_name, - RESET_HEAD_REFS_ONLY, - orig_head_reflog.buf, head_reflog.buf, NULL); + ropts.branch = opts->head_name; + ropts.flags = RESET_HEAD_REFS_ONLY; + ropts.orig_head_msg = orig_head_reflog.buf; + ropts.head_msg = head_reflog.buf; + ret = reset_head(the_repository, &ropts); strbuf_release(&orig_head_reflog); strbuf_release(&head_reflog); @@ -669,13 +672,15 @@ static int run_am(struct rebase_options *opts) status = run_command(&format_patch); if (status) { + struct reset_head_opts ropts = { 0 }; unlink(rebased_patches); free(rebased_patches); strvec_clear(&am.args); - reset_head(the_repository, &opts->orig_head, - opts->head_name, 0, - NULL, NULL, DEFAULT_REFLOG_ACTION); + ropts.oid = &opts->orig_head; + ropts.branch = opts->head_name; + ropts.default_reflog_action = DEFAULT_REFLOG_ACTION; + reset_head(the_repository, &ropts); error(_("\ngit encountered an error while preparing the " "patches to replay\n" "these revisions:\n" @@ -814,14 +819,17 @@ static int rebase_config(const char *var, const char *value, void *data) static int checkout_up_to_date(struct rebase_options *options) { struct strbuf buf = STRBUF_INIT; + struct reset_head_opts ropts = { 0 }; int ret = 0; strbuf_addf(&buf, "%s: checkout %s", getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options->switch_to); - if (reset_head(the_repository, &options->orig_head, - options->head_name, RESET_HEAD_RUN_POST_CHECKOUT_HOOK, - NULL, buf.buf, NULL) < 0) + ropts.oid = &options->orig_head; + ropts.branch = options->head_name; + ropts.flags = RESET_HEAD_RUN_POST_CHECKOUT_HOOK; + ropts.head_msg = buf.buf; + if (reset_head(the_repository, &ropts) < 0) ret = error(_("could not switch to %s"), options->switch_to); strbuf_release(&buf); @@ -1033,6 +1041,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) int reschedule_failed_exec = -1; int allow_preemptive_ff = 1; int preserve_merges_selected = 0; + struct reset_head_opts ropts = { 0 }; struct option builtin_rebase_options[] = { OPT_STRING(0, "onto", &options.onto_name, N_("revision"), @@ -1270,9 +1279,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) rerere_clear(the_repository, &merge_rr); string_list_clear(&merge_rr, 1); - - if (reset_head(the_repository, NULL, NULL, RESET_HEAD_HARD, - NULL, NULL, NULL) < 0) + ropts.flags = RESET_HEAD_HARD; + if (reset_head(the_repository, &ropts) < 0) die(_("could not discard worktree changes")); remove_branch_state(the_repository, 0); if (read_basic_state(&options)) @@ -1289,9 +1297,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (read_basic_state(&options)) exit(1); - if (reset_head(the_repository, &options.orig_head, - options.head_name, RESET_HEAD_HARD, - NULL, NULL, DEFAULT_REFLOG_ACTION) < 0) + ropts.oid = &options.orig_head; + ropts.branch = options.head_name; + ropts.flags = RESET_HEAD_HARD; + ropts.default_reflog_action = DEFAULT_REFLOG_ACTION; + if (reset_head(the_repository, &ropts) < 0) die(_("could not move back to %s"), oid_to_hex(&options.orig_head)); remove_branch_state(the_repository, 0); @@ -1758,10 +1768,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) strbuf_addf(&msg, "%s: checkout %s", getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name); - if (reset_head(the_repository, &options.onto->object.oid, NULL, - RESET_HEAD_DETACH | RESET_ORIG_HEAD | - RESET_HEAD_RUN_POST_CHECKOUT_HOOK, - NULL, msg.buf, DEFAULT_REFLOG_ACTION)) + ropts.oid = &options.onto->object.oid; + ropts.flags = RESET_HEAD_DETACH | RESET_ORIG_HEAD | + RESET_HEAD_RUN_POST_CHECKOUT_HOOK; + ropts.head_msg = msg.buf; + ropts.default_reflog_action = DEFAULT_REFLOG_ACTION; + if (reset_head(the_repository, &ropts)) die(_("Could not detach HEAD")); strbuf_release(&msg); @@ -1776,8 +1788,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) strbuf_addf(&msg, "rebase finished: %s onto %s", options.head_name ? options.head_name : "detached HEAD", oid_to_hex(&options.onto->object.oid)); - reset_head(the_repository, NULL, options.head_name, - RESET_HEAD_REFS_ONLY, NULL, msg.buf, NULL); + memset(&ropts, 0, sizeof(ropts)); + ropts.branch = options.head_name; + ropts.flags = RESET_HEAD_REFS_ONLY; + ropts.head_msg = msg.buf; + reset_head(the_repository, &ropts); strbuf_release(&msg); ret = finish_rebase(&options); goto cleanup; diff --git a/reset.c b/reset.c index 4a92e4bc30..78145d5c45 100644 --- a/reset.c +++ b/reset.c @@ -8,14 +8,17 @@ #include "tree.h" #include "unpack-trees.h" -static int update_refs(const struct object_id *oid, const char *switch_to_branch, - const struct object_id *head, const char *reflog_head, - const char *reflog_orig_head, - const char *default_reflog_action, unsigned flags) +static int update_refs(const struct reset_head_opts *opts, + const struct object_id *oid, + const struct object_id *head) { - unsigned detach_head = flags & RESET_HEAD_DETACH; - unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK; - unsigned update_orig_head = flags & RESET_ORIG_HEAD; + unsigned detach_head = opts->flags & RESET_HEAD_DETACH; + unsigned run_hook = opts->flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK; + unsigned update_orig_head = opts->flags & RESET_ORIG_HEAD; + const char *switch_to_branch = opts->branch; + const char *reflog_head = opts->head_msg; + const char *reflog_orig_head = opts->orig_head_msg; + const char *default_reflog_action = opts->default_reflog_action; struct object_id *old_orig = NULL, oid_old_orig; struct strbuf msg = STRBUF_INIT; const char *reflog_action; @@ -69,14 +72,13 @@ static int update_refs(const struct object_id *oid, const char *switch_to_branch return ret; } -int reset_head(struct repository *r, struct object_id *oid, - const char *switch_to_branch, unsigned flags, - const char *reflog_orig_head, const char *reflog_head, - const char *default_reflog_action) +int reset_head(struct repository *r, const struct reset_head_opts *opts) { - unsigned reset_hard = flags & RESET_HEAD_HARD; - unsigned refs_only = flags & RESET_HEAD_REFS_ONLY; - unsigned update_orig_head = flags & RESET_ORIG_HEAD; + const struct object_id *oid = opts->oid; + const char *switch_to_branch = opts->branch; + unsigned reset_hard = opts->flags & RESET_HEAD_HARD; + unsigned refs_only = opts->flags & RESET_HEAD_REFS_ONLY; + unsigned update_orig_head = opts->flags & RESET_ORIG_HEAD; struct object_id *head = NULL, head_oid; struct tree_desc desc[2] = { { NULL }, { NULL } }; struct lock_file lock = LOCK_INIT; @@ -104,9 +106,7 @@ int reset_head(struct repository *r, struct object_id *oid, oid = &head_oid; if (refs_only) - return update_refs(oid, switch_to_branch, head, reflog_head, - reflog_orig_head, default_reflog_action, - flags); + return update_refs(opts, oid, head); action = reset_hard ? "reset" : "checkout"; setup_unpack_trees_porcelain(&unpack_tree_opts, action); @@ -151,9 +151,7 @@ int reset_head(struct repository *r, struct object_id *oid, } if (oid != &head_oid || update_orig_head || switch_to_branch) - ret = update_refs(oid, switch_to_branch, head, reflog_head, - reflog_orig_head, default_reflog_action, - flags); + ret = update_refs(opts, oid, head); leave_reset_head: rollback_lock_file(&lock); diff --git a/reset.h b/reset.h index 2daec80425..a205be2fb8 100644 --- a/reset.h +++ b/reset.h @@ -6,15 +6,47 @@ #define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" +/* Request a detached checkout */ #define RESET_HEAD_DETACH (1<<0) +/* Request a reset rather than a checkout */ #define RESET_HEAD_HARD (1<<1) +/* Run the post-checkout hook */ #define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2) +/* Only update refs, do not touch the worktree */ #define RESET_HEAD_REFS_ONLY (1<<3) +/* Update ORIG_HEAD as well as HEAD */ #define RESET_ORIG_HEAD (1<<4) -int reset_head(struct repository *r, struct object_id *oid, - const char *switch_to_branch, unsigned flags, - const char *reflog_orig_head, const char *reflog_head, - const char *default_reflog_action); +struct reset_head_opts { + /* + * The commit to checkout/reset to. Defaults to HEAD. + */ + const struct object_id *oid; + /* + * Optional branch to switch to. + */ + const char *branch; + /* + * Flags defined above. + */ + unsigned flags; + /* + * Optional reflog message for HEAD, if this omitted but oid or branch + * are given then default_reflog_action must be given. + */ + const char *head_msg; + /* + * Optional reflog message for ORIG_HEAD, if this omitted and flags + * contains RESET_ORIG_HEAD then default_reflog_action must be given. + */ + const char *orig_head_msg; + /* + * Action to use in default reflog messages, only required if a ref is + * being updated and the reflog messages above are omitted. + */ + const char *default_reflog_action; +}; + +int reset_head(struct repository *r, const struct reset_head_opts *opts); #endif diff --git a/sequencer.c b/sequencer.c index 119564f435..55ed074ae7 100644 --- a/sequencer.c +++ b/sequencer.c @@ -4115,6 +4115,7 @@ void create_autostash(struct repository *r, const char *path) if (has_unstaged_changes(r, 1) || has_uncommitted_changes(r, 1)) { struct child_process stash = CHILD_PROCESS_INIT; + struct reset_head_opts ropts = { .flags = RESET_HEAD_HARD }; struct object_id oid; strvec_pushl(&stash.args, @@ -4136,10 +4137,8 @@ void create_autostash(struct repository *r, const char *path) path); write_file(path, "%s", oid_to_hex(&oid)); printf(_("Created autostash: %s\n"), buf.buf); - if (reset_head(r, NULL, NULL, RESET_HEAD_HARD, NULL, NULL, - NULL) < 0) + if (reset_head(r, &ropts) < 0) die(_("could not reset --hard")); - if (discard_index(r->index) < 0 || repo_read_index(r) < 0) die(_("could not read index")); -- cgit v1.3 From 38c541ce94048cf72aa4f465be9314423a57f445 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Wed, 26 Jan 2022 13:05:49 +0000 Subject: rebase -m: don't fork git checkout Now that reset_head() can handle the initial checkout of onto correctly use it in the "merge" backend instead of forking "git checkout". This opens the way for us to stop calling the post-checkout hook in the future. Not running "git checkout" means that "rebase -i/m" no longer recurse submodules when checking out "onto" (thanks to Philippe Blain for pointing this out). As the rest of rebase does not know what to do with submodules this is probably a good thing. When using merge-ort rebase ought be able to handle submodules correctly if it parsed the submodule config, such a change is left for a future patch series. The "apply" based rebase has avoided forking git checkout since ac7f467fef ("builtin/rebase: support running "git rebase "", 2018-08-07). The code that handles the checkout was moved into libgit by b309a97108 ("reset: extract reset_head() from rebase", 2020-04-07). Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano --- sequencer.c | 38 +++++++++++--------------------------- 1 file changed, 11 insertions(+), 27 deletions(-) (limited to 'sequencer.c') diff --git a/sequencer.c b/sequencer.c index 55ed074ae7..bdd66b4b67 100644 --- a/sequencer.c +++ b/sequencer.c @@ -4223,42 +4223,26 @@ int apply_autostash_oid(const char *stash_oid) return apply_save_autostash_oid(stash_oid, 1); } -static int run_git_checkout(struct repository *r, struct replay_opts *opts, - const char *commit, const char *action) -{ - struct child_process cmd = CHILD_PROCESS_INIT; - int ret; - - cmd.git_cmd = 1; - - strvec_push(&cmd.args, "checkout"); - strvec_push(&cmd.args, commit); - strvec_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action); - - if (opts->verbose) - ret = run_command(&cmd); - else - ret = run_command_silent_on_success(&cmd); - - if (!ret) - discard_index(r->index); - - return ret; -} - static int checkout_onto(struct repository *r, struct replay_opts *opts, const char *onto_name, const struct object_id *onto, const struct object_id *orig_head) { - const char *action = reflog_message(opts, "start", "checkout %s", onto_name); - - if (run_git_checkout(r, opts, oid_to_hex(onto), action)) { + struct reset_head_opts ropts = { + .oid = onto, + .orig_head = orig_head, + .flags = RESET_HEAD_DETACH | RESET_ORIG_HEAD | + RESET_HEAD_RUN_POST_CHECKOUT_HOOK, + .head_msg = reflog_message(opts, "start", "checkout %s", + onto_name), + .default_reflog_action = "rebase" + }; + if (reset_head(r, &ropts)) { apply_autostash(rebase_path_autostash()); sequencer_remove_state(opts); return error(_("could not detach HEAD")); } - return update_ref(NULL, "ORIG_HEAD", orig_head, NULL, 0, UPDATE_REFS_MSG_ON_ERR); + return 0; } static int stopped_at_head(struct repository *r) -- cgit v1.3