From: erik@cervined•in
To: git@vger•kernel.org
Cc: gitster@pobox•com, charvi077@gmail•com,
Erik Cervin-Edin <erik@cervined•in>
Subject: [PATCH 1/1] commit: allow -m/-F with --fixup=amend: or reword:
Date: Mon, 18 May 2026 13:22:26 +0200 [thread overview]
Message-ID: <20260518112225.73172-4-erik@cervined.in> (raw)
In-Reply-To: <20260518112225.73172-2-erik@cervined.in>
From: Erik Cervin-Edin <erik@cervined•in>
--fixup=amend: and --fixup=reword: require an editor to supply the
replacement commit message. The -m and -F flags are rejected: -m is
caught by a die() in prepare_to_commit(), and -F is caught by
die_for_incompatible_opt4() which groups -F with --fixup as mutually
exclusive. This makes these modes unusable in non-interactive
workflows -- notably AI coding agents.
When the amend suboption was introduced in 494d314a05 (commit: add
amend suboption to --fixup to create amend! commit, 2021-03-15),
-m support for amend fixups was discussed but not pursued, and -F
was already caught by the higher-layer incompatibility check grouping
it with --fixup.
Allow -m and -F to supply the replacement message body for amend and
reword fixups. When provided, bypass the editor and directly use the
user's message as the body, replacing the original commit's message. For
-F, the file contents are read into the message strbuf and then handled
identically to -m.
Plain --fixup (without amend: or reword:) continues to reject -F but
still accepts -m (even though it's practically a no-op).
Signed-off-by: Erik Cervin Edin <erik@cervined•in>
---
Documentation/git-commit.adoc | 13 +++--
builtin/commit.c | 41 ++++++++++----
t/t7500-commit-template-squash-signoff.sh | 67 +++++++++++++++++++----
3 files changed, 92 insertions(+), 29 deletions(-)
diff --git a/Documentation/git-commit.adoc b/Documentation/git-commit.adoc
index 8329c1034b..9478d5d265 100644
--- a/Documentation/git-commit.adoc
+++ b/Documentation/git-commit.adoc
@@ -111,12 +111,13 @@ commit, but the additional commentary will be thrown away once the
The commit created by `--fixup=amend:<commit>` is similar but its
title is instead prefixed with "amend!". The log message of
_<commit>_ is copied into the log message of the "amend!" commit and
-opened in an editor so it can be refined. When `git rebase
---autosquash` squashes the "amend!" commit into _<commit>_, the
-log message of _<commit>_ is replaced by the refined log message
-from the "amend!" commit. It is an error for the "amend!" commit's
-log message to be empty unless `--allow-empty-message` is
-specified.
+opened in an editor so it can be refined. The replacement message may
+also be supplied directly using `-m` or `-F`, bypassing the need to open
+an editor. When `git rebase --autosquash` squashes the "amend!" commit
+into _<commit>_, the log message of _<commit>_ is replaced by the
+refined log message from the "amend!" commit. It is an error for the
+"amend!" commit's log message to be empty unless `--allow-empty-message`
+is specified.
+
`--fixup=reword:<commit>` is shorthand for `--fixup=amend:<commit>
--only`. It creates an "amend!" commit with only a log message
diff --git a/builtin/commit.c b/builtin/commit.c
index 28f6174503..269c2d782b 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -837,21 +837,19 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
hook_arg1 = "message";
/*
- * Only `-m` commit message option is checked here, as
- * it supports `--fixup` to append the commit message.
- *
- * The other commit message options `-c`/`-C`/`-F` are
- * incompatible with all the forms of `--fixup` and
- * have already errored out while parsing the `git commit`
- * options.
+ * `-m` (and `-F`, converted to `-m` earlier for
+ * amend/reword) appends the message body here.
+ * `-c`/`-C` are still incompatible with all forms
+ * of `--fixup`.
*/
if (have_option_m && !strcmp(fixup_prefix, "fixup"))
strbuf_addbuf(&sb, &message);
if (!strcmp(fixup_prefix, "amend")) {
if (have_option_m)
- die(_("options '%s' and '%s:%s' cannot be used together"), "-m", "--fixup", fixup_message);
- prepare_amend_commit(commit, &sb, &ctx);
+ strbuf_addbuf(&sb, &message);
+ else
+ prepare_amend_commit(commit, &sb, &ctx);
}
} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
size_t merge_msg_start;
@@ -1338,10 +1336,12 @@ static int parse_and_validate_options(int argc, const char *argv[],
}
if (fixup_message && squash_message)
die(_("options '%s' and '%s' cannot be used together"), "--squash", "--fixup");
- die_for_incompatible_opt4(!!use_message, "-C",
+ die_for_incompatible_opt3(!!use_message, "-C",
!!edit_message, "-c",
- !!logfile, "-F",
!!fixup_message, "--fixup");
+ die_for_incompatible_opt3(!!use_message, "-C",
+ !!edit_message, "-c",
+ !!logfile, "-F");
die_for_incompatible_opt4(have_option_m, "-m",
!!edit_message, "-c",
!!use_message, "-C",
@@ -1410,6 +1410,9 @@ static int parse_and_validate_options(int argc, const char *argv[],
}
}
+ if (logfile && fixup_message && !strcmp(fixup_prefix, "fixup"))
+ die(_("options '%s' and '%s' cannot be used together"), "-F", "--fixup");
+
if (0 <= edit_flag)
use_editor = edit_flag;
@@ -1821,6 +1824,22 @@ int cmd_commit(int argc,
argc = parse_and_validate_options(argc, argv, builtin_commit_options,
builtin_commit_usage,
prefix, current_head, &s);
+
+ if (logfile && fixup_message && !strcmp(fixup_prefix, "amend")) {
+ if (!strcmp(logfile, "-")) {
+ if (isatty(0))
+ fprintf(stderr, _("(reading log message from standard input)\n"));
+ if (strbuf_read(&message, 0, 0) < 0)
+ die_errno(_("could not read log from standard input"));
+ } else {
+ if (strbuf_read_file(&message, logfile, 0) < 0)
+ die_errno(_("could not read log file '%s'"), logfile);
+ }
+ strbuf_complete_line(&message);
+ have_option_m = 1;
+ FREE_AND_NULL(logfile);
+ }
+
if (trailer_args.nr)
trailer_config_init();
diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh
index 66aff8e097..b7579ad789 100755
--- a/t/t7500-commit-template-squash-signoff.sh
+++ b/t/t7500-commit-template-squash-signoff.sh
@@ -384,18 +384,28 @@ test_expect_success '--fixup=reword: ignores staged changes' '
test_cmp foo actual
'
-test_expect_success '--fixup=reword: error out with -m option' '
+test_expect_success '--fixup=amend: with -m option' '
commit_for_rebase_autosquash_setup &&
- echo "fatal: options '\''-m'\'' and '\''--fixup:reword'\'' cannot be used together" >expect &&
- test_must_fail git commit --fixup=reword:HEAD~ -m "reword commit message" 2>actual &&
- test_cmp expect actual
+ cat >expected <<-EOF &&
+ amend! $(git log -1 --format=%s HEAD~)
+
+ amend commit message
+ EOF
+ git commit --fixup=amend:HEAD~ -m "amend commit message" &&
+ get_commit_msg HEAD >actual &&
+ test_cmp expected actual
'
-test_expect_success '--fixup=amend: error out with -m option' '
+test_expect_success '--fixup=reword: with -m option' '
commit_for_rebase_autosquash_setup &&
- echo "fatal: options '\''-m'\'' and '\''--fixup:amend'\'' cannot be used together" >expect &&
- test_must_fail git commit --fixup=amend:HEAD~ -m "amend commit message" 2>actual &&
- test_cmp expect actual
+ cat >expected <<-EOF &&
+ amend! $(git log -1 --format=%s HEAD~)
+
+ reword commit message
+ EOF
+ git commit --fixup=reword:HEAD~ -m "reword commit message" &&
+ get_commit_msg HEAD >actual &&
+ test_cmp expected actual
'
test_expect_success 'consecutive amend! commits remove amend! line from commit msg body' '
@@ -432,6 +442,12 @@ test_expect_success 'deny to create amend! commit if its commit msg body is empt
test_cmp expected actual
'
+test_expect_success '--fixup=amend: -m with empty message aborts' '
+ commit_for_rebase_autosquash_setup &&
+ test_must_fail git commit --fixup=amend:HEAD~ -m "" 2>err &&
+ test_grep "empty commit message body" err
+'
+
test_expect_success 'amend! commit allows empty commit msg body with --allow-empty-message' '
commit_for_rebase_autosquash_setup &&
cat >expected <<-EOF &&
@@ -468,10 +484,37 @@ test_expect_success '--fixup=reword: give error with pathsec' '
test_cmp expect actual
'
-test_expect_success '--fixup=reword: -F give error message' '
- echo "fatal: options '\''-F'\'' and '\''--fixup'\'' cannot be used together" >expect &&
- test_must_fail git commit --fixup=reword:HEAD~ -F msg 2>actual &&
- test_cmp expect actual
+test_expect_success '--fixup=reword: with -F option' '
+ commit_for_rebase_autosquash_setup &&
+ echo "message from file" >msgfile &&
+ cat >expected <<-EOF &&
+ amend! $(git log -1 --format=%s HEAD~)
+
+ message from file
+ EOF
+ git commit --fixup=reword:HEAD~ -F msgfile &&
+ get_commit_msg HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '--fixup=amend: with -F option' '
+ commit_for_rebase_autosquash_setup &&
+ echo "amend message from file" >msgfile &&
+ cat >expected <<-EOF &&
+ amend! $(git log -1 --format=%s HEAD~)
+
+ amend message from file
+ EOF
+ git commit --fixup=amend:HEAD~ -F msgfile &&
+ get_commit_msg HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '-F with plain --fixup still errors' '
+ commit_for_rebase_autosquash_setup &&
+ echo "message" >msgfile &&
+ test_must_fail git commit --fixup HEAD~ -F msgfile 2>err &&
+ test_grep "cannot be used together" err
'
test_expect_success 'commit --squash works with -F' '
--
2.54.0.772.g683d7313b1
next prev parent reply other threads:[~2026-05-18 11:23 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-18 11:22 [PATCH 0/1] commit: allow -m/-F with --fixup=amend: or reword: erik
2026-05-18 11:22 ` erik [this message]
2026-05-18 12:39 ` [PATCH 1/1] " Junio C Hamano
2026-05-18 15:27 ` Phillip Wood
2026-05-24 15:00 ` Erik Cervin Edin
2026-05-26 10:47 ` [PATCH v2 0/2] commit: allow -m/-F/-c/-C for all --fixup variations erik
2026-05-26 10:47 ` [PATCH v2 1/2] commit: allow -m/-F for all kinds of --fixup erik
2026-05-26 10:47 ` [PATCH v2 2/2] commit: allow -c/-C " erik
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260518112225.73172-4-erik@cervined.in \
--to=erik@cervined$(echo .)in \
--cc=charvi077@gmail$(echo .)com \
--cc=git@vger$(echo .)kernel.org \
--cc=gitster@pobox$(echo .)com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox