git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 1/1] commit: allow -m/-F with --fixup=amend: or reword:

From
Eerik@cervined.in <erik@cervined.in>
Date
May 18, 2026, 11:22 UTC
Message-ID
<20260518112225.73172-4-erik@cervined.in>
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
Previous: erik@cervined.inNext: Junio C Hamano
Message 2 of 13 in “commit: allow -m/-F with --fixup=amend: or reword:”
  1. 0/1 commit: allow -m/-F with --fixup=amend: or reword:erik@cervined.in, May 18, 2026
  2. 1/1 commit: allow -m/-F with --fixup=amend: or reword:erik@cervined.in, May 18, 2026
  3. Junio C HamanoMay 18, 2026
  4. Phillip WoodMay 18, 2026
  5. Erik Cervin EdinMay 24, 2026
  6. 0/2 commit: allow -m/-F/-c/-C for all --fixup variationserik@cervined.in, May 26, 2026
  7. 1/2 commit: allow -m/-F for all kinds of --fixuperik@cervined.in, May 26, 2026
  8. 2/2 commit: allow -c/-C for all kinds of --fixuperik@cervined.in, May 26, 2026
  9. Junio C HamanoAug 26, 2026
  10. Erik Cervin EdinSep 4, 2026
  11. 0/2 commit: allow -m/-F/-c/-C for all --fixup variationserik@cervined.in, Sep 22, 2026
  12. 1/2 commit: allow -m/-F for all kinds of --fixuperik@cervined.in, Sep 22, 2026
  13. 2/2 commit: allow -c/-C for all kinds of --fixuperik@cervined.in, Sep 22, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.