Skip to content

Commit 494d314

Browse files
charvi-077gitster
authored andcommitted
commit: add amend suboption to --fixup to create amend! commit
`git commit --fixup=amend:<commit>` will create an "amend!" commit. The resulting commit message subject will be "amend! ..." where "..." is the subject line of <commit> and the initial message body will be <commit>'s message. The "amend!" commit when rebased with --autosquash will fixup the contents and replace the commit message of <commit> with the "amend!" commit's message body. In order to prevent rebase from creating commits with an empty message we refuse to create an "amend!" commit if commit message body is empty. Mentored-by: Christian Couder <[email protected]> Mentored-by: Phillip Wood <[email protected]> Helped-by: Junio C Hamano <[email protected]> Helped-by: Eric Sunshine <[email protected]> Signed-off-by: Charvi Mendiratta <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 6e0e288 commit 494d314

File tree

1 file changed

+93
-10
lines changed

1 file changed

+93
-10
lines changed

builtin/commit.c

Lines changed: 93 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ static const char *template_file;
105105
*/
106106
static const char *author_message, *author_message_buffer;
107107
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;
109110
static int all, also, interactive, patch_interactive, only, amend, signoff;
110111
static int edit_flag = -1; /* unspecified */
111112
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,
357358
die(_("--pathspec-file-nul requires --pathspec-from-file"));
358359
}
359360

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"))))))
361363
die(_("No paths with --include/--only does not make sense."));
362364

363365
if (read_cache_preload(&pathspec) < 0)
@@ -681,6 +683,22 @@ static void adjust_comment_line_char(const struct strbuf *sb)
681683
comment_line_char = *p;
682684
}
683685

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+
684702
static int prepare_to_commit(const char *index_file, const char *prefix,
685703
struct commit *current_head,
686704
struct wt_status *s,
@@ -745,15 +763,33 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
745763
} else if (fixup_message) {
746764
struct pretty_print_context ctx = {0};
747765
struct commit *commit;
748-
commit = lookup_commit_reference_by_name(fixup_message);
766+
char *fmt;
767+
commit = lookup_commit_reference_by_name(fixup_commit);
749768
if (!commit)
750-
die(_("could not lookup commit %s"), fixup_message);
769+
die(_("could not lookup commit %s"), fixup_commit);
751770
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);
756774
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+
}
757793
} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
758794
size_t merge_msg_start;
759795

@@ -1170,7 +1206,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
11701206
if (force_author && renew_authorship)
11711207
die(_("Using both --reset-author and --author does not make sense"));
11721208

1173-
if (logfile || have_option_m || use_message || fixup_message)
1209+
if (logfile || have_option_m || use_message)
11741210
use_editor = 0;
11751211
if (0 <= edit_flag)
11761212
use_editor = edit_flag;
@@ -1227,6 +1263,36 @@ static int parse_and_validate_options(int argc, const char *argv[],
12271263

12281264
if (also + only + all + interactive > 1)
12291265
die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
1266+
1267+
if (fixup_message) {
1268+
/*
1269+
* We limit --fixup's suboptions to only alpha characters.
1270+
* If the first character after a run of alpha is colon,
1271+
* then the part before the colon may be a known suboption
1272+
* name `amend` or a misspelt suboption name. In this case,
1273+
* we treat it as --fixup=<suboption>:<arg>.
1274+
*
1275+
* Otherwise, we are dealing with --fixup=<commit>.
1276+
*/
1277+
char *p = fixup_message;
1278+
while (isalpha(*p))
1279+
p++;
1280+
if (p > fixup_message && *p == ':') {
1281+
*p = '\0';
1282+
fixup_commit = p + 1;
1283+
if (!strcmp("amend", fixup_message)) {
1284+
fixup_prefix = "amend";
1285+
allow_empty = 1;
1286+
} else {
1287+
die(_("unknown option: --fixup=%s:%s"), fixup_message, fixup_commit);
1288+
}
1289+
} else {
1290+
fixup_commit = fixup_message;
1291+
fixup_prefix = "fixup";
1292+
use_editor = 0;
1293+
}
1294+
}
1295+
12301296
cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
12311297

12321298
handle_untracked_files_arg(s);
@@ -1504,7 +1570,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
15041570
OPT_CALLBACK('m', "message", &message, N_("message"), N_("commit message"), opt_parse_m),
15051571
OPT_STRING('c', "reedit-message", &edit_message, N_("commit"), N_("reuse and edit message from specified commit")),
15061572
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")),
1573+
/*
1574+
* TRANSLATORS: Leave "[amend:]" as-is, and
1575+
* only translate <commit>.
1576+
*/
1577+
OPT_STRING(0, "fixup", &fixup_message, N_("[amend:]commit"), N_("use autosquash formatted message to fixup or amend specified commit")),
15081578
OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
15091579
OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
15101580
OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
@@ -1663,6 +1733,19 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
16631733
exit(1);
16641734
}
16651735

1736+
if (fixup_message && starts_with(sb.buf, "amend! ") &&
1737+
!allow_empty_message) {
1738+
struct strbuf body = STRBUF_INIT;
1739+
size_t len = commit_subject_length(sb.buf);
1740+
strbuf_addstr(&body, sb.buf + len);
1741+
if (message_is_empty(&body, cleanup_mode)) {
1742+
rollback_index_files();
1743+
fprintf(stderr, _("Aborting commit due to empty commit message body.\n"));
1744+
exit(1);
1745+
}
1746+
strbuf_release(&body);
1747+
}
1748+
16661749
if (amend) {
16671750
const char *exclude_gpgsig[3] = { "gpgsig", "gpgsig-sha256", NULL };
16681751
extra = read_commit_extra_headers(current_head, exclude_gpgsig);

0 commit comments

Comments
 (0)