rebase: write script before initializing state

If rebase.instructionFormat is invalid the repository is left in a strange state when the interactive rebase fails. `git status` outputs boths the same as it would in the normal case *and* something related to interactive rebase: $ git -c rebase.instructionFormat=blah rebase -i fatal: invalid --pretty format: blah $ git status On branch master Your branch is ahead of 'upstream/master' by 1 commit. (use "git push" to publish your local commits) git-rebase-todo is missing. No commands done. No commands remaining. You are currently editing a commit while rebasing branch 'master' on '8db3019401'. (use "git commit --amend" to amend the current commit) (use "git rebase --continue" once you are satisfied with your changes) By attempting to write the rebase script before initializing the state this potential scenario is avoided. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Øystein Walle committed Jun 10, 2025 at 00:10 UTC ade14bffd71b75974492cf7afc525b8feb8a70b2
2 files changed +31 -21
builtin/rebase.c
+21 -21
@@ -292,15 +292,6 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
292 &revisions, &shortrevisions))
293 goto cleanup;
294
295 - if (init_basic_state(&replay,
296 - opts->head_name ? opts->head_name : "detached HEAD",
297 - opts->onto, &opts->orig_head->object.oid))
298 - goto cleanup;
299 -
300 - if (!opts->upstream && opts->squash_onto)
301 - write_file(path_squash_onto(), "%s\n",
302 - oid_to_hex(opts->squash_onto));
303 -
295 strvec_pushl(&make_script_args, "", revisions, NULL);
296 if (opts->restrict_revision)
297 strvec_pushf(&make_script_args, "^%s",
@@ -309,21 +300,30 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
300 ret = sequencer_make_script(the_repository, &todo_list.buf,
301 make_script_args.nr, make_script_args.v,
302 flags);
312 -
313 - if (ret)
303 + if (ret) {
304 error(_("could not generate todo list"));
315 - else {
316 - discard_index(the_repository->index);
317 - if (todo_list_parse_insn_buffer(the_repository, &replay,
318 - todo_list.buf.buf, &todo_list))
319 - BUG("unusable todo list");
320 -
321 - ret = complete_action(the_repository, &replay, flags,
322 - shortrevisions, opts->onto_name, opts->onto,
323 - &opts->orig_head->object.oid, &opts->exec,
324 - opts->autosquash, opts->update_refs, &todo_list);
305 + goto cleanup;
306 }
307
308 + if (init_basic_state(&replay,
309 + opts->head_name ? opts->head_name : "detached HEAD",
310 + opts->onto, &opts->orig_head->object.oid))
311 + goto cleanup;
312 +
313 + if (!opts->upstream && opts->squash_onto)
314 + write_file(path_squash_onto(), "%s\n",
315 + oid_to_hex(opts->squash_onto));
316 +
317 + discard_index(the_repository->index);
318 + if (todo_list_parse_insn_buffer(the_repository, &replay,
319 + todo_list.buf.buf, &todo_list))
320 + BUG("unusable todo list");
321 +
322 + ret = complete_action(the_repository, &replay, flags,
323 + shortrevisions, opts->onto_name, opts->onto,
324 + &opts->orig_head->object.oid, &opts->exec,
325 + opts->autosquash, opts->update_refs, &todo_list);
326 +
327 cleanup:
328 replay_opts_release(&replay);
329 free(revisions);
t/t3415-rebase-autosquash.sh
+10
@@ -394,6 +394,16 @@ test_expect_success 'autosquash with empty custom instructionFormat' '
394 )
395 '
396
397 +test_expect_success 'autosquash with invalid custom instructionFormat' '
398 + git reset --hard base &&
399 + test_commit invalid-instructionFormat-test &&
400 + (
401 + test_must_fail git -c rebase.instructionFormat=blah \
402 + rebase --autosquash --force-rebase -i HEAD^ &&
403 + test_path_is_missing .git/rebase-merge
404 + )
405 +'
406 +
407 set_backup_editor () {
408 write_script backup-editor.sh <<-\EOF
409 cp "$1" .git/backup-"$(basename "$1")"