This patch series teaches `git commit --fixup` to create "amend!" commit
as an alternative that works with `git rebase --autosquash`. It allows to
fixup both the content and the commit message of the specified commit.
Here we add two suboptions to the `--fixup`, first `amend` suboption that
creates an "amend!" commit. It takes the staged changes and also allows to
edit the commit message of the commit we are fixing.
Example usuage:
git commit --fixup=amend:<commit>
Secondly, `reword` suboption that creates an empty "amend!" commit i.e it
ignores the staged changes and only allows to reword/edit the commit message
of the commit we are fixing.
Example usuage:
git commit --fixup=reword:<commit>
** This work is rebased on the top of cm/rebase-i-updates.
Changes from v2 :
* doc/git-commit.c: reword and simplify the `--fixup=[amend|reword]`
* error out "foo" in the output message when `--fixup=reword:` combined
with pathspec i.e
$ git commit --fixup=reword:HEAD~ -- foo
(I am bit unsure about this as I have directly used the "argv" to get the
first foo (file) passed. I am doubtful and not sure about, if we can
use the `pathspec_from_file` in (builtin/commit.c) ? )
* fixes the TRANSLATOR, commit message and other minor cleanup (v2 2/6)
Charvi Mendiratta (6):
sequencer: export subject_length()
commit: add amend suboption to --fixup to create amend! commit
commit: add a reword suboption to --fixup
t7500: add tests for --fixup=[amend|reword] options
t3437: use --fixup with options to create amend! commit
doc/git-commit: add documentation for fixup=[amend|reword] options
Documentation/git-commit.txt | 40 ++++++-
Documentation/git-rebase.txt | 21 ++--
builtin/commit.c | 125 ++++++++++++++++++--
commit.c | 14 +++
commit.h | 3 +
sequencer.c | 14 ---
t/t3437-rebase-fixup-options.sh | 30 +----
t/t7500-commit-template-squash-signoff.sh | 135 ++++++++++++++++++++++
8 files changed, 316 insertions(+), 66 deletions(-)
--
2.29.0.rc1
`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. -m can be used to override the
message body.
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 [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Helped-by: Junio C Hamano [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
builtin/commit.c | 97 +++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 88 insertions(+), 9 deletions(-)
@@ -745,15 +761,33 @@ static int prepare_to_commit(const char *index_file, const char *prefix,}elseif(fixup_message){structpretty_print_contextctx={0};structcommit*commit;-commit=lookup_commit_reference_by_name(fixup_message);+char*fmt=xstrfmt("%s! %%s\n\n",fixup_prefix);+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);+format_commit_message(commit,fmt,&sb,&ctx);+free(fmt);hook_arg1="message";++/*+*Only`-m`commitmessageoptionischeckedhere,as+*itsupports`--fixup`toappendthecommitmessage.+*+*Theothercommitmessageoptions`-c`/`-C`/`-F`are+*incompatiblewithalltheformsof`--fixup`and+*havealreadyerroredoutwhileparsingthe`gitcommit`+*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);+else+prepare_amend_commit(commit,&sb,&ctx);+}}elseif(!stat(git_path_merge_msg(the_repository),&statbuf)){size_tmerge_msg_start;
@@ -1152,6 +1186,12 @@ static void finalize_deferred_config(struct wt_status *s)s->ahead_behind_flags=AHEAD_BEHIND_FULL;}+/* returns the length of intial segment of alpha characters only */+staticsize_tget_alpha_len(char*fixup_message){+constcharalphas[]="abcdefghijklmnopqrstuvwxyz";+returnstrspn(fixup_message,alphas);+}+staticintparse_and_validate_options(intargc,constchar*argv[],conststructoption*options,constchar*constusage[],
@@ -1170,7 +1210,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 +1267,28 @@ 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){+/*+*As`amend`suboptioncontainsonlyalpha+*character.Socheckiffirstnonalpha+*characterinfixup_messageis':'.+*/+size_tlen=get_alpha_len(fixup_message);+if(len&&fixup_message[len]==':'){+fixup_message[len++]='\0';+fixup_commit=fixup_message+len;+if(starts_with("amend",fixup_message))+fixup_prefix="amend";+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 +1566,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+*onlytranslate<commit>.+*/+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 +1729,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){+structstrbufbody=STRBUF_INIT;+size_tlen=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){constchar*exclude_gpgsig[3]={"gpgsig","gpgsig-sha256",NULL};extra=read_commit_extra_headers(current_head,exclude_gpgsig);--
This function can be used in other parts of git. Let's move the
function to commit.c.
Mentored-by: Christian Couder [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
commit.c | 14 ++++++++++++++
commit.h | 3 +++
sequencer.c | 14 --------------
3 files changed, 17 insertions(+), 14 deletions(-)
@@ -1724,20 +1724,6 @@ enum todo_item_flags {TODO_EDIT_FIXUP_MSG=(1<<2),};-staticsize_tsubject_length(constchar*body)-{-constchar*p=body;-while(*p){-constchar*next=skip_blank_lines(p);-if(next!=p)-break;-p=strchrnul(p,'\n');-if(*p)-p++;-}-returnp-body;-}-staticconstcharfirst_commit_msg_str[]=N_("This is the 1st commit message:");staticconstcharnth_commit_msg_fmt[]=N_("This is the commit message #%d:");staticconstcharskip_first_commit_msg_str[]=N_("The 1st commit message will be skipped:");
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 [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
t/t3437-rebase-fixup-options.sh | 30 +++---------------------------
1 file changed, 3 insertions(+), 27 deletions(-)
@@ -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.expectactual=commit_msg_is.actual
@@ -279,6 +281,139 @@ test_expect_success 'commit --fixup -m"something" -m"extra"' ' extra"'+get_commit_msg(){+rev="$1"&&+gitlog-1--pretty=format:"%B""$rev"+}++test_expect_success'commit --fixup=amend: creates amend! commit''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!$(gitlog-1--format=%sHEAD~)++$(get_commit_msgHEAD~)++edited+EOF+(+set_fake_editor&&+FAKE_COMMIT_AMEND="edited"\+gitcommit--fixup=amend:HEAD~+)&&+get_commit_msgHEAD>actual&&+test_cmpexpectedactual+'++test_expect_success'--fixup=reword: does not commit staged changes''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!$(gitlog-1--format=%sHEAD~)++$(get_commit_msgHEAD~)++edited+EOF+(+set_fake_editor&&+FAKE_COMMIT_AMEND="edited"\+gitcommit--fixup=reword:HEAD~+)&&+get_commit_msgHEAD>actual&&+test_cmpexpectedactual&&+test_cmp_revHEAD@{1}^{tree}HEAD^{tree}&&+test_cmp_revHEAD@{1}HEAD^&&+test_expect_code1gitdiff--cached--exit-code&&+gitcat-fileblob:foo>actual&&+test_cmpfooactual+'++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_failgitcommit--fixup=reword:HEAD~-m"reword commit message"2>actual&&+test_cmpexpectactual+'++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_failgitcommit--fixup=amend:HEAD~-m"amend commit message"2>actual&&+test_cmpexpectactual+'++test_expect_success'consecutive amend! commits remove amend! line from commit msg body''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!amend!$(gitlog-1--format=%sHEAD~)++$(get_commit_msgHEAD~)++edited1++edited2+EOF+echo"reword new commit message">actual&&+(+set_fake_editor&&+FAKE_COMMIT_AMEND="edited 1"\+gitcommit--fixup=reword:HEAD~&&+FAKE_COMMIT_AMEND="edited 2"\+gitcommit--fixup=reword:HEAD+)&&+get_commit_msgHEAD>actual&&+test_cmpexpectedactual+'++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_failenvFAKE_COMMIT_MESSAGE="amend! target message subject line"\+gitcommit--fixup=amend:HEAD~2>actual+)&&+test_cmpexpectedactual+'++test_expect_success'amend! commit allows empty commit msg body with --allow-empty-message''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!$(gitlog-1--format=%sHEAD~)+EOF+(+set_fake_editor&&+FAKE_COMMIT_MESSAGE="amend! target message subject line"\+gitcommit--fixup=amend:HEAD~--allow-empty-message&&+get_commit_msgHEAD>actual+)&&+test_cmpexpectedactual+'++test_fixup_reword_opt(){+test_expect_successC_LOCALE_OUTPUT"--fixup=reword: incompatible with $1""+echo'fatal: cannot combine reword option of --fixup with $1'>expect&&+test_must_failgitcommit--fixup=reword:HEAD~$12>actual&&+test_cmpexpectactual+"+}++foroptin--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_failgitcommit--fixup=reword:HEAD~--foo2>actual&&+test_cmpexpectactual+'++test_expect_success'--fixup=reword: -F give error message''+echo"fatal: Only one of -c/-C/-F/--fixup can be used.">expect&&+test_must_failgitcommit--fixup=reword:HEAD~-Fmsg2>actual&&+test_cmpexpectactual+' test_expect_success'commit --squash works with -F''commit_for_rebase_autosquash_setup&&
`git commit --fixup=reword:<commit>` creates an empty "amend!" commit
that will reword <commit> without changing its contents when it is
rebased with --autosquash.
Apart from ignoring staged changes it works similarly to
`--fixup=amend:<commit>`.
Mentored-by: Christian Couder [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Helped-by: Junio C Hamano [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
builtin/commit.c | 44 ++++++++++++++++++++++++++++++++++++--------
1 file changed, 36 insertions(+), 8 deletions(-)
@@ -1186,6 +1186,27 @@ static void finalize_deferred_config(struct wt_status *s)s->ahead_behind_flags=AHEAD_BEHIND_FULL;}+staticvoidcheck_fixup_reword_options(intargc,constchar*argv[]){+if(whence!=FROM_COMMIT){+if(whence==FROM_MERGE)+die(_("You are in the middle of a merge -- cannot reword."));+elseif(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)+die(_("cannot combine reword option of --fixup with --patch"));+if(interactive)+die(_("cannot combine reword option of --fixup with --interactive"));+if(all)+die(_("cannot combine reword option of --fixup with --all"));+if(also)+die(_("cannot combine reword option of --fixup with --include"));+if(only)+die(_("cannot combine reword option of --fixup with --only"));+}+/* returns the length of intial segment of alpha characters only */staticsize_tget_alpha_len(char*fixup_message){constcharalphas[]="abcdefghijklmnopqrstuvwxyz";
@@ -1567,10 +1595,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-*onlytranslate<commit>.+*TRANSLATORS:Leave"[(amend|reword):]"as-is,+*andonlytranslate<commit>.*/-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")),--
@@ -86,11 +86,39 @@ OPTIONS Like '-C', but with `-c` the editor is invoked, so that the user can further edit the commit message.---fixup=<commit>::- 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):]<commit>::+ Without `amend:` or `reword:`, create a `fixup!` commit where+ the commit message will be the subject line from the specified+ commit with a prefix of "fixup!'". The resulting "fixup!" commit+ is further used with `git rebase --autosquash` to fixup the+ content of the specified commit.+++The `--fixup=amend:<commit>` form creates an "amend!" commit to+fixup both the content and the commit log message of the specified+commit. The resulting "amend!" commit's commit message subject+will be the subject line from the specified commit with a prefix of+"amend!'" and the message body will be commit log message of the+specified commit. It also invokes an editor seeded with the log+message of the "amend!" commit to allow to edit further. And it+refuses to create "amend!" commit if it's commit message body is+empty unless used with the `--allow-empty-message` option. "amend!"+commit when rebased with `--autosquash` will fixup the contents and+replace the commit message of the specified commit with the "amend!"+commit's message body.+++The `--fixup=reword:<commit>` form creates an "amend!" commit similar+to `--fixup=amend:<commit>` creates, but it records the same tree as+`HEAD`, i.e. it does not take any staged changes and only allows to+fixup the commit message of the specified commit. It will reword the+specified commit when it is rebased with `--autosquash`.+++The `--fixup=amend:` and `--fixup=reword:` forms cannot be used with+other options to add to the commit log message i.e it is incompatible+with `-m`/`-F`/`-c`/`-C` options.+++Also, after fixing the commit using `--fixup`, with or without option+and rebased with `--autosquash`, the authorship of the original commit+remains unchanged. See linkgit:git-rebase[1] for details. --squash=<commit>:: Construct a commit message for use with `rebase --autosquash`.
@@ -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/squash/amend+ commits is by using the `--fixup=[amend|reword]`/`--squash` options of+ linkgit:git-commit[1]. + If the `--autosquash` option is enabled by default using the configuration variable `rebase.autoSquash`, this option can be
From: Eric Sunshine <hidden> Date: 2021-03-01 20:29:46
On Mon, Mar 1, 2021 at 3:50 AM Charvi Mendiratta [off-list ref] wrote:
quoted hunk
This function can be used in other parts of git. Let's move the
function to commit.c.
Signed-off-by: Charvi Mendiratta <redacted>
---
diff --git a/commit.h b/commit.h
@@ -165,6 +165,9 @@ const void *detach_commit_buffer(struct commit *, unsigned long *sizep);+/* Return length of the commit subject from commit log message. */+size_t subject_length(const char *body);
Now that this function is public, is the name too generic? Most other
functions in this header have "commit" in the name. So,
commit_subject_length() might be one possibility (assuming the current
name is too generic).
From: Eric Sunshine <hidden> Date: 2021-03-01 22:21:42
On Mon, Mar 1, 2021 at 3:50 AM Charvi Mendiratta [off-list ref] wrote:
quoted hunk
`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. -m can be used to override the
message body.
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.
Signed-off-by: Charvi Mendiratta <redacted>
---
diff --git a/builtin/commit.c b/builtin/commit.c
@@ -105,7 +105,8 @@ static const char *template_file;+static int prepare_amend_commit(struct commit *commit, struct strbuf *sb,+ struct pretty_print_context *ctx) {+ /*+ * If we amend the 'amend!' commit then we don't want to+ * duplicate the subject line.+ */+ const char *format = NULL;+ if (starts_with(sb->buf, "amend! amend!"))
Is the content of the incoming strbuf created mechanically so that we
know that there will only ever be one space between the two "amend!"
literals? If not, then this starts_with() check feels fragile.
(Compare with the code in sequencer.c which checks for this sort of
duplication but is tolerant of one or more spaces, not just a single
space.)
quoted hunk
+ format = "%b";+ else+ format = "%B";
It's subjective and minor, but this could be expressed more compactly as:
const char *fmt = starts_with(...) ? "%b" : "%B";
Also, no need to initialize `format` to NULL since it gets assigned in
all code paths.
Not worth a re-roll.
quoted hunk
@@ -745,15 +761,33 @@ static int prepare_to_commit(const char *index_file, const char *prefix,+ if (!strcmp(fixup_prefix, "amend")) {+ if (have_option_m)+ die(_("cannot combine -m with --fixup:%s"), fixup_message);+ else+ prepare_amend_commit(commit, &sb, &ctx);+ }
This is minor, but the way this is written, the error case and the
normal case appear to have the same significance, whereas, if you
write it like this:
if (!strcmp(...)) {
if (have_option_m)
die(...);
prepare_amend_commit(...);
}
then it's easier to see that you're checking for and getting an error
case out of the way early, which allows the reader to concentrate
without distraction on the normal case. As a minor benefit, you also
get to eliminate an indentation level for the normal case, which could
be important if more code is added to that case.
Not worth a re-roll.
quoted hunk
@@ -1227,6 +1267,28 @@ static int parse_and_validate_options(int argc, const char *argv[],+ if (fixup_message) {+ /*+ * As `amend` suboption contains only alpha+ * character. So check if first non alpha+ * character in fixup_message is ':'.+ */+ size_t len = get_alpha_len(fixup_message);+ if (len && fixup_message[len] == ':') {+ fixup_message[len++] = '\0';+ fixup_commit = fixup_message + len;+ if (starts_with("amend", fixup_message))+ fixup_prefix = "amend";+ else+ die(_("unknown option: --fixup=%s:%s"), fixup_message, fixup_commit);
I haven't read ahead in the series yet, but I presume you're making
this code extra generic because you plan to support additional `fixup`
options (such as `reword`), but I wonder if the cognitive overhead is
warranted or you could get by with something simpler, such as:
if (skip_prefix(msg, "amend:", &arg) ||
skip_prefix(msg, "reword:", &arg)) {
...
}
Also, am I misreading when I think that the use of starts_with() could
be replaced with a simple strcmp() since you've already inserted a
'\0' immediately after the final alphabetic character?
Not necessarily worth a re-roll.
quoted hunk
@@ -1663,6 +1729,19 @@ int cmd_commit(int argc, const char **argv, const char *prefix)+ if (message_is_empty(&body, cleanup_mode)) {+ rollback_index_files();+ fprintf(stderr, _("Aborting commit due to empty commit message body.\n"));+ exit(1);
I was wondering why you are capitalizing the error message (these days
we don't) and using exit() instead of die(), but I see that you're
mirroring existing practice in this function. Okay.
From: Eric Sunshine <hidden> Date: 2021-03-01 23:34:59
On Mon, Mar 1, 2021 at 3:50 AM Charvi Mendiratta [off-list ref] wrote:
quoted hunk
`git commit --fixup=reword:<commit>` creates an empty "amend!" commit
that will reword <commit> without changing its contents when it is
rebased with --autosquash.
Apart from ignoring staged changes it works similarly to
`--fixup=amend:<commit>`.
Signed-off-by: Charvi Mendiratta <redacted>
---
diff --git a/builtin/commit.c b/builtin/commit.c
@@ -1186,6 +1186,27 @@ static void finalize_deferred_config(struct wt_status *s)+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)+ die(_("cannot combine reword option of --fixup with --patch"));+ if (interactive)+ die(_("cannot combine reword option of --fixup with --interactive"));+ if (all)+ die(_("cannot combine reword option of --fixup with --all"));+ if (also)+ die(_("cannot combine reword option of --fixup with --include"));+ if (only)+ die(_("cannot combine reword option of --fixup with --only"));+}
Or, more concisely:
if (argc)
die(_("--fixup mutually exclusive with path '%s'), ...);
if (patch_interactive || interactive || all || also || only)
die(_("--fixup mutually exclusive with
--patch/--interactive/--all/--include/--only);
The mix of two different error message styles (capitalized with
full-stop vs. lowercase no-full-stop) is a bit jarring, but minor.
Not worth re-roll.
From: Eric Sunshine <hidden> Date: 2021-03-02 15:28:35
On Mon, Mar 1, 2021 at 3:50 AM Charvi Mendiratta [off-list ref] wrote:
t7500: add tests for --fixup=[amend|reword] options
It's usually preferable for tests and documentation updates to be
bundled along with the patch which makes a particular change[1] rather
than waiting until the very end of the series and adding tests and
documentation covering all the changes made by patches earlier in the
series. As a reviewer, it is much harder to tell if the late-added
tests and documentation updates are comprehensive since it's difficult
to keep in mind all the changes made by earlier patches.
When reading earlier patches in this series, I questioned whether or
not certain features of each patch were going to be covered by tests
or documentation updates, but I couldn't tell because those updates
weren't made at the same time as the change about which I was reading.
For instance, when reading the implementation of `--fixup:reword`, I
was wondering if the documentation was going to be updated to mention
that it would ignore changes staged in the index and leave the index
untouched, and I wondered if and hoped that tests would be added to
verify that the index was indeed left untouched. Over the course of
many patches, it can be difficult to keep track of all the accumulated
questions, which makes it onerous to review the final patches adding
the tests and documentation updates enmasse.
I'm not necessarily suggesting that you re-roll merely to incorporate
the tests and documentation updates into the patches to which they
belong, but it's something to keep in mind for future submissions.
FOOTNOTES
[1]: Once in a while a patch introducing a change is so large on its
own that it may make sense to split tests and documentation updates
out to their own patches which immediately follow the patch to which
they apply, but that's different from delaying _all_ tests and
documentation updates and plopping them at the end of the series all
crammed together.
Although technically correct, I can't help but wonder if we can be
more friendly to readers by rephrasing this as:
--fixup=<commit>::
--fixup=amend:<commit>::
--fixup=reword:<commit>::
which is probably a lot easier to take in and understand at a glance.
Same comment applies to the synopsis.
Not necessarily worth a re-roll.
quoted hunk
+ Without `amend:` or `reword:`, create a `fixup!` commit where+ the commit message will be the subject line from the specified+ commit with a prefix of "fixup!'". The resulting "fixup!" commit+ is further used with `git rebase --autosquash` to fixup the+ content of the specified commit.
I think it becomes important at this point to make it more clear that
_only_ the content of <commit> gets changed by the "fixup!" commit,
and that the log message of <commit> is untouched.
quoted hunk
+The `--fixup=amend:<commit>` form creates an "amend!" commit to+fixup both the content and the commit log message of the specified+commit. The resulting "amend!" commit's commit message subject+will be the subject line from the specified commit with a prefix of+"amend!'" and the message body will be commit log message of the+specified commit. It also invokes an editor seeded with the log+message of the "amend!" commit to allow to edit further. And it+refuses to create "amend!" commit if it's commit message body is+empty unless used with the `--allow-empty-message` option. "amend!"+commit when rebased with `--autosquash` will fixup the contents and+replace the commit message of the specified commit with the "amend!"+commit's message body.
I had to read this several times to understand what it is trying to
say. I believe that part of the problem is that the bulk of the
description goes into great detail describing bits and behaviors which
make no sense without understanding what an "amend!" commit actually
does, which isn't explained until the very last sentence. So, I think
the entire description needs to be flipped on its head. In particular,
it should start by saying "create a new commit which both fixes up the
content of <commit> and replaces <commit>'s log message", and only
then dive into the details.
In fact, what I just wrote suggests a larger problem with the
description of `--fixup` overall. There is no high-level explanation
of what a "fixup" (or "amend" or "reword") is; it just dives right
into the minutiae without providing the reader with sufficient context
to understand any of it. Only a reader who is already familiar with
interactive rebase is likely to grok what is being said here. So,
extending the thought I expressed above, it would be helpful for the
description of `--fixup=[amend:|reword:]` to start by first explaining
what a "fixup" is, followed by simple descriptions of "amend" and
"reword" (building upon "fixup"), and followed finally by details of
each. Very roughly, something like this:
Creates a new commit which "fixes up" <commit> when applied with
`git rebase --autosquash`.
A "fixup" commit changes the content of <commit> but leaves its
log message untouched.
An "amend" commit is like "fixup" but also replaces the log
message of <commit> with the log message of the "amend" commit.
A "reword" commit replaces the log message of <commit> with its
own log message but makes no changes to the content.
And then dive into the details of each variation.
quoted hunk
+The `--fixup=amend:` and `--fixup=reword:` forms cannot be used with+other options to add to the commit log message i.e it is incompatible+with `-m`/`-F`/`-c`/`-C` options.
I suppose it doesn't hurt, but I wonder if it's really necessary to
document this considering that the user will learn soon enough upon
trying invalid combinations.
quoted hunk
+Also, after fixing the commit using `--fixup`, with or without option+and rebased with `--autosquash`, the authorship of the original commit+remains unchanged. See linkgit:git-rebase[1] for details.
@@ -593,16 +593,17 @@ See also INCOMPATIBLE OPTIONS below. --autosquash:: --no-autosquash::+ When the commit log message begins with "squash! ..." (or "fixup! ..."+ or "amend! ..."), and there is already a commit in the todo list that
Should this also be mentioning `reword!`?
quoted hunk
+ 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
It's becoming difficult to know which of the "foo!" prefixes get
transformed into which sequencer command since there is no longer a
one-to-one correspondence between "foo!" prefixes and sequencer
commands as there was when only "squash!" and "fixup!" existed. The
reader should be told what sequencer command(s) "amend!" and "reword!"
become.
quoted hunk
+ 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/amend+ commits is by using the `--fixup=[amend|reword]`/`--squash` options of+ linkgit:git-commit[1].
At this point, it may be beneficial to write these out long-form to
make it easier on the reader; something along the lines of:
... the `--fixup`, `--fixup:amend:`, `--fixup:reword:`, and
`--squash` options of ...
On Tue, 2 Mar 2021 at 11:13, Eric Sunshine [off-list ref] wrote:
On Mon, Mar 1, 2021 at 3:50 AM Charvi Mendiratta [off-list ref] wrote:
quoted
t7500: add tests for --fixup=[amend|reword] options
It's usually preferable for tests and documentation updates to be
bundled along with the patch which makes a particular change[1] rather
than waiting until the very end of the series and adding tests and
documentation covering all the changes made by patches earlier in the
series. As a reviewer, it is much harder to tell if the late-added
tests and documentation updates are comprehensive since it's difficult
to keep in mind all the changes made by earlier patches.
When reading earlier patches in this series, I questioned whether or
not certain features of each patch were going to be covered by tests
or documentation updates, but I couldn't tell because those updates
weren't made at the same time as the change about which I was reading.
For instance, when reading the implementation of `--fixup:reword`, I
was wondering if the documentation was going to be updated to mention
that it would ignore changes staged in the index and leave the index
untouched, and I wondered if and hoped that tests would be added to
verify that the index was indeed left untouched. Over the course of
many patches, it can be difficult to keep track of all the accumulated
questions, which makes it onerous to review the final patches adding
the tests and documentation updates enmasse.
I'm not necessarily suggesting that you re-roll merely to incorporate
the tests and documentation updates into the patches to which they
belong, but it's something to keep in mind for future submissions.
FOOTNOTES
[1]: Once in a while a patch introducing a change is so large on its
own that it may make sense to split tests and documentation updates
out to their own patches which immediately follow the patch to which
they apply, but that's different from delaying _all_ tests and
documentation updates and plopping them at the end of the series all
crammed together.
Noted. Thanks for guiding, I will take care of it.
From: Eric Sunshine <hidden> Date: 2021-03-04 00:23:07
On Wed, Mar 3, 2021 at 2:37 AM Charvi Mendiratta [off-list ref] wrote:
On Tue, 2 Mar 2021 at 03:45, Eric Sunshine [off-list ref] wrote:
quoted
quoted
+ if (starts_with(sb->buf, "amend! amend!"))
Is the content of the incoming strbuf created mechanically so that we
know that there will only ever be one space between the two "amend!"
literals? If not, then this starts_with() check feels fragile.
Yes, so for preparing each "amend!" commit we add prefix "amend! '' to
the subject of the specific commit. And further if we amend the
"amend!" commit then this above code is checked before creating a
"amend! amend!" commit for the user. So I think maybe we don't need to
check for multiple spaces ?
Okay, if this is guaranteed to be created mechanically, then what you
have should work, though it may be a good idea to add an in-code
comment stating the reason it is okay to expect just the single space.
The alternative would be to avoid having "amend! amend!" in the first
place. I didn't trace through the code carefully so I don't know if it
is possible, but would it make sense for the caller(s) to check before
adding a second "amend!", thus eliminating the need to do so here?
(Perhaps I'm misunderstanding, but the above code almost feels like a
case of "whoops, we did something undesirable, so let's undo it.".)
From: Eric Sunshine <hidden> Date: 2021-03-04 00:23:08
On Wed, Mar 3, 2021 at 2:44 AM Charvi Mendiratta [off-list ref] wrote:
On Tue, 2 Mar 2021 at 12:09, Eric Sunshine [off-list ref] wrote:
quoted
On Mon, Mar 1, 2021 at 3:52 AM Charvi Mendiratta [off-list ref] wrote:
quoted
Signed-off-by: Charvi Mendiratta <redacted>
+ Without `amend:` or `reword:`, create a `fixup!` commit where
+ the commit message will be the subject line from the specified
+ commit with a prefix of "fixup!'". The resulting "fixup!" commit
+ is further used with `git rebase --autosquash` to fixup the
+ content of the specified commit.
By the way, now that you explained in the other thread that "short"
prefix-matching of "amend" and "reword" are allowed, I realize that
the documentation doesn't mention it (or at least I don't remember
reading it).
(Nevertheless, I still feel uncomfortable about supporting short
prefix-matching in the initial implementation without any evidence
that users will demand it, since we can't change that decision once
it's in the hands of users.)
quoted
quoted
+ When the commit log message begins with "squash! ..." (or "fixup! ..."
+ or "amend! ..."), and there is already a commit in the todo list that
Should this also be mentioning `reword!`?
No, as both `amend` and `reword` suboptions create "amend!" commit
only. I think it seems a bit confusing but I will try another attempt
to reword the document.
Hmm, I see. So "reword!" is really just an "amend!" with only commit
message but no patch content. That makes perfect sense from an
implementation standpoint, but it makes me wonder if it would be
easier for users to understand if it created a "reword!" commit which
would be recognized as an alias of "amend!". (But maybe that's getting
too confusing, and my musing should be ignored.)
This also answers an unasked question I had regarding the duplicate
"amend! amend!" check. I was wondering why it wasn't also checking for
"reword! reword!".
On Tue, 2 Mar 2021 at 04:06, Eric Sunshine [off-list ref] wrote:
quoted
+ if (argc)+ die(_("cannot combine reword option of --fixup with path %s"), *argv);+ if (patch_interactive)+ die(_("cannot combine reword option of --fixup with --patch"));+ if (interactive)+ die(_("cannot combine reword option of --fixup with --interactive"));+ if (all)+ die(_("cannot combine reword option of --fixup with --all"));+ if (also)+ die(_("cannot combine reword option of --fixup with --include"));+ if (only)+ die(_("cannot combine reword option of --fixup with --only"));+}
Or, more concisely:
if (argc)
die(_("--fixup mutually exclusive with path '%s'), ...);
if (patch_interactive || interactive || all || also || only)
die(_("--fixup mutually exclusive with
--patch/--interactive/--all/--include/--only);
Got it ! Its seems more clear, I will fix it.
The mix of two different error message styles (capitalized with
full-stop vs. lowercase no-full-stop) is a bit jarring, but minor.
On Tue, 2 Mar 2021 at 01:55, Eric Sunshine [off-list ref] wrote:
Now that this function is public, is the name too generic? Most other
functions in this header have "commit" in the name. So,
commit_subject_length() might be one possibility (assuming the current
name is too generic).
Agree, I will change it and update the patch. Thanks for this fix.
On Tue, 2 Mar 2021 at 03:45, Eric Sunshine [off-list ref] wrote:
quoted
@@ -105,7 +105,8 @@ static const char *template_file;+static int prepare_amend_commit(struct commit *commit, struct strbuf *sb,+ struct pretty_print_context *ctx) {+ /*+ * If we amend the 'amend!' commit then we don't want to+ * duplicate the subject line.+ */+ const char *format = NULL;+ if (starts_with(sb->buf, "amend! amend!"))
Is the content of the incoming strbuf created mechanically so that we
know that there will only ever be one space between the two "amend!"
literals? If not, then this starts_with() check feels fragile.
(Compare with the code in sequencer.c which checks for this sort of
duplication but is tolerant of one or more spaces, not just a single
space.)
Yes, so for preparing each "amend!" commit we add prefix "amend! '' to
the subject of the specific commit. And further if we amend the
"amend!" commit then this above code is checked before creating a
"amend! amend!" commit for the user. So I think maybe we don't need to
check for multiple spaces ?
quoted
+ format = "%b";+ else+ format = "%B";
It's subjective and minor, but this could be expressed more compactly as:
const char *fmt = starts_with(...) ? "%b" : "%B";
Also, no need to initialize `format` to NULL since it gets assigned in
all code paths.
Not worth a re-roll.
I agree, I will fix it.
quoted
@@ -745,15 +761,33 @@ static int prepare_to_commit(const char *index_file, const char *prefix,+ if (!strcmp(fixup_prefix, "amend")) {+ if (have_option_m)+ die(_("cannot combine -m with --fixup:%s"), fixup_message);+ else+ prepare_amend_commit(commit, &sb, &ctx);+ }
This is minor, but the way this is written, the error case and the
normal case appear to have the same significance, whereas, if you
write it like this:
if (!strcmp(...)) {
if (have_option_m)
die(...);
prepare_amend_commit(...);
}
then it's easier to see that you're checking for and getting an error
case out of the way early, which allows the reader to concentrate
without distraction on the normal case. As a minor benefit, you also
get to eliminate an indentation level for the normal case, which could
be important if more code is added to that case.
Not worth a re-roll.
Thanks for pointing this out, I will remove the unnecessary `else`.
Although technically correct, I can't help but wonder if we can be
more friendly to readers by rephrasing this as:
--fixup=<commit>::
--fixup=amend:<commit>::
--fixup=reword:<commit>::
which is probably a lot easier to take in and understand at a glance.
Same comment applies to the synopsis.
Not necessarily worth a re-roll.
quoted
+ Without `amend:` or `reword:`, create a `fixup!` commit where+ the commit message will be the subject line from the specified+ commit with a prefix of "fixup!'". The resulting "fixup!" commit+ is further used with `git rebase --autosquash` to fixup the+ content of the specified commit.
I think it becomes important at this point to make it more clear that
_only_ the content of <commit> gets changed by the "fixup!" commit,
and that the log message of <commit> is untouched.
quoted
+The `--fixup=amend:<commit>` form creates an "amend!" commit to+fixup both the content and the commit log message of the specified+commit. The resulting "amend!" commit's commit message subject+will be the subject line from the specified commit with a prefix of+"amend!'" and the message body will be commit log message of the+specified commit. It also invokes an editor seeded with the log+message of the "amend!" commit to allow to edit further. And it+refuses to create "amend!" commit if it's commit message body is+empty unless used with the `--allow-empty-message` option. "amend!"+commit when rebased with `--autosquash` will fixup the contents and+replace the commit message of the specified commit with the "amend!"+commit's message body.
I had to read this several times to understand what it is trying to
say. I believe that part of the problem is that the bulk of the
description goes into great detail describing bits and behaviors which
make no sense without understanding what an "amend!" commit actually
does, which isn't explained until the very last sentence. So, I think
the entire description needs to be flipped on its head. In particular,
it should start by saying "create a new commit which both fixes up the
content of <commit> and replaces <commit>'s log message", and only
then dive into the details.
In fact, what I just wrote suggests a larger problem with the
description of `--fixup` overall. There is no high-level explanation
of what a "fixup" (or "amend" or "reword") is; it just dives right
into the minutiae without providing the reader with sufficient context
to understand any of it. Only a reader who is already familiar with
interactive rebase is likely to grok what is being said here. So,
extending the thought I expressed above, it would be helpful for the
description of `--fixup=[amend:|reword:]` to start by first explaining
what a "fixup" is, followed by simple descriptions of "amend" and
"reword" (building upon "fixup"), and followed finally by details of
each. Very roughly, something like this:
Creates a new commit which "fixes up" <commit> when applied with
`git rebase --autosquash`.
A "fixup" commit changes the content of <commit> but leaves its
log message untouched.
An "amend" commit is like "fixup" but also replaces the log
message of <commit> with the log message of the "amend" commit.
A "reword" commit replaces the log message of <commit> with its
own log message but makes no changes to the content.
And then dive into the details of each variation.
Agree, thanks for pointing this out in detail. I will rewrite the doc
in the above suggested way and update in the next version.
quoted
+The `--fixup=amend:` and `--fixup=reword:` forms cannot be used with+other options to add to the commit log message i.e it is incompatible+with `-m`/`-F`/`-c`/`-C` options.
I suppose it doesn't hurt, but I wonder if it's really necessary to
document this considering that the user will learn soon enough upon
trying invalid combinations.
Not necessary, but I thought that users must know that `-m` is
otherwise supported with plain `--fixup` and not with the `amend` and
`reword` suboptions. So, I think to reword it and add with the above.
@@ -593,16 +593,17 @@ See also INCOMPATIBLE OPTIONS below. --autosquash:: --no-autosquash::+ When the commit log message begins with "squash! ..." (or "fixup! ..."+ or "amend! ..."), and there is already a commit in the todo list that
Should this also be mentioning `reword!`?
No, as both `amend` and `reword` suboptions create "amend!" commit
only. I think it seems a bit confusing but I will try another attempt
to reword the document.
quoted
+ 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
It's becoming difficult to know which of the "foo!" prefixes get
transformed into which sequencer command since there is no longer a
one-to-one correspondence between "foo!" prefixes and sequencer
commands as there was when only "squash!" and "fixup!" existed. The
reader should be told what sequencer command(s) "amend!" and "reword!"
become.
Okay, I will change it and explain it in more detail.
quoted
+ 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/amend+ commits is by using the `--fixup=[amend|reword]`/`--squash` options of+ linkgit:git-commit[1].
At this point, it may be beneficial to write these out long-form to
make it easier on the reader; something along the lines of:
... the `--fixup`, `--fixup:amend:`, `--fixup:reword:`, and
`--squash` options of ...
Agree, I will fix it.
Thanks for all the detailed reviews and suggestions. I will update all
the changes in the next revision.
Thanks and Regards,
Charvi
Hi Eric,
On Wed, 3 Mar 2021 at 13:16, Eric Sunshine [off-list ref] wrote:
On Wed, Mar 3, 2021 at 2:37 AM Charvi Mendiratta [off-list ref] wrote:
quoted
On Tue, 2 Mar 2021 at 03:45, Eric Sunshine [off-list ref] wrote:
quoted
quoted
+ if (starts_with(sb->buf, "amend! amend!"))
Is the content of the incoming strbuf created mechanically so that we
know that there will only ever be one space between the two "amend!"
literals? If not, then this starts_with() check feels fragile.
Yes, so for preparing each "amend!" commit we add prefix "amend! '' to
the subject of the specific commit. And further if we amend the
"amend!" commit then this above code is checked before creating a
"amend! amend!" commit for the user. So I think maybe we don't need to
check for multiple spaces ?
Okay, if this is guaranteed to be created mechanically, then what you
have should work, though it may be a good idea to add an in-code
comment stating the reason it is okay to expect just the single space.
The alternative would be to avoid having "amend! amend!" in the first
place.
Agree. I think we can do this...
I didn't trace through the code carefully so I don't know if it
is possible, but would it make sense for the caller(s) to check before
adding a second "amend!", thus eliminating the need to do so here?
(Perhaps I'm misunderstanding, but the above code almost feels like a
case of "whoops, we did something undesirable, so let's undo it.".)
I looked into it and got another alternative, to extend the same
prepare_amend_commit() function and replace the check condition of
if (starts_with(sb->buf, "amend! amend!")) with the code as below :
const char *buffer = get_commit_buffer(commit, NULL);
const char *subject;
find_commit_subject(buffer, &subject);
if (starts_with(subject, "amend!"))
const char *fmt = starts_with(subject, "amend!") ? "%b" : "%B";
format_commit_message(commit, fmt, sb, ctx);
unuse_commit_buffer(commit, buffer);
So, now it checks the commit subject here only. Otherwise as you have
suggested above to check before adding a second "amend!", I think that
can result in confusion as currently both "fixup!" and "amend!"
commits (commit's subject) are prepared by same code and further for
"amend!" commit as we write a commit message body also so we used
prepare_amend_commit() to do that stuff.
On Wed, 3 Mar 2021 at 13:48, Eric Sunshine [off-list ref] wrote:
[...]
By the way, now that you explained in the other thread that "short"
prefix-matching of "amend" and "reword" are allowed, I realize that
the documentation doesn't mention it (or at least I don't remember
reading it).
Yes, I admit it was not included. I will add it too.
(Nevertheless, I still feel uncomfortable about supporting short
prefix-matching in the initial implementation without any evidence
that users will demand it, since we can't change that decision once
it's in the hands of users.)
I am not sure about strong evidence but I tried to keep the major
points discussed earlier, as mentioned in the previous thread. Also I
think otherwise the short prefix will ease out / shorten the command
to prepare the "amend!" and also mirrors the commands in interactive
rebase.
quoted
quoted
quoted
+ When the commit log message begins with "squash! ..." (or "fixup! ..."
+ or "amend! ..."), and there is already a commit in the todo list that
Should this also be mentioning `reword!`?
No, as both `amend` and `reword` suboptions create "amend!" commit
only. I think it seems a bit confusing but I will try another attempt
to reword the document.
Hmm, I see. So "reword!" is really just an "amend!" with only commit
message but no patch content. That makes perfect sense from an
implementation standpoint, but it makes me wonder if it would be
easier for users to understand if it created a "reword!" commit which
would be recognized as an alias of "amend!". (But maybe that's getting
too confusing, and my musing should be ignored.)
Yes, we didn't choose to make "reword!" commit because if we do so
then again it would be expected to implicitly change 'pick' command to
'reword' in sequencer/ rebase to-do list when combined with 'git
rebase -- autosquash'. But here we are changing 'pick' to ' fixup -C'
to fulfill the working. So, we decided to create a variant of
'--fixup' and serve it as "amend!" commit.
This also answers an unasked question I had regarding the duplicate
"amend! amend!" check. I was wondering why it wasn't also checking for
"reword! reword!".
Yes, it's true.
Thanks for the reveiws, I will add the above mentioned changes too.
Thanks and Regards,
Charvi
This patch series teaches `git commit --fixup` to create "amend!" commit
as an alternative that works with `git rebase --autosquash`. It allows to
fixup both the content and the commit message of the specified commit.
Here we add two suboptions to the `--fixup`, first `amend` suboption that
creates an "amend!" commit. It takes the staged changes and also allows to
edit the commit message of the commit we are fixing.
Example usuage:
git commit --fixup=amend:<commit>
Secondly, `reword` suboption that creates an empty "amend!" commit i.e it
ignores the staged changes and only allows to reword/edit the commit message
of the commit we are fixing. `--fixup=reword:<commit>` is a short-hand of
`--fixup=amend:<commit> --only`.
Example usuage:
git commit --fixup=reword:<commit>
** This work is rebased on the top of cm/rebase-i-updates.
Changes from v3 :
(Thanks to Junio C Hamano and Eric Sunshine for the reviews and suggesting the
improvements required in the previous patches )
* Now `--fixup=reword:` aliases `--fixup=amend: --only`. Added the respective
test and updated documentation.
* Modified prepared_amend_commit() in commit.c.
* Renamed the subject_length() to commit_subject_length() [v3 - 1/6]
* Modified the --fixup=[amend|reword] documentation
* Also, updated the commit messages and some other minor cleanup.
Charvi Mendiratta (6):
sequencer: export and rename subject_length()
commit: add amend suboption to --fixup to create amend! commit
commit: add a reword suboption to --fixup
t7500: add tests for --fixup=[amend|reword] options
t3437: use --fixup with options to create amend! commit
doc/git-commit: add documentation for fixup=[amend|reword] options
Documentation/git-commit.txt | 40 ++++++-
Documentation/git-rebase.txt | 21 ++--
builtin/commit.c | 118 +++++++++++++++++--
commit.c | 14 +++
commit.h | 3 +
sequencer.c | 16 +--
t/t3437-rebase-fixup-options.sh | 30 +----
t/t7500-commit-template-squash-signoff.sh | 135 ++++++++++++++++++++++
8 files changed, 309 insertions(+), 68 deletions(-)
--
2.29.0.rc1
`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 [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Helped-by: Junio C Hamano [off-list ref]
Helped-by: Eric Sunshine [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
builtin/commit.c | 107 ++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 97 insertions(+), 10 deletions(-)
@@ -745,15 +764,32 @@ static int prepare_to_commit(const char *index_file, const char *prefix,}elseif(fixup_message){structpretty_print_contextctx={0};structcommit*commit;-commit=lookup_commit_reference_by_name(fixup_message);+char*fmt=xstrfmt("%s! %%s\n\n",fixup_prefix);+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);+format_commit_message(commit,fmt,&sb,&ctx);+free(fmt);hook_arg1="message";++/*+*Only`-m`commitmessageoptionischeckedhere,as+*itsupports`--fixup`toappendthecommitmessage.+*+*Theothercommitmessageoptions`-c`/`-C`/`-F`are+*incompatiblewithalltheformsof`--fixup`and+*havealreadyerroredoutwhileparsingthe`gitcommit`+*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);+}}elseif(!stat(git_path_merge_msg(the_repository),&statbuf)){size_tmerge_msg_start;
@@ -1152,6 +1188,12 @@ static void finalize_deferred_config(struct wt_status *s)s->ahead_behind_flags=AHEAD_BEHIND_FULL;}+/* returns the length of intial segment of alpha characters only */+staticsize_tget_alpha_len(char*fixup_message){+constcharalphas[]="abcdefghijklmnopqrstuvwxyz";+returnstrspn(fixup_message,alphas);+}+staticintparse_and_validate_options(intargc,constchar*argv[],conststructoption*options,constchar*constusage[],
@@ -1170,7 +1212,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 +1269,34 @@ 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){+/*+*Tocheckiffixup_messagethatcontains':'isacommit+*referenceforexample:--fixup="HEAD^{/^area: string}"or+*asuboptionof`--fixup`.+*+*As`amend`suboptioncontainsonlyalphacharacter.+*Socheckiffirstnonalphacharacterinfixup_message+*is':'.+*/+size_tlen=get_alpha_len(fixup_message);+if(len&&fixup_message[len]==':'){+fixup_message[len++]='\0';+fixup_commit=fixup_message+len;+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 +1574,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+*onlytranslate<commit>.+*/+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 +1737,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){+structstrbufbody=STRBUF_INIT;+size_tlen=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){constchar*exclude_gpgsig[3]={"gpgsig","gpgsig-sha256",NULL};extra=read_commit_extra_headers(current_head,exclude_gpgsig);
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 [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Helped-by: Eric Sunshine [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
commit.c | 14 ++++++++++++++
commit.h | 3 +++
sequencer.c | 16 +---------------
3 files changed, 18 insertions(+), 15 deletions(-)
@@ -1724,20 +1724,6 @@ enum todo_item_flags {TODO_EDIT_FIXUP_MSG=(1<<2),};-staticsize_tsubject_length(constchar*body)-{-constchar*p=body;-while(*p){-constchar*next=skip_blank_lines(p);-if(next!=p)-break;-p=strchrnul(p,'\n');-if(*p)-p++;-}-returnp-body;-}-staticconstcharfirst_commit_msg_str[]=N_("This is the 1st commit message:");staticconstcharnth_commit_msg_fmt[]=N_("This is the commit message #%d:");staticconstcharskip_first_commit_msg_str[]=N_("The 1st commit message will be skipped:");
`git commit --fixup=reword:<commit>` aliases
`--fixup=amend:<commit> --only`, where it creates an empty "amend!"
commit that will reword <commit> without changing its contents when
it is rebased with `--autosquash`.
Mentored-by: Christian Couder [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Helped-by: Junio C Hamano [off-list ref]
Helped-by: Eric Sunshine [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
builtin/commit.c | 32 +++++++++++++++++++++++++-------
1 file changed, 25 insertions(+), 7 deletions(-)
@@ -1188,6 +1188,19 @@ static void finalize_deferred_config(struct wt_status *s)s->ahead_behind_flags=AHEAD_BEHIND_FULL;}+staticvoidcheck_fixup_reword_options(intargc,constchar*argv[]){+if(whence!=FROM_COMMIT){+if(whence==FROM_MERGE)+die(_("You are in the middle of a merge -- cannot reword."));+elseif(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"));+}+/* returns the length of intial segment of alpha characters only */staticsize_tget_alpha_len(char*fixup_message){constcharalphas[]="abcdefghijklmnopqrstuvwxyz";
@@ -1575,10 +1593,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-*onlytranslate<commit>.+*TRANSLATORS:Leave"[(amend|reword):]"as-is,+*andonlytranslate<commit>.*/-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")),
@@ -86,11 +86,40 @@ OPTIONS Like '-C', but with `-c` the editor is invoked, so that the user can further edit the commit message.---fixup=<commit>::- 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):]<commit>::+ Construct a new commit for use with `rebase --autosquash`,+ which fixes the specified commit. The plain form+ `--fixup=<commit>` creates a "fixup!" commit, that allows+ to fixup only the content of the specified commit and leave+ it's commit log message untouched. When used with `amend:`+ or `reword:`, it creates "amend!" commit that is like "fixup!"+ commit but it allows to fixup both the content and the commit+ log message of the specified commit. The commit log message of+ the specified commit is fixed implicitly by replacing it with+ the "amend!" commit's message body upon `rebase --autosquash`.+++The resulting "fixup!" commit message will be the subject line+from the specified commit with a prefix of "fixup!". Can be used+with additional commit message option `-m`.+++The `--fixup=amend:<commit>` form creates an "amend!" commit where+its commit message subject will be the subject line from the+specified commit with a prefix of "amend!" and the message body+will be commit log message of the specified commit. It also invokes+an editor seeded with the log message of the "amend!" commit to+allow to edit further. It refuses to create "amend!" commit if it's+commit message body is empty unless used with the+`--allow-empty-message` option.+++The `--fixup=reword:<commit>` aliases `--fixup=amend:<commit> --only`+and it also creates an "amend!" commit, but here it records the same+tree as `HEAD`, i.e. it does not take any staged changes and only allows+to fixup the commit message of the specified commit. It will reword the+specified commit when it is rebased with `--autosquash`.+++Also, after fixing the commit using `--fixup`, with or without option+and rebased with `--autosquash`, the authorship of the original commit+remains unchanged. See linkgit:git-rebase[1] for details. --squash=<commit>:: Construct a commit message for use with `rebase --autosquash`.
@@ -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
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 [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
t/t3437-rebase-fixup-options.sh | 30 +++---------------------------
1 file changed, 3 insertions(+), 27 deletions(-)
@@ -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.expectactual=commit_msg_is.actual
@@ -279,6 +281,163 @@ test_expect_success 'commit --fixup -m"something" -m"extra"' ' extra"'+get_commit_msg(){+rev="$1"&&+gitlog-1--pretty=format:"%B""$rev"+}++test_expect_success'commit --fixup=amend: creates amend! commit''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!$(gitlog-1--format=%sHEAD~)++$(get_commit_msgHEAD~)++edited+EOF+(+set_fake_editor&&+FAKE_COMMIT_AMEND="edited"\+gitcommit--fixup=amend:HEAD~+)&&+get_commit_msgHEAD>actual&&+test_cmpexpectedactual+'++test_expect_success'--fixup=amend: --only ignores staged changes''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!$(gitlog-1--format=%sHEAD~)++$(get_commit_msgHEAD~)++edited+EOF+(+set_fake_editor&&+FAKE_COMMIT_AMEND="edited"\+gitcommit--fixup=amend:HEAD~--only+)&&+get_commit_msgHEAD>actual&&+test_cmpexpectedactual&&+test_cmp_revHEAD@{1}^{tree}HEAD^{tree}&&+test_cmp_revHEAD@{1}HEAD^&&+test_expect_code1gitdiff--cached--exit-code&&+gitcat-fileblob:foo>actual&&+test_cmpfooactual+'++test_expect_success'--fixup=reword: ignores staged changes''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!$(gitlog-1--format=%sHEAD~)++$(get_commit_msgHEAD~)++edited+EOF+(+set_fake_editor&&+FAKE_COMMIT_AMEND="edited"\+gitcommit--fixup=reword:HEAD~+)&&+get_commit_msgHEAD>actual&&+test_cmpexpectedactual&&+test_cmp_revHEAD@{1}^{tree}HEAD^{tree}&&+test_cmp_revHEAD@{1}HEAD^&&+test_expect_code1gitdiff--cached--exit-code&&+gitcat-fileblob:foo>actual&&+test_cmpfooactual+'++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_failgitcommit--fixup=reword:HEAD~-m"reword commit message"2>actual&&+test_cmpexpectactual+'++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_failgitcommit--fixup=amend:HEAD~-m"amend commit message"2>actual&&+test_cmpexpectactual+'++test_expect_success'consecutive amend! commits remove amend! line from commit msg body''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!amend!$(gitlog-1--format=%sHEAD~)++$(get_commit_msgHEAD~)++edited1++edited2+EOF+echo"reword new commit message">actual&&+(+set_fake_editor&&+FAKE_COMMIT_AMEND="edited 1"\+gitcommit--fixup=reword:HEAD~&&+FAKE_COMMIT_AMEND="edited 2"\+gitcommit--fixup=reword:HEAD+)&&+get_commit_msgHEAD>actual&&+test_cmpexpectedactual+'++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_failenvFAKE_COMMIT_MESSAGE="amend! target message subject line"\+gitcommit--fixup=amend:HEAD~2>actual+)&&+test_cmpexpectedactual+'++test_expect_success'amend! commit allows empty commit msg body with --allow-empty-message''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!$(gitlog-1--format=%sHEAD~)+EOF+(+set_fake_editor&&+FAKE_COMMIT_MESSAGE="amend! target message subject line"\+gitcommit--fixup=amend:HEAD~--allow-empty-message&&+get_commit_msgHEAD>actual+)&&+test_cmpexpectedactual+'++test_fixup_reword_opt(){+test_expect_successC_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_failgitcommit--fixup=reword:HEAD~$12>actual&&+test_cmpexpectactual+"+}++foroptin--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_failgitcommit--fixup=reword:HEAD~--foo2>actual&&+test_cmpexpectactual+'++test_expect_success'--fixup=reword: -F give error message''+echo"fatal: Only one of -c/-C/-F/--fixup can be used.">expect&&+test_must_failgitcommit--fixup=reword:HEAD~-Fmsg2>actual&&+test_cmpexpectactual+' test_expect_success'commit --squash works with -F''commit_for_rebase_autosquash_setup&&
From: Junio C Hamano <hidden> Date: 2021-03-11 00:32:25
Charvi Mendiratta [off-list ref] writes:
quoted hunk
* reference for example: --fixup="HEAD^{/^area: string}" or * a suboption of `--fixup`. *- * As `amend` suboption contains only alpha character.- * So check if first non alpha character in fixup_message- * is ':'.+ * As `amend`/`reword` suboptions contains only alpha+ * characters. So check if first non alpha character+ * in fixup_message is ':'.
Sorry, but I cannot quite follow the logic.
We limit --fixup's suboptions to only alpha characters. If
the first character after a len 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.
Otherwise, we are dealing with --fixup=<commit> that happens
to have a colon in <commit> object name.
perhaps?
On Thu, 11 Mar 2021 at 06:01, Junio C Hamano [off-list ref] wrote:
Charvi Mendiratta [off-list ref] writes:
quoted
* reference for example: --fixup="HEAD^{/^area: string}" or * a suboption of `--fixup`. *- * As `amend` suboption contains only alpha character.- * So check if first non alpha character in fixup_message- * is ':'.+ * As `amend`/`reword` suboptions contains only alpha+ * characters. So check if first non alpha character+ * in fixup_message is ':'.
Sorry, but I cannot quite follow the logic.
We limit --fixup's suboptions to only alpha characters. If
the first character after a len 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.
Otherwise, we are dealing with --fixup=<commit> that happens
to have a colon in <commit> object name.
perhaps?
Yes, Agree. Here I just intend to mention the special case
"--fixup=HEAD^{/^area: string}" because of which we chose the method
to check if first non alpha char is ':' instead of directly checking
the suboption like (skip_prefix(msg, "amend:", &arg). So maybe we can
reword it like
- To check if fixup_message that contains ':' is a commit
- reference for example: --fixup="HEAD^{/^area: string}" or
- a suboption of `--fixup`.
+ fixup_message could be a commit reference for example:
+ --fixup="HEAD^{/^area:string}" or a suboption of `--fixup`.
+
+ As `amend` ...
Thanks and Regards,
Charvi
From: Eric Sunshine <hidden> Date: 2021-03-11 06:26:54
On Wed, Mar 10, 2021 at 2:44 PM Charvi Mendiratta [off-list ref] wrote:
quoted hunk
`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.
[...]
Signed-off-by: Charvi Mendiratta <redacted>
---
Two style nits:
* opening curly brace of function goes on its own line
* we don't normally have a blank line at the top of the function body
preceding the declarations
So:
static int prepare_amend_commit(...)
{
const char *buffer, *subject, *fmt;
quoted hunk
+ 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);+ return 0;+}
What is the significance of this function's return value? At least in
this patch, the single caller of this function ignores the return
value, which suggests that the function need not return any value.
Will a later patch add other possible return values to indicate an
error or something?
Nit: it would reduce the cognitive load slightly if `fmt` is prepared
just before it is used rather than being prepared at the top of the
block:
fmt = xstrfmt("%s! %%s\n\n", fixup_prefix);
format_commit_message(commit, fmt, &sb, &ctx);
free(fmt);
Subjective and not at all worth a re-roll.
quoted hunk
@@ -1227,6 +1269,34 @@ static int parse_and_validate_options(int argc, const char *argv[],+ if (fixup_message) {+ /*+ * To check if fixup_message that contains ':' is a commit+ * reference for example: --fixup="HEAD^{/^area: string}" or+ * a suboption of `--fixup`.+ *+ * As `amend` suboption contains only alpha character.+ * So check if first non alpha character in fixup_message+ * is ':'.+ */
I have a tough time figuring out what this comment is trying to say,
and I don't think I would have understood it if Junio had not already
explained earlier in this thread why this code is as complex as it is
(rather than using, say, skip_prefix()). Perhaps the entire comment
can be replaced with this:
Extract <option> (i.e. `amend`) from `--fixup=<option>:<commit>`,
if present. To avoid being fooled by a legitimate ":" in <commit>
(i.e. `--fixup="HEAD^{/^area: string}"`), <option> must be
composed of only alphabetic characters.
Not necessarily worth a re-roll.
quoted hunk
+ size_t len = get_alpha_len(fixup_message);+ if (len && fixup_message[len] == ':') {+ fixup_message[len++] = '\0';+ fixup_commit = fixup_message + len;
An alternate -- just about as compact and perhaps more idiomatic --
way to write all this without introducing the new get_alpha_len()
function:
char *p = fixup_mesage;
while (isalpha(*p))
p++;
if (p > fixup_message && *p == ':') {
*p = '\0';
fixup_commit = p + 1;
Subjective and not at all worth a re-roll.
@@ -86,11 +86,40 @@ OPTIONS+--fixup=[(amend|reword):]<commit>::+ Construct a new commit for use with `rebase --autosquash`,+ which fixes the specified commit. The plain form+ `--fixup=<commit>` creates a "fixup!" commit, that allows+ to fixup only the content of the specified commit and leave+ it's commit log message untouched. When used with `amend:`
s/it's/its/
quoted hunk
+ or `reword:`, it creates "amend!" commit that is like "fixup!"+ commit but it allows to fixup both the content and the commit+ log message of the specified commit. The commit log message of+ the specified commit is fixed implicitly by replacing it with+ the "amend!" commit's message body upon `rebase --autosquash`.
The first half of this description is clear. The second half gets
bogged down and difficult to decipher. It also seems to claim that
"reword:" can change the content of <commit>, which isn't accurate at
the UI level (even if it happens to reflect the underlying
implementation). I might have written the above description like this:
Create a new commit which "fixes up" `<commit>` when applied with
`git rebase --autosquash`. Plain `--fixup=<commit>` creates a
"fixup!" commit which changes the content of `<commit>` but leaves
its log message untouched. `--fixup=amend:<commit>` is similar but
creates an "amend!" commit which also replaces the log message of
`<commit>` with the log message of the "amend!" commit.
`--fixup=reword:<commit>` creates an "amend!" commit which
replaces the log message of `<commit>` with its own log message
but makes no changes to the content of `<commit>`.
quoted hunk
+The resulting "fixup!" commit message will be the subject line+from the specified commit with a prefix of "fixup!". Can be used+with additional commit message option `-m`.
This gives details without providing meaning. If I didn't already know
how this all works, I think I'd probably be mystified about what it is
trying to say. Providing context by mentioning `git rebase
--autosquash` would help explain the significance of "fixup!".
Similarly, it's not clear on the surface why this mentions `-m` at
all. I might have written it like this:
The commit created by plain `--fixup=<commit>` has a subject
composed of "fixup!" followed by the subject line from <commit>,
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 `<commit>` by `git rebase
--autosquash`.
quoted hunk
+The `--fixup=amend:<commit>` form creates an "amend!" commit where+its commit message subject will be the subject line from the+specified commit with a prefix of "amend!" and the message body+will be commit log message of the specified commit. It also invokes+an editor seeded with the log message of the "amend!" commit to+allow to edit further. It refuses to create "amend!" commit if it's+commit message body is empty unless used with the+`--allow-empty-message` option.
This is reasonable, but does get into the weeds somewhat and uses
potentially unusual terms such as "seeded". It can be tightened up a
bit by building upon what was explained earlier for plain
`--fixup=<commit>`. To really round it out and give proper context for
understanding the purpose, it would also be helpful to explain how an
"amend!" commit is handled by `git rebase --autosquash`. I might have
written it like this:
The commit created by `--fixup=amend:<commit>` is similar but its
subject 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.
quoted hunk
+The `--fixup=reword:<commit>` aliases `--fixup=amend:<commit> --only`+and it also creates an "amend!" commit, but here it records the same+tree as `HEAD`, i.e. it does not take any staged changes and only allows+to fixup the commit message of the specified commit. It will reword the+specified commit when it is rebased with `--autosquash`.
This gets too deep into the techno-speak by talking about "tree" and
`HEAD`. You can convey the same concept more simply by saying merely
that it creates an empty commit. I might have written it like this:
`--fixup=reword:<commit>` is shorthand for `--fixup=amend:<commit>
--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 `<commit>`
without making any other changes.
quoted hunk
+Also, after fixing the commit using `--fixup`, with or without option+and rebased with `--autosquash`, the authorship of the original commit+remains unchanged. See linkgit:git-rebase[1] for details.
It sounds odd to start this sentence with "also". Perhaps:
Neither "fixup!" nor "amend!" commits change authorship of
`<commit>` when applied by `git rebase --autosquash`.
From: Eric Sunshine <hidden> Date: 2021-03-11 08:07:36
On Wed, Mar 10, 2021 at 2:43 PM Charvi Mendiratta [off-list ref] wrote:
Changes from v3 :
(Thanks to Junio C Hamano and Eric Sunshine for the reviews and suggesting the
improvements required in the previous patches )
* Now `--fixup=reword:` aliases `--fixup=amend: --only`. Added the respective
test and updated documentation.
* Modified prepared_amend_commit() in commit.c.
* Renamed the subject_length() to commit_subject_length() [v3 - 1/6]
* Modified the --fixup=[amend|reword] documentation
* Also, updated the commit messages and some other minor cleanup.
Thanks for re-rolling. I left some relatively minor comments on patch
[2/6] and some substantial documentation-related comments on [6/6].
(If you take my suggested documentation rewrites verbatim, you can
have my Signed-off-by: along with them.)
One final request: Please consider using the --range-diff or
--interdiff options or both when preparing your series with
git-format-patch. Doing so can really help reviewers focus on the
changes made from re-roll to re-roll without having to painstakingly
re-read each patch in the series -- even if a patch didn't change --
thus saving time.
On Thu, 11 Mar 2021 at 11:55, Eric Sunshine [off-list ref] wrote:
[...]
Two style nits:
* opening curly brace of function goes on its own line
* we don't normally have a blank line at the top of the function body
preceding the declarations
So:
static int prepare_amend_commit(...)
{
const char *buffer, *subject, *fmt;
Okay, I will fix it.
quoted
+ 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);+ return 0;+}
What is the significance of this function's return value? At least in
this patch, the single caller of this function ignores the return
value, which suggests that the function need not return any value.
Will a later patch add other possible return values to indicate an
error or something?
No, it will not return another value later. I will remove it from here.
Nit: it would reduce the cognitive load slightly if `fmt` is prepared
just before it is used rather than being prepared at the top of the
block:
fmt = xstrfmt("%s! %%s\n\n", fixup_prefix);
format_commit_message(commit, fmt, &sb, &ctx);
free(fmt);
Subjective and not at all worth a re-roll.
Agree, will fix it.
quoted
@@ -1227,6 +1269,34 @@ static int parse_and_validate_options(int argc, const char *argv[],+ if (fixup_message) {+ /*+ * To check if fixup_message that contains ':' is a commit+ * reference for example: --fixup="HEAD^{/^area: string}" or+ * a suboption of `--fixup`.+ *+ * As `amend` suboption contains only alpha character.+ * So check if first non alpha character in fixup_message+ * is ':'.+ */
I have a tough time figuring out what this comment is trying to say,
and I don't think I would have understood it if Junio had not already
explained earlier in this thread why this code is as complex as it is
(rather than using, say, skip_prefix()). Perhaps the entire comment
can be replaced with this:
I admit, this comment seems confusing...
Extract <option> (i.e. `amend`) from `--fixup=<option>:<commit>`,
if present. To avoid being fooled by a legitimate ":" in <commit>
(i.e. `--fixup="HEAD^{/^area: string}"`), <option> must be
composed of only alphabetic characters.
Not necessarily worth a re-roll.
.. and I think we can reword it as suggested by Junio in patch[v4
3/6], as it seems more clear.
quoted
+ size_t len = get_alpha_len(fixup_message);+ if (len && fixup_message[len] == ':') {+ fixup_message[len++] = '\0';+ fixup_commit = fixup_message + len;
An alternate -- just about as compact and perhaps more idiomatic --
way to write all this without introducing the new get_alpha_len()
function:
char *p = fixup_mesage;
while (isalpha(*p))
p++;
if (p > fixup_message && *p == ':') {
*p = '\0';
fixup_commit = p + 1;
Subjective and not at all worth a re-roll.
Earlier we had discussed[1] keeping a separate helper function, so
that it may re-use it later. But I agree above is easier to get and
compact so I think maybe it will be ok, for this patch series to
replace it with the above and remove the function.
[1] https://lore.kernel.org/git/xmqqpn0xdse8.fsf@gitster.g/
I agree, below reworded documentation is easier to understand. I
realized I am quite poor at documenting the things (Apology for
mistakes, but I learned a lot and will try my best from next time)
I really appreciate all the suggestions and guidance.
Thanks and Regards,
Charvi
On Thu, 11 Mar 2021 at 13:18, Eric Sunshine [off-list ref] wrote:
On Wed, Mar 10, 2021 at 2:45 PM Charvi Mendiratta [off-list ref] wrote:
@@ -86,11 +86,40 @@ OPTIONS+--fixup=[(amend|reword):]<commit>::+ Construct a new commit for use with `rebase --autosquash`,+ which fixes the specified commit. The plain form+ `--fixup=<commit>` creates a "fixup!" commit, that allows+ to fixup only the content of the specified commit and leave+ it's commit log message untouched. When used with `amend:`
s/it's/its/
quoted
+ or `reword:`, it creates "amend!" commit that is like "fixup!"+ commit but it allows to fixup both the content and the commit+ log message of the specified commit. The commit log message of+ the specified commit is fixed implicitly by replacing it with+ the "amend!" commit's message body upon `rebase --autosquash`.
The first half of this description is clear. The second half gets
bogged down and difficult to decipher. It also seems to claim that
"reword:" can change the content of <commit>, which isn't accurate at
the UI level (even if it happens to reflect the underlying
implementation). I might have written the above description like this:
Create a new commit which "fixes up" `<commit>` when applied with
`git rebase --autosquash`. Plain `--fixup=<commit>` creates a
"fixup!" commit which changes the content of `<commit>` but leaves
its log message untouched. `--fixup=amend:<commit>` is similar but
creates an "amend!" commit which also replaces the log message of
`<commit>` with the log message of the "amend!" commit.
`--fixup=reword:<commit>` creates an "amend!" commit which
replaces the log message of `<commit>` with its own log message
but makes no changes to the content of `<commit>`.
quoted
+The resulting "fixup!" commit message will be the subject line+from the specified commit with a prefix of "fixup!". Can be used+with additional commit message option `-m`.
This gives details without providing meaning. If I didn't already know
how this all works, I think I'd probably be mystified about what it is
trying to say. Providing context by mentioning `git rebase
--autosquash` would help explain the significance of "fixup!".
Similarly, it's not clear on the surface why this mentions `-m` at
all. I might have written it like this:
The commit created by plain `--fixup=<commit>` has a subject
composed of "fixup!" followed by the subject line from <commit>,
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 `<commit>` by `git rebase
--autosquash`.
quoted
+The `--fixup=amend:<commit>` form creates an "amend!" commit where+its commit message subject will be the subject line from the+specified commit with a prefix of "amend!" and the message body+will be commit log message of the specified commit. It also invokes+an editor seeded with the log message of the "amend!" commit to+allow to edit further. It refuses to create "amend!" commit if it's+commit message body is empty unless used with the+`--allow-empty-message` option.
This is reasonable, but does get into the weeds somewhat and uses
potentially unusual terms such as "seeded". It can be tightened up a
bit by building upon what was explained earlier for plain
`--fixup=<commit>`. To really round it out and give proper context for
understanding the purpose, it would also be helpful to explain how an
"amend!" commit is handled by `git rebase --autosquash`. I might have
written it like this:
The commit created by `--fixup=amend:<commit>` is similar but its
subject 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.
quoted
+The `--fixup=reword:<commit>` aliases `--fixup=amend:<commit> --only`+and it also creates an "amend!" commit, but here it records the same+tree as `HEAD`, i.e. it does not take any staged changes and only allows+to fixup the commit message of the specified commit. It will reword the+specified commit when it is rebased with `--autosquash`.
This gets too deep into the techno-speak by talking about "tree" and
`HEAD`. You can convey the same concept more simply by saying merely
that it creates an empty commit. I might have written it like this:
`--fixup=reword:<commit>` is shorthand for `--fixup=amend:<commit>
--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 `<commit>`
without making any other changes.
quoted
+Also, after fixing the commit using `--fixup`, with or without option+and rebased with `--autosquash`, the authorship of the original commit+remains unchanged. See linkgit:git-rebase[1] for details.
It sounds odd to start this sentence with "also". Perhaps:
Neither "fixup!" nor "amend!" commits change authorship of
`<commit>` when applied by `git rebase --autosquash`.
On Thu, 11 Mar 2021 at 13:36, Eric Sunshine [off-list ref] wrote:
[...]
Thanks for re-rolling. I left some relatively minor comments on patch
[2/6] and some substantial documentation-related comments on [6/6].
(If you take my suggested documentation rewrites verbatim, you can
have my Signed-off-by: along with them.)
Sure, I will add the Signed-off-by.
Thanks for all the suggestions, I will fix them and update in the next version.
One final request: Please consider using the --range-diff or
--interdiff options or both when preparing your series with
git-format-patch. Doing so can really help reviewers focus on the
changes made from re-roll to re-roll without having to painstakingly
re-read each patch in the series -- even if a patch didn't change --
thus saving time.
Okay, I will add it next time.
Thanks and Regards,
Charvi
From: Eric Sunshine <hidden> Date: 2021-03-11 17:09:05
On Thu, Mar 11, 2021 at 10:24 AM Charvi Mendiratta [off-list ref] wrote:
On Thu, 11 Mar 2021 at 11:55, Eric Sunshine [off-list ref] wrote:
quoted
quoted
+ size_t len = get_alpha_len(fixup_message);+ if (len && fixup_message[len] == ':') {+ fixup_message[len++] = '\0';+ fixup_commit = fixup_message + len;
An alternate -- just about as compact and perhaps more idiomatic --
way to write all this without introducing the new get_alpha_len()
function:
char *p = fixup_mesage;
while (isalpha(*p))
p++;
if (p > fixup_message && *p == ':') {
*p = '\0';
fixup_commit = p + 1;
Earlier we had discussed[1] keeping a separate helper function, so
that it may re-use it later. But I agree above is easier to get and
compact so I think maybe it will be ok, for this patch series to
replace it with the above and remove the function.
I don't have strong feelings one way or the other whether you should
use a function or inline it as I showed above, and since our aim is to
land this series rather than endlessly re-rolling it, let's not spend
a lot of cycles worrying about it.
The one thing that does bother me, however, is the name of the
function, get_alpha_len(), which tells you (somewhat) literally what
it does but doesn't convey to the reader its actual purpose (which is
something we should strive for when naming functions and variables).
In that previous discussion you referenced, Junio mentioned that a
future sub-option might want to have punctuation in its name. If that
ever comes about, then the name get_alpha_len() no longer makes sense
and needs to be renamed so it doesn't become a lie. Giving the
function a better name up front would not only help readers now to
understand what is going on, but would also help down the road when or
if punctuation (or numbers or whatnot) become valid in a sub-option
name. suboption_length() is one possibility. With a slight semantic
change, skip_suboption() or latch_suboption() are other possibilities.
Or, if you were to open-code the loop as I did above, then you might
have a function named is_suboption_char() and use that in place of
isalpha().
So, if you do re-roll, I wouldn't mind seeing a better name for the
function; but the rest is subjective and not worth spending time
refining unless you actually feel that one style has a clear advantage
over others.
On Thu, 11 Mar 2021 at 22:38, Eric Sunshine [off-list ref] wrote:
On Thu, Mar 11, 2021 at 10:24 AM Charvi Mendiratta [off-list ref] wrote:
quoted
On Thu, 11 Mar 2021 at 11:55, Eric Sunshine [off-list ref] wrote:
quoted
quoted
+ size_t len = get_alpha_len(fixup_message);+ if (len && fixup_message[len] == ':') {+ fixup_message[len++] = '\0';+ fixup_commit = fixup_message + len;
An alternate -- just about as compact and perhaps more idiomatic --
way to write all this without introducing the new get_alpha_len()
function:
char *p = fixup_mesage;
while (isalpha(*p))
p++;
if (p > fixup_message && *p == ':') {
*p = '\0';
fixup_commit = p + 1;
Earlier we had discussed[1] keeping a separate helper function, so
that it may re-use it later. But I agree above is easier to get and
compact so I think maybe it will be ok, for this patch series to
replace it with the above and remove the function.
I don't have strong feelings one way or the other whether you should
use a function or inline it as I showed above, and since our aim is to
land this series rather than endlessly re-rolling it, let's not spend
a lot of cycles worrying about it.
The one thing that does bother me, however, is the name of the
function, get_alpha_len(), which tells you (somewhat) literally what
it does but doesn't convey to the reader its actual purpose (which is
something we should strive for when naming functions and variables).
In that previous discussion you referenced, Junio mentioned that a
future sub-option might want to have punctuation in its name. If that
ever comes about, then the name get_alpha_len() no longer makes sense
and needs to be renamed so it doesn't become a lie. Giving the
function a better name up front would not only help readers now to
understand what is going on, but would also help down the road when or
if punctuation (or numbers or whatnot) become valid in a sub-option
name. suboption_length() is one possibility. With a slight semantic
change, skip_suboption() or latch_suboption() are other possibilities.
Or, if you were to open-code the loop as I did above, then you might
have a function named is_suboption_char() and use that in place of
isalpha().
So, if you do re-roll, I wouldn't mind seeing a better name for the
function; but the rest is subjective and not worth spending time
refining unless you actually feel that one style has a clear advantage
over others.
Okay, I will rename the get_alpha_len() to skip_suboption().
Thanks and Regards,
Charvi
`git commit --fixup=reword:<commit>` aliases
`--fixup=amend:<commit> --only`, where it creates an empty "amend!"
commit that will reword <commit> without changing its contents when
it is rebased with `--autosquash`.
Mentored-by: Christian Couder [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Helped-by: Junio C Hamano [off-list ref]
Helped-by: Eric Sunshine [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
builtin/commit.c | 31 +++++++++++++++++++++++++------
1 file changed, 25 insertions(+), 6 deletions(-)
@@ -1188,6 +1188,19 @@ static void finalize_deferred_config(struct wt_status *s)s->ahead_behind_flags=AHEAD_BEHIND_FULL;}+staticvoidcheck_fixup_reword_options(intargc,constchar*argv[]){+if(whence!=FROM_COMMIT){+if(whence==FROM_MERGE)+die(_("You are in the middle of a merge -- cannot reword."));+elseif(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"));+}+/* returns the length of intial segment of alpha characters only */staticsize_tskip_suboption(char*fixup_message){constcharalphas[]="abcdefghijklmnopqrstuvwxyz";
@@ -1575,10 +1594,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-*onlytranslate<commit>.+*TRANSLATORS:Leave"[(amend|reword):]"as-is,+*andonlytranslate<commit>.*/-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")),
This patch series teaches `git commit --fixup` to create "amend!" commit
as an alternative that works with `git rebase --autosquash`. It allows to
fixup both the content and the commit message of the specified commit.
Here we add two suboptions to the `--fixup`, first `amend` suboption that
creates an "amend!" commit. It takes the staged changes and also allows to
edit the commit message of the commit we are fixing.
Example usuage:
git commit --fixup=amend:<commit>
Secondly, `reword` suboption that creates an empty "amend!" commit i.e it
ignores the staged changes and only allows to reword/edit the commit message
of the commit we are fixing. `--fixup=reword:<commit>` is a short-hand of
`--fixup=amend:<commit> --only`.
Example usuage:
git commit --fixup=reword:<commit>
** This work is rebased on the top of cm/rebase-i-updates.
Charvi Mendiratta (6):
sequencer: export and rename subject_length()
commit: add amend suboption to --fixup to create amend! commit
commit: add a reword suboption to --fixup
t7500: add tests for --fixup=[amend|reword] options
t3437: use --fixup with options to create amend! commit
doc/git-commit: add documentation for fixup=[amend|reword] options
Documentation/git-commit.txt | 45 +++++-
Documentation/git-rebase.txt | 21 +--
builtin/commit.c | 126 +++++++++++++++--
commit.c | 14 ++
commit.h | 3 +
sequencer.c | 16 +--
t/t3437-rebase-fixup-options.sh | 30 +---
t/t7500-commit-template-squash-signoff.sh | 159 ++++++++++++++++++++++
8 files changed, 346 insertions(+), 68 deletions(-)
Range-diff against v4:
-: ---------- > 1: a2e89540ec sequencer: export and rename subject_length()
1: 492ab8ec08 ! 2: be2808a255 commit: add amend suboption to --fixup to create amend! commit
@@ builtin/commit.c: static void adjust_comment_line_char(const struct strbuf *sb)
comment_line_char = *p;
}
-+static int prepare_amend_commit(struct commit *commit, struct strbuf *sb,
-+ struct pretty_print_context *ctx) {
-+
++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);
@@ builtin/commit.c: static void adjust_comment_line_char(const struct strbuf *sb)
+ fmt = starts_with(subject, "amend!") ? "%b" : "%B";
+ format_commit_message(commit, fmt, sb, ctx);
+ unuse_commit_buffer(commit, buffer);
-+ return 0;
+}
+
static int prepare_to_commit(const char *index_file, const char *prefix,
@@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const cha
struct pretty_print_context ctx = {0};
struct commit *commit;
- commit = lookup_commit_reference_by_name(fixup_message);
-+ char *fmt = xstrfmt("%s! %%s\n\n", fixup_prefix);
++ char *fmt;
+ commit = lookup_commit_reference_by_name(fixup_commit);
if (!commit)
- die(_("could not lookup commit %s"), fixup_message);
@@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const cha
- &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";
@@ builtin/commit.c: static void finalize_deferred_config(struct wt_status *s)
}
+/* returns the length of intial segment of alpha characters only */
-+static size_t get_alpha_len(char *fixup_message) {
++static size_t skip_suboption(char *fixup_message) {
+ const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
+ return strspn(fixup_message, alphas);
+}
@@ builtin/commit.c: static int parse_and_validate_options(int argc, const char *ar
+
+ if (fixup_message) {
+ /*
-+ * To check if fixup_message that contains ':' is a commit
-+ * reference for example: --fixup="HEAD^{/^area: string}" or
-+ * a suboption of `--fixup`.
++ * 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=<suboption>:<arg>.
+ *
-+ * As `amend` suboption contains only alpha character.
-+ * So check if first non alpha character in fixup_message
-+ * is ':'.
++ * Otherwise, we are dealing with --fixup=<commit>.
+ */
-+ size_t len = get_alpha_len(fixup_message);
++ size_t len = skip_suboption(fixup_message);
+ if (len && fixup_message[len] == ':') {
+ fixup_message[len++] = '\0';
+ fixup_commit = fixup_message + len;
2: a20b85c3a1 ! 3: f6217338c1 commit: add a reword suboption to --fixup
@@ builtin/commit.c: static void finalize_deferred_config(struct wt_status *s)
+}
+
/* returns the length of intial segment of alpha characters only */
- static size_t get_alpha_len(char *fixup_message) {
+ static size_t skip_suboption(char *fixup_message) {
const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
@@ builtin/commit.c: static int parse_and_validate_options(int argc, const char *argv[],
- * reference for example: --fixup="HEAD^{/^area: string}" or
- * a suboption of `--fixup`.
+ * 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=<suboption>:<arg>.
++ * name like `amend` or `reword`, or a misspelt suboption
++ * name. In either case, we treat it as
++ * --fixup=<suboption>:<arg>.
*
-- * As `amend` suboption contains only alpha character.
-- * So check if first non alpha character in fixup_message
-- * is ':'.
-+ * As `amend`/`reword` suboptions contains only alpha
-+ * characters. So check if first non alpha character
-+ * in fixup_message is ':'.
+ * Otherwise, we are dealing with --fixup=<commit>.
*/
- size_t len = get_alpha_len(fixup_message);
+@@ builtin/commit.c: static int parse_and_validate_options(int argc, const char *argv[],
if (len && fixup_message[len] == ':') {
fixup_message[len++] = '\0';
fixup_commit = fixup_message + len;
3: 41297d7e8e = 4: 1a127dc0b3 t7500: add tests for --fixup=[amend|reword] options
4: 7ae75dc27d = 5: be6f4fa0d1 t3437: use --fixup with options to create amend! commit
5: 533fed36ca ! 6: 79c098df2c doc/git-commit: add documentation for fixup=[amend|reword] options
@@ Commit message
Mentored-by: Christian Couder [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Helped-by: Junio C Hamano [off-list ref]
- Helped-by: Eric Sunshine [off-list ref]
+ Signed-off-by: Eric Sunshine [off-list ref]
Signed-off-by: Charvi Mendiratta [off-list ref]
## Documentation/git-commit.txt ##
@@ Documentation/git-commit.txt: OPTIONS
- commit with a prefix of "fixup! ". See linkgit:git-rebase[1]
- for details.
+--fixup=[(amend|reword):]<commit>::
-+ Construct a new commit for use with `rebase --autosquash`,
-+ which fixes the specified commit. The plain form
-+ `--fixup=<commit>` creates a "fixup!" commit, that allows
-+ to fixup only the content of the specified commit and leave
-+ it's commit log message untouched. When used with `amend:`
-+ or `reword:`, it creates "amend!" commit that is like "fixup!"
-+ commit but it allows to fixup both the content and the commit
-+ log message of the specified commit. The commit log message of
-+ the specified commit is fixed implicitly by replacing it with
-+ the "amend!" commit's message body upon `rebase --autosquash`.
++ Create a new commit which "fixes up" `<commit>` when applied with
++ `git rebase --autosquash`. Plain `--fixup=<commit>` creates a
++ "fixup!" commit which changes the content of `<commit>` but leaves
++ its log message untouched. `--fixup=amend:<commit>` is similar but
++ creates an "amend!" commit which also replaces the log message of
++ `<commit>` with the log message of the "amend!" commit.
++ `--fixup=reword:<commit>` creates an "amend!" commit which
++ replaces the log message of `<commit>` with its own log message
++ but makes no changes to the content of `<commit>`.
++
-+The resulting "fixup!" commit message will be the subject line
-+from the specified commit with a prefix of "fixup!". Can be used
-+with additional commit message option `-m`.
++The commit created by plain `--fixup=<commit>` has a subject
++composed of "fixup!" followed by the subject line from <commit>,
++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 `<commit>` by
++`git rebase --autosquash`.
++
-+The `--fixup=amend:<commit>` form creates an "amend!" commit where
-+its commit message subject will be the subject line from the
-+specified commit with a prefix of "amend!" and the message body
-+will be commit log message of the specified commit. It also invokes
-+an editor seeded with the log message of the "amend!" commit to
-+allow to edit further. It refuses to create "amend!" commit if it's
-+commit message body is empty unless used with the
-+`--allow-empty-message` option.
++The commit created by `--fixup=amend:<commit>` is similar but its
++subject 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.
++
-+The `--fixup=reword:<commit>` aliases `--fixup=amend:<commit> --only`
-+and it also creates an "amend!" commit, but here it records the same
-+tree as `HEAD`, i.e. it does not take any staged changes and only allows
-+to fixup the commit message of the specified commit. It will reword the
-+specified commit when it is rebased with `--autosquash`.
++`--fixup=reword:<commit>` is shorthand for `--fixup=amend:<commit>
++--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 `<commit>`
++without making any other changes.
++
-+Also, after fixing the commit using `--fixup`, with or without option
-+and rebased with `--autosquash`, the authorship of the original commit
-+remains unchanged. See linkgit:git-rebase[1] for details.
++Neither "fixup!" nor "amend!" commits change authorship of
++`<commit>` when applied by `git rebase --autosquash`.
++See linkgit:git-rebase[1] for details.
--squash=<commit>::
Construct a commit message for use with `rebase --autosquash`.
--
2.29.0.rc1
`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 [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Helped-by: Junio C Hamano [off-list ref]
Helped-by: Eric Sunshine [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
builtin/commit.c | 107 ++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 97 insertions(+), 10 deletions(-)
@@ -745,15 +763,33 @@ static int prepare_to_commit(const char *index_file, const char *prefix,}elseif(fixup_message){structpretty_print_contextctx={0};structcommit*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`commitmessageoptionischeckedhere,as+*itsupports`--fixup`toappendthecommitmessage.+*+*Theothercommitmessageoptions`-c`/`-C`/`-F`are+*incompatiblewithalltheformsof`--fixup`and+*havealreadyerroredoutwhileparsingthe`gitcommit`+*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);+}}elseif(!stat(git_path_merge_msg(the_repository),&statbuf)){size_tmerge_msg_start;
@@ -1152,6 +1188,12 @@ static void finalize_deferred_config(struct wt_status *s)s->ahead_behind_flags=AHEAD_BEHIND_FULL;}+/* returns the length of intial segment of alpha characters only */+staticsize_tskip_suboption(char*fixup_message){+constcharalphas[]="abcdefghijklmnopqrstuvwxyz";+returnstrspn(fixup_message,alphas);+}+staticintparse_and_validate_options(intargc,constchar*argv[],conststructoption*options,constchar*constusage[],
@@ -1170,7 +1212,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 +1269,34 @@ 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){+/*+*Welimit--fixup'ssuboptionstoonlyalphacharacters.+*Ifthefirstcharacterafterarunofalphaiscolon,+*thenthepartbeforethecolonmaybeaknownsuboption+*name`amend`oramisspeltsuboptionname.Inthiscase,+*wetreatitas--fixup=<suboption>:<arg>.+*+*Otherwise,wearedealingwith--fixup=<commit>.+*/+size_tlen=skip_suboption(fixup_message);+if(len&&fixup_message[len]==':'){+fixup_message[len++]='\0';+fixup_commit=fixup_message+len;+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 +1574,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+*onlytranslate<commit>.+*/+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 +1737,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){+structstrbufbody=STRBUF_INIT;+size_tlen=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){constchar*exclude_gpgsig[3]={"gpgsig","gpgsig-sha256",NULL};extra=read_commit_extra_headers(current_head,exclude_gpgsig);
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 [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Helped-by: Eric Sunshine [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
commit.c | 14 ++++++++++++++
commit.h | 3 +++
sequencer.c | 16 +---------------
3 files changed, 18 insertions(+), 15 deletions(-)
@@ -1724,20 +1724,6 @@ enum todo_item_flags {TODO_EDIT_FIXUP_MSG=(1<<2),};-staticsize_tsubject_length(constchar*body)-{-constchar*p=body;-while(*p){-constchar*next=skip_blank_lines(p);-if(next!=p)-break;-p=strchrnul(p,'\n');-if(*p)-p++;-}-returnp-body;-}-staticconstcharfirst_commit_msg_str[]=N_("This is the 1st commit message:");staticconstcharnth_commit_msg_fmt[]=N_("This is the commit message #%d:");staticconstcharskip_first_commit_msg_str[]=N_("The 1st commit message will be skipped:");
@@ -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.expectactual=commit_msg_is.actual
@@ -279,6 +281,163 @@ test_expect_success 'commit --fixup -m"something" -m"extra"' ' extra"'+get_commit_msg(){+rev="$1"&&+gitlog-1--pretty=format:"%B""$rev"+}++test_expect_success'commit --fixup=amend: creates amend! commit''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!$(gitlog-1--format=%sHEAD~)++$(get_commit_msgHEAD~)++edited+EOF+(+set_fake_editor&&+FAKE_COMMIT_AMEND="edited"\+gitcommit--fixup=amend:HEAD~+)&&+get_commit_msgHEAD>actual&&+test_cmpexpectedactual+'++test_expect_success'--fixup=amend: --only ignores staged changes''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!$(gitlog-1--format=%sHEAD~)++$(get_commit_msgHEAD~)++edited+EOF+(+set_fake_editor&&+FAKE_COMMIT_AMEND="edited"\+gitcommit--fixup=amend:HEAD~--only+)&&+get_commit_msgHEAD>actual&&+test_cmpexpectedactual&&+test_cmp_revHEAD@{1}^{tree}HEAD^{tree}&&+test_cmp_revHEAD@{1}HEAD^&&+test_expect_code1gitdiff--cached--exit-code&&+gitcat-fileblob:foo>actual&&+test_cmpfooactual+'++test_expect_success'--fixup=reword: ignores staged changes''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!$(gitlog-1--format=%sHEAD~)++$(get_commit_msgHEAD~)++edited+EOF+(+set_fake_editor&&+FAKE_COMMIT_AMEND="edited"\+gitcommit--fixup=reword:HEAD~+)&&+get_commit_msgHEAD>actual&&+test_cmpexpectedactual&&+test_cmp_revHEAD@{1}^{tree}HEAD^{tree}&&+test_cmp_revHEAD@{1}HEAD^&&+test_expect_code1gitdiff--cached--exit-code&&+gitcat-fileblob:foo>actual&&+test_cmpfooactual+'++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_failgitcommit--fixup=reword:HEAD~-m"reword commit message"2>actual&&+test_cmpexpectactual+'++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_failgitcommit--fixup=amend:HEAD~-m"amend commit message"2>actual&&+test_cmpexpectactual+'++test_expect_success'consecutive amend! commits remove amend! line from commit msg body''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!amend!$(gitlog-1--format=%sHEAD~)++$(get_commit_msgHEAD~)++edited1++edited2+EOF+echo"reword new commit message">actual&&+(+set_fake_editor&&+FAKE_COMMIT_AMEND="edited 1"\+gitcommit--fixup=reword:HEAD~&&+FAKE_COMMIT_AMEND="edited 2"\+gitcommit--fixup=reword:HEAD+)&&+get_commit_msgHEAD>actual&&+test_cmpexpectedactual+'++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_failenvFAKE_COMMIT_MESSAGE="amend! target message subject line"\+gitcommit--fixup=amend:HEAD~2>actual+)&&+test_cmpexpectedactual+'++test_expect_success'amend! commit allows empty commit msg body with --allow-empty-message''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!$(gitlog-1--format=%sHEAD~)+EOF+(+set_fake_editor&&+FAKE_COMMIT_MESSAGE="amend! target message subject line"\+gitcommit--fixup=amend:HEAD~--allow-empty-message&&+get_commit_msgHEAD>actual+)&&+test_cmpexpectedactual+'++test_fixup_reword_opt(){+test_expect_successC_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_failgitcommit--fixup=reword:HEAD~$12>actual&&+test_cmpexpectactual+"+}++foroptin--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_failgitcommit--fixup=reword:HEAD~--foo2>actual&&+test_cmpexpectactual+'++test_expect_success'--fixup=reword: -F give error message''+echo"fatal: Only one of -c/-C/-F/--fixup can be used.">expect&&+test_must_failgitcommit--fixup=reword:HEAD~-Fmsg2>actual&&+test_cmpexpectactual+' test_expect_success'commit --squash works with -F''commit_for_rebase_autosquash_setup&&
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 [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
t/t3437-rebase-fixup-options.sh | 30 +++---------------------------
1 file changed, 3 insertions(+), 27 deletions(-)
@@ -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=<commit>::- 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):]<commit>::+ Create a new commit which "fixes up" `<commit>` when applied with+ `git rebase --autosquash`. Plain `--fixup=<commit>` creates a+ "fixup!" commit which changes the content of `<commit>` but leaves+ its log message untouched. `--fixup=amend:<commit>` is similar but+ creates an "amend!" commit which also replaces the log message of+ `<commit>` with the log message of the "amend!" commit.+ `--fixup=reword:<commit>` creates an "amend!" commit which+ replaces the log message of `<commit>` with its own log message+ but makes no changes to the content of `<commit>`.+++The commit created by plain `--fixup=<commit>` has a subject+composed of "fixup!" followed by the subject line from <commit>,+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 `<commit>` by+`git rebase --autosquash`.+++The commit created by `--fixup=amend:<commit>` is similar but its+subject 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.+++`--fixup=reword:<commit>` is shorthand for `--fixup=amend:<commit>+--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 `<commit>`+without making any other changes.+++Neither "fixup!" nor "amend!" commits change authorship of+`<commit>` when applied by `git rebase --autosquash`.+See linkgit:git-rebase[1] for details. --squash=<commit>:: Construct a commit message for use with `rebase --autosquash`.
@@ -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
From: Eric Sunshine <hidden> Date: 2021-03-14 01:27:23
On Sat, Mar 13, 2021 at 8:43 AM Charvi Mendiratta [off-list ref] wrote:
Helped-by: Junio C Hamano [off-list ref]
Signed-off-by: Eric Sunshine <redacted>
Signed-off-by: Charvi Mendiratta <redacted>
It may have been more accurate to retain the Helped-by: with my name
while adding the Signed-off-by: (which I offered in case you
incorporated my significant rewrites), rather than replacing the
Helped-by: altogether. Not worth a re-roll, though.
@@ -86,11 +86,44 @@ OPTIONS+--fixup=[(amend|reword):]<commit>::+ Create a new commit which "fixes up" `<commit>` when applied with+ `git rebase --autosquash`. Plain `--fixup=<commit>` creates a+ "fixup!" commit which changes the content of `<commit>` but leaves+ its log message untouched. `--fixup=amend:<commit>` is similar but+ creates an "amend!" commit which also replaces the log message of+ `<commit>` with the log message of the "amend!" commit.+ `--fixup=reword:<commit>` creates an "amend!" commit which+ replaces the log message of `<commit>` with its own log message+ but makes no changes to the content of `<commit>`.+++The commit created by plain `--fixup=<commit>` has a subject+composed of "fixup!" followed by the subject line from <commit>,+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 `<commit>` by+`git rebase --autosquash`.+++The commit created by `--fixup=amend:<commit>` is similar but its+subject 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.+++`--fixup=reword:<commit>` is shorthand for `--fixup=amend:<commit>+--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 `<commit>`+without making any other changes.+++Neither "fixup!" nor "amend!" commits change authorship of+`<commit>` when applied by `git rebase --autosquash`.+See linkgit:git-rebase[1] for details.
I see that you took my entire rewrite verbatim. That's fine. My bias
is probably showing, but I do now find this documentation patch easier
to understand.
Thanks.
From: Eric Sunshine <hidden> Date: 2021-03-14 01:33:23
On Sat, Mar 13, 2021 at 8:42 AM Charvi Mendiratta [off-list ref] wrote:
quoted hunk
`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.
Signed-off-by: Charvi Mendiratta <redacted>
---
diff --git a/builtin/commit.c b/builtin/commit.c
@@ -1152,6 +1188,12 @@ static void finalize_deferred_config(struct wt_status *s)+/* returns the length of intial segment of alpha characters only */+static size_t skip_suboption(char *fixup_message) {+ const char alphas[] = "abcdefghijklmnopqrstuvwxyz";+ return strspn(fixup_message, alphas);+}
With the function name change, the comment above the function has
become outdated. Instead, it should instead explain the function's
purpose at a high-level (not at the low-level of skipping over
alpha-only characters -- especially since the alpha-only restriction
may change in the future). By high-level, I mean talking about
skipping past a token which is likely to be a --fixup suboption, and
giving the reason why the set of characters which comprise the token
is limited (perhaps citing the example Junio had given earlier in
which the code should not mistakenly scan too far to a legitimate ":"
in <commit>).
quoted hunk
@@ -1227,6 +1269,34 @@ static int parse_and_validate_options(int argc, const char *argv[],+ 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=<suboption>:<arg>.+ *+ * Otherwise, we are dealing with --fixup=<commit>.+ */
This comment is also now out of date following the function name
change. It no longer makes sense for this comment to talk about
skipping alpha-only characters; it should be written at a semantically
higher level, talking instead about skipping the suboption prefix (or
something) since that's what the function call is all about.
On Sun, 14 Mar 2021 at 06:40, Eric Sunshine [off-list ref] wrote:
On Sat, Mar 13, 2021 at 8:43 AM Charvi Mendiratta [off-list ref] wrote:
quoted
Helped-by: Junio C Hamano [off-list ref]
Signed-off-by: Eric Sunshine <redacted>
Signed-off-by: Charvi Mendiratta <redacted>
It may have been more accurate to retain the Helped-by: with my name
while adding the Signed-off-by: (which I offered in case you
incorporated my significant rewrites), rather than replacing the
Helped-by: altogether. Not worth a re-roll, though.
This patch series teaches `git commit --fixup` to create "amend!" commit
as an alternative that works with `git rebase --autosquash`. It allows to
fixup both the content and the commit message of the specified commit.
Here we add two suboptions to the `--fixup`, first `amend` suboption that
creates an "amend!" commit. It takes the staged changes and also allows to
edit the commit message of the commit we are fixing.
Example usuage:
git commit --fixup=amend:<commit>
Secondly, `reword` suboption that creates an empty "amend!" commit i.e it
ignores the staged changes and only allows to reword/edit the commit message
of the commit we are fixing. `--fixup=reword:<commit>` is a short-hand of
`--fixup=amend:<commit> --only`.
Example usuage:
git commit --fixup=reword:<commit>
** This work is rebased on the top of cm/rebase-i-updates.
Charvi Mendiratta (6):
sequencer: export and rename subject_length()
commit: add amend suboption to --fixup to create amend! commit
commit: add a reword suboption to --fixup
t7500: add tests for --fixup=[amend|reword] options
t3437: use --fixup with options to create amend! commit
doc/git-commit: add documentation for fixup=[amend|reword] options
Documentation/git-commit.txt | 45 +++++-
Documentation/git-rebase.txt | 21 +--
builtin/commit.c | 122 +++++++++++++++--
commit.c | 14 ++
commit.h | 3 +
sequencer.c | 16 +--
t/t3437-rebase-fixup-options.sh | 30 +---
t/t7500-commit-template-squash-signoff.sh | 159 ++++++++++++++++++++++
8 files changed, 342 insertions(+), 68 deletions(-)
Range-diff against v5:
-: ---------- > 1: a2e89540ec sequencer: export and rename subject_length()
1: be2808a255 ! 2: f3cdb3eb1e commit: add amend suboption to --fixup to create amend! commit
@@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const cha
} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
size_t merge_msg_start;
-@@ builtin/commit.c: static void finalize_deferred_config(struct wt_status *s)
- s->ahead_behind_flags = AHEAD_BEHIND_FULL;
- }
-
-+/* returns the length of intial segment of alpha characters only */
-+static size_t skip_suboption(char *fixup_message) {
-+ const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
-+ return strspn(fixup_message, alphas);
-+}
-+
- static int parse_and_validate_options(int argc, const char *argv[],
- const struct option *options,
- const char * const usage[],
@@ builtin/commit.c: 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"));
@@ builtin/commit.c: static int parse_and_validate_options(int argc, const char *ar
+ *
+ * Otherwise, we are dealing with --fixup=<commit>.
+ */
-+ size_t len = skip_suboption(fixup_message);
-+ if (len && fixup_message[len] == ':') {
-+ fixup_message[len++] = '\0';
-+ fixup_commit = fixup_message + len;
++ 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;
2: f6217338c1 ! 3: ded339706f commit: add a reword suboption to --fixup
@@ builtin/commit.c: static void finalize_deferred_config(struct wt_status *s)
+ die(_("reword option of --fixup is mutually exclusive with --patch/--interactive/--all/--include/--only"));
+}
+
- /* returns the length of intial segment of alpha characters only */
- static size_t skip_suboption(char *fixup_message) {
- const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
+ static int parse_and_validate_options(int argc, const char *argv[],
+ const struct option *options,
+ const char * const usage[],
@@ builtin/commit.c: 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,
@@ builtin/commit.c: static int parse_and_validate_options(int argc, const char *ar
* Otherwise, we are dealing with --fixup=<commit>.
*/
@@ builtin/commit.c: static int parse_and_validate_options(int argc, const char *argv[],
- if (len && fixup_message[len] == ':') {
- fixup_message[len++] = '\0';
- fixup_commit = fixup_message + len;
+ 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)) {
3: 1a127dc0b3 = 4: ec6f3d5d7d t7500: add tests for --fixup=[amend|reword] options
4: be6f4fa0d1 = 5: 148087b133 t3437: use --fixup with options to create amend! commit
5: 79c098df2c ! 6: 2b750d305e doc/git-commit: add documentation for fixup=[amend|reword] options
@@ Commit message
Mentored-by: Christian Couder [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
+ Helped-by: Eric Sunshine [off-list ref]
Helped-by: Junio C Hamano [off-list ref]
Signed-off-by: Eric Sunshine [off-list ref]
Signed-off-by: Charvi Mendiratta [off-list ref]
--
2.29.0.rc1
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 [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Helped-by: Eric Sunshine [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
commit.c | 14 ++++++++++++++
commit.h | 3 +++
sequencer.c | 16 +---------------
3 files changed, 18 insertions(+), 15 deletions(-)
@@ -1724,20 +1724,6 @@ enum todo_item_flags {TODO_EDIT_FIXUP_MSG=(1<<2),};-staticsize_tsubject_length(constchar*body)-{-constchar*p=body;-while(*p){-constchar*next=skip_blank_lines(p);-if(next!=p)-break;-p=strchrnul(p,'\n');-if(*p)-p++;-}-returnp-body;-}-staticconstcharfirst_commit_msg_str[]=N_("This is the 1st commit message:");staticconstcharnth_commit_msg_fmt[]=N_("This is the commit message #%d:");staticconstcharskip_first_commit_msg_str[]=N_("The 1st commit message will be skipped:");
`git commit --fixup=reword:<commit>` aliases
`--fixup=amend:<commit> --only`, where it creates an empty "amend!"
commit that will reword <commit> without changing its contents when
it is rebased with `--autosquash`.
Mentored-by: Christian Couder [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Helped-by: Junio C Hamano [off-list ref]
Helped-by: Eric Sunshine [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
builtin/commit.c | 31 +++++++++++++++++++++++++------
1 file changed, 25 insertions(+), 6 deletions(-)
@@ -1188,6 +1188,19 @@ static void finalize_deferred_config(struct wt_status *s)s->ahead_behind_flags=AHEAD_BEHIND_FULL;}+staticvoidcheck_fixup_reword_options(intargc,constchar*argv[]){+if(whence!=FROM_COMMIT){+if(whence==FROM_MERGE)+die(_("You are in the middle of a merge -- cannot reword."));+elseif(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"));+}+staticintparse_and_validate_options(intargc,constchar*argv[],conststructoption*options,constchar*constusage[],
@@ -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-*onlytranslate<commit>.+*TRANSLATORS:Leave"[(amend|reword):]"as-is,+*andonlytranslate<commit>.*/-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")),
`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 [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Helped-by: Junio C Hamano [off-list ref]
Helped-by: Eric Sunshine [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
builtin/commit.c | 103 ++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 93 insertions(+), 10 deletions(-)
@@ -745,15 +763,33 @@ static int prepare_to_commit(const char *index_file, const char *prefix,}elseif(fixup_message){structpretty_print_contextctx={0};structcommit*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`commitmessageoptionischeckedhere,as+*itsupports`--fixup`toappendthecommitmessage.+*+*Theothercommitmessageoptions`-c`/`-C`/`-F`are+*incompatiblewithalltheformsof`--fixup`and+*havealreadyerroredoutwhileparsingthe`gitcommit`+*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);+}}elseif(!stat(git_path_merge_msg(the_repository),&statbuf)){size_tmerge_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){+/*+*Welimit--fixup'ssuboptionstoonlyalphacharacters.+*Ifthefirstcharacterafterarunofalphaiscolon,+*thenthepartbeforethecolonmaybeaknownsuboption+*name`amend`oramisspeltsuboptionname.Inthiscase,+*wetreatitas--fixup=<suboption>:<arg>.+*+*Otherwise,wearedealingwith--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)){+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+*onlytranslate<commit>.+*/+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){+structstrbufbody=STRBUF_INIT;+size_tlen=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){constchar*exclude_gpgsig[3]={"gpgsig","gpgsig-sha256",NULL};extra=read_commit_extra_headers(current_head,exclude_gpgsig);
@@ -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.expectactual=commit_msg_is.actual
@@ -279,6 +281,163 @@ test_expect_success 'commit --fixup -m"something" -m"extra"' ' extra"'+get_commit_msg(){+rev="$1"&&+gitlog-1--pretty=format:"%B""$rev"+}++test_expect_success'commit --fixup=amend: creates amend! commit''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!$(gitlog-1--format=%sHEAD~)++$(get_commit_msgHEAD~)++edited+EOF+(+set_fake_editor&&+FAKE_COMMIT_AMEND="edited"\+gitcommit--fixup=amend:HEAD~+)&&+get_commit_msgHEAD>actual&&+test_cmpexpectedactual+'++test_expect_success'--fixup=amend: --only ignores staged changes''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!$(gitlog-1--format=%sHEAD~)++$(get_commit_msgHEAD~)++edited+EOF+(+set_fake_editor&&+FAKE_COMMIT_AMEND="edited"\+gitcommit--fixup=amend:HEAD~--only+)&&+get_commit_msgHEAD>actual&&+test_cmpexpectedactual&&+test_cmp_revHEAD@{1}^{tree}HEAD^{tree}&&+test_cmp_revHEAD@{1}HEAD^&&+test_expect_code1gitdiff--cached--exit-code&&+gitcat-fileblob:foo>actual&&+test_cmpfooactual+'++test_expect_success'--fixup=reword: ignores staged changes''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!$(gitlog-1--format=%sHEAD~)++$(get_commit_msgHEAD~)++edited+EOF+(+set_fake_editor&&+FAKE_COMMIT_AMEND="edited"\+gitcommit--fixup=reword:HEAD~+)&&+get_commit_msgHEAD>actual&&+test_cmpexpectedactual&&+test_cmp_revHEAD@{1}^{tree}HEAD^{tree}&&+test_cmp_revHEAD@{1}HEAD^&&+test_expect_code1gitdiff--cached--exit-code&&+gitcat-fileblob:foo>actual&&+test_cmpfooactual+'++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_failgitcommit--fixup=reword:HEAD~-m"reword commit message"2>actual&&+test_cmpexpectactual+'++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_failgitcommit--fixup=amend:HEAD~-m"amend commit message"2>actual&&+test_cmpexpectactual+'++test_expect_success'consecutive amend! commits remove amend! line from commit msg body''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!amend!$(gitlog-1--format=%sHEAD~)++$(get_commit_msgHEAD~)++edited1++edited2+EOF+echo"reword new commit message">actual&&+(+set_fake_editor&&+FAKE_COMMIT_AMEND="edited 1"\+gitcommit--fixup=reword:HEAD~&&+FAKE_COMMIT_AMEND="edited 2"\+gitcommit--fixup=reword:HEAD+)&&+get_commit_msgHEAD>actual&&+test_cmpexpectedactual+'++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_failenvFAKE_COMMIT_MESSAGE="amend! target message subject line"\+gitcommit--fixup=amend:HEAD~2>actual+)&&+test_cmpexpectedactual+'++test_expect_success'amend! commit allows empty commit msg body with --allow-empty-message''+commit_for_rebase_autosquash_setup&&+cat>expected<<-EOF&&+amend!$(gitlog-1--format=%sHEAD~)+EOF+(+set_fake_editor&&+FAKE_COMMIT_MESSAGE="amend! target message subject line"\+gitcommit--fixup=amend:HEAD~--allow-empty-message&&+get_commit_msgHEAD>actual+)&&+test_cmpexpectedactual+'++test_fixup_reword_opt(){+test_expect_successC_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_failgitcommit--fixup=reword:HEAD~$12>actual&&+test_cmpexpectactual+"+}++foroptin--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_failgitcommit--fixup=reword:HEAD~--foo2>actual&&+test_cmpexpectactual+'++test_expect_success'--fixup=reword: -F give error message''+echo"fatal: Only one of -c/-C/-F/--fixup can be used.">expect&&+test_must_failgitcommit--fixup=reword:HEAD~-Fmsg2>actual&&+test_cmpexpectactual+' test_expect_success'commit --squash works with -F''commit_for_rebase_autosquash_setup&&
@@ -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=<commit>::- 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):]<commit>::+ Create a new commit which "fixes up" `<commit>` when applied with+ `git rebase --autosquash`. Plain `--fixup=<commit>` creates a+ "fixup!" commit which changes the content of `<commit>` but leaves+ its log message untouched. `--fixup=amend:<commit>` is similar but+ creates an "amend!" commit which also replaces the log message of+ `<commit>` with the log message of the "amend!" commit.+ `--fixup=reword:<commit>` creates an "amend!" commit which+ replaces the log message of `<commit>` with its own log message+ but makes no changes to the content of `<commit>`.+++The commit created by plain `--fixup=<commit>` has a subject+composed of "fixup!" followed by the subject line from <commit>,+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 `<commit>` by+`git rebase --autosquash`.+++The commit created by `--fixup=amend:<commit>` is similar but its+subject 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.+++`--fixup=reword:<commit>` is shorthand for `--fixup=amend:<commit>+--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 `<commit>`+without making any other changes.+++Neither "fixup!" nor "amend!" commits change authorship of+`<commit>` when applied by `git rebase --autosquash`.+See linkgit:git-rebase[1] for details. --squash=<commit>:: Construct a commit message for use with `rebase --autosquash`.
@@ -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
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 [off-list ref]
Mentored-by: Phillip Wood [off-list ref]
Signed-off-by: Charvi Mendiratta <redacted>
---
t/t3437-rebase-fixup-options.sh | 30 +++---------------------------
1 file changed, 3 insertions(+), 27 deletions(-)