]> git.ipfire.org Git - thirdparty/git.git/commitdiff
rebase: add --[no-]edit to --continue
authorHugo Sales <hugo@hsal.es>
Tue, 21 Jul 2026 14:04:42 +0000 (15:04 +0100)
committerJunio C Hamano <gitster@pobox.com>
Tue, 21 Jul 2026 16:42:41 +0000 (09:42 -0700)
Allow skipping the editor when continuing after resolving conflicts,
via --no-edit or the rebase.noEdit configuration variable. The --edit
option overrides rebase.noEdit when both are set.

Signed-off-by: Hugo Sales <hugo@hsal.es>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/config/rebase.adoc
Documentation/git-rebase.adoc
builtin/rebase.c
sequencer.c
t/t3436-rebase-more-options.sh

index c6187ab28b2fb731fad341c7d3c193872558645d..321ab8b529ca3fc79527ff8eac5ec335d695d2cf 100644 (file)
@@ -62,6 +62,12 @@ instead of:
 +
 Defaults to false.
 
+rebase.noEdit::
+       When set to true, `git rebase --continue` uses the commit message
+       without launching $EDITOR, as if `--no-edit` were given.  The
+       `--edit` option to `git rebase --continue` overrides this setting.
+       Defaults to false.
+
 rebase.rescheduleFailedExec::
        Automatically reschedule `exec` commands that failed. This only makes
        sense in interactive mode (or when an `--exec` option was provided).
index f6c22d1598978a88cbc690e9012c12197b9ea3ab..cc0a69b5a599a327eb8084e4b1be97b7cbc489a3 100644 (file)
@@ -181,6 +181,16 @@ including not with each other:
 
 --continue::
        Restart the rebasing process after having resolved a merge conflict.
++
+-e::
+--edit::
+--no-edit::
+       With `--continue`, edit or do not edit the commit message,
+       respectively. By default, the configured $EDITOR is opened so you
+       can update the commit message after resolving conflicts.
+       `--no-edit` reuses the existing message without launching an
+       editor. The `rebase.noEdit` configuration variable can be used to
+       enable `--no-edit` by default; `--edit` overrides that setting.
 
 --skip::
        Restart the rebasing process by skipping the current patch.
@@ -783,9 +793,10 @@ Commit Rewording
 When a conflict occurs while rebasing, rebase stops and asks the user
 to resolve.  Since the user may need to make notable changes while
 resolving conflicts, after conflicts are resolved and the user has run
-`git rebase --continue`, the rebase should open an editor and ask the
-user to update the commit message.  The 'merge' backend does this, while
-the 'apply' backend blindly applies the original commit message.
+`git rebase --continue`, the rebase opens an editor and asks the
+user to update the commit message, unless `rebase.noEdit` is set or
+`--no-edit` is passed to `--continue`.  The 'merge' backend does this,
+while the 'apply' backend blindly applies the original commit message.
 
 Miscellaneous differences
 ~~~~~~~~~~~~~~~~~~~~~~~~~
index fa4f5d9306b856336970bf77b037cd5870aae4de..58c1d999c9ef2594aff2ba564f5e889e07565b4d 100644 (file)
@@ -43,7 +43,7 @@ static char const * const builtin_rebase_usage[] = {
                "[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"),
        N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
                "--root [<branch>]"),
-       "git rebase --continue | --abort | --skip | --edit-todo",
+       "git rebase --continue [--[no-]edit] | --abort | --skip | --edit-todo",
        NULL
 };
 
@@ -135,6 +135,8 @@ struct rebase_options {
        int config_autosquash;
        int config_rebase_merges;
        int config_update_refs;
+       int config_no_edit;
+       int edit;
 };
 
 #define REBASE_OPTIONS_INIT {                          \
@@ -156,6 +158,8 @@ struct rebase_options {
                .update_refs = -1,                      \
                .config_update_refs = -1,               \
                .strategy_opts = STRING_LIST_INIT_NODUP,\
+               .config_no_edit = -1,                   \
+               .edit = -1,                             \
        }
 
 static void rebase_options_release(struct rebase_options *opts)
@@ -215,6 +219,13 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
                replay.have_squash_onto = 1;
        }
 
+       if (opts->action == ACTION_CONTINUE) {
+               if (opts->edit >= 0)
+                       replay.edit = opts->edit;
+               else if (opts->config_no_edit > 0)
+                       replay.edit = 0;
+       }
+
        return replay;
 }
 
@@ -839,6 +850,11 @@ static int rebase_config(const char *var, const char *value,
                return 0;
        }
 
+       if (!strcmp(var, "rebase.noedit")) {
+               opts->config_no_edit = git_config_bool(var, value);
+               return 0;
+       }
+
        if (!strcmp(var, "rebase.forkpoint")) {
                opts->fork_point = git_config_bool(var, value) ? -1 : 0;
                return 0;
@@ -1168,6 +1184,8 @@ int cmd_rebase(int argc,
                            ACTION_CONTINUE),
                OPT_CMDMODE(0, "skip", &options.action,
                            N_("skip current patch and continue"), ACTION_SKIP),
+               OPT_BOOL('e', "edit", &options.edit,
+                        N_("edit the commit message")),
                OPT_CMDMODE(0, "abort", &options.action,
                            N_("abort and check out the original branch"),
                            ACTION_ABORT),
@@ -1308,10 +1326,15 @@ int cmd_rebase(int argc,
                        "which is no longer supported; use 'merges' instead"));
 
        if (options.action != ACTION_NONE && total_argc != 2) {
-               usage_with_options(builtin_rebase_usage,
-                                  builtin_rebase_options);
+               if (options.action != ACTION_CONTINUE ||
+                   options.edit < 0 || total_argc != 3)
+                       usage_with_options(builtin_rebase_usage,
+                                          builtin_rebase_options);
        }
 
