]> git.ipfire.org Git - thirdparty/git.git/commitdiff
rebase: remember fixup -c after skipping fixup/squash
authorPhillip Wood <phillip.wood@dunelm.org.uk>
Sun, 26 Jul 2026 15:39:00 +0000 (16:39 +0100)
committerJunio C Hamano <gitster@pobox.com>
Sun, 26 Jul 2026 21:02:01 +0000 (14:02 -0700)
When the final command in a chain of "fixup" and "squash" commands
is skipped, we should prompt the user to edit the commit message
if the chain contains a "fixup -c" command that was not skipped.
Unfortunately, commit_staged_changes() only looks for completed "squash"
commands and so does not prompt the user to edit the message. Fix
this by recording whether a fixup command has the "-c" flag set and
then checking whether we have seen either a "fixup -c" or a "squash"
command. Add regression tests for skipping a command in the middle
of the chain (which currently works but has no test coverage), and
for skipping the final command (which is fixed by this patch).

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
sequencer.c
t/t3437-rebase-fixup-options.sh

index 4640ee9b7f536b7e3bfb37b7c82ecff3307759b4..1a0a283b42c3c2cc3596c78532e0c00b92a6c646 100644 (file)
@@ -1926,6 +1926,13 @@ static int seen_squash(struct replay_ctx *ctx)
                strstr(ctx->current_fixups.buf, "\nsquash");
 }
 
+/* Does the current fixup chain contain a "fixup -c" command? */
+static int seen_fixup_edit_msg(struct replay_ctx *ctx)
+{
+       return starts_with(ctx->current_fixups.buf, "fixup -c") ||
+               strstr(ctx->current_fixups.buf, "\nfixup -c");
+}
+
 static void update_comment_bufs(struct strbuf *buf1, struct strbuf *buf2, int n)
 {
        strbuf_setlen(buf1, strlen(comment_line_str) + 1);
@@ -2148,9 +2155,14 @@ static int update_squash_messages(struct repository *r,
        strbuf_release(&buf);
 
        if (!res) {
-               strbuf_addf(&ctx->current_fixups, "%s%s %s",
+               const char *fixup_flag = "";
+
+               if (is_fixup_flag(command, flag) && (flag & TODO_EDIT_FIXUP_MSG))
+                       fixup_flag = " -c";
+
+               strbuf_addf(&ctx->current_fixups, "%s%s%s %s",
                            ctx->current_fixups.len ? "\n" : "",
-                           command_to_string(command),
+                           command_to_string(command), fixup_flag,
                            oid_to_hex(&commit->object.oid));
                res = write_message(ctx->current_fixups.buf,
                                    ctx->current_fixups.len,
@@ -5391,8 +5403,8 @@ static int commit_staged_changes(struct repository *r,
                                 * message, no need to bother the user with
                                 * opening the commit message in the editor.
                                 */
-                               if (!starts_with(p, "squash ") &&
-                                   !strstr(p, "\nsquash "))
+                               if (!seen_squash(ctx) &&
+                                   !seen_fixup_edit_msg(ctx))
                                        flags = (flags & ~EDIT_MSG) | CLEANUP_MSG;
                        } else if (is_fixup(peek_command(todo_list, 0))) {
                                /*
index 5d306a476928b1838d62c67ab895c2d541e00c53..a4b2a631654f1c3d1fc19b67ac2fd58ad0c3f4cf 100755 (executable)
@@ -186,6 +186,53 @@ test_expect_success 'multiple fixup -c opens editor once' '
        test_commit_message HEAD expected-message
 '
 
+test_expect_success 'fixup -c is remembered after skipping final fixup' '
+       test_when_finished "test_might_fail git rebase --abort" &&
+       cat >todo <<-\EOF &&
+       pick B
+       fixup -c A1
+       fixup A3
+       EOF
+       (
+               set_fake_editor &&
+               set_replace_editor todo &&
+               test_must_fail git rebase -i A A &&
+               git show && cat .git/rebase-merge/message-squash &&
+               FAKE_COMMIT_AMEND=edited git rebase --skip
+       ) &&
+       test_commit_message HEAD <<-\EOF
+       new subject
+
+       new
+       body
+
+       edited
+       EOF
+'
+test_expect_success 'fixup -c is remembered after skipping later fixup' '
+       test_when_finished "test_might_fail git rebase --abort" &&
+       cat >todo <<-\EOF &&
+       pick B
+       fixup -c A1
+       fixup A3
+       fixup A2
+       EOF
+       (
+               set_fake_editor &&
+               set_replace_editor todo &&
+               test_must_fail git rebase -i A A &&
+               FAKE_COMMIT_AMEND=edited git rebase --skip
+       ) &&
+       test_commit_message HEAD <<-\EOF
+       new subject
+
+       new
+       body
+
+       edited
+       EOF
+'
+
 test_expect_success 'sequence squash, fixup & fixup -c gives combined message' '
        test_when_finished "test_might_fail git rebase --abort" &&
        git checkout --detach A3 &&