Merge branch 'cm/rebase-i-fixup-amend-reword'
"git commit --fixup=<commit>", which was to tweak the changes made to the contents while keeping the original log message intact, learned "--fixup=(amend|reword):<commit>", that can be used to tweak both the message and the contents, and only the message, respectively. * cm/rebase-i-fixup-amend-reword: doc/git-commit: add documentation for fixup=[amend|reword] options t3437: use --fixup with options to create amend! commit t7500: add tests for --fixup=[amend|reword] options commit: add a reword suboption to --fixup commit: add amend suboption to --fixup to create amend! commit sequencer: export and rename subject_length()
This commit is contained in:
122
builtin/commit.c
122
builtin/commit.c
@ -105,7 +105,8 @@ static const char *template_file;
|
||||
*/
|
||||
static const char *author_message, *author_message_buffer;
|
||||
static char *edit_message, *use_message;
|
||||
static char *fixup_message, *squash_message;
|
||||
static char *fixup_message, *fixup_commit, *squash_message;
|
||||
static const char *fixup_prefix;
|
||||
static int all, also, interactive, patch_interactive, only, amend, signoff;
|
||||
static int edit_flag = -1; /* unspecified */
|
||||
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,
|
||||
die(_("--pathspec-file-nul requires --pathspec-from-file"));
|
||||
}
|
||||
|
||||
if (!pathspec.nr && (also || (only && !amend && !allow_empty)))
|
||||
if (!pathspec.nr && (also || (only && !allow_empty &&
|
||||
(!amend || (fixup_message && strcmp(fixup_prefix, "amend"))))))
|
||||
die(_("No paths with --include/--only does not make sense."));
|
||||
|
||||
if (read_cache_preload(&pathspec) < 0)
|
||||
@ -681,6 +683,22 @@ static void adjust_comment_line_char(const struct strbuf *sb)
|
||||
comment_line_char = *p;
|
||||
}
|
||||
|
||||
static void prepare_amend_commit(struct commit *commit, struct strbuf *sb,
|
||||
struct pretty_print_context *ctx)
|
||||
{
|
||||
const char *buffer, *subject, *fmt;
|
||||
|
||||
buffer = get_commit_buffer(commit, NULL);
|
||||
find_commit_subject(buffer, &subject);
|
||||
/*
|
||||
* If we amend the 'amend!' commit then we don't want to
|
||||
* duplicate the subject line.
|
||||
*/
|
||||
fmt = starts_with(subject, "amend!") ? "%b" : "%B";
|
||||
format_commit_message(commit, fmt, sb, ctx);
|
||||
unuse_commit_buffer(commit, buffer);
|
||||
}
|
||||
|
||||
static int prepare_to_commit(const char *index_file, const char *prefix,
|
||||
struct commit *current_head,
|
||||
struct wt_status *s,
|
||||
@ -745,15 +763,33 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
|
||||
} else if (fixup_message) {
|
||||
struct pretty_print_context ctx = {0};
|
||||
struct commit *commit;
|
||||
commit = lookup_commit_reference_by_name(fixup_message);
|
||||
char *fmt;
|
||||
commit = lookup_commit_reference_by_name(fixup_commit);
|
||||
if (!commit)
|
||||
die(_("could not lookup commit %s"), fixup_message);
|
||||
die(_("could not lookup commit %s"), fixup_commit);
|
||||
ctx.output_encoding = get_commit_output_encoding();
|
||||
format_commit_message(commit, "fixup! %s\n\n",
|
||||
&sb, &ctx);
|
||||
if (have_option_m)
|
||||
strbuf_addbuf(&sb, &message);
|
||||
fmt = xstrfmt("%s! %%s\n\n", fixup_prefix);
|
||||
format_commit_message(commit, fmt, &sb, &ctx);
|
||||
free(fmt);
|
||||
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.
|
||||
*/
|
||||
if (have_option_m && !strcmp(fixup_prefix, "fixup"))
|
||||
strbuf_addbuf(&sb, &message);
|
||||
|
||||
if (!strcmp(fixup_prefix, "amend")) {
|
||||
if (have_option_m)
|
||||
die(_("cannot combine -m with --fixup:%s"), fixup_message);
|
||||
prepare_amend_commit(commit, &sb, &ctx);
|
||||
}
|
||||
} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
|
||||
size_t merge_msg_start;
|
||||
|
||||
@ -1152,6 +1188,19 @@ static void finalize_deferred_config(struct wt_status *s)
|
||||
s->ahead_behind_flags = AHEAD_BEHIND_FULL;
|
||||
}
|
||||
|
||||
static void check_fixup_reword_options(int argc, const char *argv[]) {
|
||||
if (whence != FROM_COMMIT) {
|
||||
if (whence == FROM_MERGE)
|
||||
die(_("You are in the middle of a merge -- cannot reword."));
|
||||
else if (is_from_cherry_pick(whence))
|
||||
die(_("You are in the middle of a cherry-pick -- cannot reword."));
|
||||
}
|
||||
if (argc)
|
||||
die(_("cannot combine reword option of --fixup with path '%s'"), *argv);
|
||||
if (patch_interactive || interactive || all || also || only)
|
||||
die(_("reword option of --fixup is mutually exclusive with --patch/--interactive/--all/--include/--only"));
|
||||
}
|
||||
|
||||
static int parse_and_validate_options(int argc, const char *argv[],
|
||||
const struct option *options,
|
||||
const char * const usage[],
|
||||
@ -1170,7 +1219,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
|
||||
if (force_author && renew_authorship)
|
||||
die(_("Using both --reset-author and --author does not make sense"));
|
||||
|
||||
if (logfile || have_option_m || use_message || fixup_message)
|
||||
if (logfile || have_option_m || use_message)
|
||||
use_editor = 0;
|
||||
if (0 <= edit_flag)
|
||||
use_editor = edit_flag;
|
||||
@ -1227,6 +1276,42 @@ static int parse_and_validate_options(int argc, const char *argv[],
|
||||
|
||||
if (also + only + all + interactive > 1)
|
||||
die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
|
||||
|
||||
if (fixup_message) {
|
||||
/*
|
||||
* We limit --fixup's suboptions to only alpha characters.
|
||||
* If the first character after a run of alpha is colon,
|
||||
* then the part before the colon may be a known suboption
|
||||
* name like `amend` or `reword`, or a misspelt suboption
|
||||
* name. In either case, we treat it as
|
||||
* --fixup=<suboption>:<arg>.
|
||||
*
|
||||
* Otherwise, we are dealing with --fixup=<commit>.
|
||||
*/
|
||||
char *p = fixup_message;
|
||||
while (isalpha(*p))
|
||||
p++;
|
||||
if (p > fixup_message && *p == ':') {
|
||||
*p = '\0';
|
||||
fixup_commit = p + 1;
|
||||
if (!strcmp("amend", fixup_message) ||
|
||||
!strcmp("reword", fixup_message)) {
|
||||
fixup_prefix = "amend";
|
||||
allow_empty = 1;
|
||||
if (*fixup_message == 'r') {
|
||||
check_fixup_reword_options(argc, argv);
|
||||
only = 1;
|
||||
}
|
||||
} else {
|
||||
die(_("unknown option: --fixup=%s:%s"), fixup_message, fixup_commit);
|
||||
}
|
||||
} else {
|
||||
fixup_commit = fixup_message;
|
||||
fixup_prefix = "fixup";
|
||||
use_editor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
|
||||
|
||||
handle_untracked_files_arg(s);
|
||||
@ -1504,7 +1589,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
|
||||
OPT_CALLBACK('m', "message", &message, N_("message"), N_("commit message"), opt_parse_m),
|
||||
OPT_STRING('c', "reedit-message", &edit_message, N_("commit"), N_("reuse and edit message from specified commit")),
|
||||
OPT_STRING('C', "reuse-message", &use_message, N_("commit"), N_("reuse message from specified commit")),
|
||||
OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup specified commit")),
|
||||
/*
|
||||
* TRANSLATORS: Leave "[(amend|reword):]" as-is,
|
||||
* and only translate <commit>.
|
||||
*/
|
||||
OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword specified commit")),
|
||||
OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
|
||||
OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
|
||||
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)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (fixup_message && starts_with(sb.buf, "amend! ") &&
|
||||
!allow_empty_message) {
|
||||
struct strbuf body = STRBUF_INIT;
|
||||
size_t len = commit_subject_length(sb.buf);
|
||||
strbuf_addstr(&body, sb.buf + len);
|
||||
if (message_is_empty(&body, cleanup_mode)) {
|
||||
rollback_index_files();
|
||||
fprintf(stderr, _("Aborting commit due to empty commit message body.\n"));
|
||||
exit(1);
|
||||
}
|
||||
strbuf_release(&body);
|
||||
}
|
||||
|
||||
if (amend) {
|
||||
const char *exclude_gpgsig[3] = { "gpgsig", "gpgsig-sha256", NULL };
|
||||
extra = read_commit_extra_headers(current_head, exclude_gpgsig);
|
||||
|
Reference in New Issue
Block a user