+       if (options.edit >= 0 && options.action != ACTION_CONTINUE)
+               die(_("--edit and --no-edit can only be used with --continue"));
+
        if (argc > 2)
                usage_with_options(builtin_rebase_usage,
                                   builtin_rebase_options);
index 57855b0066ac98646facc3b3a610248b5d80a28f..e724605e8431ad3ee1d3decba6d43e34ab243997 100644 (file)
@@ -2211,6 +2211,25 @@ static int should_edit(struct replay_opts *opts) {
        return opts->edit;
 }
 
+static int should_edit_rebase_continue(struct replay_opts *opts)
+{
+       if (opts->edit < 0)
+               return 1;
+       return opts->edit;
+}
+
+static void finalize_continue_edit_flags(struct replay_opts *opts,
+                                        unsigned int *flags)
+{
+       if (*flags & CLEANUP_MSG)
+               return;
+
+       if (should_edit_rebase_continue(opts))
+               *flags |= EDIT_MSG;
+       else
+               *flags &= ~EDIT_MSG;
+}
+
 static void refer_to_commit(struct repository *r, struct strbuf *msgbuf,
                            const struct commit *commit,
                            bool use_commit_reference)
@@ -5261,7 +5280,7 @@ static int commit_staged_changes(struct repository *r,
                                 struct todo_list *todo_list)
 {
        struct replay_ctx *ctx = opts->ctx;
-       unsigned int flags = ALLOW_EMPTY | EDIT_MSG;
+       unsigned int flags = ALLOW_EMPTY;
        unsigned int final_fixup = 0, is_clean;
        struct strbuf rev = STRBUF_INIT;
        const char *reflog_action = reflog_message(opts, "continue", NULL);
@@ -5426,6 +5445,8 @@ static int commit_staged_changes(struct repository *r,
                }
        }
 
+       finalize_continue_edit_flags(opts, &flags);
+
        if (run_git_commit(final_fixup ? NULL : rebase_path_message(),
                           reflog_action, opts, flags)) {
                ret = error(_("could not commit staged changes."));
@@ -5483,6 +5504,12 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
                        res = -1;
                        goto release_todo_list;
                }
+
+               /*
+                * Command-line --[no-]edit applies only to this
+                * --continue invocation, not to subsequent picks.
+                */
+               opts->edit = -1;
        } else if (!file_exists(get_todo_path(opts)))
                return continue_single_pick(r, opts);
        else if ((res = read_populate_todo(r, &todo_list, opts)))
index 94671d3c4650463525006090702210eda76594e0..c84c6717ab41f83955c6f29bb9037a16a1f52885 100755 (executable)
@@ -201,6 +201,58 @@ test_expect_success '--ignore-date is an alias for --reset-author-date' '
        test_atime_is_ignored -2
 '
 
+test_expect_success '--no-edit on continue uses existing commit message' '
+       git checkout commit2 &&
+       test_must_fail git rebase -m --onto commit2^^ commit2^ &&
+       echo resolved >foo &&
+       git add foo &&
+       write_script fail-if-editor-invoked <<-\EOF &&
+       echo editor invoked >&2
+       exit 1
+       EOF
+       GIT_EDITOR=./fail-if-editor-invoked git rebase --continue --no-edit &&
+       git log --format=%s -1 >actual &&
+       echo commit2 >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success '--no-edit cannot be used when starting a rebase' '
+       test_must_fail git rebase --no-edit -m main side 2>err &&
+       test_grep "only be used with --continue" err
+'
+
+test_expect_success 'rebase.noEdit skips editor on continue' '
+       git config rebase.noEdit true &&
+       git checkout commit2 &&
+       test_must_fail git rebase -m --onto commit2^^ commit2^ &&
+       echo resolved >foo &&
+       git add foo &&
+       write_script fail-if-editor-invoked <<-\EOF &&
+       echo editor invoked >&2
+       exit 1
+       EOF
+       GIT_EDITOR=./fail-if-editor-invoked git rebase --continue &&
+       git log --format=%s -1 >actual &&
+       echo commit2 >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success '--edit on continue overrides rebase.noEdit' '
+       git config rebase.noEdit true &&
+       git checkout commit2 &&
+       test_must_fail git rebase -m --onto commit2^^ commit2^ &&
+       echo resolved >foo &&
+       git add foo &&
+       (
+               set_fake_editor &&
+               FAKE_COMMIT_MESSAGE="edited on continue" \
+                       git rebase --continue --edit
+       ) &&
+       test_write_lines "edited on continue" "" >expect &&
+       git log --format=%B -1 >actual &&
+       test_cmp expect actual
+'
+
 # This must be the last test in this file
 test_expect_success '$EDITOR and friends are unchanged' '
        test_editor_unchanged