]> git.ipfire.org Git - thirdparty/git.git/commitdiff
sequencer: share the squash message marker helpers and flags
authorHarald Nordgren <haraldnordgren@gmail.com>
Mon, 20 Jul 2026 08:27:03 +0000 (08:27 +0000)
committerJunio C Hamano <gitster@pobox.com>
Wed, 29 Jul 2026 17:36:55 +0000 (10:36 -0700)
When "git rebase -i" squashes commits it builds an editor template with a
"This is a combination of N commits." banner, a "This is the 1st/Nth
commit message:" header above each kept message (or a "will be skipped"
header for a dropped one), and a commented-out subject for any fixup!,
squash! or amend! commit. The banner, the headers and the
subject-commenting all live in static helpers in sequencer.c wired to the
rebase state, so no other command can present a squash the same way.

Pull the three pieces out into add_squash_combination_header(),
add_squash_message_header() (which takes a flag for the "will be skipped"
variant) and squash_subject_comment_len(), and use them from
update_squash_messages() and append_squash_message(). Also move the
todo_item_flags enum to the header, so a caller reading the output of
todo_list_rearrange_squash() can tell an amend! (TODO_REPLACE_FIXUP_MSG)
from a plain fixup!. A later change reuses all of this to give "git
history squash --reedit-message" the same template.

No change in behavior.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
sequencer.c
sequencer.h

index 1355a99a0922681a4443b03df7387974009d1fee..3c704fd5ab1346cb19f0d217fb5587208826db8f 100644 (file)
@@ -1880,18 +1880,38 @@ static int is_pick_or_similar(enum todo_command command)
        }
 }
 
-enum todo_item_flags {
-       TODO_EDIT_MERGE_MSG    = (1 << 0),
-       TODO_REPLACE_FIXUP_MSG = (1 << 1),
-       TODO_EDIT_FIXUP_MSG    = (1 << 2),
-};
-
 static const char first_commit_msg_str[] = N_("This is the 1st commit message:");
 static const char nth_commit_msg_fmt[] = N_("This is the commit message #%d:");
 static const char skip_first_commit_msg_str[] = N_("The 1st commit message will be skipped:");
 static const char skip_nth_commit_msg_fmt[] = N_("The commit message #%d will be skipped:");
 static const char combined_commit_msg_fmt[] = N_("This is a combination of %d commits.");
 
