@@ -105,7 +105,8 @@ static const char *template_file;
105
105
*/
106
106
static const char * author_message , * author_message_buffer ;
107
107
static char * edit_message , * use_message ;
108
- static char * fixup_message , * squash_message ;
108
+ static char * fixup_message , * fixup_commit , * squash_message ;
109
+ static const char * fixup_prefix ;
109
110
static int all , also , interactive , patch_interactive , only , amend , signoff ;
110
111
static int edit_flag = -1 ; /* unspecified */
111
112
static int quiet , verbose , no_verify , allow_empty , dry_run , renew_authorship ;
@@ -357,7 +358,8 @@ static const char *prepare_index(const char **argv, const char *prefix,
357
358
die (_ ("--pathspec-file-nul requires --pathspec-from-file" ));
358
359
}
359
360
360
- if (!pathspec .nr && (also || (only && !amend && !allow_empty )))
361
+ if (!pathspec .nr && (also || (only && !allow_empty &&
362
+ (!amend || (fixup_message && strcmp (fixup_prefix , "amend" ))))))
361
363
die (_ ("No paths with --include/--only does not make sense." ));
362
364
363
365
if (read_cache_preload (& pathspec ) < 0 )
@@ -681,6 +683,22 @@ static void adjust_comment_line_char(const struct strbuf *sb)
681
683
comment_line_char = * p ;
682
684
}
683
685
686
+ static void prepare_amend_commit (struct commit * commit , struct strbuf * sb ,
687
+ struct pretty_print_context * ctx )
688
+ {
689
+ const char * buffer , * subject , * fmt ;
690
+
691
+ buffer = get_commit_buffer (commit , NULL );
692
+ find_commit_subject (buffer , & subject );
693
+ /*
694
+ * If we amend the 'amend!' commit then we don't want to
695
+ * duplicate the subject line.
696
+ */
697
+ fmt = starts_with (subject , "amend!" ) ? "%b" : "%B" ;
698
+ format_commit_message (commit , fmt , sb , ctx );
699
+ unuse_commit_buffer (commit , buffer );
700
+ }
701
+
684
702
static int prepare_to_commit (const char * index_file , const char * prefix ,
685
703
struct commit * current_head ,
686
704
struct wt_status * s ,
@@ -745,15 +763,33 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
745
763
} else if (fixup_message ) {
746
764
struct pretty_print_context ctx = {0 };
747
765
struct commit * commit ;
748
- commit = lookup_commit_reference_by_name (fixup_message );
766
+ char * fmt ;
767
+ commit = lookup_commit_reference_by_name (fixup_commit );
749
768
if (!commit )
750
- die (_ ("could not lookup commit %s" ), fixup_message );
769
+ die (_ ("could not lookup commit %s" ), fixup_commit );
751
770
ctx .output_encoding = get_commit_output_encoding ();
752
- format_commit_message (commit , "fixup! %s\n\n" ,
753
- & sb , & ctx );
754
- if (have_option_m )
755
- strbuf_addbuf (& sb , & message );
771
+ fmt = xstrfmt ("%s! %%s\n\n" , fixup_prefix );
772
+ format_commit_message (commit , fmt , & sb , & ctx );
773
+ free (fmt );
756
774
hook_arg1 = "message" ;
775
+
776
+ /*
777
+ * Only `-m` commit message option is checked here, as
778
+ * it supports `--fixup` to append the commit message.
779
+ *
780
+ * The other commit message options `-c`/`-C`/`-F` are
781
+ * incompatible with all the forms of `--fixup` and
782
+ * have already errored out while parsing the `git commit`
783
+ * options.
784
+ */
785
+ if (have_option_m && !strcmp (fixup_prefix , "fixup" ))
786
+ strbuf_addbuf (& sb , & message );
787
+
788
+ if (!strcmp (fixup_prefix , "amend" )) {
789
+ if (have_option_m )
790
+ die (_ ("cannot combine -m with --fixup:%s" ), fixup_message );
791
+ prepare_amend_commit (commit , & sb , & ctx );
792
+ }
757
793
} else if (!stat (git_path_merge_msg (the_repository ), & statbuf )) {
758
794
size_t merge_msg_start ;
759
795
@@ -1152,6 +1188,19 @@ static void finalize_deferred_config(struct wt_status *s)
1152
1188
s -> ahead_behind_flags = AHEAD_BEHIND_FULL ;
1153
1189
}
1154
1190
1191
+ static void check_fixup_reword_options (int argc , const char * argv []) {
1192
+ if (whence != FROM_COMMIT ) {
1193
+ if (whence == FROM_MERGE )
1194
+ die (_ ("You are in the middle of a merge -- cannot reword." ));
1195
+ else if (is_from_cherry_pick (whence ))
1196
+ die (_ ("You are in the middle of a cherry-pick -- cannot reword." ));
1197
+ }
1198
+ if (argc )
1199
+ die (_ ("cannot combine reword option of --fixup with path '%s'" ), * argv );
1200
+ if (patch_interactive || interactive || all || also || only )
1201
+ die (_ ("reword option of --fixup is mutually exclusive with --patch/--interactive/--all/--include/--only" ));
1202
+ }
1203
+
1155
1204
static int parse_and_validate_options (int argc , const char * argv [],
1156
1205
const struct option * options ,
1157
1206
const char * const usage [],
@@ -1170,7 +1219,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
1170
1219
if (force_author && renew_authorship )
1171
1220
die (_ ("Using both --reset-author and --author does not make sense" ));
1172
1221
1173
- if (logfile || have_option_m || use_message || fixup_message )
1222
+ if (logfile || have_option_m || use_message )
1174
1223
use_editor = 0 ;
1175
1224
if (0 <= edit_flag )
1176
1225
use_editor = edit_flag ;
@@ -1227,6 +1276,42 @@ static int parse_and_validate_options(int argc, const char *argv[],
1227
1276
1228
1277
if (also + only + all + interactive > 1 )
1229
1278
die (_ ("Only one of --include/--only/--all/--interactive/--patch can be used." ));
1279
+
1280
+ if (fixup_message ) {
1281
+ /*
1282
+ * We limit --fixup's suboptions to only alpha characters.
1283
+ * If the first character after a run of alpha is colon,
1284
+ * then the part before the colon may be a known suboption
1285
+ * name like `amend` or `reword`, or a misspelt suboption
1286
+ * name. In either case, we treat it as
1287
+ * --fixup=<suboption>:<arg>.
1288
+ *
1289
+ * Otherwise, we are dealing with --fixup=<commit>.
1290
+ */
1291
+ char * p = fixup_message ;
1292
+ while (isalpha (* p ))
1293
+ p ++ ;
1294
+ if (p > fixup_message && * p == ':' ) {
1295
+ * p = '\0' ;
1296
+ fixup_commit = p + 1 ;
1297
+ if (!strcmp ("amend" , fixup_message ) ||
1298
+ !strcmp ("reword" , fixup_message )) {
1299
+ fixup_prefix = "amend" ;
1300
+ allow_empty = 1 ;
1301
+ if (* fixup_message == 'r' ) {
1302
+ check_fixup_reword_options (argc , argv );
1303
+ only = 1 ;
1304
+ }
1305
+ } else {
1306
+ die (_ ("unknown option: --fixup=%s:%s" ), fixup_message , fixup_commit );
1307
+ }
1308
+ } else {
1309
+ fixup_commit = fixup_message ;
1310
+ fixup_prefix = "fixup" ;
1311
+ use_editor = 0 ;
1312
+ }
1313
+ }
1314
+
1230
1315
cleanup_mode = get_cleanup_mode (cleanup_arg , use_editor );
1231
1316
1232
1317
handle_untracked_files_arg (s );
@@ -1504,7 +1589,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
1504
1589
OPT_CALLBACK ('m' , "message" , & message , N_ ("message" ), N_ ("commit message" ), opt_parse_m ),
1505
1590
OPT_STRING ('c' , "reedit-message" , & edit_message , N_ ("commit" ), N_ ("reuse and edit message from specified commit" )),
1506
1591
OPT_STRING ('C' , "reuse-message" , & use_message , N_ ("commit" ), N_ ("reuse message from specified commit" )),
1507
- OPT_STRING (0 , "fixup" , & fixup_message , N_ ("commit" ), N_ ("use autosquash formatted message to fixup specified commit" )),
1592
+ /*
1593
+ * TRANSLATORS: Leave "[(amend|reword):]" as-is,
1594
+ * and only translate <commit>.
1595
+ */
1596
+ OPT_STRING (0 , "fixup" , & fixup_message , N_ ("[(amend|reword):]commit" ), N_ ("use autosquash formatted message to fixup or amend/reword specified commit" )),
1508
1597
OPT_STRING (0 , "squash" , & squash_message , N_ ("commit" ), N_ ("use autosquash formatted message to squash specified commit" )),
1509
1598
OPT_BOOL (0 , "reset-author" , & renew_authorship , N_ ("the commit is authored by me now (used with -C/-c/--amend)" )),
1510
1599
OPT_BOOL ('s' , "signoff" , & signoff , N_ ("add a Signed-off-by trailer" )),
@@ -1663,6 +1752,19 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
1663
1752
exit (1 );
1664
1753
}
1665
1754
1755
+ if (fixup_message && starts_with (sb .buf , "amend! " ) &&
1756
+ !allow_empty_message ) {
1757
+ struct strbuf body = STRBUF_INIT ;
1758
+ size_t len = commit_subject_length (sb .buf );
1759
+ strbuf_addstr (& body , sb .buf + len );
1760
+ if (message_is_empty (& body , cleanup_mode )) {
1761
+ rollback_index_files ();
1762
+ fprintf (stderr , _ ("Aborting commit due to empty commit message body.\n" ));
1763
+ exit (1 );
1764
+ }
1765
+ strbuf_release (& body );
1766
+ }
1767
+
1666
1768
if (amend ) {
1667
1769
const char * exclude_gpgsig [3 ] = { "gpgsig" , "gpgsig-sha256" , NULL };
1668
1770
extra = read_commit_extra_headers (current_head , exclude_gpgsig );
0 commit comments