From 6e0e28877988f8b98eb1e5cf4d4c5d29cc0b7f77 Mon Sep 17 00:00:00 2001 From: Charvi Mendiratta Date: Mon, 15 Mar 2021 13:24:31 +0530 Subject: [PATCH 1/6] sequencer: export and rename subject_length() This function can be used in other parts of git. Let's move the function to commit.c and also rename it to make the name of the function more generic. Mentored-by: Christian Couder Mentored-by: Phillip Wood Helped-by: Eric Sunshine Signed-off-by: Charvi Mendiratta Signed-off-by: Junio C Hamano --- commit.c | 14 ++++++++++++++ commit.h | 3 +++ sequencer.c | 16 +--------------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/commit.c b/commit.c index bab8d5ab07c98f..199c7e274c024a 100644 --- a/commit.c +++ b/commit.c @@ -535,6 +535,20 @@ int find_commit_subject(const char *commit_buffer, const char **subject) return eol - p; } +size_t commit_subject_length(const char *body) +{ + const char *p = body; + while (*p) { + const char *next = skip_blank_lines(p); + if (next != p) + break; + p = strchrnul(p, '\n'); + if (*p) + p++; + } + return p - body; +} + struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p) { struct commit_list *new_list = xmalloc(sizeof(struct commit_list)); diff --git a/commit.h b/commit.h index f4e7b0158e2595..e65d9b3e7525ae 100644 --- a/commit.h +++ b/commit.h @@ -165,6 +165,9 @@ const void *detach_commit_buffer(struct commit *, unsigned long *sizep); /* Find beginning and length of commit subject. */ int find_commit_subject(const char *commit_buffer, const char **subject); +/* Return length of the commit subject from commit log message. */ +size_t commit_subject_length(const char *body); + struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list); int commit_list_contains(struct commit *item, diff --git a/sequencer.c b/sequencer.c index abc6d5cdfd4500..da7d54c7cef6c1 100644 --- a/sequencer.c +++ b/sequencer.c @@ -1724,20 +1724,6 @@ enum todo_item_flags { TODO_EDIT_FIXUP_MSG = (1 << 2), }; -static size_t subject_length(const char *body) -{ - const char *p = body; - while (*p) { - const char *next = skip_blank_lines(p); - if (next != p) - break; - p = strchrnul(p, '\n'); - if (*p) - p++; - } - return p - body; -} - static const char first_commit_msg_str[] = N_("This is the 1st commit message:"); static const char nth_commit_msg_fmt[] = N_("This is the commit message #%d:"); static const char skip_first_commit_msg_str[] = N_("The 1st commit message will be skipped:"); @@ -1861,7 +1847,7 @@ static int append_squash_message(struct strbuf *buf, const char *body, if (starts_with(body, "amend!") || ((command == TODO_SQUASH || seen_squash(opts)) && (starts_with(body, "squash!") || starts_with(body, "fixup!")))) - commented_len = subject_length(body); + commented_len = commit_subject_length(body); strbuf_addf(buf, "\n%c ", comment_line_char); strbuf_addf(buf, _(nth_commit_msg_fmt), From 494d314a0526a8bee9f8af7a6e6b74b66043c9fa Mon Sep 17 00:00:00 2001 From: Charvi Mendiratta Date: Mon, 15 Mar 2021 13:24:32 +0530 Subject: [PATCH 2/6] commit: add amend suboption to --fixup to create amend! commit `git commit --fixup=amend:` will create an "amend!" commit. The resulting commit message subject will be "amend! ..." where "..." is the subject line of and the initial message body will be 's message. The "amend!" commit when rebased with --autosquash will fixup the contents and replace the commit message of 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 Mentored-by: Phillip Wood Helped-by: Junio C Hamano Helped-by: Eric Sunshine Signed-off-by: Charvi Mendiratta Signed-off-by: Junio C Hamano --- builtin/commit.c | 103 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 93 insertions(+), 10 deletions(-) diff --git a/builtin/commit.c b/builtin/commit.c index 505fe60956db38..9697bba10bf45f 100644 --- a/builtin/commit.c +++ b/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; @@ -1170,7 +1206,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 +1263,36 @@ 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 `amend` or a misspelt suboption name. In this case, + * we treat it as --fixup=:. + * + * Otherwise, we are dealing with --fixup=. + */ + char *p = fixup_message; + while (isalpha(*p)) + p++; + if (p > fixup_message && *p == ':') { + *p = '\0'; + fixup_commit = p + 1; + if (!strcmp("amend", fixup_message)) { + fixup_prefix = "amend"; + allow_empty = 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 +1570,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:]" as-is, and + * only translate . + */ + OPT_STRING(0, "fixup", &fixup_message, N_("[amend:]commit"), N_("use autosquash formatted message to fixup or amend 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 +1733,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); From 3270ae82acdec3d25491427e0ecff43c014ac4d9 Mon Sep 17 00:00:00 2001 From: Charvi Mendiratta Date: Mon, 15 Mar 2021 13:24:33 +0530 Subject: [PATCH 3/6] commit: add a reword suboption to --fixup `git commit --fixup=reword:` aliases `--fixup=amend: --only`, where it creates an empty "amend!" commit that will reword without changing its contents when it is rebased with `--autosquash`. Mentored-by: Christian Couder Mentored-by: Phillip Wood Helped-by: Junio C Hamano Helped-by: Eric Sunshine Signed-off-by: Charvi Mendiratta Signed-off-by: Junio C Hamano --- builtin/commit.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/builtin/commit.c b/builtin/commit.c index 9697bba10bf45f..b17e6e32f85d32 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1188,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[], @@ -1269,8 +1282,9 @@ static int parse_and_validate_options(int argc, const char *argv[], * 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 `amend` or a misspelt suboption name. In this case, - * we treat it as --fixup=:. + * name like `amend` or `reword`, or a misspelt suboption + * name. In either case, we treat it as + * --fixup=:. * * Otherwise, we are dealing with --fixup=. */ @@ -1280,9 +1294,14 @@ static int parse_and_validate_options(int argc, const char *argv[], if (p > fixup_message && *p == ':') { *p = '\0'; fixup_commit = p + 1; - if (!strcmp("amend", fixup_message)) { + 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); } @@ -1571,10 +1590,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix) 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")), /* - * TRANSLATORS: Leave "[amend:]" as-is, and - * only translate . + * TRANSLATORS: Leave "[(amend|reword):]" as-is, + * and only translate . */ - OPT_STRING(0, "fixup", &fixup_message, N_("[amend:]commit"), N_("use autosquash formatted message to fixup or amend specified 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")), From 3d1bda6b5bed6b29723178b1b417b3a5abe62565 Mon Sep 17 00:00:00 2001 From: Charvi Mendiratta Date: Mon, 15 Mar 2021 13:24:34 +0530 Subject: [PATCH 4/6] t7500: add tests for --fixup=[amend|reword] options Mentored-by: Christian Couder Mentored-by: Phillip Wood Signed-off-by: Charvi Mendiratta Signed-off-by: Junio C Hamano --- t/t7500-commit-template-squash-signoff.sh | 159 ++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh index 6d19ece05dd320..05e43a31d3d4c6 100755 --- a/t/t7500-commit-template-squash-signoff.sh +++ b/t/t7500-commit-template-squash-signoff.sh @@ -9,6 +9,8 @@ Tests for template, signoff, squash and -F functions.' . ./test-lib.sh +. "$TEST_DIRECTORY"/lib-rebase.sh + commit_msg_is () { expect=commit_msg_is.expect actual=commit_msg_is.actual @@ -279,6 +281,163 @@ test_expect_success 'commit --fixup -m"something" -m"extra"' ' extra" ' +get_commit_msg () { + rev="$1" && + git log -1 --pretty=format:"%B" "$rev" +} + +test_expect_success 'commit --fixup=amend: creates amend! commit' ' + commit_for_rebase_autosquash_setup && + cat >expected <<-EOF && + amend! $(git log -1 --format=%s HEAD~) + + $(get_commit_msg HEAD~) + + edited + EOF + ( + set_fake_editor && + FAKE_COMMIT_AMEND="edited" \ + git commit --fixup=amend:HEAD~ + ) && + get_commit_msg HEAD >actual && + test_cmp expected actual +' + +test_expect_success '--fixup=amend: --only ignores staged changes' ' + commit_for_rebase_autosquash_setup && + cat >expected <<-EOF && + amend! $(git log -1 --format=%s HEAD~) + + $(get_commit_msg HEAD~) + + edited + EOF + ( + set_fake_editor && + FAKE_COMMIT_AMEND="edited" \ + git commit --fixup=amend:HEAD~ --only + ) && + get_commit_msg HEAD >actual && + test_cmp expected actual && + test_cmp_rev HEAD@{1}^{tree} HEAD^{tree} && + test_cmp_rev HEAD@{1} HEAD^ && + test_expect_code 1 git diff --cached --exit-code && + git cat-file blob :foo >actual && + test_cmp foo actual +' + +test_expect_success '--fixup=reword: ignores staged changes' ' + commit_for_rebase_autosquash_setup && + cat >expected <<-EOF && + amend! $(git log -1 --format=%s HEAD~) + + $(get_commit_msg HEAD~) + + edited + EOF + ( + set_fake_editor && + FAKE_COMMIT_AMEND="edited" \ + git commit --fixup=reword:HEAD~ + ) && + get_commit_msg HEAD >actual && + test_cmp expected actual && + test_cmp_rev HEAD@{1}^{tree} HEAD^{tree} && + test_cmp_rev HEAD@{1} HEAD^ && + test_expect_code 1 git diff --cached --exit-code && + git cat-file blob :foo >actual && + test_cmp foo actual +' + +test_expect_success '--fixup=reword: error out with -m option' ' + commit_for_rebase_autosquash_setup && + echo "fatal: cannot combine -m with --fixup:reword" >expect && + test_must_fail git commit --fixup=reword:HEAD~ -m "reword commit message" 2>actual && + test_cmp expect actual +' + +test_expect_success '--fixup=amend: error out with -m option' ' + commit_for_rebase_autosquash_setup && + echo "fatal: cannot combine -m with --fixup:amend" >expect && + test_must_fail git commit --fixup=amend:HEAD~ -m "amend commit message" 2>actual && + test_cmp expect actual +' + +test_expect_success 'consecutive amend! commits remove amend! line from commit msg body' ' + commit_for_rebase_autosquash_setup && + cat >expected <<-EOF && + amend! amend! $(git log -1 --format=%s HEAD~) + + $(get_commit_msg HEAD~) + + edited 1 + + edited 2 + EOF + echo "reword new commit message" >actual && + ( + set_fake_editor && + FAKE_COMMIT_AMEND="edited 1" \ + git commit --fixup=reword:HEAD~ && + FAKE_COMMIT_AMEND="edited 2" \ + git commit --fixup=reword:HEAD + ) && + get_commit_msg HEAD >actual && + test_cmp expected actual +' + +test_expect_success 'deny to create amend! commit if its commit msg body is empty' ' + commit_for_rebase_autosquash_setup && + echo "Aborting commit due to empty commit message body." >expected && + ( + set_fake_editor && + test_must_fail env FAKE_COMMIT_MESSAGE="amend! target message subject line" \ + git commit --fixup=amend:HEAD~ 2>actual + ) && + test_cmp expected actual +' + +test_expect_success 'amend! commit allows empty commit msg body with --allow-empty-message' ' + commit_for_rebase_autosquash_setup && + cat >expected <<-EOF && + amend! $(git log -1 --format=%s HEAD~) + EOF + ( + set_fake_editor && + FAKE_COMMIT_MESSAGE="amend! target message subject line" \ + git commit --fixup=amend:HEAD~ --allow-empty-message && + get_commit_msg HEAD >actual + ) && + test_cmp expected actual +' + +test_fixup_reword_opt () { + test_expect_success C_LOCALE_OUTPUT "--fixup=reword: incompatible with $1" " + echo 'fatal: reword option of --fixup is mutually exclusive with'\ + '--patch/--interactive/--all/--include/--only' >expect && + test_must_fail git commit --fixup=reword:HEAD~ $1 2>actual && + test_cmp expect actual + " +} + +for opt in --all --include --only --interactive --patch +do + test_fixup_reword_opt $opt +done + +test_expect_success '--fixup=reword: give error with pathsec' ' + commit_for_rebase_autosquash_setup && + echo "fatal: cannot combine reword option of --fixup with path '\''foo'\''" >expect && + test_must_fail git commit --fixup=reword:HEAD~ -- foo 2>actual && + test_cmp expect actual +' + +test_expect_success '--fixup=reword: -F give error message' ' + echo "fatal: Only one of -c/-C/-F/--fixup can be used." >expect && + test_must_fail git commit --fixup=reword:HEAD~ -F msg 2>actual && + test_cmp expect actual +' test_expect_success 'commit --squash works with -F' ' commit_for_rebase_autosquash_setup && From 8bedae4599fafa8ae8b2885454db7c4bb7660aeb Mon Sep 17 00:00:00 2001 From: Charvi Mendiratta Date: Mon, 15 Mar 2021 13:24:35 +0530 Subject: [PATCH 5/6] t3437: use --fixup with options to create amend! commit We taught `git commit --fixup` to create "amend!" commit. Let's also update the tests and use it to setup the rebase tests. Mentored-by: Christian Couder Mentored-by: Phillip Wood Signed-off-by: Charvi Mendiratta Signed-off-by: Junio C Hamano --- t/t3437-rebase-fixup-options.sh | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/t/t3437-rebase-fixup-options.sh b/t/t3437-rebase-fixup-options.sh index a5a20354e3ff6f..d0bdc7ed02d701 100755 --- a/t/t3437-rebase-fixup-options.sh +++ b/t/t3437-rebase-fixup-options.sh @@ -72,40 +72,16 @@ test_expect_success 'setup' ' git commit --fixup=HEAD -a && git tag B1 && test_tick && - git commit --allow-empty -F - <<-EOF && - amend! B - $EMPTY - B - $EMPTY - edited 1 - EOF + FAKE_COMMIT_AMEND="edited 1" git commit --fixup=reword:B && test_tick && - git commit --allow-empty -F - <<-EOF && - amend! amend! B - $EMPTY - B - $EMPTY - edited 1 - $EMPTY - edited 2 - EOF + FAKE_COMMIT_AMEND="edited 2" git commit --fixup=reword:HEAD && echo B2 >B && test_tick && FAKE_COMMIT_AMEND="edited squash" git commit --squash=HEAD -a && git tag B2 && echo B3 >B && test_tick && - git commit -a -F - <<-EOF && - amend! amend! amend! B - $EMPTY - B - $EMPTY - edited 1 - $EMPTY - edited 2 - $EMPTY - edited 3 - EOF + FAKE_COMMIT_AMEND="edited 3" git commit -a --fixup=amend:HEAD^ && git tag B3 && GIT_AUTHOR_NAME="Rebase Author" && From 00ea64ed7a0ee5ef0901b1eeb5add6954925590f Mon Sep 17 00:00:00 2001 From: Charvi Mendiratta Date: Mon, 15 Mar 2021 13:24:36 +0530 Subject: [PATCH 6/6] doc/git-commit: add documentation for fixup=[amend|reword] options Mentored-by: Christian Couder Mentored-by: Phillip Wood Helped-by: Eric Sunshine Helped-by: Junio C Hamano Signed-off-by: Eric Sunshine Signed-off-by: Charvi Mendiratta Signed-off-by: Junio C Hamano --- Documentation/git-commit.txt | 45 +++++++++++++++++++++++++++++++----- Documentation/git-rebase.txt | 21 +++++++++-------- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 17150fa7eabe80..3c69f461c9af1d 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git commit' [-a | --interactive | --patch] [-s] [-v] [-u] [--amend] - [--dry-run] [(-c | -C | --fixup | --squash) ] + [--dry-run] [(-c | -C | --squash) | --fixup [(amend|reword):])] [-F | -m ] [--reset-author] [--allow-empty] [--allow-empty-message] [--no-verify] [-e] [--author=] [--date=] [--cleanup=] [--[no-]status] @@ -86,11 +86,44 @@ OPTIONS Like '-C', but with `-c` the editor is invoked, so that the user can further edit the commit message. ---fixup=:: - Construct a commit message for use with `rebase --autosquash`. - The commit message will be the subject line from the specified - commit with a prefix of "fixup! ". See linkgit:git-rebase[1] - for details. +--fixup=[(amend|reword):]:: + Create a new commit which "fixes up" `` when applied with + `git rebase --autosquash`. Plain `--fixup=` creates a + "fixup!" commit which changes the content of `` but leaves + its log message untouched. `--fixup=amend:` is similar but + creates an "amend!" commit which also replaces the log message of + `` with the log message of the "amend!" commit. + `--fixup=reword:` creates an "amend!" commit which + replaces the log message of `` with its own log message + but makes no changes to the content of ``. ++ +The commit created by plain `--fixup=` has a subject +composed of "fixup!" followed by the subject line from , +and is recognized specially by `git rebase --autosquash`. The `-m` +option may be used to supplement the log message of the created +commit, but the additional commentary will be thrown away once the +"fixup!" commit is squashed into `` by +`git rebase --autosquash`. ++ +The commit created by `--fixup=amend:` is similar but its +subject is instead prefixed with "amend!". The log message of + 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 ``, the +log message of `` 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:` is shorthand for `--fixup=amend: +--only`. It creates an "amend!" commit with only a log message +(ignoring any changes staged in the index). When squashed by `git +rebase --autosquash`, it replaces the log message of `` +without making any other changes. ++ +Neither "fixup!" nor "amend!" commits change authorship of +`` when applied by `git rebase --autosquash`. +See linkgit:git-rebase[1] for details. --squash=:: Construct a commit message for use with `rebase --autosquash`. diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 8bfa5a927220ab..f08ae27e2aefc0 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -593,16 +593,17 @@ See also INCOMPATIBLE OPTIONS below. --autosquash:: --no-autosquash:: - When the commit log message begins with "squash! ..." (or - "fixup! ..."), and there is already a commit in the todo list that - matches the same `...`, automatically modify the todo list of rebase - -i so that the commit marked for squashing comes right after the - commit to be modified, and change the action of the moved commit - from `pick` to `squash` (or `fixup`). A commit matches the `...` if - the commit subject matches, or if the `...` refers to the commit's - hash. As a fall-back, partial matches of the commit subject work, - too. The recommended way to create fixup/squash commits is by using - the `--fixup`/`--squash` options of linkgit:git-commit[1]. + When the commit log message begins with "squash! ..." or "fixup! ..." + or "amend! ...", and there is already a commit in the todo list that + matches the same `...`, automatically modify the todo list of + `rebase -i`, so that the commit marked for squashing comes right after + the commit to be modified, and change the action of the moved commit + from `pick` to `squash` or `fixup` or `fixup -C` respectively. A commit + matches the `...` if the commit subject matches, or if the `...` refers + to the commit's hash. As a fall-back, partial matches of the commit + subject work, too. The recommended way to create fixup/amend/squash + commits is by using the `--fixup`, `--fixup=amend:` or `--fixup=reword:` + and `--squash` options respectively of linkgit:git-commit[1]. + If the `--autosquash` option is enabled by default using the configuration variable `rebase.autoSquash`, this option can be