+void add_squash_combination_header(struct strbuf *buf, int n)
+{
+       strbuf_addf(buf, "%s ", comment_line_str);
+       strbuf_addf(buf, _(combined_commit_msg_fmt), n);
+}
+
+void add_squash_message_header(struct strbuf *buf, int n, int skip)
+{
+       strbuf_addf(buf, "%s ", comment_line_str);
+       if (n == 1)
+               strbuf_addstr(buf, skip ? _(skip_first_commit_msg_str) :
+                                  _(first_commit_msg_str));
+       else
+               strbuf_addf(buf, skip ? _(skip_nth_commit_msg_fmt) :
+                           _(nth_commit_msg_fmt), n);
+}
+
+size_t squash_subject_comment_len(const char *body, int squashing)
+{
+       if (starts_with(body, "amend!") ||
+           (squashing && (starts_with(body, "squash!") ||
+                          starts_with(body, "fixup!"))))
+               return commit_subject_length(body);
+       return 0;
+}
+
 static int is_fixup_flag(enum todo_command command, unsigned flag)
 {
        return command == TODO_FIXUP && ((flag & TODO_REPLACE_FIXUP_MSG) ||
@@ -2005,20 +2025,13 @@ static int append_squash_message(struct strbuf *buf, const char *body,
 {
        struct replay_ctx *ctx = opts->ctx;
        const char *fixup_msg;
-       size_t commented_len = 0, fixup_off;
-       /*
-        * amend is non-interactive and not normally used with fixup!
-        * or squash! commits, so only comment out those subjects when
-        * squashing commit messages.
-        */
-       if (starts_with(body, "amend!") ||
-           ((command == TODO_SQUASH || seen_squash(ctx)) &&
-            (starts_with(body, "squash!") || starts_with(body, "fixup!"))))
-               commented_len = commit_subject_length(body);
+       size_t commented_len, fixup_off;
+
+       commented_len = squash_subject_comment_len(body,
+                               command == TODO_SQUASH || seen_squash(ctx));
 
-       strbuf_addf(buf, "\n%s ", comment_line_str);
-       strbuf_addf(buf, _(nth_commit_msg_fmt),
-                   ++ctx->current_fixup_count + 1);
+       strbuf_addch(buf, '\n');
+       add_squash_message_header(buf, ++ctx->current_fixup_count + 1, 0);
        strbuf_addstr(buf, "\n\n");
        strbuf_add_commented_lines(buf, body, commented_len, comment_line_str);
        /* buf->buf may be reallocated so store an offset into the buffer */
@@ -2083,9 +2096,8 @@ static int update_squash_messages(struct repository *r,
                eol = !starts_with(buf.buf, comment_line_str) ?
                        buf.buf : strchrnul(buf.buf, '\n');
 
-               strbuf_addf(&header, "%s ", comment_line_str);
-               strbuf_addf(&header, _(combined_commit_msg_fmt),
-                           ctx->current_fixup_count + 2);
+               add_squash_combination_header(&header,
+                                             ctx->current_fixup_count + 2);
                strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);
                strbuf_release(&header);
                if (is_fixup_flag(command, flag) && !seen_squash(ctx))
@@ -2109,12 +2121,9 @@ static int update_squash_messages(struct repository *r,
                        repo_unuse_commit_buffer(r, head_commit, head_message);
                        return error(_("cannot write '%s'"), rebase_path_fixup_msg());
                }
-               strbuf_addf(&buf, "%s ", comment_line_str);
-               strbuf_addf(&buf, _(combined_commit_msg_fmt), 2);
-               strbuf_addf(&buf, "\n%s ", comment_line_str);
-               strbuf_addstr(&buf, is_fixup_flag(command, flag) ?
-                             _(skip_first_commit_msg_str) :
-                             _(first_commit_msg_str));
+               add_squash_combination_header(&buf, 2);
+               strbuf_addch(&buf, '\n');
+               add_squash_message_header(&buf, 1, is_fixup_flag(command, flag));
                strbuf_addstr(&buf, "\n\n");
                if (is_fixup_flag(command, flag))
                        strbuf_add_commented_lines(&buf, body, strlen(body),
@@ -2133,9 +2142,8 @@ static int update_squash_messages(struct repository *r,
        if (command == TODO_SQUASH || is_fixup_flag(command, flag)) {
                res = append_squash_message(&buf, body, command, opts, flag);
        } else if (command == TODO_FIXUP) {
-               strbuf_addf(&buf, "\n%s ", comment_line_str);
-               strbuf_addf(&buf, _(skip_nth_commit_msg_fmt),
-                           ++ctx->current_fixup_count + 1);
+               strbuf_addch(&buf, '\n');
+               add_squash_message_header(&buf, ++ctx->current_fixup_count + 1, 1);
                strbuf_addstr(&buf, "\n\n");
                strbuf_add_commented_lines(&buf, body, strlen(body),
                                           comment_line_str);
index 64a9c7fb1beccd6f91f7e6e4263d37d40f4885c3..b01f8970201fc36b116fc76d1223a5bd8b9526b2 100644 (file)
@@ -119,6 +119,13 @@ enum todo_command {
        TODO_COMMENT
 };
 
+/* Bits for the "flags" member of struct todo_item */
+enum todo_item_flags {
+       TODO_EDIT_MERGE_MSG    = (1 << 0),
+       TODO_REPLACE_FIXUP_MSG = (1 << 1),
+       TODO_EDIT_FIXUP_MSG    = (1 << 2),
+};
+
 struct todo_item {
        enum todo_command command;
        struct commit *commit;
@@ -208,6 +215,29 @@ int todo_list_rearrange_squash(struct todo_list *todo_list);
  */
 void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);
 
+/*
+ * Append the "This is a combination of N commits." banner that "git rebase
+ * -i" writes at the top of a squashed commit's message, commented out with
+ * the comment character.
+ */
+void add_squash_combination_header(struct strbuf *buf, int n);
+
+/*
+ * Append the header (1-based N) that "git rebase -i" writes above each message
+ * when squashing, commented out with the comment character. With SKIP it reads
+ * "The ... commit message will be skipped" for a message that is dropped (a
+ * fixup), otherwise "This is the ... commit message".
+ */
+void add_squash_message_header(struct strbuf *buf, int n, int skip);
+
+/*
+ * Return the length of the leading subject of BODY when it should be commented
+ * out in a squash message, or 0 otherwise. An "amend!" subject always
+ * qualifies; "squash!" and "fixup!" subjects only when SQUASHING, since a
+ * plain fixup chain keeps them.
+ */
+size_t squash_subject_comment_len(const char *body, int squashing);
+
 void append_conflicts_hint(struct index_state *istate,
                struct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode);
 enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,