rebase: add --[no-]edit to --continue

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>

Hugo Sales committed Jul 21, 2026 at 15:04 UTC 179eccf0d01729c19a3238905b951b1880aa4ba1
5 files changed +126 -7
Documentation/config/rebase.adoc
+6
@@ -62,6 +62,12 @@ instead of:
62 +
63 Defaults to false.
64
65 +rebase.noEdit::
66 + When set to true, `git rebase --continue` uses the commit message
67 + without launching $EDITOR, as if `--no-edit` were given. The
68 + `--edit` option to `git rebase --continue` overrides this setting.
69 + Defaults to false.
70 +
71 rebase.rescheduleFailedExec::
72 Automatically reschedule `exec` commands that failed. This only makes
73 sense in interactive mode (or when an `--exec` option was provided).
Documentation/git-rebase.adoc
+14 -3
@@ -181,6 +181,16 @@ including not with each other:
181
182 --continue::
183 Restart the rebasing process after having resolved a merge conflict.
184 ++
185 +-e::
186 +--edit::
187 +--no-edit::
188 + With `--continue`, edit or do not edit the commit message,
189 + respectively. By default, the configured $EDITOR is opened so you
190 + can update the commit message after resolving conflicts.
191 + `--no-edit` reuses the existing message without launching an
192 + editor. The `rebase.noEdit` configuration variable can be used to
193 + enable `--no-edit` by default; `--edit` overrides that setting.
194
195 --skip::
196 Restart the rebasing process by skipping the current patch.
@@ -783,9 +793,10 @@ Commit Rewording
793 When a conflict occurs while rebasing, rebase stops and asks the user
794 to resolve. Since the user may need to make notable changes while
795 resolving conflicts, after conflicts are resolved and the user has run
786 -`git rebase --continue`, the rebase should open an editor and ask the
787 -user to update the commit message. The 'merge' backend does this, while
788 -the 'apply' backend blindly applies the original commit message.
796 +`git rebase --continue`, the rebase opens an editor and asks the
797 +user to update the commit message, unless `rebase.noEdit` is set or
798 +`--no-edit` is passed to `--continue`. The 'merge' backend does this,
799 +while the 'apply' backend blindly applies the original commit message.
800
801 Miscellaneous differences
802 ~~~~~~~~~~~~~~~~~~~~~~~~~
builtin/rebase.c
+26 -3
@@ -43,7 +43,7 @@ static char const * const builtin_rebase_usage[] = {
43 "[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"),
44 N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
45 "--root [<branch>]"),
46 - "git rebase --continue | --abort | --skip | --edit-todo",
46 + "git rebase --continue [--[no-]edit] | --abort | --skip | --edit-todo",
47 NULL
48 };
49
@@ -135,6 +135,8 @@ struct rebase_options {
135 int config_autosquash;
136 int config_rebase_merges;
137 int config_update_refs;
138 + int config_no_edit;
139 + int edit;
140 };
141
142 #define REBASE_OPTIONS_INIT { \
@@ -156,6 +158,8 @@ struct rebase_options {
158 .update_refs = -1, \
159 .config_update_refs = -1, \
160 .strategy_opts = STRING_LIST_INIT_NODUP,\
161 + .config_no_edit = -1, \
162 + .edit = -1, \
163 }
164
165 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)
219 replay.have_squash_onto = 1;
220 }
221
222 + if (opts->action == ACTION_CONTINUE) {
223 + if (opts->edit >= 0)
224 + replay.edit = opts->edit;
225 + else if (opts->config_no_edit > 0)
226 + replay.edit = 0;
227 + }
228 +
229 return replay;
230 }
231
@@ -839,6 +850,11 @@ static int rebase_config(const char *var, const char *value,
850 return 0;
851 }
852
853 + if (!strcmp(var, "rebase.noedit")) {
854 + opts->config_no_edit = git_config_bool(var, value);
855 + return 0;
856 + }
857 +
858 if (!strcmp(var, "rebase.forkpoint")) {
859 opts->fork_point = git_config_bool(var, value) ? -1 : 0;
860 return 0;
@@ -1168,6 +1184,8 @@ int cmd_rebase(int argc,
1184 ACTION_CONTINUE),
1185 OPT_CMDMODE(0, "skip", &options.action,
1186 N_("skip current patch and continue"), ACTION_SKIP),
1187 + OPT_BOOL('e', "edit", &options.edit,
1188 + N_("edit the commit message")),
1189 OPT_CMDMODE(0, "abort", &options.action,
1190 N_("abort and check out the original branch"),
1191 ACTION_ABORT),
@@ -1308,10 +1326,15 @@ int cmd_rebase(int argc,
1326 "which is no longer supported; use 'merges' instead"));
1327
1328 if (options.action != ACTION_NONE && total_argc != 2) {
1311 - usage_with_options(builtin_rebase_usage,
1312 - builtin_rebase_options);
1329 + if (options.action != ACTION_CONTINUE ||
1330 + options.edit < 0 || total_argc != 3)
1331 + usage_with_options(builtin_rebase_usage,
1332 + builtin_rebase_options);
1333 }
1334
1335 + if (options.edit >= 0 && options.action != ACTION_CONTINUE)
1336 + die(_("--edit and --no-edit can only be used with --continue"));
1337 +
1338 if (argc > 2)
1339 usage_with_options(builtin_rebase_usage,
1340 builtin_rebase_options);
sequencer.c
+28 -1
@@ -2211,6 +2211,25 @@ static int should_edit(struct replay_opts *opts) {
2211 return opts->edit;
2212 }
2213
2214 +static int should_edit_rebase_continue(struct replay_opts *opts)
2215 +{
2216 + if (opts->edit < 0)
2217 + return 1;
2218 + return opts->edit;
2219 +}
2220 +
2221 +static void finalize_continue_edit_flags(struct replay_opts *opts,
2222 + unsigned int *flags)
2223 +{
2224 + if (*flags & CLEANUP_MSG)
2225 + return;
2226 +
2227 + if (should_edit_rebase_continue(opts))
2228 + *flags |= EDIT_MSG;
2229 + else
2230 + *flags &= ~EDIT_MSG;
2231 +}
2232 +
2233 static void refer_to_commit(struct repository *r, struct strbuf *msgbuf,
2234 const struct commit *commit,
2235 bool use_commit_reference)
@@ -5261,7 +5280,7 @@ static int commit_staged_changes(struct repository *r,
5280 struct todo_list *todo_list)
5281 {
5282 struct replay_ctx *ctx = opts->ctx;
5264 - unsigned int flags = ALLOW_EMPTY | EDIT_MSG;
5283 + unsigned int flags = ALLOW_EMPTY;
5284 unsigned int final_fixup = 0, is_clean;
5285 struct strbuf rev = STRBUF_INIT;
5286 const char *reflog_action = reflog_message(opts, "continue", NULL);
@@ -5426,6 +5445,8 @@ static int commit_staged_changes(struct repository *r,
5445 }
5446 }
5447
5448 + finalize_continue_edit_flags(opts, &flags);
5449 +
5450 if (run_git_commit(final_fixup ? NULL : rebase_path_message(),
5451 reflog_action, opts, flags)) {
5452 ret = error(_("could not commit staged changes."));
@@ -5483,6 +5504,12 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
5504 res = -1;
5505 goto release_todo_list;
5506 }
5507 +
5508 + /*
5509 + * Command-line --[no-]edit applies only to this
5510 + * --continue invocation, not to subsequent picks.
5511 + */
5512 + opts->edit = -1;
5513 } else if (!file_exists(get_todo_path(opts)))
5514 return continue_single_pick(r, opts);
5515 else if ((res = read_populate_todo(r, &todo_list, opts)))
t/t3436-rebase-more-options.sh
+52
@@ -201,6 +201,58 @@ test_expect_success '--ignore-date is an alias for --reset-author-date' '
201 test_atime_is_ignored -2
202 '
203
204 +test_expect_success '--no-edit on continue uses existing commit message' '
205 + git checkout commit2 &&
206 + test_must_fail git rebase -m --onto commit2^^ commit2^ &&
207 + echo resolved >foo &&
208 + git add foo &&
209 + write_script fail-if-editor-invoked <<-\EOF &&
210 + echo editor invoked >&2
211 + exit 1
212 + EOF
213 + GIT_EDITOR=./fail-if-editor-invoked git rebase --continue --no-edit &&
214 + git log --format=%s -1 >actual &&
215 + echo commit2 >expect &&
216 + test_cmp expect actual
217 +'
218 +
219 +test_expect_success '--no-edit cannot be used when starting a rebase' '
220 + test_must_fail git rebase --no-edit -m main side 2>err &&
221 + test_grep "only be used with --continue" err
222 +'
223 +
224 +test_expect_success 'rebase.noEdit skips editor on continue' '
225 + git config rebase.noEdit true &&
226 + git checkout commit2 &&
227 + test_must_fail git rebase -m --onto commit2^^ commit2^ &&
228 + echo resolved >foo &&
229 + git add foo &&
230 + write_script fail-if-editor-invoked <<-\EOF &&
231 + echo editor invoked >&2
232 + exit 1
233 + EOF
234 + GIT_EDITOR=./fail-if-editor-invoked git rebase --continue &&
235 + git log --format=%s -1 >actual &&
236 + echo commit2 >expect &&
237 + test_cmp expect actual
238 +'
239 +
240 +test_expect_success '--edit on continue overrides rebase.noEdit' '
241 + git config rebase.noEdit true &&
242 + git checkout commit2 &&
243 + test_must_fail git rebase -m --onto commit2^^ commit2^ &&
244 + echo resolved >foo &&
245 + git add foo &&
246 + (
247 + set_fake_editor &&
248 + FAKE_COMMIT_MESSAGE="edited on continue" \
249 + git rebase --continue --edit
250 + ) &&
251 + test_write_lines "edited on continue" "" >expect &&
252 + git log --format=%B -1 >actual &&
253 + test_cmp expect actual
254 +'
255 +
256 # This must be the last test in this file
257 test_expect_success '$EDITOR and friends are unchanged' '
258 test_editor_unchanged