git@vger.kernel.org list mirror (unofficial, one of many)
 help / color / mirror / code / Atom feed
* [PATCH] [GSOC] commit: provides multiple common signatures
@ 2021-03-11  7:16 ZheNing Hu via GitGitGadget
  2021-03-11 15:03 ` Shourya Shukla
                   ` (2 more replies)
  0 siblings, 3 replies; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-11  7:16 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Similar to "Helped-by", "Reported-by", "Reviewed-by", "Mentored-by"
these signatures are often seen in git commit messages. After
referring to the simple implementation of `commit --signoff`
and `send-email -cc=" commiter <email>"`, I am considering
whether to provide multiple signature parameters from the
command line. I think this might help maintainers and
developers directly uses the shell to provide these signatures
instead of multiple times repetitive writing those trailers
each time.

To achieve this goal, i refactored the `append_signoff` design and
provided `append_message` and `append_message_string_list` interfaces,
providing new ways to generate those various signatures.

Users now can use `commit -H "helper <eamil>"` to generate "Helped-by" trailer,
`commit -R "reviewer <eamil>"` to generate "Reviewed-by" trailer,
`commit -r "reporter <eamil> "`to generate "Reported-by" trailer,
`commit -M "mentor <eamil>"` to generate "Mentored-by" trailer.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC] commit: provides multiple signatures from command line
    
    I don’t know if my idea will satisfy everyone, I'm also thinking about
    whether we can provide a more generalized version (I think this idea can
    be extended: because users and developers have other signature methods
    that they want, such as "Based-on-patch-by") I hope someone can give me
    pointers (on the correctness of ideas or codes)

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/901

 Documentation/git-commit.txt |  24 +++++++-
 builtin/commit.c             |  63 +++++++++++++++++++++
 sequencer.c                  |  40 +++++++++----
 sequencer.h                  |   4 ++
 t/t7502-commit-porcelain.sh  | 106 +++++++++++++++++++++++++++++++++++
 5 files changed, 226 insertions(+), 11 deletions(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 17150fa7eabe..e1b528d70c1a 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -14,7 +14,9 @@ SYNOPSIS
 	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
 	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
 	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
-	   [-S[<keyid>]] [--] [<pathspec>...]
+	   [-S[<keyid>]] [--] [<pathspec>...] [(-H|--helped-by)=<address>...]
+	   [(-R|--reviewed-by)=<address>...] [(-r|--reported-by)=<address>...]
+	   [(-M|--mentored)=<address>...]
 
 DESCRIPTION
 -----------
@@ -166,6 +168,26 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
 
 include::signoff-option.txt[]
 
+-H=<address>...::
+--helped-by=<address>...::
+	Add one or more `Helped-by` trailer by the committer at the end of the commit
+	log message.
+
+-R=<address>...::
+--reviewed-by=<address>...::
+	Add one or more `Reviewed-by` trailer by the committer at the end of the commit
+	log message.
+
+-r=<address>...::
+--reported-by=<address>...::
+	Add one or more `Reported-by` trailer by the committer at the end of the commit
+	log message.
+
+-M=<address>...::
+--mentored-by=<address>...::
+	Add one or more `Mentored-by` trailer by the committer at the end of the commit
+	log message.
+
 -n::
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
diff --git a/builtin/commit.c b/builtin/commit.c
index 739110c5a7f6..4b312af03181 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,6 +113,10 @@ static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit, *pathspec_from_file;
+static struct string_list helped_by = STRING_LIST_INIT_NODUP;
+static struct string_list mentored_by = STRING_LIST_INIT_NODUP;
+static struct string_list reviewed_by = STRING_LIST_INIT_NODUP;
+static struct string_list reported_by = STRING_LIST_INIT_NODUP;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -829,6 +833,20 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 	if (signoff)
 		append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0);
 
+	if(helped_by.items)
+		append_message_string_list(&sb, "Helped-by: ", &helped_by, ignore_non_trailer(sb.buf, sb.len), 0);
+	if(reviewed_by.items)
+		append_message_string_list(&sb, "Reviewed-by: ", &reviewed_by, ignore_non_trailer(sb.buf, sb.len), 0);
+	if(reported_by.items)
+		append_message_string_list(&sb, "Reported-by: ", &reported_by, ignore_non_trailer(sb.buf, sb.len), 0);
+	if(mentored_by.items)
+		append_message_string_list(&sb, "Mentored-by: ", &mentored_by, ignore_non_trailer(sb.buf, sb.len), 0);
+
+	string_list_clear(&helped_by, 0);
+	string_list_clear(&reviewed_by, 0);
+	string_list_clear(&reported_by, 0);
+	string_list_clear(&mentored_by, 0);
+
 	if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
 		die_errno(_("could not write commit template"));
 
@@ -1490,6 +1508,42 @@ static int git_commit_config(const char *k, const char *v, void *cb)
 	return git_status_config(k, v, s);
 }
 
+static int help_callback(const struct option *opt, const char *arg, int unset)
+{
+	if (unset)
+		string_list_clear(&helped_by, 0);
+	else
+		string_list_append(&helped_by, arg);
+	return 0;
+}
+
+static int review_callback(const struct option *opt, const char *arg, int unset)
+{
+	if (unset)
+		string_list_clear(&reviewed_by, 0);
+	else
+		string_list_append(&reviewed_by, arg);
+	return 0;
+}
+
+static int report_callback(const struct option *opt, const char *arg, int unset)
+{
+	if (unset)
+		string_list_clear(&reported_by, 0);
+	else
+		string_list_append(&reported_by, arg);
+	return 0;
+}
+
+static int mentor_callback(const struct option *opt, const char *arg, int unset)
+{
+	if (unset)
+		string_list_clear(&mentored_by, 0);
+	else
+		string_list_append(&mentored_by, arg);
+	return 0;
+}
+
 int cmd_commit(int argc, const char **argv, const char *prefix)
 {
 	static struct wt_status s;
@@ -1507,6 +1561,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK('H', "helped-by", NULL, N_("email"), N_("add a Helped-by trailer"), help_callback),
+		OPT_CALLBACK('r', "reported-by", NULL, N_("email"), N_("add a Reported-by trailer"), report_callback),
+		OPT_CALLBACK('R', "reviewed-by", NULL, N_("email"), N_("add a Reviewed-by trailer"), review_callback),
+		OPT_CALLBACK('M', "mentored-by", NULL, N_("email"), N_("add a Mentored-by trailer"), mentor_callback),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
@@ -1561,6 +1619,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	struct commit_extra_header *extra = NULL;
 	struct strbuf err = STRBUF_INIT;
 
+	helped_by.strdup_strings = 1;
+	reviewed_by.strdup_strings = 1;
+	reported_by.strdup_strings = 1;
+	mentored_by.strdup_strings = 1;
+
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage_with_options(builtin_commit_usage, builtin_commit_options);
 
diff --git a/sequencer.c b/sequencer.c
index d2332d3e1787..528daf9df252 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4668,16 +4668,12 @@ int sequencer_pick_revisions(struct repository *r,
 	return res;
 }
 
-void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
+void append_message(struct strbuf *msgbuf, struct strbuf *sob,
+			size_t ignore_footer, unsigned flag)
 {
 	unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP;
-	struct strbuf sob = STRBUF_INIT;
 	int has_footer;
 
-	strbuf_addstr(&sob, sign_off_header);
-	strbuf_addstr(&sob, fmt_name(WANT_COMMITTER_IDENT));
-	strbuf_addch(&sob, '\n');
-
 	if (!ignore_footer)
 		strbuf_complete_line(msgbuf);
 
@@ -4685,11 +4681,11 @@ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
 	 * If the whole message buffer is equal to the sob, pretend that we
 	 * found a conforming footer with a matching sob
 	 */
-	if (msgbuf->len - ignore_footer == sob.len &&
-	    !strncmp(msgbuf->buf, sob.buf, sob.len))
+	if (msgbuf->len - ignore_footer == sob->len &&
+	    !strncmp(msgbuf->buf, sob->buf, sob->len))
 		has_footer = 3;
 	else
-		has_footer = has_conforming_footer(msgbuf, &sob, ignore_footer);
+		has_footer = has_conforming_footer(msgbuf, sob, ignore_footer);
 
 	if (!has_footer) {
 		const char *append_newlines = NULL;
@@ -4723,8 +4719,32 @@ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
 
 	if (has_footer != 3 && (!no_dup_sob || has_footer != 2))
 		strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0,
-				sob.buf, sob.len);
+				sob->buf, sob->len);
+}
+
+void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
+{
+	struct strbuf sob = STRBUF_INIT;
+	strbuf_addstr(&sob, sign_off_header);
+	strbuf_addstr(&sob, fmt_name(WANT_COMMITTER_IDENT));
+	strbuf_addch(&sob, '\n');
+	append_message(msgbuf, &sob, ignore_footer, flag);
+	strbuf_release(&sob);
+}
 
+void append_message_string_list(struct strbuf *msgbuf, const char *header,
+		struct string_list *sobs, size_t ignore_footer, unsigned flag) {
+	int i;
+	struct strbuf sob = STRBUF_INIT;
+
+	for ( i = 0; i < sobs->nr; i++)
+	{
+		strbuf_addstr(&sob, header);
+		strbuf_addstr(&sob, sobs->items[i].string);
+		strbuf_addch(&sob, '\n');
+		append_message(msgbuf, &sob, ignore_footer, flag);
+		strbuf_reset(&sob);
+	}
 	strbuf_release(&sob);
 }
 
diff --git a/sequencer.h b/sequencer.h
index f8b2e4ab8527..b24e274f4c62 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -174,6 +174,10 @@ int todo_list_rearrange_squash(struct todo_list *todo_list);
  * and the new signoff will be spliced into the buffer before those bytes.
  */
 void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);
+void append_message(struct strbuf *msgbuf, struct strbuf *sob,
+		size_t ignore_footer, unsigned flag);
+void append_message_string_list(struct strbuf *msgbuf, const char*header,
+		struct string_list *sobs, size_t ignore_footer, unsigned flag);
 
 void append_conflicts_hint(struct index_state *istate,
 		struct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode);
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 6396897cc818..40823152a51c 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -154,6 +154,112 @@ test_expect_success 'sign off' '
 
 '
 
+test_expect_success 'helped-by' '
+
+	>file1 &&
+	git add file1 &&
+	git commit --helped-by="foo <bar@frotz>" \
+	--helped-by="foo2 <bar2@frotz>" -m "thank you" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -ne "s/Helped-by: //p" commit.msg >actual &&
+	cat >expected <<-\EOF &&
+	foo <bar@frotz>
+	foo2 <bar2@frotz>
+	EOF
+	test_cmp expected actual
+
+'
+
+test_expect_success 'reported-by' '
+
+	>file2 &&
+	git add file2 &&
+	git commit --reported-by="foo <bar@frotz>" \
+	--reported-by="foo2 <bar2@frotz>" -m "thank you" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -ne "s/Reported-by: //p" commit.msg >actual &&
+	cat >expected <<-\EOF &&
+	foo <bar@frotz>
+	foo2 <bar2@frotz>
+	EOF
+	test_cmp expected actual
+
+'
+
+test_expect_success 'reviewed-by' '
+
+	>file3 &&
+	git add file3 &&
+	git commit --reviewed-by="foo <bar@frotz>" \
+	--reviewed-by="foo2 <bar2@frotz>" -m "thank you" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -ne "s/Reviewed-by: //p" commit.msg >actual &&
+	cat >expected <<-\EOF &&
+	foo <bar@frotz>
+	foo2 <bar2@frotz>
+	EOF
+	test_cmp expected actual
+
+'
+
+test_expect_success 'mentored-by' '
+
+	>file4 &&
+	git add file4 &&
+	git commit --mentored-by="foo <bar@frotz>" \
+	--mentored-by="foo2 <bar2@frotz>" -m "thank you" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -ne "s/Mentored-by: //p" commit.msg >actual &&
+	cat >expected <<-\EOF &&
+	foo <bar@frotz>
+	foo2 <bar2@frotz>
+	EOF
+	test_cmp expected actual
+
+'
+
+test_expect_success 'multiple signatures' '
+
+	>file5 &&
+	git add file5 &&
+	git commit --helped-by="foo <bar@frotz>" \
+	--reviewed-by="foo2 <bar2@frotz>" \
+	--mentored-by="foo3 <bar3@frotz>" \
+	--reported-by="foo4 <bar4@frotz>" -s -m "thank you" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,7d" commit.msg >actual &&
+	cat >expected <<-\EOF &&
+	Signed-off-by: C O Mitter <committer@example.com>
+	Helped-by: foo <bar@frotz>
+	Reviewed-by: foo2 <bar2@frotz>
+	Reported-by: foo4 <bar4@frotz>
+	Mentored-by: foo3 <bar3@frotz>
+	EOF
+	test_cmp expected actual
+
+'
+
+test_expect_success 'multiple signatures (use abbreviations)' '
+
+	>file6 &&
+	git add file6 &&
+	git commit -H "foo <bar@frotz>" \
+	-R "foo2 <bar2@frotz>" \
+	-M "foo3 <bar3@frotz>" \
+	-r "foo4 <bar4@frotz>" -s -m "thank you" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,7d" commit.msg >actual &&
+	cat >expected <<-\EOF &&
+	Signed-off-by: C O Mitter <committer@example.com>
+	Helped-by: foo <bar@frotz>
+	Reviewed-by: foo2 <bar2@frotz>
+	Reported-by: foo4 <bar4@frotz>
+	Mentored-by: foo3 <bar3@frotz>
+	EOF
+	test_cmp expected actual
+
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&

base-commit: 13d7ab6b5d7929825b626f050b62a11241ea4945
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH] [GSOC] commit: provides multiple common signatures
  2021-03-11  7:16 [PATCH] [GSOC] commit: provides multiple common signatures ZheNing Hu via GitGitGadget
@ 2021-03-11 15:03 ` Shourya Shukla
  2021-03-12 11:41   ` ZheNing Hu
  2021-03-11 17:28 ` Junio C Hamano
  2021-03-12 15:54 ` [PATCH v2] [GSOC] commit: add trailer command ZheNing Hu via GitGitGadget
  2 siblings, 1 reply; 84+ messages in thread
From: Shourya Shukla @ 2021-03-11 15:03 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget; +Cc: git

Hey!

The idea seems very useful to me though I am not sure what others feel
about this and whether Git is ready for such a thing or not. Keeping
this concern aside, I will still review the patch due to my own interest
in it as well.

On 11/03 07:16, ZheNing Hu via GitGitGadget wrote:
> From: ZheNing Hu <adlternative@gmail.com>
> 
> Similar to "Helped-by", "Reported-by", "Reviewed-by", "Mentored-by"
> these signatures are often seen in git commit messages. After

I think it will be better to rephrase the line so that it is easier to
understand. It took me a couple of reads to figure out what you meant.

Something like:

Similar to "Signed-off-by", trailers such as "Reported-by", "Helped-by"
and "Mentored-by" are also seen in Git commits.

> referring to the simple implementation of `commit --signoff`
> and `send-email -cc=" commiter <email>"`, I am considering
> whether to provide multiple signature parameters from the
> command line. I think this might help maintainers and
> developers directly uses the shell to provide these signatures
> instead of multiple times repetitive writing those trailers
> each time.

The above para is more appropriate for a cover letter than a commit
message. Your thought process for the patch you sent is equally valuable
but this does not belong in the commit message. Commit messages are more
about what you did and any nuances that follow. You get me?

Use 'git format-patch --cover-letter <...>' to create a cover letter for
your patch.

> To achieve this goal, i refactored the `append_signoff` design and
> provided `append_message` and `append_message_string_list` interfaces,
> providing new ways to generate those various signatures.

s/i/I

Also, commit messages are generally written in the imperative tense when
desciribing what you have done in the commit.

> Users now can use `commit -H "helper <eamil>"` to generate "Helped-by" trailer,
> `commit -R "reviewer <eamil>"` to generate "Reviewed-by" trailer,
> `commit -r "reporter <eamil> "`to generate "Reported-by" trailer,
> `commit -M "mentor <eamil>"` to generate "Mentored-by" trailer.

Multiple typos.

> Signed-off-by: ZheNing Hu <adlternative@gmail.com>

So, an improved commit message could be:

-----8<-----
commit: support multiple trailers from the command line

Historically, Git has supported the 'Signed-off-by' commit trailer
using the '--signoff' and the '-s' option from the command line. Extend
this list to include other commonly used trailers viz., "Helped-by",
"Reviewed-by", "Reported-by" and "Mentored-by". Introduce options '-H',
'-R', '-r' and '-M' corresponding to the aforementioned trailers.

While at it, add tests in the script 't7502-commit-porcelain.sh' and add
information regarding these options in the Documentation of 'git
commit'.
----->8-----

>     [GSOC] commit: provides multiple signatures from command line
>     
>     I don’t know if my idea will satisfy everyone, I'm also thinking about
>     whether we can provide a more generalized version (I think this idea can
>     be extended: because users and developers have other signature methods
>     that they want, such as "Based-on-patch-by") I hope someone can give me
>     pointers (on the correctness of ideas or codes)

It will be great if we let the user add customised options for the
respective trailers but I think that trying to cater to such a large
audience will only complicate the whole process and decrease the support
avialable for the options/command. Apart from this, I think that the
trailer "Reviewed-by" may not be that widely used and it would be better
if we remove it from this patch.

>  Documentation/git-commit.txt |  24 +++++++-
>  builtin/commit.c             |  63 +++++++++++++++++++++
>  sequencer.c                  |  40 +++++++++----
>  sequencer.h                  |   4 ++
>  t/t7502-commit-porcelain.sh  | 106 +++++++++++++++++++++++++++++++++++
>  5 files changed, 226 insertions(+), 11 deletions(-)
> 
> diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
> index 17150fa7eabe..e1b528d70c1a 100644
> --- a/Documentation/git-commit.txt
> +++ b/Documentation/git-commit.txt
> @@ -14,7 +14,9 @@ SYNOPSIS
>  	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
>  	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
>  	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
> -	   [-S[<keyid>]] [--] [<pathspec>...]
> +	   [-S[<keyid>]] [--] [<pathspec>...] [(-H|--helped-by)=<address>...]
> +	   [(-R|--reviewed-by)=<address>...] [(-r|--reported-by)=<address>...]
> +	   [(-M|--mentored)=<address>...]
>  
>  DESCRIPTION
>  -----------
> @@ -166,6 +168,26 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
>  
>  include::signoff-option.txt[]
>  
> +-H=<address>...::
> +--helped-by=<address>...::
> +	Add one or more `Helped-by` trailer by the committer at the end of the commit
> +	log message.
> +
> +-R=<address>...::
> +--reviewed-by=<address>...::
> +	Add one or more `Reviewed-by` trailer by the committer at the end of the commit
> +	log message.
> +
> +-r=<address>...::
> +--reported-by=<address>...::
> +	Add one or more `Reported-by` trailer by the committer at the end of the commit
> +	log message.
> +
> +-M=<address>...::
> +--mentored-by=<address>...::
> +	Add one or more `Mentored-by` trailer by the committer at the end of the commit
> +	log message.

Oh! I did not think you had added long forms of these options and was
about to comment on that. Good that you added. Do talk about them as
well in the commit message.

>  -n::
>  --no-verify::
>  	This option bypasses the pre-commit and commit-msg hooks.
> diff --git a/builtin/commit.c b/builtin/commit.c
> index 739110c5a7f6..4b312af03181 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -113,6 +113,10 @@ static int config_commit_verbose = -1; /* unspecified */
>  static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
>  static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
>  static char *sign_commit, *pathspec_from_file;
> +static struct string_list helped_by = STRING_LIST_INIT_NODUP;
> +static struct string_list mentored_by = STRING_LIST_INIT_NODUP;
> +static struct string_list reviewed_by = STRING_LIST_INIT_NODUP;
> +static struct string_list reported_by = STRING_LIST_INIT_NODUP;

Good that you kept the variables as 'string_list's instead of a usual
'char*'.

>  /*
>   * The default commit message cleanup mode will remove the lines
> @@ -829,6 +833,20 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
>  	if (signoff)
>  		append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0);
>  
> +	if(helped_by.items)
> +		append_message_string_list(&sb, "Helped-by: ", &helped_by, ignore_non_trailer(sb.buf, sb.len), 0);
> +	if(reviewed_by.items)
> +		append_message_string_list(&sb, "Reviewed-by: ", &reviewed_by, ignore_non_trailer(sb.buf, sb.len), 0);
> +	if(reported_by.items)
> +		append_message_string_list(&sb, "Reported-by: ", &reported_by, ignore_non_trailer(sb.buf, sb.len), 0);
> +	if(mentored_by.items)
> +		append_message_string_list(&sb, "Mentored-by: ", &mentored_by, ignore_non_trailer(sb.buf, sb.len), 0);

I think this part will look prettier if you wrap around the text. For
code segments, the wrap around limit is 80 chars. For commit messages
its 72 chars. Wrap around means that as soon as you hit N number of
characters, you proceed to the next(new) line.

> +	string_list_clear(&helped_by, 0);
> +	string_list_clear(&reviewed_by, 0);
> +	string_list_clear(&reported_by, 0);
> +	string_list_clear(&mentored_by, 0);
> +
>  	if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
>  		die_errno(_("could not write commit template"));
>  
> @@ -1490,6 +1508,42 @@ static int git_commit_config(const char *k, const char *v, void *cb)
>  	return git_status_config(k, v, s);
>  }
>  
> +static int help_callback(const struct option *opt, const char *arg, int unset)
> +{
> +	if (unset)
> +		string_list_clear(&helped_by, 0);
> +	else
> +		string_list_append(&helped_by, arg);
> +	return 0;
> +}
> +
> +static int review_callback(const struct option *opt, const char *arg, int unset)
> +{
> +	if (unset)
> +		string_list_clear(&reviewed_by, 0);
> +	else
> +		string_list_append(&reviewed_by, arg);
> +	return 0;
> +}
> +
> +static int report_callback(const struct option *opt, const char *arg, int unset)
> +{
> +	if (unset)
> +		string_list_clear(&reported_by, 0);
> +	else
> +		string_list_append(&reported_by, arg);
> +	return 0;
> +}
> +
> +static int mentor_callback(const struct option *opt, const char *arg, int unset)
> +{
> +	if (unset)
> +		string_list_clear(&mentored_by, 0);
> +	else
> +		string_list_append(&mentored_by, arg);
> +	return 0;
> +}
>  int cmd_commit(int argc, const char **argv, const char *prefix)
>  {
>  	static struct wt_status s;
> @@ -1507,6 +1561,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>  		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK('H', "helped-by", NULL, N_("email"), N_("add a Helped-by trailer"), help_callback),
> +		OPT_CALLBACK('r', "reported-by", NULL, N_("email"), N_("add a Reported-by trailer"), report_callback),
> +		OPT_CALLBACK('R', "reviewed-by", NULL, N_("email"), N_("add a Reviewed-by trailer"), review_callback),
> +		OPT_CALLBACK('M', "mentored-by", NULL, N_("email"), N_("add a Mentored-by trailer"), mentor_callback),

It would be lovely if you could line wrap these above additions too!

>  		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
>  		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
>  		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
> @@ -1561,6 +1619,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>  	struct commit_extra_header *extra = NULL;
>  	struct strbuf err = STRBUF_INIT;
>  
> +	helped_by.strdup_strings = 1;
> +	reviewed_by.strdup_strings = 1;
> +	reported_by.strdup_strings = 1;
> +	mentored_by.strdup_strings = 1;
> +
>  	if (argc == 2 && !strcmp(argv[1], "-h"))
>  		usage_with_options(builtin_commit_usage, builtin_commit_options);
>  
> diff --git a/sequencer.c b/sequencer.c
> index d2332d3e1787..528daf9df252 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -4668,16 +4668,12 @@ int sequencer_pick_revisions(struct repository *r,
>  	return res;
>  }
>  
> -void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
> +void append_message(struct strbuf *msgbuf, struct strbuf *sob,
> +			size_t ignore_footer, unsigned flag)

Its nice to see that you generalised the pre-exisiting function instead
of creating a new one(s) for the new trailers. Good.

It will be better to name 'struct strbuf *sob' to something more
generic, maybe 'struct strbuf *trailer' since 'sob' stands for
'Signed-off-by' and since the function is becoming more generic, its
contents should too.

>  {
>  	unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP;
> -	struct strbuf sob = STRBUF_INIT;
>  	int has_footer;
>  
> -	strbuf_addstr(&sob, sign_off_header);
> -	strbuf_addstr(&sob, fmt_name(WANT_COMMITTER_IDENT));
> -	strbuf_addch(&sob, '\n');
> -
>  	if (!ignore_footer)
>  		strbuf_complete_line(msgbuf);
>  
> @@ -4685,11 +4681,11 @@ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
>  	 * If the whole message buffer is equal to the sob, pretend that we
>  	 * found a conforming footer with a matching sob
>  	 */
> -	if (msgbuf->len - ignore_footer == sob.len &&
> -	    !strncmp(msgbuf->buf, sob.buf, sob.len))
> +	if (msgbuf->len - ignore_footer == sob->len &&
> +	    !strncmp(msgbuf->buf, sob->buf, sob->len))
>  		has_footer = 3;
>  	else
> -		has_footer = has_conforming_footer(msgbuf, &sob, ignore_footer);
> +		has_footer = has_conforming_footer(msgbuf, sob, ignore_footer);

Again, rename the variables.

>  	if (!has_footer) {
>  		const char *append_newlines = NULL;
> @@ -4723,8 +4719,32 @@ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
>  
>  	if (has_footer != 3 && (!no_dup_sob || has_footer != 2))
>  		strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0,
> -				sob.buf, sob.len);
> +				sob->buf, sob->len);
> +}
> +void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
> +{
> +	struct strbuf sob = STRBUF_INIT;
> +	strbuf_addstr(&sob, sign_off_header);
> +	strbuf_addstr(&sob, fmt_name(WANT_COMMITTER_IDENT));
> +	strbuf_addch(&sob, '\n');
> +	append_message(msgbuf, &sob, ignore_footer, flag);
> +	strbuf_release(&sob);
> +}

I think it will be nice if you could use a flag to denote whether the
entity to be appended is a 'sign-off' or not. This way when say the
variable 'int signoff' is 1, then use the above part in the
'append_message()' function otherwise go with the flow that exists.

> +void append_message_string_list(struct strbuf *msgbuf, const char *header,
> +		struct string_list *sobs, size_t ignore_footer, unsigned flag) {
> +	int i;
> +	struct strbuf sob = STRBUF_INIT;
> +
> +	for ( i = 0; i < sobs->nr; i++)
> +	{
> +		strbuf_addstr(&sob, header);
> +		strbuf_addstr(&sob, sobs->items[i].string);
> +		strbuf_addch(&sob, '\n');
> +		append_message(msgbuf, &sob, ignore_footer, flag);
> +		strbuf_reset(&sob);
> +	}
>  	strbuf_release(&sob);
>  }

Similarly, here, if 'signoff = 0' then the above part goes on. Or
another alternative can be to create a 'append_message_helper()' and
shift the current contents of 'append_message()' into that and use the
above function to work as per the value of signoff.

So a possible flow can be:

void append_message_string_list(...params...) {

	if (signoff) {
		..excecute the necessary segments and call the
		'append_message_helper()' function...
	} else {
		..similarly here..
	}
}

This way we save ourselves some extra functions.

> diff --git a/sequencer.h b/sequencer.h
> index f8b2e4ab8527..b24e274f4c62 100644
> --- a/sequencer.h
> +++ b/sequencer.h
> @@ -174,6 +174,10 @@ int todo_list_rearrange_squash(struct todo_list *todo_list);
>   * and the new signoff will be spliced into the buffer before those bytes.
>   */
>  void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);
> +void append_message(struct strbuf *msgbuf, struct strbuf *sob,
> +		size_t ignore_footer, unsigned flag);
> +void append_message_string_list(struct strbuf *msgbuf, const char*header,
> +		struct string_list *sobs, size_t ignore_footer, unsigned flag);
>  
>  void append_conflicts_hint(struct index_state *istate,
>  		struct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode);

Then, these would have to be corrected too.

> diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
> index 6396897cc818..40823152a51c 100755
> --- a/t/t7502-commit-porcelain.sh
> +++ b/t/t7502-commit-porcelain.sh
> @@ -154,6 +154,112 @@ test_expect_success 'sign off' '
>  
>  '

I feel that the tests deserve a commit of their own. A follow-up commit
in this patch.

> +test_expect_success 'helped-by' '
> +
> +	>file1 &&
> +	git add file1 &&
> +	git commit --helped-by="foo <bar@frotz>" \
> +	--helped-by="foo2 <bar2@frotz>" -m "thank you" &&
> +	git cat-file commit HEAD >commit.msg &&
> +	sed -ne "s/Helped-by: //p" commit.msg >actual &&
> +	cat >expected <<-\EOF &&
> +	foo <bar@frotz>
> +	foo2 <bar2@frotz>
> +	EOF
> +	test_cmp expected actual
> +

Was this extra line feed intentional? It looks odd here and other test
(scripts) don't have this. Though, this test script seems to have many
tests which have an extra line feed. My suggestion would be to eliminate
it. Maybe this script is old and hasn't been reviewed for a cleanup in
a long time.

> +'
> +
> +test_expect_success 'reported-by' '
> +
> +	>file2 &&
> +	git add file2 &&
> +	git commit --reported-by="foo <bar@frotz>" \
> +	--reported-by="foo2 <bar2@frotz>" -m "thank you" &&
> +	git cat-file commit HEAD >commit.msg &&
> +	sed -ne "s/Reported-by: //p" commit.msg >actual &&
> +	cat >expected <<-\EOF &&
> +	foo <bar@frotz>
> +	foo2 <bar2@frotz>
> +	EOF
> +	test_cmp expected actual
> +

Similarly here and in the parts that follow.

<...>

This seems like a good patch to me but more experienced members will be
able to comment better I think. Good job anyways.

Regards,
Shourya Shukla


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH] [GSOC] commit: provides multiple common signatures
  2021-03-11  7:16 [PATCH] [GSOC] commit: provides multiple common signatures ZheNing Hu via GitGitGadget
  2021-03-11 15:03 ` Shourya Shukla
@ 2021-03-11 17:28 ` Junio C Hamano
  2021-03-12 12:01   ` ZheNing Hu
  2021-03-12 13:22   ` ZheNing Hu
  2021-03-12 15:54 ` [PATCH v2] [GSOC] commit: add trailer command ZheNing Hu via GitGitGadget
  2 siblings, 2 replies; 84+ messages in thread
From: Junio C Hamano @ 2021-03-11 17:28 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Brandon Casey, ZheNing Hu

"ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: ZheNing Hu <adlternative@gmail.com>
>
> Similar to "Helped-by", "Reported-by", "Reviewed-by", "Mentored-by"
> these signatures are often seen in git commit messages. After
> referring to the simple implementation of `commit --signoff`
> and `send-email -cc=" commiter <email>"`, I am considering
> whether to provide multiple signature parameters from the
> command line.
> + ...
> +	>file6 &&
> +	git add file6 &&
> +	git commit -H "foo <bar@frotz>" \
> +	-R "foo2 <bar2@frotz>" \
> +	-M "foo3 <bar3@frotz>" \
> +	-r "foo4 <bar4@frotz>" -s -m "thank you" &&

Firm NAK.

Especially, not in this form that squats on short-and-sweet single
letter option names only to support the convention of this single
project (namely, Git).

cf. https://lore.kernel.org/git/20200824061156.1929850-1-espeer@gmail.com/

Thanks.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH] [GSOC] commit: provides multiple common signatures
  2021-03-11 15:03 ` Shourya Shukla
@ 2021-03-12 11:41   ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-12 11:41 UTC (permalink / raw)
  To: Shourya Shukla; +Cc: ZheNing Hu via GitGitGadget, Git List

Shourya Shukla <periperidip@gmail.com> 于2021年3月11日周四 下午11:05写道:
>
> Hey!
>

Hi!

> The idea seems very useful to me though I am not sure what others feel
> about this and whether Git is ready for such a thing or not. Keeping
> this concern aside, I will still review the patch due to my own interest
> in it as well.
>

haha, thank you! I'm glad that my whims can be recognized by you and help
git itself.

> On 11/03 07:16, ZheNing Hu via GitGitGadget wrote:
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > Similar to "Helped-by", "Reported-by", "Reviewed-by", "Mentored-by"
> > these signatures are often seen in git commit messages. After
>
> I think it will be better to rephrase the line so that it is easier to
> understand. It took me a couple of reads to figure out what you meant.
>
> Something like:
>
> Similar to "Signed-off-by", trailers such as "Reported-by", "Helped-by"
> and "Mentored-by" are also seen in Git commits.
>

It's exactly what you said.
My lack of English sometimes limits my expression.

> > referring to the simple implementation of `commit --signoff`
> > and `send-email -cc=" commiter <email>"`, I am considering
> > whether to provide multiple signature parameters from the
> > command line. I think this might help maintainers and
> > developers directly uses the shell to provide these signatures
> > instead of multiple times repetitive writing those trailers
> > each time.
>
> The above para is more appropriate for a cover letter than a commit
> message. Your thought process for the patch you sent is equally valuable
> but this does not belong in the commit message. Commit messages are more
> about what you did and any nuances that follow. You get me?
>
> Use 'git format-patch --cover-letter <...>' to create a cover letter for
> your patch.
>

I understand it now, because I use GGG, I will put this content into the
GGG conversation.

> > To achieve this goal, i refactored the `append_signoff` design and
> > provided `append_message` and `append_message_string_list` interfaces,
> > providing new ways to generate those various signatures.
>
> s/i/I
>
> Also, commit messages are generally written in the imperative tense when
> desciribing what you have done in the commit.
>

You're right.

> > Users now can use `commit -H "helper <eamil>"` to generate "Helped-by" trailer,
> > `commit -R "reviewer <eamil>"` to generate "Reviewed-by" trailer,
> > `commit -r "reporter <eamil> "`to generate "Reported-by" trailer,
> > `commit -M "mentor <eamil>"` to generate "Mentored-by" trailer.
>
> Multiple typos.
>
> > Signed-off-by: ZheNing Hu <adlternative@gmail.com>
>
> So, an improved commit message could be:
>
> -----8<-----
> commit: support multiple trailers from the command line
>
> Historically, Git has supported the 'Signed-off-by' commit trailer
> using the '--signoff' and the '-s' option from the command line. Extend
> this list to include other commonly used trailers viz., "Helped-by",
> "Reviewed-by", "Reported-by" and "Mentored-by". Introduce options '-H',
> '-R', '-r' and '-M' corresponding to the aforementioned trailers.
>
> While at it, add tests in the script 't7502-commit-porcelain.sh' and add
> information regarding these options in the Documentation of 'git
> commit'.

I think this paragraph may not be needed. Junio said in reply to one
of my previous patches that we may not need to add instructions about test
files and documents, because `git log -p --stat` can clearly see that what we
have done in testing and documentation.

> ----->8-----
>
> >     [GSOC] commit: provides multiple signatures from command line
> >
> >     I don’t know if my idea will satisfy everyone, I'm also thinking about
> >     whether we can provide a more generalized version (I think this idea can
> >     be extended: because users and developers have other signature methods
> >     that they want, such as "Based-on-patch-by") I hope someone can give me
> >     pointers (on the correctness of ideas or codes)
>
> It will be great if we let the user add customised options for the
> respective trailers but I think that trying to cater to such a large
> audience will only complicate the whole process and decrease the support
> avialable for the options/command. Apart from this, I think that the
> trailer "Reviewed-by" may not be that widely used and it would be better
> if we remove it from this patch.

Okay, I will delete it.

>
> >  Documentation/git-commit.txt |  24 +++++++-
> >  builtin/commit.c             |  63 +++++++++++++++++++++
> >  sequencer.c                  |  40 +++++++++----
> >  sequencer.h                  |   4 ++
> >  t/t7502-commit-porcelain.sh  | 106 +++++++++++++++++++++++++++++++++++
> >  5 files changed, 226 insertions(+), 11 deletions(-)
> >
> > diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
> > index 17150fa7eabe..e1b528d70c1a 100644
> > --- a/Documentation/git-commit.txt
> > +++ b/Documentation/git-commit.txt
> > @@ -14,7 +14,9 @@ SYNOPSIS
> >          [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
> >          [--date=<date>] [--cleanup=<mode>] [--[no-]status]
> >          [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
> > -        [-S[<keyid>]] [--] [<pathspec>...]
> > +        [-S[<keyid>]] [--] [<pathspec>...] [(-H|--helped-by)=<address>...]
> > +        [(-R|--reviewed-by)=<address>...] [(-r|--reported-by)=<address>...]
> > +        [(-M|--mentored)=<address>...]
> >
> >  DESCRIPTION
> >  -----------
> > @@ -166,6 +168,26 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
> >
> >  include::signoff-option.txt[]
> >
> > +-H=<address>...::
> > +--helped-by=<address>...::
> > +     Add one or more `Helped-by` trailer by the committer at the end of the commit
> > +     log message.
> > +
> > +-R=<address>...::
> > +--reviewed-by=<address>...::
> > +     Add one or more `Reviewed-by` trailer by the committer at the end of the commit
> > +     log message.
> > +
> > +-r=<address>...::
> > +--reported-by=<address>...::
> > +     Add one or more `Reported-by` trailer by the committer at the end of the commit
> > +     log message.
> > +
> > +-M=<address>...::
> > +--mentored-by=<address>...::
> > +     Add one or more `Mentored-by` trailer by the committer at the end of the commit
> > +     log message.
>
> Oh! I did not think you had added long forms of these options and was
> about to comment on that. Good that you added. Do talk about them as
> well in the commit message.
>

Yes, I forgot to mention these long formats in the commit message.

> >  -n::
> >  --no-verify::
> >       This option bypasses the pre-commit and commit-msg hooks.
> > diff --git a/builtin/commit.c b/builtin/commit.c
> > index 739110c5a7f6..4b312af03181 100644
> > --- a/builtin/commit.c
> > +++ b/builtin/commit.c
> > @@ -113,6 +113,10 @@ static int config_commit_verbose = -1; /* unspecified */
> >  static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
> >  static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
> >  static char *sign_commit, *pathspec_from_file;
> > +static struct string_list helped_by = STRING_LIST_INIT_NODUP;
> > +static struct string_list mentored_by = STRING_LIST_INIT_NODUP;
> > +static struct string_list reviewed_by = STRING_LIST_INIT_NODUP;
> > +static struct string_list reported_by = STRING_LIST_INIT_NODUP;
>
> Good that you kept the variables as 'string_list's instead of a usual
> 'char*'.
>
> >  /*
> >   * The default commit message cleanup mode will remove the lines
> > @@ -829,6 +833,20 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
> >       if (signoff)
> >               append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0);
> >
> > +     if(helped_by.items)
> > +             append_message_string_list(&sb, "Helped-by: ", &helped_by, ignore_non_trailer(sb.buf, sb.len), 0);
> > +     if(reviewed_by.items)
> > +             append_message_string_list(&sb, "Reviewed-by: ", &reviewed_by, ignore_non_trailer(sb.buf, sb.len), 0);
> > +     if(reported_by.items)
> > +             append_message_string_list(&sb, "Reported-by: ", &reported_by, ignore_non_trailer(sb.buf, sb.len), 0);
> > +     if(mentored_by.items)
> > +             append_message_string_list(&sb, "Mentored-by: ", &mentored_by, ignore_non_trailer(sb.buf, sb.len), 0);
>
> I think this part will look prettier if you wrap around the text. For
> code segments, the wrap around limit is 80 chars. For commit messages
> its 72 chars. Wrap around means that as soon as you hit N number of
> characters, you proceed to the next(new) line.
>

Fine.

> > +     string_list_clear(&helped_by, 0);
> > +     string_list_clear(&reviewed_by, 0);
> > +     string_list_clear(&reported_by, 0);
> > +     string_list_clear(&mentored_by, 0);
> > +
> >       if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
> >               die_errno(_("could not write commit template"));
> >
> > @@ -1490,6 +1508,42 @@ static int git_commit_config(const char *k, const char *v, void *cb)
> >       return git_status_config(k, v, s);
> >  }
> >
> > +static int help_callback(const struct option *opt, const char *arg, int unset)
> > +{
> > +     if (unset)
> > +             string_list_clear(&helped_by, 0);
> > +     else
> > +             string_list_append(&helped_by, arg);
> > +     return 0;
> > +}
> > +
> > +static int review_callback(const struct option *opt, const char *arg, int unset)
> > +{
> > +     if (unset)
> > +             string_list_clear(&reviewed_by, 0);
> > +     else
> > +             string_list_append(&reviewed_by, arg);
> > +     return 0;
> > +}
> > +
> > +static int report_callback(const struct option *opt, const char *arg, int unset)
> > +{
> > +     if (unset)
> > +             string_list_clear(&reported_by, 0);
> > +     else
> > +             string_list_append(&reported_by, arg);
> > +     return 0;
> > +}
> > +
> > +static int mentor_callback(const struct option *opt, const char *arg, int unset)
> > +{
> > +     if (unset)
> > +             string_list_clear(&mentored_by, 0);
> > +     else
> > +             string_list_append(&mentored_by, arg);
> > +     return 0;
> > +}
> >  int cmd_commit(int argc, const char **argv, const char *prefix)
> >  {
> >       static struct wt_status s;
> > @@ -1507,6 +1561,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
> >               OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK('H', "helped-by", NULL, N_("email"), N_("add a Helped-by trailer"), help_callback),
> > +             OPT_CALLBACK('r', "reported-by", NULL, N_("email"), N_("add a Reported-by trailer"), report_callback),
> > +             OPT_CALLBACK('R', "reviewed-by", NULL, N_("email"), N_("add a Reviewed-by trailer"), review_callback),
> > +             OPT_CALLBACK('M', "mentored-by", NULL, N_("email"), N_("add a Mentored-by trailer"), mentor_callback),
>
> It would be lovely if you could line wrap these above additions too!
>
> >               OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
> >               OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
> >               OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
> > @@ -1561,6 +1619,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
> >       struct commit_extra_header *extra = NULL;
> >       struct strbuf err = STRBUF_INIT;
> >
> > +     helped_by.strdup_strings = 1;
> > +     reviewed_by.strdup_strings = 1;
> > +     reported_by.strdup_strings = 1;
> > +     mentored_by.strdup_strings = 1;
> > +
> >       if (argc == 2 && !strcmp(argv[1], "-h"))
> >               usage_with_options(builtin_commit_usage, builtin_commit_options);
> >
> > diff --git a/sequencer.c b/sequencer.c
> > index d2332d3e1787..528daf9df252 100644
> > --- a/sequencer.c
> > +++ b/sequencer.c
> > @@ -4668,16 +4668,12 @@ int sequencer_pick_revisions(struct repository *r,
> >       return res;
> >  }
> >
> > -void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
> > +void append_message(struct strbuf *msgbuf, struct strbuf *sob,
> > +                     size_t ignore_footer, unsigned flag)
>
> Its nice to see that you generalised the pre-exisiting function instead
> of creating a new one(s) for the new trailers. Good.
>
> It will be better to name 'struct strbuf *sob' to something more
> generic, maybe 'struct strbuf *trailer' since 'sob' stands for
> 'Signed-off-by' and since the function is becoming more generic, its
> contents should too.
>
When I wrote it, I didn’t realize that sob refers to the abbreviation of
'Signed-off-by', I will change it.
> >  {
> >       unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP;
> > -     struct strbuf sob = STRBUF_INIT;
> >       int has_footer;
> >
> > -     strbuf_addstr(&sob, sign_off_header);
> > -     strbuf_addstr(&sob, fmt_name(WANT_COMMITTER_IDENT));
> > -     strbuf_addch(&sob, '\n');
> > -
> >       if (!ignore_footer)
> >               strbuf_complete_line(msgbuf);
> >
> > @@ -4685,11 +4681,11 @@ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
> >        * If the whole message buffer is equal to the sob, pretend that we
> >        * found a conforming footer with a matching sob
> >        */
> > -     if (msgbuf->len - ignore_footer == sob.len &&
> > -         !strncmp(msgbuf->buf, sob.buf, sob.len))
> > +     if (msgbuf->len - ignore_footer == sob->len &&
> > +         !strncmp(msgbuf->buf, sob->buf, sob->len))
> >               has_footer = 3;
> >       else
> > -             has_footer = has_conforming_footer(msgbuf, &sob, ignore_footer);
> > +             has_footer = has_conforming_footer(msgbuf, sob, ignore_footer);
>
> Again, rename the variables.
>
> >       if (!has_footer) {
> >               const char *append_newlines = NULL;
> > @@ -4723,8 +4719,32 @@ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
> >
> >       if (has_footer != 3 && (!no_dup_sob || has_footer != 2))
> >               strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0,
> > -                             sob.buf, sob.len);
> > +                             sob->buf, sob->len);
> > +}
> > +void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
> > +{
> > +     struct strbuf sob = STRBUF_INIT;
> > +     strbuf_addstr(&sob, sign_off_header);
> > +     strbuf_addstr(&sob, fmt_name(WANT_COMMITTER_IDENT));
> > +     strbuf_addch(&sob, '\n');
> > +     append_message(msgbuf, &sob, ignore_footer, flag);
> > +     strbuf_release(&sob);
> > +}
>
> I think it will be nice if you could use a flag to denote whether the
> entity to be appended is a 'sign-off' or not. This way when say the
> variable 'int signoff' is 1, then use the above part in the
> 'append_message()' function otherwise go with the flow that exists.
>
> > +void append_message_string_list(struct strbuf *msgbuf, const char *header,
> > +             struct string_list *sobs, size_t ignore_footer, unsigned flag) {
> > +     int i;
> > +     struct strbuf sob = STRBUF_INIT;
> > +
> > +     for ( i = 0; i < sobs->nr; i++)
> > +     {
> > +             strbuf_addstr(&sob, header);
> > +             strbuf_addstr(&sob, sobs->items[i].string);
> > +             strbuf_addch(&sob, '\n');
> > +             append_message(msgbuf, &sob, ignore_footer, flag);
> > +             strbuf_reset(&sob);
> > +     }
> >       strbuf_release(&sob);
> >  }
>
> Similarly, here, if 'signoff = 0' then the above part goes on. Or
> another alternative can be to create a 'append_message_helper()' and
> shift the current contents of 'append_message()' into that and use the
> above function to work as per the value of signoff.
>
> So a possible flow can be:
>
> void append_message_string_list(...params...) {
>
>         if (signoff) {
>                 ..excecute the necessary segments and call the
>                 'append_message_helper()' function...
>         } else {
>                 ..similarly here..
>         }
> }
>
> This way we save ourselves some extra functions.

May some thing like this:

if (signoff)
        append_message_string_list(&sb, "Signed-off-by: ", NULL,
                   ignore_non_trailer(sb.buf, sb.len), 0);

And then we could judge in `append_message_string_list`
if the arguement `string_list *trailers` set to NULL. If so,
we do something similar to `append_signoff`, otherwise,
we carry out other situations.

So that we can delete the `append_signoff` and user can only
need to call  `append_message_string_list` in any where, I don't
 know if it is better.

>
> > diff --git a/sequencer.h b/sequencer.h
> > index f8b2e4ab8527..b24e274f4c62 100644
> > --- a/sequencer.h
> > +++ b/sequencer.h
> > @@ -174,6 +174,10 @@ int todo_list_rearrange_squash(struct todo_list *todo_list);
> >   * and the new signoff will be spliced into the buffer before those bytes.
> >   */
> >  void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);
> > +void append_message(struct strbuf *msgbuf, struct strbuf *sob,
> > +             size_t ignore_footer, unsigned flag);
> > +void append_message_string_list(struct strbuf *msgbuf, const char*header,
> > +             struct string_list *sobs, size_t ignore_footer, unsigned flag);
> >
> >  void append_conflicts_hint(struct index_state *istate,
> >               struct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode);
>
> Then, these would have to be corrected too.
>
> > diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
> > index 6396897cc818..40823152a51c 100755
> > --- a/t/t7502-commit-porcelain.sh
> > +++ b/t/t7502-commit-porcelain.sh
> > @@ -154,6 +154,112 @@ test_expect_success 'sign off' '
> >
> >  '
>
> I feel that the tests deserve a commit of their own. A follow-up commit
> in this patch.
>
> > +test_expect_success 'helped-by' '
> > +
> > +     >file1 &&
> > +     git add file1 &&
> > +     git commit --helped-by="foo <bar@frotz>" \
> > +     --helped-by="foo2 <bar2@frotz>" -m "thank you" &&
> > +     git cat-file commit HEAD >commit.msg &&
> > +     sed -ne "s/Helped-by: //p" commit.msg >actual &&
> > +     cat >expected <<-\EOF &&
> > +     foo <bar@frotz>
> > +     foo2 <bar2@frotz>
> > +     EOF
> > +     test_cmp expected actual
> > +
>
> Was this extra line feed intentional? It looks odd here and other test
> (scripts) don't have this. Though, this test script seems to have many
> tests which have an extra line feed. My suggestion would be to eliminate
> it. Maybe this script is old and hasn't been reviewed for a cleanup in
> a long time.
>

I might want to keep the same format with the last "sign off" test. Now it
seems that this is not necessary.

> > +'
> > +
> > +test_expect_success 'reported-by' '
> > +
> > +     >file2 &&
> > +     git add file2 &&
> > +     git commit --reported-by="foo <bar@frotz>" \
> > +     --reported-by="foo2 <bar2@frotz>" -m "thank you" &&
> > +     git cat-file commit HEAD >commit.msg &&
> > +     sed -ne "s/Reported-by: //p" commit.msg >actual &&
> > +     cat >expected <<-\EOF &&
> > +     foo <bar@frotz>
> > +     foo2 <bar2@frotz>
> > +     EOF
> > +     test_cmp expected actual
> > +
>
> Similarly here and in the parts that follow.
>
> <...>
>
> This seems like a good patch to me but more experienced members will be
> able to comment better I think. Good job anyways.
>

Your comments and encouragement are of great help to me :)

> Regards,
> Shourya Shukla
>

Regards,
ZheNing Hu

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH] [GSOC] commit: provides multiple common signatures
  2021-03-11 17:28 ` Junio C Hamano
@ 2021-03-12 12:01   ` ZheNing Hu
  2021-03-12 13:22   ` ZheNing Hu
  1 sibling, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-12 12:01 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: ZheNing Hu via GitGitGadget, Git List, Bradley M. Kuhn, Brandon Casey

Junio C Hamano <gitster@pobox.com> 于2021年3月12日周五 上午1:29写道:

> Firm NAK.
>
> Especially, not in this form that squats on short-and-sweet single
> letter option names only to support the convention of this single
> project (namely, Git).
>
> cf. https://lore.kernel.org/git/20200824061156.1929850-1-espeer@gmail.com/
>
> Thanks.

Well, Junio, you point is It's also worth thinking about, This
"trailer =<trailer>:<argtrailer>" is probably the more general
version of the tailer I want to provide to commit, Then I may
need to know how Christian Couder `trailer.c` work first,or can
you give a little directionality guidance?

Thanks.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH] [GSOC] commit: provides multiple common signatures
  2021-03-11 17:28 ` Junio C Hamano
  2021-03-12 12:01   ` ZheNing Hu
@ 2021-03-12 13:22   ` ZheNing Hu
  1 sibling, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-12 13:22 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: ZheNing Hu via GitGitGadget, Git List, Bradley M. Kuhn, Brandon Casey

Junio C Hamano <gitster@pobox.com> 于2021年3月12日周五 上午1:29写道:
> Firm NAK.
>
> Especially, not in this form that squats on short-and-sweet single
> letter option names only to support the convention of this single
> project (namely, Git).
>
> cf. https://lore.kernel.org/git/20200824061156.1929850-1-espeer@gmail.com/
>
> Thanks.

Now I understand that We have a git command now:

`git interpret-trailers --in-place --where=end \
./a.txt --trailer "Signed-off-by:adl <adlternative@gmail.com> " `
can add the trailers to specified file, so now maybe I only
need to provide `--trailer` for commit, right?

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v2] [GSOC] commit: add trailer command
  2021-03-11  7:16 [PATCH] [GSOC] commit: provides multiple common signatures ZheNing Hu via GitGitGadget
  2021-03-11 15:03 ` Shourya Shukla
  2021-03-11 17:28 ` Junio C Hamano
@ 2021-03-12 15:54 ` ZheNing Hu via GitGitGadget
  2021-03-14  4:19   ` Christian Couder
  2021-03-14 13:02   ` [PATCH v3] [GSOC] commit: add --trailer option ZheNing Hu via GitGitGadget
  2 siblings, 2 replies; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-12 15:54 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Historically, Git has supported the 'Signed-off-by' commit trailer
using the '--signoff' and the '-s' option from the command line.
But users may need to provide richer trailer information from the
command line such as "Helped-by", "Reported-by", "Mentored-by",

Now use `--trailer <token>[(=|:)<value>]` pass the trailers to
`interpret-trailers` and generate trailers in commit messages.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC] commit: provides multiple signatures from command line
    
    Now maintainers or developers can also use commit
    --trailer="Signed-off-by:commiter<email>" from the command line to
    provide trailers to commit messages. This solution may be more
    generalized than v1.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/901

Range-diff vs v1:

 1:  e9389e72ac65 ! 1:  4c507d17db4f [GSOC] commit: provides multiple common signatures
     @@ Metadata
      Author: ZheNing Hu <adlternative@gmail.com>
      
       ## Commit message ##
     -    [GSOC] commit: provides multiple common signatures
     +    [GSOC] commit: add trailer command
      
     -    Similar to "Helped-by", "Reported-by", "Reviewed-by", "Mentored-by"
     -    these signatures are often seen in git commit messages. After
     -    referring to the simple implementation of `commit --signoff`
     -    and `send-email -cc=" commiter <email>"`, I am considering
     -    whether to provide multiple signature parameters from the
     -    command line. I think this might help maintainers and
     -    developers directly uses the shell to provide these signatures
     -    instead of multiple times repetitive writing those trailers
     -    each time.
     +    Historically, Git has supported the 'Signed-off-by' commit trailer
     +    using the '--signoff' and the '-s' option from the command line.
     +    But users may need to provide richer trailer information from the
     +    command line such as "Helped-by", "Reported-by", "Mentored-by",
      
     -    To achieve this goal, i refactored the `append_signoff` design and
     -    provided `append_message` and `append_message_string_list` interfaces,
     -    providing new ways to generate those various signatures.
     -
     -    Users now can use `commit -H "helper <eamil>"` to generate "Helped-by" trailer,
     -    `commit -R "reviewer <eamil>"` to generate "Reviewed-by" trailer,
     -    `commit -r "reporter <eamil> "`to generate "Reported-by" trailer,
     -    `commit -M "mentor <eamil>"` to generate "Mentored-by" trailer.
     +    Now use `--trailer <token>[(=|:)<value>]` pass the trailers to
     +    `interpret-trailers` and generate trailers in commit messages.
      
          Signed-off-by: ZheNing Hu <adlternative@gmail.com>
      
     @@ Documentation/git-commit.txt: SYNOPSIS
       	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
       	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
      -	   [-S[<keyid>]] [--] [<pathspec>...]
     -+	   [-S[<keyid>]] [--] [<pathspec>...] [(-H|--helped-by)=<address>...]
     -+	   [(-R|--reviewed-by)=<address>...] [(-r|--reported-by)=<address>...]
     -+	   [(-M|--mentored)=<address>...]
     ++	   [-S[<keyid>]] [--] [<pathspec>...] [(--trailer <token>[(=|:)<value>])...]
       
       DESCRIPTION
       -----------
     @@ Documentation/git-commit.txt: The `-m` option is mutually exclusive with `-c`, `
       
       include::signoff-option.txt[]
       
     -+-H=<address>...::
     -+--helped-by=<address>...::
     -+	Add one or more `Helped-by` trailer by the committer at the end of the commit
     -+	log message.
     -+
     -+-R=<address>...::
     -+--reviewed-by=<address>...::
     -+	Add one or more `Reviewed-by` trailer by the committer at the end of the commit
     -+	log message.
     -+
     -+-r=<address>...::
     -+--reported-by=<address>...::
     -+	Add one or more `Reported-by` trailer by the committer at the end of the commit
     -+	log message.
     -+
     -+-M=<address>...::
     -+--mentored-by=<address>...::
     -+	Add one or more `Mentored-by` trailer by the committer at the end of the commit
     -+	log message.
     ++--trailer <token>[(=|:)<value>]::
     ++	Specify a (<token>, <value>) pair that should be applied as a
     ++	trailer. (e.g.  `git commit --trailer "Signed-off-by:C O Mitter <committer@example.com>" \
     ++	--trailer "Helped-by:C O Mitter <committer@example.com>"`will add the "Signed-off" trailer
     ++	and the "Helped-by" trailer in the commit message.)
      +
       -n::
       --no-verify::
     @@ builtin/commit.c: static int config_commit_verbose = -1; /* unspecified */
       static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
       static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
       static char *sign_commit, *pathspec_from_file;
     -+static struct string_list helped_by = STRING_LIST_INIT_NODUP;
     -+static struct string_list mentored_by = STRING_LIST_INIT_NODUP;
     -+static struct string_list reviewed_by = STRING_LIST_INIT_NODUP;
     -+static struct string_list reported_by = STRING_LIST_INIT_NODUP;
     ++struct child_process run_trailer = CHILD_PROCESS_INIT;
     ++static const char *trailer;
       
       /*
        * The default commit message cleanup mode will remove the lines
     -@@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const char *prefix,
     - 	if (signoff)
     - 		append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0);
     - 
     -+	if(helped_by.items)
     -+		append_message_string_list(&sb, "Helped-by: ", &helped_by, ignore_non_trailer(sb.buf, sb.len), 0);
     -+	if(reviewed_by.items)
     -+		append_message_string_list(&sb, "Reviewed-by: ", &reviewed_by, ignore_non_trailer(sb.buf, sb.len), 0);
     -+	if(reported_by.items)
     -+		append_message_string_list(&sb, "Reported-by: ", &reported_by, ignore_non_trailer(sb.buf, sb.len), 0);
     -+	if(mentored_by.items)
     -+		append_message_string_list(&sb, "Mentored-by: ", &mentored_by, ignore_non_trailer(sb.buf, sb.len), 0);
     -+
     -+	string_list_clear(&helped_by, 0);
     -+	string_list_clear(&reviewed_by, 0);
     -+	string_list_clear(&reported_by, 0);
     -+	string_list_clear(&mentored_by, 0);
     -+
     - 	if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
     - 		die_errno(_("could not write commit template"));
     +@@ builtin/commit.c: static struct strbuf message = STRBUF_INIT;
       
     -@@ builtin/commit.c: static int git_commit_config(const char *k, const char *v, void *cb)
     - 	return git_status_config(k, v, s);
     - }
     + static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
       
     -+static int help_callback(const struct option *opt, const char *arg, int unset)
     -+{
     -+	if (unset)
     -+		string_list_clear(&helped_by, 0);
     -+	else
     -+		string_list_append(&helped_by, arg);
     -+	return 0;
     -+}
     -+
     -+static int review_callback(const struct option *opt, const char *arg, int unset)
     ++static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
      +{
     -+	if (unset)
     -+		string_list_clear(&reviewed_by, 0);
     -+	else
     -+		string_list_append(&reviewed_by, arg);
     -+	return 0;
     -+}
     -+
     -+static int report_callback(const struct option *opt, const char *arg, int unset)
     -+{
     -+	if (unset)
     -+		string_list_clear(&reported_by, 0);
     -+	else
     -+		string_list_append(&reported_by, arg);
     -+	return 0;
     -+}
     -+
     -+static int mentor_callback(const struct option *opt, const char *arg, int unset)
     -+{
     -+	if (unset)
     -+		string_list_clear(&mentored_by, 0);
     -+	else
     -+		string_list_append(&mentored_by, arg);
     ++	if (unset) {
     ++		strvec_clear(&run_trailer.args);
     ++		return -1;
     ++	}
     ++	run_trailer.git_cmd = 1;
     ++	strvec_pushl(&run_trailer.args, "--trailer", arg, NULL);
      +	return 0;
      +}
      +
     - int cmd_commit(int argc, const char **argv, const char *prefix)
     + static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
       {
     - 	static struct wt_status s;
     + 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
     +@@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const char *prefix,
     + 
     + 	fclose(s->fp);
     + 
     ++	run_command(&run_trailer);
     ++
     + 	/*
     + 	 * Reject an attempt to record a non-merge empty commit without
     + 	 * explicit --allow-empty. In the cherry-pick case, it may be
      @@ builtin/commit.c: int cmd_commit(int argc, const char **argv, const char *prefix)
       		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK('H', "helped-by", NULL, N_("email"), N_("add a Helped-by trailer"), help_callback),
     -+		OPT_CALLBACK('r', "reported-by", NULL, N_("email"), N_("add a Reported-by trailer"), report_callback),
     -+		OPT_CALLBACK('R', "reviewed-by", NULL, N_("email"), N_("add a Reviewed-by trailer"), review_callback),
     -+		OPT_CALLBACK('M', "mentored-by", NULL, N_("email"), N_("add a Mentored-by trailer"), mentor_callback),
     ++		OPT_CALLBACK(0, "trailer", &trailer, N_("trailer"), N_("trailer(s) to add"), opt_pass_trailer),
       		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
       		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
       		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
      @@ builtin/commit.c: int cmd_commit(int argc, const char **argv, const char *prefix)
     - 	struct commit_extra_header *extra = NULL;
     - 	struct strbuf err = STRBUF_INIT;
     - 
     -+	helped_by.strdup_strings = 1;
     -+	reviewed_by.strdup_strings = 1;
     -+	reported_by.strdup_strings = 1;
     -+	mentored_by.strdup_strings = 1;
     -+
     - 	if (argc == 2 && !strcmp(argv[1], "-h"))
     - 		usage_with_options(builtin_commit_usage, builtin_commit_options);
     - 
     -
     - ## sequencer.c ##
     -@@ sequencer.c: int sequencer_pick_revisions(struct repository *r,
     - 	return res;
     - }
     - 
     --void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
     -+void append_message(struct strbuf *msgbuf, struct strbuf *sob,
     -+			size_t ignore_footer, unsigned flag)
     - {
     - 	unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP;
     --	struct strbuf sob = STRBUF_INIT;
     - 	int has_footer;
     - 
     --	strbuf_addstr(&sob, sign_off_header);
     --	strbuf_addstr(&sob, fmt_name(WANT_COMMITTER_IDENT));
     --	strbuf_addch(&sob, '\n');
     --
     - 	if (!ignore_footer)
     - 		strbuf_complete_line(msgbuf);
     - 
     -@@ sequencer.c: void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
     - 	 * If the whole message buffer is equal to the sob, pretend that we
     - 	 * found a conforming footer with a matching sob
     - 	 */
     --	if (msgbuf->len - ignore_footer == sob.len &&
     --	    !strncmp(msgbuf->buf, sob.buf, sob.len))
     -+	if (msgbuf->len - ignore_footer == sob->len &&
     -+	    !strncmp(msgbuf->buf, sob->buf, sob->len))
     - 		has_footer = 3;
     - 	else
     --		has_footer = has_conforming_footer(msgbuf, &sob, ignore_footer);
     -+		has_footer = has_conforming_footer(msgbuf, sob, ignore_footer);
     - 
     - 	if (!has_footer) {
     - 		const char *append_newlines = NULL;
     -@@ sequencer.c: void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
     - 
     - 	if (has_footer != 3 && (!no_dup_sob || has_footer != 2))
     - 		strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0,
     --				sob.buf, sob.len);
     -+				sob->buf, sob->len);
     -+}
     -+
     -+void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
     -+{
     -+	struct strbuf sob = STRBUF_INIT;
     -+	strbuf_addstr(&sob, sign_off_header);
     -+	strbuf_addstr(&sob, fmt_name(WANT_COMMITTER_IDENT));
     -+	strbuf_addch(&sob, '\n');
     -+	append_message(msgbuf, &sob, ignore_footer, flag);
     -+	strbuf_release(&sob);
     -+}
     - 
     -+void append_message_string_list(struct strbuf *msgbuf, const char *header,
     -+		struct string_list *sobs, size_t ignore_footer, unsigned flag) {
     -+	int i;
     -+	struct strbuf sob = STRBUF_INIT;
     -+
     -+	for ( i = 0; i < sobs->nr; i++)
     -+	{
     -+		strbuf_addstr(&sob, header);
     -+		strbuf_addstr(&sob, sobs->items[i].string);
     -+		strbuf_addch(&sob, '\n');
     -+		append_message(msgbuf, &sob, ignore_footer, flag);
     -+		strbuf_reset(&sob);
     -+	}
     - 	strbuf_release(&sob);
     - }
     - 
     -
     - ## sequencer.h ##
     -@@ sequencer.h: int todo_list_rearrange_squash(struct todo_list *todo_list);
     -  * and the new signoff will be spliced into the buffer before those bytes.
     -  */
     - void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);
     -+void append_message(struct strbuf *msgbuf, struct strbuf *sob,
     -+		size_t ignore_footer, unsigned flag);
     -+void append_message_string_list(struct strbuf *msgbuf, const char*header,
     -+		struct string_list *sobs, size_t ignore_footer, unsigned flag);
     - 
     - void append_conflicts_hint(struct index_state *istate,
     - 		struct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode);
     + 			die(_("could not parse HEAD commit"));
     + 	}
     + 	verbose = -1; /* unspecified */
     ++	strvec_pushl(&run_trailer.args, "interpret-trailers",
     ++		"--in-place", "--where=end", git_path_commit_editmsg(), NULL);
     + 	argc = parse_and_validate_options(argc, argv, builtin_commit_options,
     + 					  builtin_commit_usage,
     + 					  prefix, current_head, &s);
      
       ## t/t7502-commit-porcelain.sh ##
      @@ t/t7502-commit-porcelain.sh: test_expect_success 'sign off' '
       
       '
       
     -+test_expect_success 'helped-by' '
     -+
     ++test_expect_success 'trailer' '
      +	>file1 &&
      +	git add file1 &&
     -+	git commit --helped-by="foo <bar@frotz>" \
     -+	--helped-by="foo2 <bar2@frotz>" -m "thank you" &&
     -+	git cat-file commit HEAD >commit.msg &&
     -+	sed -ne "s/Helped-by: //p" commit.msg >actual &&
     -+	cat >expected <<-\EOF &&
     -+	foo <bar@frotz>
     -+	foo2 <bar2@frotz>
     -+	EOF
     -+	test_cmp expected actual
     -+
     -+'
     -+
     -+test_expect_success 'reported-by' '
     -+
     -+	>file2 &&
     -+	git add file2 &&
     -+	git commit --reported-by="foo <bar@frotz>" \
     -+	--reported-by="foo2 <bar2@frotz>" -m "thank you" &&
     -+	git cat-file commit HEAD >commit.msg &&
     -+	sed -ne "s/Reported-by: //p" commit.msg >actual &&
     -+	cat >expected <<-\EOF &&
     -+	foo <bar@frotz>
     -+	foo2 <bar2@frotz>
     -+	EOF
     -+	test_cmp expected actual
     -+
     -+'
     -+
     -+test_expect_success 'reviewed-by' '
     -+
     -+	>file3 &&
     -+	git add file3 &&
     -+	git commit --reviewed-by="foo <bar@frotz>" \
     -+	--reviewed-by="foo2 <bar2@frotz>" -m "thank you" &&
     -+	git cat-file commit HEAD >commit.msg &&
     -+	sed -ne "s/Reviewed-by: //p" commit.msg >actual &&
     -+	cat >expected <<-\EOF &&
     -+	foo <bar@frotz>
     -+	foo2 <bar2@frotz>
     -+	EOF
     -+	test_cmp expected actual
     -+
     -+'
     -+
     -+test_expect_success 'mentored-by' '
     -+
     -+	>file4 &&
     -+	git add file4 &&
     -+	git commit --mentored-by="foo <bar@frotz>" \
     -+	--mentored-by="foo2 <bar2@frotz>" -m "thank you" &&
     -+	git cat-file commit HEAD >commit.msg &&
     -+	sed -ne "s/Mentored-by: //p" commit.msg >actual &&
     -+	cat >expected <<-\EOF &&
     -+	foo <bar@frotz>
     -+	foo2 <bar2@frotz>
     -+	EOF
     -+	test_cmp expected actual
     -+
     -+'
     -+
     -+test_expect_success 'multiple signatures' '
     -+
     -+	>file5 &&
     -+	git add file5 &&
     -+	git commit --helped-by="foo <bar@frotz>" \
     -+	--reviewed-by="foo2 <bar2@frotz>" \
     -+	--mentored-by="foo3 <bar3@frotz>" \
     -+	--reported-by="foo4 <bar4@frotz>" -s -m "thank you" &&
     ++	git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
     ++	--trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
     ++	--trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
     ++	--trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
     ++	-m "hello" &&
      +	git cat-file commit HEAD >commit.msg &&
      +	sed -e "1,7d" commit.msg >actual &&
      +	cat >expected <<-\EOF &&
      +	Signed-off-by: C O Mitter <committer@example.com>
     -+	Helped-by: foo <bar@frotz>
     -+	Reviewed-by: foo2 <bar2@frotz>
     -+	Reported-by: foo4 <bar4@frotz>
     -+	Mentored-by: foo3 <bar3@frotz>
     ++	Signed-off-by: C O Mitter1 <committer1@example.com>
     ++	Helped-by: C O Mitter2 <committer2@example.com>
     ++	Reported-by: C O Mitter3 <committer3@example.com>
     ++	Mentored-by: C O Mitter4 <committer4@example.com>
      +	EOF
      +	test_cmp expected actual
     -+
     -+'
     -+
     -+test_expect_success 'multiple signatures (use abbreviations)' '
     -+
     -+	>file6 &&
     -+	git add file6 &&
     -+	git commit -H "foo <bar@frotz>" \
     -+	-R "foo2 <bar2@frotz>" \
     -+	-M "foo3 <bar3@frotz>" \
     -+	-r "foo4 <bar4@frotz>" -s -m "thank you" &&
     -+	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,7d" commit.msg >actual &&
     -+	cat >expected <<-\EOF &&
     -+	Signed-off-by: C O Mitter <committer@example.com>
     -+	Helped-by: foo <bar@frotz>
     -+	Reviewed-by: foo2 <bar2@frotz>
     -+	Reported-by: foo4 <bar4@frotz>
     -+	Mentored-by: foo3 <bar3@frotz>
     -+	EOF
     -+	test_cmp expected actual
     -+
      +'
      +
       test_expect_success 'multiple -m' '


 Documentation/git-commit.txt |  8 +++++++-
 builtin/commit.c             | 18 ++++++++++++++++++
 t/t7502-commit-porcelain.sh  | 20 ++++++++++++++++++++
 3 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 17150fa7eabe..764513a3f287 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -14,7 +14,7 @@ SYNOPSIS
 	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
 	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
 	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
-	   [-S[<keyid>]] [--] [<pathspec>...]
+	   [-S[<keyid>]] [--] [<pathspec>...] [(--trailer <token>[(=|:)<value>])...]
 
 DESCRIPTION
 -----------
@@ -166,6 +166,12 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
 
 include::signoff-option.txt[]
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g.  `git commit --trailer "Signed-off-by:C O Mitter <committer@example.com>" \
+	--trailer "Helped-by:C O Mitter <committer@example.com>"`will add the "Signed-off" trailer
+	and the "Helped-by" trailer in the commit message.)
+
 -n::
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
diff --git a/builtin/commit.c b/builtin/commit.c
index 739110c5a7f6..abbd136b27f0 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,6 +113,8 @@ static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit, *pathspec_from_file;
+struct child_process run_trailer = CHILD_PROCESS_INIT;
+static const char *trailer;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -131,6 +133,17 @@ static struct strbuf message = STRBUF_INIT;
 
 static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
 
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+	if (unset) {
+		strvec_clear(&run_trailer.args);
+		return -1;
+	}
+	run_trailer.git_cmd = 1;
+	strvec_pushl(&run_trailer.args, "--trailer", arg, NULL);
+	return 0;
+}
+
 static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
 {
 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -958,6 +971,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
 	fclose(s->fp);
 
+	run_command(&run_trailer);
+
 	/*
 	 * Reject an attempt to record a non-merge empty commit without
 	 * explicit --allow-empty. In the cherry-pick case, it may be
@@ -1507,6 +1522,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK(0, "trailer", &trailer, N_("trailer"), N_("trailer(s) to add"), opt_pass_trailer),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
@@ -1577,6 +1593,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 			die(_("could not parse HEAD commit"));
 	}
 	verbose = -1; /* unspecified */
+	strvec_pushl(&run_trailer.args, "interpret-trailers",
+		"--in-place", "--where=end", git_path_commit_editmsg(), NULL);
 	argc = parse_and_validate_options(argc, argv, builtin_commit_options,
 					  builtin_commit_usage,
 					  prefix, current_head, &s);
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 6396897cc818..4b9ac4587d17 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -154,6 +154,26 @@ test_expect_success 'sign off' '
 
 '
 
+test_expect_success 'trailer' '
+	>file1 &&
+	git add file1 &&
+	git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
+	--trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
+	--trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
+	--trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
+	-m "hello" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,7d" commit.msg >actual &&
+	cat >expected <<-\EOF &&
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C O Mitter1 <committer1@example.com>
+	Helped-by: C O Mitter2 <committer2@example.com>
+	Reported-by: C O Mitter3 <committer3@example.com>
+	Mentored-by: C O Mitter4 <committer4@example.com>
+	EOF
+	test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&

base-commit: 13d7ab6b5d7929825b626f050b62a11241ea4945
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v2] [GSOC] commit: add trailer command
  2021-03-12 15:54 ` [PATCH v2] [GSOC] commit: add trailer command ZheNing Hu via GitGitGadget
@ 2021-03-14  4:19   ` Christian Couder
  2021-03-14  7:09     ` ZheNing Hu
  2021-03-14 22:45     ` Junio C Hamano
  2021-03-14 13:02   ` [PATCH v3] [GSOC] commit: add --trailer option ZheNing Hu via GitGitGadget
  1 sibling, 2 replies; 84+ messages in thread
From: Christian Couder @ 2021-03-14  4:19 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Junio C Hamano, Brandon Casey,
	Shourya Shukla, ZheNing Hu

On Fri, Mar 12, 2021 at 4:59 PM ZheNing Hu via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> From: ZheNing Hu <adlternative@gmail.com>
>
> Historically, Git has supported the 'Signed-off-by' commit trailer
> using the '--signoff' and the '-s' option from the command line.
> But users may need to provide richer trailer information from the
> command line such as "Helped-by", "Reported-by", "Mentored-by",

Nit: not sure that "richer" is the proper word here. I would just use
"other" instead.

> Now use `--trailer <token>[(=|:)<value>]` pass the trailers to
> `interpret-trailers` and generate trailers in commit messages.

The subject says "add trailer command" while here you say "use". So
which one is it? Does "--trailer" already exist, and we are just going
to use it? Or will this patch series actually "add" it?

Looking at the existing options and the code of this patch series, the
patch series actually adds the "--trailer" (not "trailer") option, so
"add" or "implement" would be clearer than "use".

So maybe something like the following might be better:

"Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
other trailers to `interpret-trailers` and insert them into commit
messages."

Also something like "--trailer" is usually called an option (or
sometimes a flag), not a command (especially not when the word is not
a verb, and when the new feature isn't a new exclusive mode of
operation). So something like "commit: add --trailer option" might be
a better subject.

> Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> ---
>     [GSOC] commit: provides multiple signatures from command line

It looks like this is using the subject of a patch that previously
attempted to add features with a similar purpose. I don't think you
need to put it there, or if you want to refer to it, I think it might
be better to be a bit more explicit, for example like:

"This patch replaces my previous attempt to provide similar features
in a patch called: [GSOC] commit: provides multiple signatures from
command line."

>     Now maintainers or developers can also use commit
>     --trailer="Signed-off-by:commiter<email>" from the command line to
>     provide trailers to commit messages. This solution may be more
>     generalized than v1.

Ok, I agree that it's a good idea to have a good generic solution
first, before having specialized options for specific trailers.

> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v2
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v2
> Pull-Request: https://github.com/gitgitgadget/git/pull/901
>
> Range-diff vs v1:

If this patch series has very few code and commit messages in common
with a previous attempt at implementing similar features, it might be
better to make it a new patch series rather than a v2. This could
avoid sending range-diffs that are mostly useless.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v2] [GSOC] commit: add trailer command
  2021-03-14  4:19   ` Christian Couder
@ 2021-03-14  7:09     ` ZheNing Hu
  2021-03-14 22:45     ` Junio C Hamano
  1 sibling, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-14  7:09 UTC (permalink / raw)
  To: Christian Couder
  Cc: ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla

Christian Couder <christian.couder@gmail.com> 于2021年3月14日周日 下午12:19写道:
>
> On Fri, Mar 12, 2021 at 4:59 PM ZheNing Hu via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
> >
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > Historically, Git has supported the 'Signed-off-by' commit trailer
> > using the '--signoff' and the '-s' option from the command line.
> > But users may need to provide richer trailer information from the
> > command line such as "Helped-by", "Reported-by", "Mentored-by",
>
> Nit: not sure that "richer" is the proper word here. I would just use
> "other" instead.
>

OK.

> > Now use `--trailer <token>[(=|:)<value>]` pass the trailers to
> > `interpret-trailers` and generate trailers in commit messages.
>
> The subject says "add trailer command" while here you say "use". So
> which one is it? Does "--trailer" already exist, and we are just going
> to use it? Or will this patch series actually "add" it?
>
> Looking at the existing options and the code of this patch series, the
> patch series actually adds the "--trailer" (not "trailer") option, so
> "add" or "implement" would be clearer than "use".
>

You're right. "add" will be more accurate in this situation.

> So maybe something like the following might be better:
>
> "Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
> other trailers to `interpret-trailers` and insert them into commit
> messages."
>
> Also something like "--trailer" is usually called an option (or
> sometimes a flag), not a command (especially not when the word is not
> a verb, and when the new feature isn't a new exclusive mode of
> operation). So something like "commit: add --trailer option" might be
> a better subject.
>
> > Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> > ---
> >     [GSOC] commit: provides multiple signatures from command line
>
> It looks like this is using the subject of a patch that previously
> attempted to add features with a similar purpose. I don't think you
> need to put it there, or if you want to refer to it, I think it might
> be better to be a bit more explicit, for example like:
>
> "This patch replaces my previous attempt to provide similar features
> in a patch called: [GSOC] commit: provides multiple signatures from
> command line."
>

I may have thought that the effect of the two patch was closer so did not
change it.

> >     Now maintainers or developers can also use commit
> >     --trailer="Signed-off-by:commiter<email>" from the command line to
> >     provide trailers to commit messages. This solution may be more
> >     generalized than v1.
>
> Ok, I agree that it's a good idea to have a good generic solution
> first, before having specialized options for specific trailers.
>

Thanks. :)

> > Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v2
> > Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v2
> > Pull-Request: https://github.com/gitgitgadget/git/pull/901
> >
> > Range-diff vs v1:
>
> If this patch series has very few code and commit messages in common
> with a previous attempt at implementing similar features, it might be
> better to make it a new patch series rather than a v2. This could
> avoid sending range-diffs that are mostly useless.

Thank you for these pertinent suggestions. I will pay more attention.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v3] [GSOC] commit: add --trailer option
  2021-03-12 15:54 ` [PATCH v2] [GSOC] commit: add trailer command ZheNing Hu via GitGitGadget
  2021-03-14  4:19   ` Christian Couder
@ 2021-03-14 13:02   ` ZheNing Hu via GitGitGadget
  2021-03-14 13:10     ` Rafael Silva
  2021-03-14 15:58     ` [PATCH v4] " ZheNing Hu via GitGitGadget
  1 sibling, 2 replies; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-14 13:02 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Historically, Git has supported the 'Signed-off-by' commit trailer
using the '--signoff' and the '-s' option from the command line.
But users may need to provide other trailer information from the
command line such as "Helped-by", "Reported-by", "Mentored-by",

Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
other trailers to `interpret-trailers` and insert them into commit
messages.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC] commit: add --trailer option
    
    Now maintainers or developers can also use commit
    --trailer="Signed-off-by:commiter<email>" from the command line to
    provide trailers to commit messages. This solution may be more
    generalized than v1.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/901

Range-diff vs v2:

 1:  4c507d17db4f ! 1:  b4e161a98f8b [GSOC] commit: add trailer command
     @@ Metadata
      Author: ZheNing Hu <adlternative@gmail.com>
      
       ## Commit message ##
     -    [GSOC] commit: add trailer command
     +    [GSOC] commit: add --trailer option
      
          Historically, Git has supported the 'Signed-off-by' commit trailer
          using the '--signoff' and the '-s' option from the command line.
     -    But users may need to provide richer trailer information from the
     +    But users may need to provide other trailer information from the
          command line such as "Helped-by", "Reported-by", "Mentored-by",
      
     -    Now use `--trailer <token>[(=|:)<value>]` pass the trailers to
     -    `interpret-trailers` and generate trailers in commit messages.
     +    Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
     +    other trailers to `interpret-trailers` and insert them into commit
     +    messages.
      
          Signed-off-by: ZheNing Hu <adlternative@gmail.com>
      


 Documentation/git-commit.txt |  8 +++++++-
 builtin/commit.c             | 18 ++++++++++++++++++
 t/t7502-commit-porcelain.sh  | 20 ++++++++++++++++++++
 3 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 17150fa7eabe..764513a3f287 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -14,7 +14,7 @@ SYNOPSIS
 	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
 	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
 	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
-	   [-S[<keyid>]] [--] [<pathspec>...]
+	   [-S[<keyid>]] [--] [<pathspec>...] [(--trailer <token>[(=|:)<value>])...]
 
 DESCRIPTION
 -----------
@@ -166,6 +166,12 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
 
 include::signoff-option.txt[]
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g.  `git commit --trailer "Signed-off-by:C O Mitter <committer@example.com>" \
+	--trailer "Helped-by:C O Mitter <committer@example.com>"`will add the "Signed-off" trailer
+	and the "Helped-by" trailer in the commit message.)
+
 -n::
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
diff --git a/builtin/commit.c b/builtin/commit.c
index 739110c5a7f6..abbd136b27f0 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,6 +113,8 @@ static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit, *pathspec_from_file;
+struct child_process run_trailer = CHILD_PROCESS_INIT;
+static const char *trailer;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -131,6 +133,17 @@ static struct strbuf message = STRBUF_INIT;
 
 static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
 
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+	if (unset) {
+		strvec_clear(&run_trailer.args);
+		return -1;
+	}
+	run_trailer.git_cmd = 1;
+	strvec_pushl(&run_trailer.args, "--trailer", arg, NULL);
+	return 0;
+}
+
 static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
 {
 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -958,6 +971,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
 	fclose(s->fp);
 
+	run_command(&run_trailer);
+
 	/*
 	 * Reject an attempt to record a non-merge empty commit without
 	 * explicit --allow-empty. In the cherry-pick case, it may be
@@ -1507,6 +1522,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK(0, "trailer", &trailer, N_("trailer"), N_("trailer(s) to add"), opt_pass_trailer),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
@@ -1577,6 +1593,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 			die(_("could not parse HEAD commit"));
 	}
 	verbose = -1; /* unspecified */
+	strvec_pushl(&run_trailer.args, "interpret-trailers",
+		"--in-place", "--where=end", git_path_commit_editmsg(), NULL);
 	argc = parse_and_validate_options(argc, argv, builtin_commit_options,
 					  builtin_commit_usage,
 					  prefix, current_head, &s);
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 6396897cc818..4b9ac4587d17 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -154,6 +154,26 @@ test_expect_success 'sign off' '
 
 '
 
+test_expect_success 'trailer' '
+	>file1 &&
+	git add file1 &&
+	git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
+	--trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
+	--trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
+	--trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
+	-m "hello" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,7d" commit.msg >actual &&
+	cat >expected <<-\EOF &&
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C O Mitter1 <committer1@example.com>
+	Helped-by: C O Mitter2 <committer2@example.com>
+	Reported-by: C O Mitter3 <committer3@example.com>
+	Mentored-by: C O Mitter4 <committer4@example.com>
+	EOF
+	test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&

base-commit: 13d7ab6b5d7929825b626f050b62a11241ea4945
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v3] [GSOC] commit: add --trailer option
  2021-03-14 13:02   ` [PATCH v3] [GSOC] commit: add --trailer option ZheNing Hu via GitGitGadget
@ 2021-03-14 13:10     ` Rafael Silva
  2021-03-14 14:13       ` ZheNing Hu
  2021-03-14 15:58     ` [PATCH v4] " ZheNing Hu via GitGitGadget
  1 sibling, 1 reply; 84+ messages in thread
From: Rafael Silva @ 2021-03-14 13:10 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Junio C Hamano, Brandon Casey,
	Shourya Shukla, Christian Couder, ZheNing Hu


"ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:

> diff --git a/builtin/commit.c b/builtin/commit.c
> index 739110c5a7f6..abbd136b27f0 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -113,6 +113,8 @@ static int config_commit_verbose = -1; /* unspecified */
>  static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
>  static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
>  static char *sign_commit, *pathspec_from_file;
> +struct child_process run_trailer = CHILD_PROCESS_INIT;
> +static const char *trailer;
>  
>  /*
>   * The default commit message cleanup mode will remove the lines
> @@ -131,6 +133,17 @@ static struct strbuf message = STRBUF_INIT;
>  
>  static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
>  
> +static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
> +{
> +	if (unset) {
> +		strvec_clear(&run_trailer.args);
> +		return -1;
> +	}
> +	run_trailer.git_cmd = 1;
> +	strvec_pushl(&run_trailer.args, "--trailer", arg, NULL);
> +	return 0;
> +}
> +
>  static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
>  {
>  	enum wt_status_format *value = (enum wt_status_format *)opt->value;
> @@ -958,6 +971,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
>  
>  	fclose(s->fp);
>  
> +	run_command(&run_trailer);
> +

There is slight problem with running the command unconditionally.
If no --trailer is passed, then the opt_pass_trailer() backend
is never called, which consequently will not set the trailer
command ".git_cmd" option to 1.

This will lead the run_command() API to not interpret the command as git
internal, and attempt to launch as an usual command "interpret-trailers"
that will likely not exist or launch an unwanted command that is not
part of the GIT suite.

This can be seen by running `git commit` without any options:

     $ ./bin-wrappers/git -C /tmp/test commit
     error: cannot run interpret-trailers: No such file or directory
     ...

The `.git_cmd` should be set to true before running the command.

(the above output is from a built version with v2.31.0-rc2 + this patch
 for confirmation).


Furthermore, in my opinion, we shouldn't even bother to run the command
if no --trailer is passed, otherwise, we always be paying the cost of
launching an OS process regardless if the user doesn't want to add
trailers in theirs projects.

With that said and based on this current implementation, maybe an
improved version will look like:

        if (run_trailer.args.nr) {
                run_trailer.git_cmd = 1;
                run_command(&run_trailer);
        }

Naturally the `git_cmd = 1` will be removed from opt_pass_trailer()
function as it won't be necessary. As minor bonus, we don't end up
setting the value for every new --trailer :).

>  	/*
>  	 * Reject an attempt to record a non-merge empty commit without
>  	 * explicit --allow-empty. In the cherry-pick case, it may be
> @@ -1507,6 +1522,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>  		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK(0, "trailer", &trailer, N_("trailer"), N_("trailer(s) to add"), opt_pass_trailer),
>  		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
>  		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
>  		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
> @@ -1577,6 +1593,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>  			die(_("could not parse HEAD commit"));
>  	}
>  	verbose = -1; /* unspecified */
> +	strvec_pushl(&run_trailer.args, "interpret-trailers",
> +		"--in-place", "--where=end", git_path_commit_editmsg(), NULL);

Style: The "--in-place" part should be aligned with the parentheses
much the like the following line with the "argc = parse_and_validate_options....".

For example:

	strvec_pushl(&run_trailer.args, "interpret-trailers",
		     "--in-place", "--where=end", .....

>  	argc = parse_and_validate_options(argc, argv, builtin_commit_options,
>  					  builtin_commit_usage,
>  					  prefix, current_head, &s);
> diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
> index 6396897cc818..4b9ac4587d17 100755
> --- a/t/t7502-commit-porcelain.sh
> +++ b/t/t7502-commit-porcelain.sh
> @@ -154,6 +154,26 @@ test_expect_success 'sign off' '
>  
>  '
>  
> +test_expect_success 'trailer' '
> +	>file1 &&
> +	git add file1 &&
> +	git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
> +	--trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
> +	--trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
> +	--trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
> +	-m "hello" &&

Perhaps here, the --trailer lines and "-m hello" option should be
indent in order to make it clear that these option are part of the
"git commit" from the above line, something like this:

	git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
	        --trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
	        --trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
	        --trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
	        -m "hello" &&

> +	git cat-file commit HEAD >commit.msg &&
> +	sed -e "1,7d" commit.msg >actual &&
> +	cat >expected <<-\EOF &&
> +	Signed-off-by: C O Mitter <committer@example.com>
> +	Signed-off-by: C O Mitter1 <committer1@example.com>
> +	Helped-by: C O Mitter2 <committer2@example.com>
> +	Reported-by: C O Mitter3 <committer3@example.com>
> +	Mentored-by: C O Mitter4 <committer4@example.com>
> +	EOF
> +	test_cmp expected actual
> +'
> +
>  test_expect_success 'multiple -m' '


Hope these comments are useful.
-- 
Thanks
Rafael

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v3] [GSOC] commit: add --trailer option
  2021-03-14 13:10     ` Rafael Silva
@ 2021-03-14 14:13       ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-14 14:13 UTC (permalink / raw)
  To: Rafael Silva
  Cc: ZheNing Hu via GitGitGadget, Git List, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Christian Couder

Rafael Silva <rafaeloliveira.cs@gmail.com> 于2021年3月14日周日 下午9:46写道:
>
>
> "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
> > diff --git a/builtin/commit.c b/builtin/commit.c
> > index 739110c5a7f6..abbd136b27f0 100644
> > --- a/builtin/commit.c
> > +++ b/builtin/commit.c
> > @@ -113,6 +113,8 @@ static int config_commit_verbose = -1; /* unspecified */
> >  static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
> >  static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
> >  static char *sign_commit, *pathspec_from_file;
> > +struct child_process run_trailer = CHILD_PROCESS_INIT;
> > +static const char *trailer;
> >
> >  /*
> >   * The default commit message cleanup mode will remove the lines
> > @@ -131,6 +133,17 @@ static struct strbuf message = STRBUF_INIT;
> >
> >  static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
> >
> > +static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
> > +{
> > +     if (unset) {
> > +             strvec_clear(&run_trailer.args);
> > +             return -1;
> > +     }
> > +     run_trailer.git_cmd = 1;
> > +     strvec_pushl(&run_trailer.args, "--trailer", arg, NULL);
> > +     return 0;
> > +}
> > +
> >  static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
> >  {
> >       enum wt_status_format *value = (enum wt_status_format *)opt->value;
> > @@ -958,6 +971,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
> >
> >       fclose(s->fp);
> >
> > +     run_command(&run_trailer);
> > +
>
> There is slight problem with running the command unconditionally.
> If no --trailer is passed, then the opt_pass_trailer() backend
> is never called, which consequently will not set the trailer
> command ".git_cmd" option to 1.
>
> This will lead the run_command() API to not interpret the command as git
> internal, and attempt to launch as an usual command "interpret-trailers"
> that will likely not exist or launch an unwanted command that is not
> part of the GIT suite.
>
> This can be seen by running `git commit` without any options:
>
>      $ ./bin-wrappers/git -C /tmp/test commit
>      error: cannot run interpret-trailers: No such file or directory
>      ...
>
> The `.git_cmd` should be set to true before running the command.
>
> (the above output is from a built version with v2.31.0-rc2 + this patch
>  for confirmation).
>
>
> Furthermore, in my opinion, we shouldn't even bother to run the command
> if no --trailer is passed, otherwise, we always be paying the cost of
> launching an OS process regardless if the user doesn't want to add
> trailers in theirs projects.
>
> With that said and based on this current implementation, maybe an
> improved version will look like:
>
>         if (run_trailer.args.nr) {
>                 run_trailer.git_cmd = 1;
>                 run_command(&run_trailer);
>         }
>
> Naturally the `git_cmd = 1` will be removed from opt_pass_trailer()
> function as it won't be necessary. As minor bonus, we don't end up
> setting the value for every new --trailer :).

Thank you, I didn't notice the problem before :).

>
> >       /*
> >        * Reject an attempt to record a non-merge empty commit without
> >        * explicit --allow-empty. In the cherry-pick case, it may be
> > @@ -1507,6 +1522,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
> >               OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK(0, "trailer", &trailer, N_("trailer"), N_("trailer(s) to add"), opt_pass_trailer),
> >               OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
> >               OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
> >               OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
> > @@ -1577,6 +1593,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
> >                       die(_("could not parse HEAD commit"));
> >       }
> >       verbose = -1; /* unspecified */
> > +     strvec_pushl(&run_trailer.args, "interpret-trailers",
> > +             "--in-place", "--where=end", git_path_commit_editmsg(), NULL);
>
> Style: The "--in-place" part should be aligned with the parentheses
> much the like the following line with the "argc = parse_and_validate_options....".
>

Well, the CodingGuidelines say it's both vaild. But you are right,
try to keep the same format as nearby code will be better.

> For example:
>
>         strvec_pushl(&run_trailer.args, "interpret-trailers",
>                      "--in-place", "--where=end", .....
>
> >       argc = parse_and_validate_options(argc, argv, builtin_commit_options,
> >                                         builtin_commit_usage,
> >                                         prefix, current_head, &s);
> > diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
> > index 6396897cc818..4b9ac4587d17 100755
> > --- a/t/t7502-commit-porcelain.sh
> > +++ b/t/t7502-commit-porcelain.sh
> > @@ -154,6 +154,26 @@ test_expect_success 'sign off' '
> >
> >  '
> >
> > +test_expect_success 'trailer' '
> > +     >file1 &&
> > +     git add file1 &&
> > +     git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
> > +     --trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
> > +     --trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
> > +     --trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
> > +     -m "hello" &&
>
> Perhaps here, the --trailer lines and "-m hello" option should be
> indent in order to make it clear that these option are part of the
> "git commit" from the above line, something like this:
>
>         git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
>                 --trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
>                 --trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
>                 --trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
>                 -m "hello" &&
>
> > +     git cat-file commit HEAD >commit.msg &&
> > +     sed -e "1,7d" commit.msg >actual &&
> > +     cat >expected <<-\EOF &&
> > +     Signed-off-by: C O Mitter <committer@example.com>
> > +     Signed-off-by: C O Mitter1 <committer1@example.com>
> > +     Helped-by: C O Mitter2 <committer2@example.com>
> > +     Reported-by: C O Mitter3 <committer3@example.com>
> > +     Mentored-by: C O Mitter4 <committer4@example.com>
> > +     EOF
> > +     test_cmp expected actual
> > +'
> > +
> >  test_expect_success 'multiple -m' '
>
>
> Hope these comments are useful.
> --
> Thanks
> Rafael

 I am gratitude to you for helping me.
--
Thanks
ZheNing Hu

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v4] [GSOC] commit: add --trailer option
  2021-03-14 13:02   ` [PATCH v3] [GSOC] commit: add --trailer option ZheNing Hu via GitGitGadget
  2021-03-14 13:10     ` Rafael Silva
@ 2021-03-14 15:58     ` ZheNing Hu via GitGitGadget
  2021-03-14 23:52       ` Junio C Hamano
                         ` (2 more replies)
  1 sibling, 3 replies; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-14 15:58 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Historically, Git has supported the 'Signed-off-by' commit trailer
using the '--signoff' and the '-s' option from the command line.
But users may need to provide other trailer information from the
command line such as "Helped-by", "Reported-by", "Mentored-by",

Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
other trailers to `interpret-trailers` and insert them into commit
messages.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC] commit: add --trailer option
    
    Now maintainers or developers can also use commit
    --trailer="Signed-off-by:commiter<email>" from the command line to
    provide trailers to commit messages. This solution may be more
    generalized than v1.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/901

Range-diff vs v3:

 1:  b4e161a98f8b ! 1:  dc507553ef4f [GSOC] commit: add --trailer option
     @@ Documentation/git-commit.txt: The `-m` option is mutually exclusive with `-c`, `
       
      +--trailer <token>[(=|:)<value>]::
      +	Specify a (<token>, <value>) pair that should be applied as a
     -+	trailer. (e.g.  `git commit --trailer "Signed-off-by:C O Mitter <committer@example.com>" \
     -+	--trailer "Helped-by:C O Mitter <committer@example.com>"`will add the "Signed-off" trailer
     ++	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
     ++	<committer@example.com>" --trailer "Helped-by:C O Mitter \
     ++	<committer@example.com>"` will add the "Signed-off" trailer
      +	and the "Helped-by" trailer in the commit message.)
      +
       -n::
     @@ builtin/commit.c: static struct strbuf message = STRBUF_INIT;
      +		strvec_clear(&run_trailer.args);
      +		return -1;
      +	}
     -+	run_trailer.git_cmd = 1;
      +	strvec_pushl(&run_trailer.args, "--trailer", arg, NULL);
      +	return 0;
      +}
     @@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const cha
       
       	fclose(s->fp);
       
     -+	run_command(&run_trailer);
     ++	if (run_trailer.args.nr != 4) {
     ++		run_trailer.git_cmd = 1;
     ++		run_command(&run_trailer);
     ++	} else
     ++		strvec_clear(&run_trailer.args);
      +
       	/*
       	 * Reject an attempt to record a non-merge empty commit without
     @@ builtin/commit.c: int cmd_commit(int argc, const char **argv, const char *prefix
       	}
       	verbose = -1; /* unspecified */
      +	strvec_pushl(&run_trailer.args, "interpret-trailers",
     -+		"--in-place", "--where=end", git_path_commit_editmsg(), NULL);
     ++		    "--in-place", "--where=end", git_path_commit_editmsg(), NULL);
       	argc = parse_and_validate_options(argc, argv, builtin_commit_options,
       					  builtin_commit_usage,
       					  prefix, current_head, &s);
     @@ t/t7502-commit-porcelain.sh: test_expect_success 'sign off' '
      +	>file1 &&
      +	git add file1 &&
      +	git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
     -+	--trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
     -+	--trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
     -+	--trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
     -+	-m "hello" &&
     ++		--trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
     ++		--trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
     ++		--trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
     ++		-m "hello" &&
      +	git cat-file commit HEAD >commit.msg &&
      +	sed -e "1,7d" commit.msg >actual &&
      +	cat >expected <<-\EOF &&


 Documentation/git-commit.txt |  9 ++++++++-
 builtin/commit.c             | 21 +++++++++++++++++++++
 t/t7502-commit-porcelain.sh  | 20 ++++++++++++++++++++
 3 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 17150fa7eabe..73a7507db47f 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -14,7 +14,7 @@ SYNOPSIS
 	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
 	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
 	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
-	   [-S[<keyid>]] [--] [<pathspec>...]
+	   [-S[<keyid>]] [--] [<pathspec>...] [(--trailer <token>[(=|:)<value>])...]
 
 DESCRIPTION
 -----------
@@ -166,6 +166,13 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
 
 include::signoff-option.txt[]
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
+	<committer@example.com>" --trailer "Helped-by:C O Mitter \
+	<committer@example.com>"` will add the "Signed-off" trailer
+	and the "Helped-by" trailer in the commit message.)
+
 -n::
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
diff --git a/builtin/commit.c b/builtin/commit.c
index 739110c5a7f6..b35ae5c9790d 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,6 +113,8 @@ static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit, *pathspec_from_file;
+struct child_process run_trailer = CHILD_PROCESS_INIT;
+static const char *trailer;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -131,6 +133,16 @@ static struct strbuf message = STRBUF_INIT;
 
 static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
 
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+	if (unset) {
+		strvec_clear(&run_trailer.args);
+		return -1;
+	}
+	strvec_pushl(&run_trailer.args, "--trailer", arg, NULL);
+	return 0;
+}
+
 static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
 {
 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -958,6 +970,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
 	fclose(s->fp);
 
+	if (run_trailer.args.nr != 4) {
+		run_trailer.git_cmd = 1;
+		run_command(&run_trailer);
+	} else
+		strvec_clear(&run_trailer.args);
+
 	/*
 	 * Reject an attempt to record a non-merge empty commit without
 	 * explicit --allow-empty. In the cherry-pick case, it may be
@@ -1507,6 +1525,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK(0, "trailer", &trailer, N_("trailer"), N_("trailer(s) to add"), opt_pass_trailer),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
@@ -1577,6 +1596,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 			die(_("could not parse HEAD commit"));
 	}
 	verbose = -1; /* unspecified */
+	strvec_pushl(&run_trailer.args, "interpret-trailers",
+		    "--in-place", "--where=end", git_path_commit_editmsg(), NULL);
 	argc = parse_and_validate_options(argc, argv, builtin_commit_options,
 					  builtin_commit_usage,
 					  prefix, current_head, &s);
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 6396897cc818..0acf23799931 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -154,6 +154,26 @@ test_expect_success 'sign off' '
 
 '
 
+test_expect_success 'trailer' '
+	>file1 &&
+	git add file1 &&
+	git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
+		--trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
+		--trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
+		--trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
+		-m "hello" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,7d" commit.msg >actual &&
+	cat >expected <<-\EOF &&
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C O Mitter1 <committer1@example.com>
+	Helped-by: C O Mitter2 <committer2@example.com>
+	Reported-by: C O Mitter3 <committer3@example.com>
+	Mentored-by: C O Mitter4 <committer4@example.com>
+	EOF
+	test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&

base-commit: 13d7ab6b5d7929825b626f050b62a11241ea4945
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v2] [GSOC] commit: add trailer command
  2021-03-14  4:19   ` Christian Couder
  2021-03-14  7:09     ` ZheNing Hu
@ 2021-03-14 22:45     ` Junio C Hamano
  1 sibling, 0 replies; 84+ messages in thread
From: Junio C Hamano @ 2021-03-14 22:45 UTC (permalink / raw)
  To: Christian Couder
  Cc: ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn, Brandon Casey,
	Shourya Shukla, ZheNing Hu

Christian Couder <christian.couder@gmail.com> writes:

> Ok, I agree that it's a good idea to have a good generic solution
> first, before having specialized options for specific trailers.
>
>> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v2
>> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v2
>> Pull-Request: https://github.com/gitgitgadget/git/pull/901
>>
>> Range-diff vs v1:
>
> If this patch series has very few code and commit messages in common
> with a previous attempt at implementing similar features, it might be
> better to make it a new patch series rather than a v2. This could
> avoid sending range-diffs that are mostly useless.

Thanks for all the good suggestions.


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4] [GSOC] commit: add --trailer option
  2021-03-14 15:58     ` [PATCH v4] " ZheNing Hu via GitGitGadget
@ 2021-03-14 23:52       ` Junio C Hamano
  2021-03-15  1:27         ` ZheNing Hu
  2021-03-15  3:24       ` [PATCH v5] " ZheNing Hu via GitGitGadget
  2021-03-15  4:38       ` [PATCH v4] " Junio C Hamano
  2 siblings, 1 reply; 84+ messages in thread
From: Junio C Hamano @ 2021-03-14 23:52 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva, ZheNing Hu

"ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:

> @@ -958,6 +970,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
>  
>  	fclose(s->fp);
>  
> +	if (run_trailer.args.nr != 4) {
> +		run_trailer.git_cmd = 1;
> +		run_command(&run_trailer);

This hardcoded magic 4 is very brittle.  It probably makes sense to
have another string vector, that is used only to accumulate
--trailer arguments and nothing else, and check if that string
vector is empty here.

IOW this part would become ...

	if (trailer_args.nr) {
		strvec_pushl(&run_trailer.args,	"interpret-trailers", 
			     "--in-place", ...);
		strvec_pushv(&run_trailer.args, trailer_args.v);
		run_trailer.git_cmd = 1;
		run_command(&run_trailer);
	}

> +	} else
> +		strvec_clear(&run_trailer.args);

... and there is no need to have "else" that won't need to do
anything.

>  	/*
>  	 * Reject an attempt to record a non-merge empty commit without
>  	 * explicit --allow-empty. In the cherry-pick case, it may be
> @@ -1507,6 +1525,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>  		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK(0, "trailer", &trailer, N_("trailer"), N_("trailer(s) to add"), opt_pass_trailer),
>  		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
>  		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
>  		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
> @@ -1577,6 +1596,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>  			die(_("could not parse HEAD commit"));
>  	}
>  	verbose = -1; /* unspecified */
> +	strvec_pushl(&run_trailer.args, "interpret-trailers",
> +		    "--in-place", "--where=end", git_path_commit_editmsg(), NULL);

And this line will be gone.

>  	argc = parse_and_validate_options(argc, argv, builtin_commit_options,
>  					  builtin_commit_usage,
>  					  prefix, current_head, &s);

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4] [GSOC] commit: add --trailer option
  2021-03-14 23:52       ` Junio C Hamano
@ 2021-03-15  1:27         ` ZheNing Hu
  2021-03-15  4:42           ` Junio C Hamano
  0 siblings, 1 reply; 84+ messages in thread
From: ZheNing Hu @ 2021-03-15  1:27 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: ZheNing Hu via GitGitGadget, Git List, Bradley M. Kuhn,
	Brandon Casey, Shourya Shukla, Christian Couder, Rafael Silva

Junio C Hamano <gitster@pobox.com> 于2021年3月15日周一 上午7:52写道:
> IOW this part would become ...
>
>         if (trailer_args.nr) {
>                 strvec_pushl(&run_trailer.args, "interpret-trailers",
>                              "--in-place", ...);
>                 strvec_pushv(&run_trailer.args, trailer_args.v);
>                 run_trailer.git_cmd = 1;
>                 run_command(&run_trailer);
>         }
>
> > +     } else
> > +             strvec_clear(&run_trailer.args);
>
> ... and there is no need to have "else" that won't need to do
> anything.

Yes, but we also should clear "trailer_args" in "else" here, and check the
return value of the "run_command()" for clear "run_trailer.args".

>
> >       verbose = -1; /* unspecified */
> > +     strvec_pushl(&run_trailer.args, "interpret-trailers",
> > +                 "--in-place", "--where=end", git_path_commit_editmsg(), NULL);
>
> And this line will be gone.
>

Indeed so.

Thanks.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v5] [GSOC] commit: add --trailer option
  2021-03-14 15:58     ` [PATCH v4] " ZheNing Hu via GitGitGadget
  2021-03-14 23:52       ` Junio C Hamano
@ 2021-03-15  3:24       ` ZheNing Hu via GitGitGadget
  2021-03-15  5:33         ` Christian Couder
  2021-03-15  6:35         ` [PATCH v6] " ZheNing Hu via GitGitGadget
  2021-03-15  4:38       ` [PATCH v4] " Junio C Hamano
  2 siblings, 2 replies; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-15  3:24 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Historically, Git has supported the 'Signed-off-by' commit trailer
using the '--signoff' and the '-s' option from the command line.
But users may need to provide other trailer information from the
command line such as "Helped-by", "Reported-by", "Mentored-by",

Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
other trailers to `interpret-trailers` and insert them into commit
messages.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC] commit: add --trailer option
    
    Now maintainers or developers can also use commit
    --trailer="Signed-off-by:commiter<email>" from the command line to
    provide trailers to commit messages. This solution may be more
    generalized than v1.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v5
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v5
Pull-Request: https://github.com/gitgitgadget/git/pull/901

Range-diff vs v4:

 1:  dc507553ef4f ! 1:  ca91accb2852 [GSOC] commit: add --trailer option
     @@ builtin/commit.c: static int config_commit_verbose = -1; /* unspecified */
       static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
       static char *sign_commit, *pathspec_from_file;
      +struct child_process run_trailer = CHILD_PROCESS_INIT;
     ++struct strvec trailer_args = STRVEC_INIT;
      +static const char *trailer;
       
       /*
     @@ builtin/commit.c: static struct strbuf message = STRBUF_INIT;
      +static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
      +{
      +	if (unset) {
     -+		strvec_clear(&run_trailer.args);
     ++		strvec_clear(&trailer_args);
      +		return -1;
      +	}
     -+	strvec_pushl(&run_trailer.args, "--trailer", arg, NULL);
     ++	strvec_pushl(&trailer_args, "--trailer", arg, NULL);
      +	return 0;
      +}
      +
     @@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const cha
       
       	fclose(s->fp);
       
     -+	if (run_trailer.args.nr != 4) {
     ++	if (trailer_args.nr) {
     ++		strvec_pushl(&run_trailer.args, "interpret-trailers",
     ++			     "--in-place", "--where=end", git_path_commit_editmsg(), NULL);
     ++		strvec_pushv(&run_trailer.args, trailer_args.v);
      +		run_trailer.git_cmd = 1;
     -+		run_command(&run_trailer);
     -+	} else
     -+		strvec_clear(&run_trailer.args);
     ++		if (run_command(&run_trailer))
     ++			strvec_clear(&run_trailer.args);
     ++		strvec_clear(&trailer_args);
     ++	}
      +
       	/*
       	 * Reject an attempt to record a non-merge empty commit without
     @@ builtin/commit.c: int cmd_commit(int argc, const char **argv, const char *prefix
       		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
       		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
       		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
     -@@ builtin/commit.c: int cmd_commit(int argc, const char **argv, const char *prefix)
     - 			die(_("could not parse HEAD commit"));
     - 	}
     - 	verbose = -1; /* unspecified */
     -+	strvec_pushl(&run_trailer.args, "interpret-trailers",
     -+		    "--in-place", "--where=end", git_path_commit_editmsg(), NULL);
     - 	argc = parse_and_validate_options(argc, argv, builtin_commit_options,
     - 					  builtin_commit_usage,
     - 					  prefix, current_head, &s);
      
       ## t/t7502-commit-porcelain.sh ##
      @@ t/t7502-commit-porcelain.sh: test_expect_success 'sign off' '


 Documentation/git-commit.txt |  9 ++++++++-
 builtin/commit.c             | 24 ++++++++++++++++++++++++
 t/t7502-commit-porcelain.sh  | 20 ++++++++++++++++++++
 3 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 17150fa7eabe..73a7507db47f 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -14,7 +14,7 @@ SYNOPSIS
 	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
 	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
 	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
-	   [-S[<keyid>]] [--] [<pathspec>...]
+	   [-S[<keyid>]] [--] [<pathspec>...] [(--trailer <token>[(=|:)<value>])...]
 
 DESCRIPTION
 -----------
@@ -166,6 +166,13 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
 
 include::signoff-option.txt[]
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
+	<committer@example.com>" --trailer "Helped-by:C O Mitter \
+	<committer@example.com>"` will add the "Signed-off" trailer
+	and the "Helped-by" trailer in the commit message.)
+
 -n::
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
diff --git a/builtin/commit.c b/builtin/commit.c
index 739110c5a7f6..24fccb561398 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,6 +113,9 @@ static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit, *pathspec_from_file;
+struct child_process run_trailer = CHILD_PROCESS_INIT;
+struct strvec trailer_args = STRVEC_INIT;
+static const char *trailer;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -131,6 +134,16 @@ static struct strbuf message = STRBUF_INIT;
 
 static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
 
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+	if (unset) {
+		strvec_clear(&trailer_args);
+		return -1;
+	}
+	strvec_pushl(&trailer_args, "--trailer", arg, NULL);
+	return 0;
+}
+
 static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
 {
 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -958,6 +971,16 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
 	fclose(s->fp);
 
+	if (trailer_args.nr) {
+		strvec_pushl(&run_trailer.args, "interpret-trailers",
+			     "--in-place", "--where=end", git_path_commit_editmsg(), NULL);
+		strvec_pushv(&run_trailer.args, trailer_args.v);
+		run_trailer.git_cmd = 1;
+		if (run_command(&run_trailer))
+			strvec_clear(&run_trailer.args);
+		strvec_clear(&trailer_args);
+	}
+
 	/*
 	 * Reject an attempt to record a non-merge empty commit without
 	 * explicit --allow-empty. In the cherry-pick case, it may be
@@ -1507,6 +1530,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK(0, "trailer", &trailer, N_("trailer"), N_("trailer(s) to add"), opt_pass_trailer),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 6396897cc818..0acf23799931 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -154,6 +154,26 @@ test_expect_success 'sign off' '
 
 '
 
+test_expect_success 'trailer' '
+	>file1 &&
+	git add file1 &&
+	git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
+		--trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
+		--trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
+		--trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
+		-m "hello" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,7d" commit.msg >actual &&
+	cat >expected <<-\EOF &&
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C O Mitter1 <committer1@example.com>
+	Helped-by: C O Mitter2 <committer2@example.com>
+	Reported-by: C O Mitter3 <committer3@example.com>
+	Mentored-by: C O Mitter4 <committer4@example.com>
+	EOF
+	test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&

base-commit: 13d7ab6b5d7929825b626f050b62a11241ea4945
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4] [GSOC] commit: add --trailer option
  2021-03-14 15:58     ` [PATCH v4] " ZheNing Hu via GitGitGadget
  2021-03-14 23:52       ` Junio C Hamano
  2021-03-15  3:24       ` [PATCH v5] " ZheNing Hu via GitGitGadget
@ 2021-03-15  4:38       ` Junio C Hamano
  2021-03-15  5:11         ` ZheNing Hu
  2 siblings, 1 reply; 84+ messages in thread
From: Junio C Hamano @ 2021-03-15  4:38 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva, ZheNing Hu

"ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:

One thing I forgot to mention.

> +static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
> +{
> +	if (unset) {
> +		strvec_clear(&run_trailer.args);
> +		return -1;

What input is this part of the code designed to handle, and what
outcome does it want to show to the user?

Does the test added in this patch cover this codepath, where unset
is true?

I think this codepath is reached when the command line says
"--no-trailer", and I am guessing that by clearing the code wants to
say "ignore the accumulated trailers and start accumulating afresh",
i.e.

    git commit --trailer='T1: V1' --trailer='T2: V2' \
		--no-trailer \
		--trailer='T3: V3'

would want to add only the "T3: V3" trailer; which is a sensible
design, but I do not think returning -1 would be compatible with
that design.

If on the other hand the code wants to say "--no-trailer is a
command line error", then using PARSE_OPT_NONEG to let the
parse-options API issue an error message and die would be more
appropriate.  That is an often used pattern for an option that can
appear on the command line only once without accumulating, which may
be less appropriate for the "--trailer", though.

> +	}
> +	strvec_pushl(&run_trailer.args, "--trailer", arg, NULL);
> +	return 0;
> +}


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4] [GSOC] commit: add --trailer option
  2021-03-15  1:27         ` ZheNing Hu
@ 2021-03-15  4:42           ` Junio C Hamano
  2021-03-15  5:14             ` ZheNing Hu
  0 siblings, 1 reply; 84+ messages in thread
From: Junio C Hamano @ 2021-03-15  4:42 UTC (permalink / raw)
  To: ZheNing Hu
  Cc: ZheNing Hu via GitGitGadget, Git List, Bradley M. Kuhn,
	Brandon Casey, Shourya Shukla, Christian Couder, Rafael Silva

ZheNing Hu <adlternative@gmail.com> writes:

> Junio C Hamano <gitster@pobox.com> 于2021年3月15日周一 上午7:52写道:
>> IOW this part would become ...
>>
>>         if (trailer_args.nr) {
>>                 strvec_pushl(&run_trailer.args, "interpret-trailers",
>>                              "--in-place", ...);
>>                 strvec_pushv(&run_trailer.args, trailer_args.v);
>>                 run_trailer.git_cmd = 1;
>>                 run_command(&run_trailer);
>>         }
>>
>> > +     } else
>> > +             strvec_clear(&run_trailer.args);
>>
>> ... and there is no need to have "else" that won't need to do
>> anything.
>
> Yes, but we also should clear "trailer_args" in "else" here, and check the
> return value of the "run_command()" for clear "run_trailer.args".

No.  If you introduce the separate strvec, the "else" clause runs
only when trailer_args haven't got anything, so there is nothing to
clear.


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4] [GSOC] commit: add --trailer option
  2021-03-15  4:38       ` [PATCH v4] " Junio C Hamano
@ 2021-03-15  5:11         ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-15  5:11 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: ZheNing Hu via GitGitGadget, Git List, Bradley M. Kuhn,
	Brandon Casey, Shourya Shukla, Christian Couder, Rafael Silva

Junio C Hamano <gitster@pobox.com> 于2021年3月15日周一 下午12:38写道:
>
> "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
> One thing I forgot to mention.
>
> > +static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
> > +{
> > +     if (unset) {
> > +             strvec_clear(&run_trailer.args);
> > +             return -1;
>
> What input is this part of the code designed to handle, and what
> outcome does it want to show to the user?
>
> Does the test added in this patch cover this codepath, where unset
> is true?
>
> I think this codepath is reached when the command line says
> "--no-trailer", and I am guessing that by clearing the code wants to
> say "ignore the accumulated trailers and start accumulating afresh",
> i.e.
>
>     git commit --trailer='T1: V1' --trailer='T2: V2' \
>                 --no-trailer \
>                 --trailer='T3: V3'
>
> would want to add only the "T3: V3" trailer; which is a sensible
> design, but I do not think returning -1 would be compatible with
> that design.
>
> If on the other hand the code wants to say "--no-trailer is a
> command line error", then using PARSE_OPT_NONEG to let the
> parse-options API issue an error message and die would be more
> appropriate.  That is an often used pattern for an option that can
> appear on the command line only once without accumulating, which may
> be less appropriate for the "--trailer", though.
>

As you said, what I want to express is "--no-trailer is acommand line error".
`--no-trailer` may don't have any big benefits for users.
I will follow you suggections.
> > +     }
> > +     strvec_pushl(&run_trailer.args, "--trailer", arg, NULL);
> > +     return 0;
> > +}
>

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4] [GSOC] commit: add --trailer option
  2021-03-15  4:42           ` Junio C Hamano
@ 2021-03-15  5:14             ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-15  5:14 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: ZheNing Hu via GitGitGadget, Git List, Bradley M. Kuhn,
	Brandon Casey, Shourya Shukla, Christian Couder, Rafael Silva

Junio C Hamano <gitster@pobox.com> 于2021年3月15日周一 下午12:42写道:
>
> ZheNing Hu <adlternative@gmail.com> writes:
>
> > Junio C Hamano <gitster@pobox.com> 于2021年3月15日周一 上午7:52写道:
> >> IOW this part would become ...
> >>
> >>         if (trailer_args.nr) {
> >>                 strvec_pushl(&run_trailer.args, "interpret-trailers",
> >>                              "--in-place", ...);
> >>                 strvec_pushv(&run_trailer.args, trailer_args.v);
> >>                 run_trailer.git_cmd = 1;
> >>                 run_command(&run_trailer);
> >>         }
> >>
> >> > +     } else
> >> > +             strvec_clear(&run_trailer.args);
> >>
> >> ... and there is no need to have "else" that won't need to do
> >> anything.
> >
> > Yes, but we also should clear "trailer_args" in "else" here, and check the
> > return value of the "run_command()" for clear "run_trailer.args".
>
> No.  If you introduce the separate strvec, the "else" clause runs
> only when trailer_args haven't got anything, so there is nothing to
> clear.
>
I admit I was wrong before. But this may be the right thing to do.
+       if (trailer_args.nr) {
+               strvec_pushl(&run_trailer.args, "interpret-trailers",
+                            "--in-place", "--where=end",
git_path_commit_editmsg(), NULL);
+               strvec_pushv(&run_trailer.args, trailer_args.v);
+               run_trailer.git_cmd = 1;
+               if (run_command(&run_trailer))
+                       strvec_clear(&run_trailer.args);
+               strvec_clear(&trailer_args);
+       }

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v5] [GSOC] commit: add --trailer option
  2021-03-15  3:24       ` [PATCH v5] " ZheNing Hu via GitGitGadget
@ 2021-03-15  5:33         ` Christian Couder
  2021-03-15  5:41           ` Christian Couder
  2021-03-15  5:46           ` ZheNing Hu
  2021-03-15  6:35         ` [PATCH v6] " ZheNing Hu via GitGitGadget
  1 sibling, 2 replies; 84+ messages in thread
From: Christian Couder @ 2021-03-15  5:33 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Junio C Hamano, Brandon Casey,
	Shourya Shukla, Rafael Silva, ZheNing Hu

On Mon, Mar 15, 2021 at 4:24 AM ZheNing Hu via GitGitGadget
<gitgitgadget@gmail.com> wrote:

> diff --git a/builtin/commit.c b/builtin/commit.c
> index 739110c5a7f6..24fccb561398 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -113,6 +113,9 @@ static int config_commit_verbose = -1; /* unspecified */
>  static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
>  static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
>  static char *sign_commit, *pathspec_from_file;
> +struct child_process run_trailer = CHILD_PROCESS_INIT;

It seems to me that `run_trailer` is used only in the `if
(trailer_args.nr) {...}` block, so it could be declared there instead
of as a global variable.

> +struct strvec trailer_args = STRVEC_INIT;
> +static const char *trailer;

I am not sure that this `trailer`variable is really needed. It seems
to be used only as the third argument to OPT_CALLBACK(), but there are
other places in the code base where we pass NULL as the third
argument.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v5] [GSOC] commit: add --trailer option
  2021-03-15  5:33         ` Christian Couder
@ 2021-03-15  5:41           ` Christian Couder
  2021-03-15  5:46           ` ZheNing Hu
  1 sibling, 0 replies; 84+ messages in thread
From: Christian Couder @ 2021-03-15  5:41 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Junio C Hamano, Brandon Casey,
	Shourya Shukla, Rafael Silva, ZheNing Hu

On Mon, Mar 15, 2021 at 6:33 AM Christian Couder
<christian.couder@gmail.com> wrote:
>
> On Mon, Mar 15, 2021 at 4:24 AM ZheNing Hu via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
>
> > diff --git a/builtin/commit.c b/builtin/commit.c
> > index 739110c5a7f6..24fccb561398 100644
> > --- a/builtin/commit.c
> > +++ b/builtin/commit.c
> > @@ -113,6 +113,9 @@ static int config_commit_verbose = -1; /* unspecified */
> >  static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
> >  static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
> >  static char *sign_commit, *pathspec_from_file;
> > +struct child_process run_trailer = CHILD_PROCESS_INIT;
>
> It seems to me that `run_trailer` is used only in the `if
> (trailer_args.nr) {...}` block, so it could be declared there instead
> of as a global variable.
>
> > +struct strvec trailer_args = STRVEC_INIT;

Also you might want to add "static" in front of "struct strvec" in the
above line.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v5] [GSOC] commit: add --trailer option
  2021-03-15  5:33         ` Christian Couder
  2021-03-15  5:41           ` Christian Couder
@ 2021-03-15  5:46           ` ZheNing Hu
  1 sibling, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-15  5:46 UTC (permalink / raw)
  To: Christian Couder
  Cc: ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Rafael Silva

Christian Couder <christian.couder@gmail.com> 于2021年3月15日周一 下午1:33写道:
>
> On Mon, Mar 15, 2021 at 4:24 AM ZheNing Hu via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
>
> > diff --git a/builtin/commit.c b/builtin/commit.c
> > index 739110c5a7f6..24fccb561398 100644
> > --- a/builtin/commit.c
> > +++ b/builtin/commit.c
> > @@ -113,6 +113,9 @@ static int config_commit_verbose = -1; /* unspecified */
> >  static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
> >  static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
> >  static char *sign_commit, *pathspec_from_file;
> > +struct child_process run_trailer = CHILD_PROCESS_INIT;
>
> It seems to me that `run_trailer` is used only in the `if
> (trailer_args.nr) {...}` block, so it could be declared there instead
> of as a global variable.
>
> > +struct strvec trailer_args = STRVEC_INIT;
> > +static const char *trailer;
>
> I am not sure that this `trailer`variable is really needed. It seems
> to be used only as the third argument to OPT_CALLBACK(), but there are
> other places in the code base where we pass NULL as the third
> argument.

Good advice.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v6] [GSOC] commit: add --trailer option
  2021-03-15  3:24       ` [PATCH v5] " ZheNing Hu via GitGitGadget
  2021-03-15  5:33         ` Christian Couder
@ 2021-03-15  6:35         ` ZheNing Hu via GitGitGadget
  2021-03-15  8:02           ` Christian Couder
  2021-03-15  9:08           ` [PATCH v7] " ZheNing Hu via GitGitGadget
  1 sibling, 2 replies; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-15  6:35 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Historically, Git has supported the 'Signed-off-by' commit trailer
using the '--signoff' and the '-s' option from the command line.
But users may need to provide other trailer information from the
command line such as "Helped-by", "Reported-by", "Mentored-by",

Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
other trailers to `interpret-trailers` and insert them into commit
messages.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC] commit: add --trailer option
    
    Now maintainers or developers can also use commit
    --trailer="Signed-off-by:commiter<email>" from the command line to
    provide trailers to commit messages. This solution may be more
    generalized than v1.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v6
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v6
Pull-Request: https://github.com/gitgitgadget/git/pull/901

Range-diff vs v5:

 1:  ca91accb2852 ! 1:  c99ce75da792 [GSOC] commit: add --trailer option
     @@ builtin/commit.c: static int config_commit_verbose = -1; /* unspecified */
       static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
       static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
       static char *sign_commit, *pathspec_from_file;
     -+struct child_process run_trailer = CHILD_PROCESS_INIT;
      +struct strvec trailer_args = STRVEC_INIT;
     -+static const char *trailer;
       
       /*
        * The default commit message cleanup mode will remove the lines
     @@ builtin/commit.c: static struct strbuf message = STRBUF_INIT;
       
      +static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
      +{
     -+	if (unset) {
     -+		strvec_clear(&trailer_args);
     -+		return -1;
     -+	}
     ++	BUG_ON_OPT_NEG(unset);
     ++
      +	strvec_pushl(&trailer_args, "--trailer", arg, NULL);
      +	return 0;
      +}
     @@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const cha
       	fclose(s->fp);
       
      +	if (trailer_args.nr) {
     ++		static struct child_process run_trailer = CHILD_PROCESS_INIT;
     ++
      +		strvec_pushl(&run_trailer.args, "interpret-trailers",
      +			     "--in-place", "--where=end", git_path_commit_editmsg(), NULL);
      +		strvec_pushv(&run_trailer.args, trailer_args.v);
     @@ builtin/commit.c: int cmd_commit(int argc, const char **argv, const char *prefix
       		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK(0, "trailer", &trailer, N_("trailer"), N_("trailer(s) to add"), opt_pass_trailer),
     ++		OPT_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("trailer(s) to add"), PARSE_OPT_NONEG, opt_pass_trailer),
       		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
       		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
       		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),


 Documentation/git-commit.txt |  9 ++++++++-
 builtin/commit.c             | 22 ++++++++++++++++++++++
 t/t7502-commit-porcelain.sh  | 20 ++++++++++++++++++++
 3 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 17150fa7eabe..73a7507db47f 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -14,7 +14,7 @@ SYNOPSIS
 	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
 	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
 	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
-	   [-S[<keyid>]] [--] [<pathspec>...]
+	   [-S[<keyid>]] [--] [<pathspec>...] [(--trailer <token>[(=|:)<value>])...]
 
 DESCRIPTION
 -----------
@@ -166,6 +166,13 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
 
 include::signoff-option.txt[]
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
+	<committer@example.com>" --trailer "Helped-by:C O Mitter \
+	<committer@example.com>"` will add the "Signed-off" trailer
+	and the "Helped-by" trailer in the commit message.)
+
 -n::
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
diff --git a/builtin/commit.c b/builtin/commit.c
index 739110c5a7f6..726d2f4eaf5d 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,6 +113,7 @@ static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit, *pathspec_from_file;
+struct strvec trailer_args = STRVEC_INIT;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -131,6 +132,14 @@ static struct strbuf message = STRBUF_INIT;
 
 static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
 
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+
+	strvec_pushl(&trailer_args, "--trailer", arg, NULL);
+	return 0;
+}
+
 static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
 {
 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -958,6 +967,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
 	fclose(s->fp);
 
+	if (trailer_args.nr) {
+		static struct child_process run_trailer = CHILD_PROCESS_INIT;
+
+		strvec_pushl(&run_trailer.args, "interpret-trailers",
+			     "--in-place", "--where=end", git_path_commit_editmsg(), NULL);
+		strvec_pushv(&run_trailer.args, trailer_args.v);
+		run_trailer.git_cmd = 1;
+		if (run_command(&run_trailer))
+			strvec_clear(&run_trailer.args);
+		strvec_clear(&trailer_args);
+	}
+
 	/*
 	 * Reject an attempt to record a non-merge empty commit without
 	 * explicit --allow-empty. In the cherry-pick case, it may be
@@ -1507,6 +1528,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("trailer(s) to add"), PARSE_OPT_NONEG, opt_pass_trailer),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 6396897cc818..0acf23799931 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -154,6 +154,26 @@ test_expect_success 'sign off' '
 
 '
 
+test_expect_success 'trailer' '
+	>file1 &&
+	git add file1 &&
+	git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
+		--trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
+		--trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
+		--trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
+		-m "hello" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,7d" commit.msg >actual &&
+	cat >expected <<-\EOF &&
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C O Mitter1 <committer1@example.com>
+	Helped-by: C O Mitter2 <committer2@example.com>
+	Reported-by: C O Mitter3 <committer3@example.com>
+	Mentored-by: C O Mitter4 <committer4@example.com>
+	EOF
+	test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&

base-commit: 13d7ab6b5d7929825b626f050b62a11241ea4945
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v6] [GSOC] commit: add --trailer option
  2021-03-15  6:35         ` [PATCH v6] " ZheNing Hu via GitGitGadget
@ 2021-03-15  8:02           ` Christian Couder
  2021-03-15  8:21             ` ZheNing Hu
  2021-03-15  9:08           ` [PATCH v7] " ZheNing Hu via GitGitGadget
  1 sibling, 1 reply; 84+ messages in thread
From: Christian Couder @ 2021-03-15  8:02 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Junio C Hamano, Brandon Casey,
	Shourya Shukla, Rafael Silva, ZheNing Hu

On Mon, Mar 15, 2021 at 7:35 AM ZheNing Hu via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> From: ZheNing Hu <adlternative@gmail.com>
>
> Historically, Git has supported the 'Signed-off-by' commit trailer
> using the '--signoff' and the '-s' option from the command line.
> But users may need to provide other trailer information from the
> command line such as "Helped-by", "Reported-by", "Mentored-by",
>
> Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
> other trailers to `interpret-trailers` and insert them into commit
> messages.
>
> Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> ---
>     [GSOC] commit: add --trailer option
>
>     Now maintainers or developers can also use commit
>     --trailer="Signed-off-by:commiter<email>" from the command line to
>     provide trailers to commit messages. This solution may be more
>     generalized than v1.
>
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v6
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v6
> Pull-Request: https://github.com/gitgitgadget/git/pull/901
>
> Range-diff vs v5:
>
>  1:  ca91accb2852 ! 1:  c99ce75da792 [GSOC] commit: add --trailer option
>      @@ builtin/commit.c: static int config_commit_verbose = -1; /* unspecified */
>        static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
>        static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
>        static char *sign_commit, *pathspec_from_file;
>      -+struct child_process run_trailer = CHILD_PROCESS_INIT;
>       +struct strvec trailer_args = STRVEC_INIT;

I suggested added "static" in front of the above line, like this:

static struct strvec trailer_args = STRVEC_INIT;

You can check with `git grep '^static struct strvec' '*.c'` and `git
grep '^struct strvec' '*.c'` that we use "static" when declaring a
'struct strvec' globally.

[...]

>       + if (trailer_args.nr) {
>      ++         static struct child_process run_trailer = CHILD_PROCESS_INIT;

I didn't suggest using "static" here.

>      ++
>       +         strvec_pushl(&run_trailer.args, "interpret-trailers",

Thanks for working on this!

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v6] [GSOC] commit: add --trailer option
  2021-03-15  8:02           ` Christian Couder
@ 2021-03-15  8:21             ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-15  8:21 UTC (permalink / raw)
  To: Christian Couder
  Cc: ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Rafael Silva

Christian Couder <christian.couder@gmail.com> 于2021年3月15日周一 下午4:03写道:
>
> On Mon, Mar 15, 2021 at 7:35 AM ZheNing Hu via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
> >
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > Historically, Git has supported the 'Signed-off-by' commit trailer
> > using the '--signoff' and the '-s' option from the command line.
> > But users may need to provide other trailer information from the
> > command line such as "Helped-by", "Reported-by", "Mentored-by",
> >
> > Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
> > other trailers to `interpret-trailers` and insert them into commit
> > messages.
> >
> > Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> > ---
> >     [GSOC] commit: add --trailer option
> >
> >     Now maintainers or developers can also use commit
> >     --trailer="Signed-off-by:commiter<email>" from the command line to
> >     provide trailers to commit messages. This solution may be more
> >     generalized than v1.
> >
> > Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v6
> > Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v6
> > Pull-Request: https://github.com/gitgitgadget/git/pull/901
> >
> > Range-diff vs v5:
> >
> >  1:  ca91accb2852 ! 1:  c99ce75da792 [GSOC] commit: add --trailer option
> >      @@ builtin/commit.c: static int config_commit_verbose = -1; /* unspecified */
> >        static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
> >        static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
> >        static char *sign_commit, *pathspec_from_file;
> >      -+struct child_process run_trailer = CHILD_PROCESS_INIT;
> >       +struct strvec trailer_args = STRVEC_INIT;
>
> I suggested added "static" in front of the above line, like this:
>
> static struct strvec trailer_args = STRVEC_INIT;
>
> You can check with `git grep '^static struct strvec' '*.c'` and `git
> grep '^struct strvec' '*.c'` that we use "static" when declaring a
> 'struct strvec' globally.
>
> [...]
>
> >       + if (trailer_args.nr) {
> >      ++         static struct child_process run_trailer = CHILD_PROCESS_INIT;
>
> I didn't suggest using "static" here.
>
> >      ++
> >       +         strvec_pushl(&run_trailer.args, "interpret-trailers",
>
> Thanks for working on this!

Well, I was too eager to submit it.
There are many rules for git projects to explore.
Thanks.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v7] [GSOC] commit: add --trailer option
  2021-03-15  6:35         ` [PATCH v6] " ZheNing Hu via GitGitGadget
  2021-03-15  8:02           ` Christian Couder
@ 2021-03-15  9:08           ` ZheNing Hu via GitGitGadget
  2021-03-15 10:00             ` Christian Couder
                               ` (2 more replies)
  1 sibling, 3 replies; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-15  9:08 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Historically, Git has supported the 'Signed-off-by' commit trailer
using the '--signoff' and the '-s' option from the command line.
But users may need to provide other trailer information from the
command line such as "Helped-by", "Reported-by", "Mentored-by",

Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
other trailers to `interpret-trailers` and insert them into commit
messages.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC] commit: add --trailer option
    
    Now maintainers or developers can also use commit
    --trailer="Signed-off-by:commiter<email>" from the command line to
    provide trailers to commit messages. This solution may be more
    generalized than v1.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v7
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v7
Pull-Request: https://github.com/gitgitgadget/git/pull/901

Range-diff vs v6:

 1:  c99ce75da792 ! 1:  3ce8e8cac24a [GSOC] commit: add --trailer option
     @@ builtin/commit.c: static int config_commit_verbose = -1; /* unspecified */
       static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
       static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
       static char *sign_commit, *pathspec_from_file;
     -+struct strvec trailer_args = STRVEC_INIT;
     ++static struct strvec trailer_args = STRVEC_INIT;
       
       /*
        * The default commit message cleanup mode will remove the lines
     @@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const cha
       	fclose(s->fp);
       
      +	if (trailer_args.nr) {
     -+		static struct child_process run_trailer = CHILD_PROCESS_INIT;
     ++		struct child_process run_trailer = CHILD_PROCESS_INIT;
      +
      +		strvec_pushl(&run_trailer.args, "interpret-trailers",
      +			     "--in-place", "--where=end", git_path_commit_editmsg(), NULL);


 Documentation/git-commit.txt |  9 ++++++++-
 builtin/commit.c             | 22 ++++++++++++++++++++++
 t/t7502-commit-porcelain.sh  | 20 ++++++++++++++++++++
 3 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 17150fa7eabe..73a7507db47f 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -14,7 +14,7 @@ SYNOPSIS
 	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
 	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
 	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
-	   [-S[<keyid>]] [--] [<pathspec>...]
+	   [-S[<keyid>]] [--] [<pathspec>...] [(--trailer <token>[(=|:)<value>])...]
 
 DESCRIPTION
 -----------
@@ -166,6 +166,13 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
 
 include::signoff-option.txt[]
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
+	<committer@example.com>" --trailer "Helped-by:C O Mitter \
+	<committer@example.com>"` will add the "Signed-off" trailer
+	and the "Helped-by" trailer in the commit message.)
+
 -n::
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
diff --git a/builtin/commit.c b/builtin/commit.c
index 739110c5a7f6..0ddd1891f71c 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,6 +113,7 @@ static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit, *pathspec_from_file;
+static struct strvec trailer_args = STRVEC_INIT;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -131,6 +132,14 @@ static struct strbuf message = STRBUF_INIT;
 
 static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
 
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+
+	strvec_pushl(&trailer_args, "--trailer", arg, NULL);
+	return 0;
+}
+
 static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
 {
 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -958,6 +967,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
 	fclose(s->fp);
 
+	if (trailer_args.nr) {
+		struct child_process run_trailer = CHILD_PROCESS_INIT;
+
+		strvec_pushl(&run_trailer.args, "interpret-trailers",
+			     "--in-place", "--where=end", git_path_commit_editmsg(), NULL);
+		strvec_pushv(&run_trailer.args, trailer_args.v);
+		run_trailer.git_cmd = 1;
+		if (run_command(&run_trailer))
+			strvec_clear(&run_trailer.args);
+		strvec_clear(&trailer_args);
+	}
+
 	/*
 	 * Reject an attempt to record a non-merge empty commit without
 	 * explicit --allow-empty. In the cherry-pick case, it may be
@@ -1507,6 +1528,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("trailer(s) to add"), PARSE_OPT_NONEG, opt_pass_trailer),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 6396897cc818..0acf23799931 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -154,6 +154,26 @@ test_expect_success 'sign off' '
 
 '
 
+test_expect_success 'trailer' '
+	>file1 &&
+	git add file1 &&
+	git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
+		--trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
+		--trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
+		--trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
+		-m "hello" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,7d" commit.msg >actual &&
+	cat >expected <<-\EOF &&
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C O Mitter1 <committer1@example.com>
+	Helped-by: C O Mitter2 <committer2@example.com>
+	Reported-by: C O Mitter3 <committer3@example.com>
+	Mentored-by: C O Mitter4 <committer4@example.com>
+	EOF
+	test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&

base-commit: 13d7ab6b5d7929825b626f050b62a11241ea4945
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v7] [GSOC] commit: add --trailer option
  2021-03-15  9:08           ` [PATCH v7] " ZheNing Hu via GitGitGadget
@ 2021-03-15 10:00             ` Christian Couder
  2021-03-15 10:14             ` Christian Couder
  2021-03-15 13:07             ` [PATCH v8 0/2] " ZheNing Hu via GitGitGadget
  2 siblings, 0 replies; 84+ messages in thread
From: Christian Couder @ 2021-03-15 10:00 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Junio C Hamano, Brandon Casey,
	Shourya Shukla, Rafael Silva, ZheNing Hu

On Mon, Mar 15, 2021 at 10:08 AM ZheNing Hu via GitGitGadget
<gitgitgadget@gmail.com> wrote:

> @@ -166,6 +166,13 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
>
>  include::signoff-option.txt[]
>
> +--trailer <token>[(=|:)<value>]::
> +       Specify a (<token>, <value>) pair that should be applied as a
> +       trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
> +       <committer@example.com>" --trailer "Helped-by:C O Mitter \
> +       <committer@example.com>"` will add the "Signed-off" trailer

s/"Signed-off"/"Signed-off-by"/

> +       and the "Helped-by" trailer in the commit message.)

It might be useful to point users to git interpret-trailers'
documentation, for example by adding: "See
linkgit:git-interpret-trailers[1]."

Otherwise, this looks good to me!

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v7] [GSOC] commit: add --trailer option
  2021-03-15  9:08           ` [PATCH v7] " ZheNing Hu via GitGitGadget
  2021-03-15 10:00             ` Christian Couder
@ 2021-03-15 10:14             ` Christian Couder
  2021-03-15 11:32               ` ZheNing Hu
  2021-03-15 13:07             ` [PATCH v8 0/2] " ZheNing Hu via GitGitGadget
  2 siblings, 1 reply; 84+ messages in thread
From: Christian Couder @ 2021-03-15 10:14 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Junio C Hamano, Brandon Casey,
	Shourya Shukla, Rafael Silva, ZheNing Hu

On Mon, Mar 15, 2021 at 10:08 AM ZheNing Hu via GitGitGadget
<gitgitgadget@gmail.com> wrote:

>       + if (trailer_args.nr) {
>      -+         static struct child_process run_trailer = CHILD_PROCESS_INIT;
>      ++         struct child_process run_trailer = CHILD_PROCESS_INIT;
>       +
>       +         strvec_pushl(&run_trailer.args, "interpret-trailers",
>       +                      "--in-place", "--where=end", git_path_commit_editmsg(), NULL);

Actually I don't think "--where=end" should be used here. "end" is the
default for the "trailer.where" config variable, so by default if
nothing has been configured, it will work as if "--where=end" was
passed above.

If a user has configured "trailer.where" or trailer.<token>.where,
then this should be respected. And users should be able to override
such config variable using for example:

git -c trailer.where=start commit --trailer "Signed-off-by:C O Mitter
<committer@example.com>"

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v7] [GSOC] commit: add --trailer option
  2021-03-15 10:14             ` Christian Couder
@ 2021-03-15 11:32               ` ZheNing Hu
  2021-03-16  5:37                 ` Christian Couder
  0 siblings, 1 reply; 84+ messages in thread
From: ZheNing Hu @ 2021-03-15 11:32 UTC (permalink / raw)
  To: Christian Couder
  Cc: ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Rafael Silva

Christian Couder <christian.couder@gmail.com> 于2021年3月15日周一 下午6:14写道:
>
> On Mon, Mar 15, 2021 at 10:08 AM ZheNing Hu via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
>
> >       + if (trailer_args.nr) {
> >      -+         static struct child_process run_trailer = CHILD_PROCESS_INIT;
> >      ++         struct child_process run_trailer = CHILD_PROCESS_INIT;
> >       +
> >       +         strvec_pushl(&run_trailer.args, "interpret-trailers",
> >       +                      "--in-place", "--where=end", git_path_commit_editmsg(), NULL);
>
> Actually I don't think "--where=end" should be used here. "end" is the
> default for the "trailer.where" config variable, so by default if
> nothing has been configured, it will work as if "--where=end" was
> passed above.
>
> If a user has configured "trailer.where" or trailer.<token>.where,
> then this should be respected. And users should be able to override
> such config variable using for example:
>
> git -c trailer.where=start commit --trailer "Signed-off-by:C O Mitter
> <committer@example.com>"

Thanks for reminding, generally speaking, we will put the trailer at the
end of the commit messages.Take trailers in start, this should be
something I haven't considered.

I notice another question:
if we commit this again with same trailer (even same email or same commiter)
`--trailer` will not work again, because in `interpret_trailers`,
"if-exists" default
set to "addIfDifferentNeighbor", I addvice enforce use "if-exists="add".

Thanks.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v8 0/2] [GSOC] commit: add --trailer option
  2021-03-15  9:08           ` [PATCH v7] " ZheNing Hu via GitGitGadget
  2021-03-15 10:00             ` Christian Couder
  2021-03-15 10:14             ` Christian Couder
@ 2021-03-15 13:07             ` ZheNing Hu via GitGitGadget
  2021-03-15 13:07               ` [PATCH v8 1/2] " ZheNing Hu via GitGitGadget
                                 ` (2 more replies)
  2 siblings, 3 replies; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-15 13:07 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva, ZheNing Hu

Now maintainers or developers can also use commit
--trailer="Signed-off-by:commiter<email>" from the command line to provide
trailers to commit messages. This solution may be more generalized than v1.

ZheNing Hu (2):
  [GSOC] commit: add --trailer option
  interpret_trailers: for three options parse add warning

 Documentation/git-commit.txt |  9 ++++++++-
 builtin/commit.c             | 23 +++++++++++++++++++++++
 builtin/interpret-trailers.c | 25 ++++++++++++++++++++++---
 t/t7502-commit-porcelain.sh  | 20 ++++++++++++++++++++
 4 files changed, 73 insertions(+), 4 deletions(-)


base-commit: 13d7ab6b5d7929825b626f050b62a11241ea4945
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v8
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v8
Pull-Request: https://github.com/gitgitgadget/git/pull/901

Range-diff vs v7:

 1:  3ce8e8cac24a ! 1:  f81b6e66a6ba [GSOC] commit: add --trailer option
     @@ Documentation/git-commit.txt: The `-m` option is mutually exclusive with `-c`, `
      +	Specify a (<token>, <value>) pair that should be applied as a
      +	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
      +	<committer@example.com>" --trailer "Helped-by:C O Mitter \
     -+	<committer@example.com>"` will add the "Signed-off" trailer
     ++	<committer@example.com>"` will add the "Signed-off-by" trailer
      +	and the "Helped-by" trailer in the commit message.)
     -+
     ++	See linkgit:git-interpret-trailers[1] for details.
       -n::
       --no-verify::
       	This option bypasses the pre-commit and commit-msg hooks.
     @@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const cha
      +		struct child_process run_trailer = CHILD_PROCESS_INIT;
      +
      +		strvec_pushl(&run_trailer.args, "interpret-trailers",
     -+			     "--in-place", "--where=end", git_path_commit_editmsg(), NULL);
     ++			     "--in-place", "--if-exists=add",
     ++			     git_path_commit_editmsg(), NULL);
      +		strvec_pushv(&run_trailer.args, trailer_args.v);
      +		run_trailer.git_cmd = 1;
      +		if (run_command(&run_trailer))
 -:  ------------ > 2:  68e0bd9e2d6f interpret_trailers: for three options parse add warning

-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v8 1/2] [GSOC] commit: add --trailer option
  2021-03-15 13:07             ` [PATCH v8 0/2] " ZheNing Hu via GitGitGadget
@ 2021-03-15 13:07               ` ZheNing Hu via GitGitGadget
  2021-03-16 12:52                 ` Ævar Arnfjörð Bjarmason
  2021-03-15 13:07               ` [PATCH v8 2/2] interpret_trailers: for three options parse add warning ZheNing Hu via GitGitGadget
  2021-03-16 10:39               ` [PATCH v9] [GSOC] commit: add --trailer option ZheNing Hu via GitGitGadget
  2 siblings, 1 reply; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-15 13:07 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Historically, Git has supported the 'Signed-off-by' commit trailer
using the '--signoff' and the '-s' option from the command line.
But users may need to provide other trailer information from the
command line such as "Helped-by", "Reported-by", "Mentored-by",

Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
other trailers to `interpret-trailers` and insert them into commit
messages.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
 Documentation/git-commit.txt |  9 ++++++++-
 builtin/commit.c             | 23 +++++++++++++++++++++++
 t/t7502-commit-porcelain.sh  | 20 ++++++++++++++++++++
 3 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 17150fa7eabe..f74d320f0c96 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -14,7 +14,7 @@ SYNOPSIS
 	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
 	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
 	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
-	   [-S[<keyid>]] [--] [<pathspec>...]
+	   [-S[<keyid>]] [--] [<pathspec>...] [(--trailer <token>[(=|:)<value>])...]
 
 DESCRIPTION
 -----------
@@ -166,6 +166,13 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
 
 include::signoff-option.txt[]
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
+	<committer@example.com>" --trailer "Helped-by:C O Mitter \
+	<committer@example.com>"` will add the "Signed-off-by" trailer
+	and the "Helped-by" trailer in the commit message.)
+	See linkgit:git-interpret-trailers[1] for details.
 -n::
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
diff --git a/builtin/commit.c b/builtin/commit.c
index 739110c5a7f6..250e93d977df 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,6 +113,7 @@ static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit, *pathspec_from_file;
+static struct strvec trailer_args = STRVEC_INIT;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -131,6 +132,14 @@ static struct strbuf message = STRBUF_INIT;
 
 static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
 
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+
+	strvec_pushl(&trailer_args, "--trailer", arg, NULL);
+	return 0;
+}
+
 static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
 {
 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -958,6 +967,19 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
 	fclose(s->fp);
 
+	if (trailer_args.nr) {
+		struct child_process run_trailer = CHILD_PROCESS_INIT;
+
+		strvec_pushl(&run_trailer.args, "interpret-trailers",
+			     "--in-place", "--if-exists=add",
+			     git_path_commit_editmsg(), NULL);
+		strvec_pushv(&run_trailer.args, trailer_args.v);
+		run_trailer.git_cmd = 1;
+		if (run_command(&run_trailer))
+			strvec_clear(&run_trailer.args);
+		strvec_clear(&trailer_args);
+	}
+
 	/*
 	 * Reject an attempt to record a non-merge empty commit without
 	 * explicit --allow-empty. In the cherry-pick case, it may be
@@ -1507,6 +1529,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("trailer(s) to add"), PARSE_OPT_NONEG, opt_pass_trailer),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 6396897cc818..0acf23799931 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -154,6 +154,26 @@ test_expect_success 'sign off' '
 
 '
 
+test_expect_success 'trailer' '
+	>file1 &&
+	git add file1 &&
+	git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
+		--trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
+		--trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
+		--trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
+		-m "hello" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,7d" commit.msg >actual &&
+	cat >expected <<-\EOF &&
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C O Mitter1 <committer1@example.com>
+	Helped-by: C O Mitter2 <committer2@example.com>
+	Reported-by: C O Mitter3 <committer3@example.com>
+	Mentored-by: C O Mitter4 <committer4@example.com>
+	EOF
+	test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v8 2/2] interpret_trailers: for three options parse add warning
  2021-03-15 13:07             ` [PATCH v8 0/2] " ZheNing Hu via GitGitGadget
  2021-03-15 13:07               ` [PATCH v8 1/2] " ZheNing Hu via GitGitGadget
@ 2021-03-15 13:07               ` ZheNing Hu via GitGitGadget
  2021-03-16  5:53                 ` Christian Couder
  2021-03-16 10:39               ` [PATCH v9] [GSOC] commit: add --trailer option ZheNing Hu via GitGitGadget
  2 siblings, 1 reply; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-15 13:07 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

When using `interpret-trailers`, if user accidentally fill in the
wrong option values (e.g. `--if-exists=addd` or `--where=begin` or
`--if-missing=do-nothing`), git will exit quietly, and the user may
not even know what happened.

So lets provides warnings when git parsing this three options:
"unknown value '%s' for key ('where'|'--if-exist'|'--if-missing')".
This will remind the user :"Oh, it was because of my spelling error."
(or using a synonym for error).

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
 builtin/interpret-trailers.c | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 84748eafc01b..6d8e86a44d43 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -24,19 +24,38 @@ static enum trailer_if_missing if_missing;
 static int option_parse_where(const struct option *opt,
 			      const char *arg, int unset)
 {
-	return trailer_set_where(&where, arg);
+	int ret;
+
+	ret = trailer_set_where(&where, arg);
+	if (ret)
+		warning(_("unknown value '%s' for key 'where'"),
+			arg);
+	return ret;
+
 }
 
 static int option_parse_if_exists(const struct option *opt,
 				  const char *arg, int unset)
 {
-	return trailer_set_if_exists(&if_exists, arg);
+	int ret;
+
+	ret = trailer_set_if_exists(&if_exists, arg);
+	if (ret)
+		warning(_("unknown value '%s' for key 'if_exists'"),
+			arg);
+	return ret;
 }
 
 static int option_parse_if_missing(const struct option *opt,
 				   const char *arg, int unset)
 {
-	return trailer_set_if_missing(&if_missing, arg);
+	int ret;
+
+	ret = trailer_set_if_missing(&if_missing, arg);
+	if (ret)
+		warning(_("unknown value '%s' for key 'if_missing'"),
+			arg);
+	return ret;
 }
 
 static void new_trailers_clear(struct list_head *trailers)
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v7] [GSOC] commit: add --trailer option
  2021-03-15 11:32               ` ZheNing Hu
@ 2021-03-16  5:37                 ` Christian Couder
  2021-03-16  8:35                   ` ZheNing Hu
  0 siblings, 1 reply; 84+ messages in thread
From: Christian Couder @ 2021-03-16  5:37 UTC (permalink / raw)
  To: ZheNing Hu
  Cc: ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Rafael Silva

On Mon, Mar 15, 2021 at 12:32 PM ZheNing Hu <adlternative@gmail.com> wrote:
>
> Christian Couder <christian.couder@gmail.com> 于2021年3月15日周一 下午6:14写道:
> >
> > On Mon, Mar 15, 2021 at 10:08 AM ZheNing Hu via GitGitGadget
> > <gitgitgadget@gmail.com> wrote:
> >
> > >       + if (trailer_args.nr) {
> > >      -+         static struct child_process run_trailer = CHILD_PROCESS_INIT;
> > >      ++         struct child_process run_trailer = CHILD_PROCESS_INIT;
> > >       +
> > >       +         strvec_pushl(&run_trailer.args, "interpret-trailers",
> > >       +                      "--in-place", "--where=end", git_path_commit_editmsg(), NULL);
> >
> > Actually I don't think "--where=end" should be used here. "end" is the
> > default for the "trailer.where" config variable, so by default if
> > nothing has been configured, it will work as if "--where=end" was
> > passed above.
> >
> > If a user has configured "trailer.where" or trailer.<token>.where,
> > then this should be respected. And users should be able to override
> > such config variable using for example:
> >
> > git -c trailer.where=start commit --trailer "Signed-off-by:C O Mitter
> > <committer@example.com>"
>
> Thanks for reminding, generally speaking, we will put the trailer at the
> end of the commit messages.Take trailers in start, this should be
> something I haven't considered.

In general what I want to say is that `git interpret-trailers` should
be considered to have sensible defaults, that can possibly be
overridden using a number of config variables (or the git -c ...
mechanism) which is a good thing. If something in it doesn't work
well, it's possible to improve it of course. Otherwise it's better to
just fully take advantage of it.

> I notice another question:
> if we commit this again with same trailer (even same email or same commiter)
> `--trailer` will not work again, because in `interpret_trailers`,
> "if-exists" default
> set to "addIfDifferentNeighbor", I addvice enforce use "if-exists="add".

I don't agree with using "--if-exists=add". I think the default to not
add a trailer line if it would be just above or below the same line is
better, as doing that wouldn't add much information. It's better to
encourage people to use trailers in a meaningful way.

And again if we use "--if-exists=add", then people who would want to
take advantage of `git interpret-trailers` to customize what happens
when the trailer already exists would not be able to do it.

For example if we don't use "--if-exists=add", then:

- people who want to customize what happens when the trailer already
exists can do it with for example:

git -c trailer.ifexists=addIfDifferent commit --trailer
"Signed-off-by:C O Mitter <committer@example.com>"

- which means that people who want the "--if-exists=add" behavior can
still have it with:

git -c trailer.ifexists=add commit --trailer "Signed-off-by:C O Mitter
<committer@example.com>"

While if we use "--if-exists=add", then using `git -c
trailer.ifexists=... commit ...` will not customize anything as the
"--if-exists=add" command line option will override any config
customization.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v8 2/2] interpret_trailers: for three options parse add warning
  2021-03-15 13:07               ` [PATCH v8 2/2] interpret_trailers: for three options parse add warning ZheNing Hu via GitGitGadget
@ 2021-03-16  5:53                 ` Christian Couder
  2021-03-16  9:11                   ` ZheNing Hu
  0 siblings, 1 reply; 84+ messages in thread
From: Christian Couder @ 2021-03-16  5:53 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Junio C Hamano, Brandon Casey,
	Shourya Shukla, Rafael Silva, ZheNing Hu

On Mon, Mar 15, 2021 at 2:07 PM ZheNing Hu via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> From: ZheNing Hu <adlternative@gmail.com>
>
> When using `interpret-trailers`, if user accidentally fill in the
> wrong option values (e.g. `--if-exists=addd` or `--where=begin` or
> `--if-missing=do-nothing`), git will exit quietly, and the user may
> not even know what happened.
>
> So lets provides warnings when git parsing this three options:
> "unknown value '%s' for key ('where'|'--if-exist'|'--if-missing')".
> This will remind the user :"Oh, it was because of my spelling error."
> (or using a synonym for error).

I am not against this, but I just want to say that when I previously
worked on `interpret-trailers` I think I implemented or suggested such
warnings, but they were rejected.

I think the reason they were rejected was to improve compatibility
with future versions of Git where more options would be implemented.

For example if in a few years someone implements `--where=middle` and
some people use it in a script like this:

git interpret-trailers --where=middle --trailer foo=bar

Then when such a script would be used with a recent version of Git it
would work well, while if it would be used with an old version of Git
it would emit warnings. And these warnings might actually be more
annoying than the fact that the trailer is not put in the middle.

I might be wrong and there might have been other reasons though. Also
things might have changed since that time, as not many options if any
have been added since then.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v7] [GSOC] commit: add --trailer option
  2021-03-16  5:37                 ` Christian Couder
@ 2021-03-16  8:35                   ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-16  8:35 UTC (permalink / raw)
  To: Christian Couder
  Cc: ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Rafael Silva

Christian Couder <christian.couder@gmail.com> 于2021年3月16日周二 下午1:37写道:

> > Thanks for reminding, generally speaking, we will put the trailer at the
> > end of the commit messages.Take trailers in start, this should be
> > something I haven't considered.
>
> In general what I want to say is that `git interpret-trailers` should
> be considered to have sensible defaults, that can possibly be
> overridden using a number of config variables (or the git -c ...
> mechanism) which is a good thing. If something in it doesn't work
> well, it's possible to improve it of course. Otherwise it's better to
> just fully take advantage of it.
>
> > I notice another question:
> > if we commit this again with same trailer (even same email or same commiter)
> > `--trailer` will not work again, because in `interpret_trailers`,
> > "if-exists" default
> > set to "addIfDifferentNeighbor", I addvice enforce use "if-exists="add".
>
> I don't agree with using "--if-exists=add". I think the default to not
> add a trailer line if it would be just above or below the same line is
> better, as doing that wouldn't add much information. It's better to
> encourage people to use trailers in a meaningful way.
>
> And again if we use "--if-exists=add", then people who would want to
> take advantage of `git interpret-trailers` to customize what happens
> when the trailer already exists would not be able to do it.
>
> For example if we don't use "--if-exists=add", then:
>
> - people who want to customize what happens when the trailer already
> exists can do it with for example:
>
> git -c trailer.ifexists=addIfDifferent commit --trailer
> "Signed-off-by:C O Mitter <committer@example.com>"
>
> - which means that people who want the "--if-exists=add" behavior can
> still have it with:
>
> git -c trailer.ifexists=add commit --trailer "Signed-off-by:C O Mitter
> <committer@example.com>"
>
> While if we use "--if-exists=add", then using `git -c
> trailer.ifexists=... commit ...` will not customize anything as the
> "--if-exists=add" command line option will override any config
> customization.

Well, I see what you mean, this will keep git better flexibility,
give users more personalized configuration options.
And this should be more in line with git design philosophy,
I will follow your suggestion.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v8 2/2] interpret_trailers: for three options parse add warning
  2021-03-16  5:53                 ` Christian Couder
@ 2021-03-16  9:11                   ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-16  9:11 UTC (permalink / raw)
  To: Christian Couder
  Cc: ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Rafael Silva

Christian Couder <christian.couder@gmail.com> 于2021年3月16日周二 下午1:53写道:
> I am not against this, but I just want to say that when I previously
> worked on `interpret-trailers` I think I implemented or suggested such
> warnings, but they were rejected.
>
> I think the reason they were rejected was to improve compatibility
> with future versions of Git where more options would be implemented.
>
> For example if in a few years someone implements `--where=middle` and
> some people use it in a script like this:
>
> git interpret-trailers --where=middle --trailer foo=bar
>
> Then when such a script would be used with a recent version of Git it
> would work well, while if it would be used with an old version of Git
> it would emit warnings. And these warnings might actually be more
> annoying than the fact that the trailer is not put in the middle.
>

Well, this is indeed a situation I did not foresee. As a user, I don't see an
error | warning reminder which may make me a little confused.

At the same time, some sub-command like `git cat-file`, If user give a wrong
format:

$  git cat-file  --batch-check="%(deltabases)"

git will tell us, "fatal: unknown format element: deltabases".
It's easy for user to check.

> I might be wrong and there might have been other reasons though. Also
> things might have changed since that time, as not many options if any
> have been added since then.

Thanks for your patiently explanation.
If these warnings are really unnecessary, I will drop this patch.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v9] [GSOC] commit: add --trailer option
  2021-03-15 13:07             ` [PATCH v8 0/2] " ZheNing Hu via GitGitGadget
  2021-03-15 13:07               ` [PATCH v8 1/2] " ZheNing Hu via GitGitGadget
  2021-03-15 13:07               ` [PATCH v8 2/2] interpret_trailers: for three options parse add warning ZheNing Hu via GitGitGadget
@ 2021-03-16 10:39               ` ZheNing Hu via GitGitGadget
  2021-03-17  5:26                 ` Shourya Shukla
  2021-03-18 11:15                 ` [PATCH v10 0/3] " ZheNing Hu via GitGitGadget
  2 siblings, 2 replies; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-16 10:39 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Historically, Git has supported the 'Signed-off-by' commit trailer
using the '--signoff' and the '-s' option from the command line.
But users may need to provide other trailer information from the
command line such as "Helped-by", "Reported-by", "Mentored-by",

Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
other trailers to `interpret-trailers` and insert them into commit
messages.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC] commit: add --trailer option
    
    Now maintainers or developers can also use commit
    --trailer="Signed-off-by:commiter<email>" from the command line to
    provide trailers to commit messages. This solution may be more
    generalized than v1.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v9
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v9
Pull-Request: https://github.com/gitgitgadget/git/pull/901

Range-diff vs v8:

 1:  f81b6e66a6ba ! 1:  e524c4ba5dc1 [GSOC] commit: add --trailer option
     @@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const cha
      +		struct child_process run_trailer = CHILD_PROCESS_INIT;
      +
      +		strvec_pushl(&run_trailer.args, "interpret-trailers",
     -+			     "--in-place", "--if-exists=add",
     -+			     git_path_commit_editmsg(), NULL);
     ++			     "--in-place", git_path_commit_editmsg(), NULL);
      +		strvec_pushv(&run_trailer.args, trailer_args.v);
      +		run_trailer.git_cmd = 1;
      +		if (run_command(&run_trailer))
 2:  68e0bd9e2d6f < -:  ------------ interpret_trailers: for three options parse add warning


 Documentation/git-commit.txt |  9 ++++++++-
 builtin/commit.c             | 22 ++++++++++++++++++++++
 t/t7502-commit-porcelain.sh  | 20 ++++++++++++++++++++
 3 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 17150fa7eabe..f74d320f0c96 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -14,7 +14,7 @@ SYNOPSIS
 	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
 	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
 	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
-	   [-S[<keyid>]] [--] [<pathspec>...]
+	   [-S[<keyid>]] [--] [<pathspec>...] [(--trailer <token>[(=|:)<value>])...]
 
 DESCRIPTION
 -----------
@@ -166,6 +166,13 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
 
 include::signoff-option.txt[]
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
+	<committer@example.com>" --trailer "Helped-by:C O Mitter \
+	<committer@example.com>"` will add the "Signed-off-by" trailer
+	and the "Helped-by" trailer in the commit message.)
+	See linkgit:git-interpret-trailers[1] for details.
 -n::
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
diff --git a/builtin/commit.c b/builtin/commit.c
index 739110c5a7f6..a002330d9d56 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,6 +113,7 @@ static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit, *pathspec_from_file;
+static struct strvec trailer_args = STRVEC_INIT;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -131,6 +132,14 @@ static struct strbuf message = STRBUF_INIT;
 
 static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
 
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+
+	strvec_pushl(&trailer_args, "--trailer", arg, NULL);
+	return 0;
+}
+
 static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
 {
 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -958,6 +967,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
 	fclose(s->fp);
 
+	if (trailer_args.nr) {
+		struct child_process run_trailer = CHILD_PROCESS_INIT;
+
+		strvec_pushl(&run_trailer.args, "interpret-trailers",
+			     "--in-place", git_path_commit_editmsg(), NULL);
+		strvec_pushv(&run_trailer.args, trailer_args.v);
+		run_trailer.git_cmd = 1;
+		if (run_command(&run_trailer))
+			strvec_clear(&run_trailer.args);
+		strvec_clear(&trailer_args);
+	}
+
 	/*
 	 * Reject an attempt to record a non-merge empty commit without
 	 * explicit --allow-empty. In the cherry-pick case, it may be
@@ -1507,6 +1528,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("trailer(s) to add"), PARSE_OPT_NONEG, opt_pass_trailer),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 6396897cc818..0acf23799931 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -154,6 +154,26 @@ test_expect_success 'sign off' '
 
 '
 
+test_expect_success 'trailer' '
+	>file1 &&
+	git add file1 &&
+	git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
+		--trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
+		--trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
+		--trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
+		-m "hello" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,7d" commit.msg >actual &&
+	cat >expected <<-\EOF &&
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C O Mitter1 <committer1@example.com>
+	Helped-by: C O Mitter2 <committer2@example.com>
+	Reported-by: C O Mitter3 <committer3@example.com>
+	Mentored-by: C O Mitter4 <committer4@example.com>
+	EOF
+	test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&

base-commit: 13d7ab6b5d7929825b626f050b62a11241ea4945
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v8 1/2] [GSOC] commit: add --trailer option
  2021-03-15 13:07               ` [PATCH v8 1/2] " ZheNing Hu via GitGitGadget
@ 2021-03-16 12:52                 ` Ævar Arnfjörð Bjarmason
  2021-03-17  2:01                   ` ZheNing Hu
  0 siblings, 1 reply; 84+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-03-16 12:52 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Junio C Hamano, Brandon Casey,
	Shourya Shukla, Christian Couder, Rafael Silva, ZheNing Hu


On Mon, Mar 15 2021, ZheNing Hu via GitGitGadget wrote:

> From: ZheNing Hu <adlternative@gmail.com>
>
> Historically, Git has supported the 'Signed-off-by' commit trailer
> using the '--signoff' and the '-s' option from the command line.
> But users may need to provide other trailer information from the
> command line such as "Helped-by", "Reported-by", "Mentored-by",
>
> Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
> other trailers to `interpret-trailers` and insert them into commit
> messages.

I paged through v1-v8, looking mostly good so far. Some comments:

> Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> ---
>  Documentation/git-commit.txt |  9 ++++++++-
>  builtin/commit.c             | 23 +++++++++++++++++++++++
>  t/t7502-commit-porcelain.sh  | 20 ++++++++++++++++++++
>  3 files changed, 51 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
> index 17150fa7eabe..f74d320f0c96 100644
> --- a/Documentation/git-commit.txt
> +++ b/Documentation/git-commit.txt
> @@ -14,7 +14,7 @@ SYNOPSIS
>  	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
>  	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
>  	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
> -	   [-S[<keyid>]] [--] [<pathspec>...]
> +	   [-S[<keyid>]] [--] [<pathspec>...] [(--trailer <token>[(=|:)<value>])...]
>  
>  DESCRIPTION
>  -----------
> @@ -166,6 +166,13 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
>  
>  include::signoff-option.txt[]
>  
> +--trailer <token>[(=|:)<value>]::
> +	Specify a (<token>, <value>) pair that should be applied as a
> +	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
> +	<committer@example.com>" --trailer "Helped-by:C O Mitter \
> +	<committer@example.com>"` will add the "Signed-off-by" trailer
> +	and the "Helped-by" trailer in the commit message.)
> +	See linkgit:git-interpret-trailers[1] for details.
>  -n::
>  --no-verify::
>  	This option bypasses the pre-commit and commit-msg hooks.
> diff --git a/builtin/commit.c b/builtin/commit.c
> index 739110c5a7f6..250e93d977df 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -113,6 +113,7 @@ static int config_commit_verbose = -1; /* unspecified */
>  static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
>  static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
>  static char *sign_commit, *pathspec_from_file;
> +static struct strvec trailer_args = STRVEC_INIT;
>  
>  /*
>   * The default commit message cleanup mode will remove the lines
> @@ -131,6 +132,14 @@ static struct strbuf message = STRBUF_INIT;
>  
>  static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
>  
> +static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
> +{
> +	BUG_ON_OPT_NEG(unset);
> +
> +	strvec_pushl(&trailer_args, "--trailer", arg, NULL);
> +	return 0;
> +}
> +
>  static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
>  {
>  	enum wt_status_format *value = (enum wt_status_format *)opt->value;
> @@ -958,6 +967,19 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
>  
>  	fclose(s->fp);
>  
> +	if (trailer_args.nr) {
> +		struct child_process run_trailer = CHILD_PROCESS_INIT;
> +
> +		strvec_pushl(&run_trailer.args, "interpret-trailers",
> +			     "--in-place", "--if-exists=add",
> +			     git_path_commit_editmsg(), NULL);
> +		strvec_pushv(&run_trailer.args, trailer_args.v);
> +		run_trailer.git_cmd = 1;
> +		if (run_command(&run_trailer))
> +			strvec_clear(&run_trailer.args);

This is git-commit, shouldn't we die() here instead of ignoring errors
in sub-processes?

> +		strvec_clear(&trailer_args);
> +	}
> +
>  	/*
>  	 * Reject an attempt to record a non-merge empty commit without
>  	 * explicit --allow-empty. In the cherry-pick case, it may be
> @@ -1507,6 +1529,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>  		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("trailer(s) to add"), PARSE_OPT_NONEG, opt_pass_trailer),
>  		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),

Not required for this change, but perhaps a change here to N_() (if we
can get it to fit) + doc update saying that we prefer
--trailer="Signed-Off-By: to --signoff"? More on that later.

>  		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
>  		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
> diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
> index 6396897cc818..0acf23799931 100755
> --- a/t/t7502-commit-porcelain.sh
> +++ b/t/t7502-commit-porcelain.sh
> @@ -154,6 +154,26 @@ test_expect_success 'sign off' '
>  
>  '
>  
> +test_expect_success 'trailer' '
> +	>file1 &&
> +	git add file1 &&
> +	git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
> +		--trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
> +		--trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
> +		--trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
> +		-m "hello" &&
> +	git cat-file commit HEAD >commit.msg &&
> +	sed -e "1,7d" commit.msg >actual &&
> +	cat >expected <<-\EOF &&
> +	Signed-off-by: C O Mitter <committer@example.com>
> +	Signed-off-by: C O Mitter1 <committer1@example.com>
> +	Helped-by: C O Mitter2 <committer2@example.com>
> +	Reported-by: C O Mitter3 <committer3@example.com>
> +	Mentored-by: C O Mitter4 <committer4@example.com>
> +	EOF
> +	test_cmp expected actual
> +'
> +

How does this interact with cases where the user has configured
"trailer.separators" to have a value that doesn't contain ":"?  I
haven't tested, but my reading of git-interpret-trailers(1) is that if
you supplied "=" instead that case would just work:

    By default only : is recognized as a trailer separator, except that
    = is always accepted on the command line for compatibility with
    other git commands.

I don't know if that does the right thing in the presence of
--if-exists=add.

So it would be good to update these tests so you test:

 * For the --if-exists=add case at all, there's no tests for it
   now. I.e. add some trailers manually to the commit (via -F or
   whatever) and then see if they get added to, replacet etc.

 * Ditto but for the user having configured trailer.separators (see the
   test_config helper for how to set config in a test). I.e. if it's "="
   does adding trailers work, how about if it's "=" on the CLI but the
   config/commit message has ";" instead of ":" or something?

 * Hrm, actually I think tweaking "-c trailer.ifexists" won't work at
   all, since the CLI switch would override it. I honestly don't know,
   but why not not supply it and keep the addIfDifferentNeighbor
   default?

   If it's essential that seems like a good test / documentation
   addition...

 * For the above -c ... case I can't think of a good way to deal with it
   that doesn't involve pulling in git_trailer_config() into
   git_commit_config(), but perhaps the least nasty way is to just set a
   flag in git_commit_config() if we see a "trailer.ifexists" flag, and
   if so don't provide "--if-exists=add", if there's no config (this
   will include "git -c ... commit" we set provide "--if-exists=add" )
   or as noted above, maybe we can skip the whole thing and use the
   addIfDifferentNeighbor default.

And, not needed for this patch but worth thinking about:

 * We pass through --trailer to git-interpret-trailers, what should we
   do about the other options? Should git-commit eventually support
   --trailer-where and pass it along as --where to
   git-interpret-trailers, or is "git -c trailer.where=... commit" good
   enough?

 * It would be good to test for and document if that "-c trailer.*"
   trick works (no reason it shouldn't). I.e. to add something like this
   after what you have (along with tests, and check if it's even true):

       Only the `--trailer` argument to
       linkgit:git-interpret-trailers[1] is supported. Other
       pass-through switches may be added in the future, but currently
       you'll need to pass arguments to
       linkgit:git-interpret-trailers[1] along as config, e.g. `git -c
       trailer.where=start commit [...] --trailer=[...]`.
      
 * We have a longer-term goal of having the .mailmap apply to trailers,
   it would be nice if git-interpret-trailers had some fuzzy-matching to
   check if the RHS of a trailer is a name/E-Mail pair, and if so did
   stricter validation on it with the ident functions we use for fsck
   etc. (that's copied & subtly different in several different places in
   the codebase, unfortunately[1]).

More thoughts:

 * Having written all the above I checked how --signoff is implemented.

   It seems to me to be a good idea to (at least for testing) convert
   the --signoff trailer to your implementation. We have plenty of tests
   for it, does migrating it over pass or fail those?

 * I also agree with Junio that we shouldn't have a --fixed-by or
   whatever and wouldn't add --signoff today, but it seems very useful
   to me to have a shortcut like:

       --trailer "Signed-off-by"

   I.e. omitting the value, or:

      --trailer "Signed-off-by="

   Or some other thing we deem sufficiently useful/sane
   syntax/unambiguous.n

   Then the value would be provided by fmt_name(WANT_COMMITTER_IDENT)
   just as we do in append_signoff() now. I think a *very common* case
   for this would be something like:

       git commit --amend -v --trailer "Reviewed-by"

   And it would be useful to help that along and not have to do:

       git commit --amend -v --trailer "Reviewed-by=$(git config user.name) <$(git config user.email)>"

   Or worse yet, manually typo your name/e-mail address, as I'm sure I
   and many others will inevitably do when using this option...

1. https://lore.kernel.org/git/87bld8ov9q.fsf@evledraar.gmail.com/

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v8 1/2] [GSOC] commit: add --trailer option
  2021-03-16 12:52                 ` Ævar Arnfjörð Bjarmason
@ 2021-03-17  2:01                   ` ZheNing Hu
  2021-03-17  8:08                     ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 84+ messages in thread
From: ZheNing Hu @ 2021-03-17  2:01 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: ZheNing Hu via GitGitGadget, Git List, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Christian Couder,
	Rafael Silva

Ævar Arnfjörð Bjarmason <avarab@gmail.com> 于2021年3月16日周二 下午8:52写道:
> > +             if (run_command(&run_trailer))
> > +                     strvec_clear(&run_trailer.args);
>
> This is git-commit, shouldn't we die() here instead of ignoring errors
> in sub-processes?

After thinking about it carefully, your opinion is more
reasonable, because if the user uses the wrong `--trailer`
and does not get the information he needs, I think he will
have to use `--amend` to modify, and `die()` can exit
this commit directly.

>
> > +             strvec_clear(&trailer_args);
> > +     }
> > +
> >       /*
> >        * Reject an attempt to record a non-merge empty commit without
> >        * explicit --allow-empty. In the cherry-pick case, it may be
> > @@ -1507,6 +1529,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
> >               OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("trailer(s) to add"), PARSE_OPT_NONEG, opt_pass_trailer),
> >               OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
>
> Not required for this change, but perhaps a change here to N_() (if we
> can get it to fit) + doc update saying that we prefer
> --trailer="Signed-Off-By: to --signoff"? More on that later.
>
> >               OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
> >               OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
> > diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
> > index 6396897cc818..0acf23799931 100755
> > --- a/t/t7502-commit-porcelain.sh
> > +++ b/t/t7502-commit-porcelain.sh
> > @@ -154,6 +154,26 @@ test_expect_success 'sign off' '
> >
> >  '
> >
> > +test_expect_success 'trailer' '
> > +     >file1 &&
> > +     git add file1 &&
> > +     git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
> > +             --trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
> > +             --trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
> > +             --trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
> > +             -m "hello" &&
> > +     git cat-file commit HEAD >commit.msg &&
> > +     sed -e "1,7d" commit.msg >actual &&
> > +     cat >expected <<-\EOF &&
> > +     Signed-off-by: C O Mitter <committer@example.com>
> > +     Signed-off-by: C O Mitter1 <committer1@example.com>
> > +     Helped-by: C O Mitter2 <committer2@example.com>
> > +     Reported-by: C O Mitter3 <committer3@example.com>
> > +     Mentored-by: C O Mitter4 <committer4@example.com>
> > +     EOF
> > +     test_cmp expected actual
> > +'
> > +
>
> How does this interact with cases where the user has configured
> "trailer.separators" to have a value that doesn't contain ":"?  I
> haven't tested, but my reading of git-interpret-trailers(1) is that if
> you supplied "=" instead that case would just work:
>
>     By default only : is recognized as a trailer separator, except that
>     = is always accepted on the command line for compatibility with
>     other git commands.
>
But interpret_trailers interface allow us use "=" instead of other separators.

I did a simple test and modified the configuration "trailer.separators"
and it still works. Now things are good here:

$ git -c trailer.separators="@" commit --trailer="Signed-off-by=C O <email>"

or

$ git -c trailer.separators="@" commit --trailer="Signed-off-by@C O <email>"

Both can work normally,

--trailer="Signed-off-by@ C O <email>"

will output in the commit message.

> I don't know if that does the right thing in the presence of
> --if-exists=add.
>

Yesterday, Christian Couder and I had already discussed this issue:
Your idea is correct, I should not add "--if-exists = add",  this will destroy
the user's rights to configure by using `git -c trailer.if-exist`.

> So it would be good to update these tests so you test:
>
>  * For the --if-exists=add case at all, there's no tests for it
>    now. I.e. add some trailers manually to the commit (via -F or
>    whatever) and then see if they get added to, replacet etc.
>
>  * Ditto but for the user having configured trailer.separators (see the
>    test_config helper for how to set config in a test). I.e. if it's "="
>    does adding trailers work, how about if it's "=" on the CLI but the
>    config/commit message has ";" instead of ":" or something?
>

As mentioned above, it works normally.

>  * Hrm, actually I think tweaking "-c trailer.ifexists" won't work at
>    all, since the CLI switch would override it. I honestly don't know,
>    but why not not supply it and keep the addIfDifferentNeighbor
>    default?
>
>    If it's essential that seems like a good test / documentation
>    addition...
>
>  * For the above -c ... case I can't think of a good way to deal with it
>    that doesn't involve pulling in git_trailer_config() into
>    git_commit_config(), but perhaps the least nasty way is to just set a
>    flag in git_commit_config() if we see a "trailer.ifexists" flag, and
>    if so don't provide "--if-exists=add", if there's no config (this
>    will include "git -c ... commit" we set provide "--if-exists=add" )
>    or as noted above, maybe we can skip the whole thing and use the
>    addIfDifferentNeighbor default.
>

Has been restored to the default settings.

> And, not needed for this patch but worth thinking about:
>
>  * We pass through --trailer to git-interpret-trailers, what should we
>    do about the other options? Should git-commit eventually support
>    --trailer-where and pass it along as --where to
>    git-interpret-trailers, or is "git -c trailer.where=... commit" good
>    enough?
>
Logically speaking, `interpret_trailers` should be dedicated to `commit`
or other sub-commands that require trailers.

But I think that in the later stage, the parse_options of the `cmd_commit`
can keep the unrecognized options, and then these choices can be directly
passed to the `interpret_trailers` backend.

>  * It would be good to test for and document if that "-c trailer.*"
>    trick works (no reason it shouldn't). I.e. to add something like this
>    after what you have (along with tests, and check if it's even true):
>

I haven't tested them for the time being, but I will do it.

>        Only the `--trailer` argument to
>        linkgit:git-interpret-trailers[1] is supported. Other
>        pass-through switches may be added in the future, but currently
>        you'll need to pass arguments to
>        linkgit:git-interpret-trailers[1] along as config, e.g. `git -c
>        trailer.where=start commit [...] --trailer=[...]`.
>

I think this is worth writing in the documentation.

>  * We have a longer-term goal of having the .mailmap apply to trailers,
>    it would be nice if git-interpret-trailers had some fuzzy-matching to
>    check if the RHS of a trailer is a name/E-Mail pair, and if so did
>    stricter validation on it with the ident functions we use for fsck
>    etc. (that's copied & subtly different in several different places in
>    the codebase, unfortunately[1]).
>

I may not know much about fuzzy-matching, which may be worth studying later.

> More thoughts:
>
>  * Having written all the above I checked how --signoff is implemented.
>
>    It seems to me to be a good idea to (at least for testing) convert
>    the --signoff trailer to your implementation. We have plenty of tests
>    for it, does migrating it over pass or fail those?
>
I don’t know how to migrating yet, it may take a long time.
Even I think I can leave it as #leftoverbit later.

>  * I also agree with Junio that we shouldn't have a --fixed-by or
>    whatever and wouldn't add --signoff today, but it seems very useful
>    to me to have a shortcut like:
>
>        --trailer "Signed-off-by"
>
>    I.e. omitting the value, or:
>
>       --trailer "Signed-off-by="
>
>    Or some other thing we deem sufficiently useful/sane
>    syntax/unambiguous.n
>
>    Then the value would be provided by fmt_name(WANT_COMMITTER_IDENT)
>    just as we do in append_signoff() now. I think a *very common* case
>    for this would be something like:
>
>        git commit --amend -v --trailer "Reviewed-by"
>
>    And it would be useful to help that along and not have to do:
>
>        git commit --amend -v --trailer "Reviewed-by=$(git config user.name) <$(git config user.email)>"
>
>    Or worse yet, manually typo your name/e-mail address, as I'm sure I
>    and many others will inevitably do when using this option...
>
I think this idea is very good and easy to implement.
We only need to do a simple string match when we get the "trailer" string,
If it can be completed, it can indeed bring great convenience to users.

> 1. https://lore.kernel.org/git/87bld8ov9q.fsf@evledraar.gmail.com/

Thanks, Ævar Arnfjörð Bjarmason!

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v9] [GSOC] commit: add --trailer option
  2021-03-16 10:39               ` [PATCH v9] [GSOC] commit: add --trailer option ZheNing Hu via GitGitGadget
@ 2021-03-17  5:26                 ` Shourya Shukla
  2021-03-17  6:06                   ` ZheNing Hu
  2021-03-18 11:15                 ` [PATCH v10 0/3] " ZheNing Hu via GitGitGadget
  1 sibling, 1 reply; 84+ messages in thread
From: Shourya Shukla @ 2021-03-17  5:26 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget; +Cc: christian.couder, adlternative, git

On 16/03 10:39, ZheNing Hu via GitGitGadget wrote:

Hey ZheNing!

> From: ZheNing Hu <adlternative@gmail.com>
> 
> Historically, Git has supported the 'Signed-off-by' commit trailer
> using the '--signoff' and the '-s' option from the command line.
> But users may need to provide other trailer information from the
> command line such as "Helped-by", "Reported-by", "Mentored-by",
> 
> Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
> other trailers to `interpret-trailers` and insert them into commit
> messages.
> 
> Signed-off-by: ZheNing Hu <adlternative@gmail.com>

I have been away for a while and directly seeing a V9 of this patch
feels great! Its good that you have worked upon the patch. The above
approach seems good to me!

>  	/*
>  	 * Reject an attempt to record a non-merge empty commit without
>  	 * explicit --allow-empty. In the cherry-pick case, it may be
> @@ -1507,6 +1528,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>  		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("trailer(s) to add"), PARSE_OPT_NONEG, opt_pass_trailer),

I feel that a better option description could be offered? Maybe
something like: 'add custom trailer(s)'.


>  		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
>  		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
>  		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),

I have not yet gone through the the V2-V8s but I have a comment not
associated with the contents of the patch. I feel that you should wait a
little before posting a new version of the patch. I see that V4-V8 are
put up in almost 3 hour gaps. This isn't technically wrong or prohibited
by the communication rules of the List but I feel that posting a patch
in such short intervals makes it hard to review and unnecessarily
increases the versions of the patch. The reviewer lags behind the patch
series in fact.

What you could do instead is post one patch per day instead of 3-4 in
one single day so that your patches get thorough reviews. This way, you
won't create 3-4 new versions of the patch containing not-so-many
significant changes. You get me?

Also, in your reply on the V1 here:
https://lore.kernel.org/git/CAOLTT8SpAOj51jqYUYqYwXaVKSn1fANvetauaG0z4etiBMzVEw@mail.gmail.com/

I read:

> It's exactly what you said.
> My lack of English sometimes limits my expression.

It is okay please do not worry. Neither do we have English as our first
language nor have we ever communicated this much with an English
speaking audience online. I struggled initially too especially with many
American terms used here. You will get the hang of it soon.

Keep contributing!

Regards,
Shourya Shukla


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v9] [GSOC] commit: add --trailer option
  2021-03-17  5:26                 ` Shourya Shukla
@ 2021-03-17  6:06                   ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-17  6:06 UTC (permalink / raw)
  To: Shourya Shukla; +Cc: ZheNing Hu via GitGitGadget, Christian Couder, Git List

Shourya Shukla <periperidip@gmail.com> 于2021年3月17日周三 下午1:26写道:
>
> On 16/03 10:39, ZheNing Hu via GitGitGadget wrote:
>
> Hey ZheNing!
>
>
> I have been away for a while and directly seeing a V9 of this patch
> feels great! Its good that you have worked upon the patch. The above
> approach seems good to me!
>
Hi! :-)
> >       /*
> >        * Reject an attempt to record a non-merge empty commit without
> >        * explicit --allow-empty. In the cherry-pick case, it may be
> > @@ -1507,6 +1528,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
> >               OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("trailer(s) to add"), PARSE_OPT_NONEG, opt_pass_trailer),
>
> I feel that a better option description could be offered? Maybe
> something like: 'add custom trailer(s)'.
>

Feel ok.

>
> >               OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
> >               OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
> >               OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
>
> I have not yet gone through the the V2-V8s but I have a comment not
> associated with the contents of the patch. I feel that you should wait a
> little before posting a new version of the patch. I see that V4-V8 are
> put up in almost 3 hour gaps. This isn't technically wrong or prohibited
> by the communication rules of the List but I feel that posting a patch
> in such short intervals makes it hard to review and unnecessarily
> increases the versions of the patch. The reviewer lags behind the patch
> series in fact.
>
> What you could do instead is post one patch per day instead of 3-4 in
> one single day so that your patches get thorough reviews. This way, you
> won't create 3-4 new versions of the patch containing not-so-many
> significant changes. You get me?
>

Get it. Especially seeing Ævar Arnfjörð Bjarmason give me comments
behind the new iterations I posted, I knew that I might be sending new
versions too frequently.

> Also, in your reply on the V1 here:
> https://lore.kernel.org/git/CAOLTT8SpAOj51jqYUYqYwXaVKSn1fANvetauaG0z4etiBMzVEw@mail.gmail.com/
>
> I read:
>
> > It's exactly what you said.
> > My lack of English sometimes limits my expression.
>
> It is okay please do not worry. Neither do we have English as our first
> language nor have we ever communicated this much with an English
> speaking audience online. I struggled initially too especially with many
> American terms used here. You will get the hang of it soon.
>
> Keep contributing!
>

Thank you for your encouragement. Many people come to discuss and
share some ideas in English with me every day. It is a cool thing in itself.

> Regards,
> Shourya Shukla
>

Regards,
ZheNing Hu

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v8 1/2] [GSOC] commit: add --trailer option
  2021-03-17  2:01                   ` ZheNing Hu
@ 2021-03-17  8:08                     ` Ævar Arnfjörð Bjarmason
  2021-03-17 13:54                       ` ZheNing Hu
  0 siblings, 1 reply; 84+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-03-17  8:08 UTC (permalink / raw)
  To: ZheNing Hu
  Cc: ZheNing Hu via GitGitGadget, Git List, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Christian Couder,
	Rafael Silva


On Wed, Mar 17 2021, ZheNing Hu wrote:

> Ævar Arnfjörð Bjarmason <avarab@gmail.com> 于2021年3月16日周二 下午8:52写道:
>> > +             if (run_command(&run_trailer))
>> > +                     strvec_clear(&run_trailer.args);
>>
>> This is git-commit, shouldn't we die() here instead of ignoring errors
>> in sub-processes?
>
> After thinking about it carefully, your opinion is more
> reasonable, because if the user uses the wrong `--trailer`
> and does not get the information he needs, I think he will
> have to use `--amend` to modify, and `die()` can exit
> this commit directly.

Yeah, we don't want to silently lose data.

>>
>> > +             strvec_clear(&trailer_args);
>> > +     }
>> > +
>> >       /*
>> >        * Reject an attempt to record a non-merge empty commit without
>> >        * explicit --allow-empty. In the cherry-pick case, it may be
>> > @@ -1507,6 +1529,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>> >               OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("trailer(s) to add"), PARSE_OPT_NONEG, opt_pass_trailer),
>> >               OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
>>
>> Not required for this change, but perhaps a change here to N_() (if we
>> can get it to fit) + doc update saying that we prefer
>> --trailer="Signed-Off-By: to --signoff"? More on that later.
>>
>> >               OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
>> >               OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
>> > diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
>> > index 6396897cc818..0acf23799931 100755
>> > --- a/t/t7502-commit-porcelain.sh
>> > +++ b/t/t7502-commit-porcelain.sh
>> > @@ -154,6 +154,26 @@ test_expect_success 'sign off' '
>> >
>> >  '
>> >
>> > +test_expect_success 'trailer' '
>> > +     >file1 &&
>> > +     git add file1 &&
>> > +     git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
>> > +             --trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
>> > +             --trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
>> > +             --trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
>> > +             -m "hello" &&
>> > +     git cat-file commit HEAD >commit.msg &&
>> > +     sed -e "1,7d" commit.msg >actual &&
>> > +     cat >expected <<-\EOF &&
>> > +     Signed-off-by: C O Mitter <committer@example.com>
>> > +     Signed-off-by: C O Mitter1 <committer1@example.com>
>> > +     Helped-by: C O Mitter2 <committer2@example.com>
>> > +     Reported-by: C O Mitter3 <committer3@example.com>
>> > +     Mentored-by: C O Mitter4 <committer4@example.com>
>> > +     EOF
>> > +     test_cmp expected actual
>> > +'
>> > +
>>
>> How does this interact with cases where the user has configured
>> "trailer.separators" to have a value that doesn't contain ":"?  I
>> haven't tested, but my reading of git-interpret-trailers(1) is that if
>> you supplied "=" instead that case would just work:
>>
>>     By default only : is recognized as a trailer separator, except that
>>     = is always accepted on the command line for compatibility with
>>     other git commands.
>>
> But interpret_trailers interface allow us use "=" instead of other separators.
>
> I did a simple test and modified the configuration "trailer.separators"
> and it still works. Now things are good here:
>
> $ git -c trailer.separators="@" commit --trailer="Signed-off-by=C O <email>"
>
> or
>
> $ git -c trailer.separators="@" commit --trailer="Signed-off-by@C O <email>"
>
> Both can work normally,
>
> --trailer="Signed-off-by@ C O <email>"
>
> will output in the commit message.
>
>> I don't know if that does the right thing in the presence of
>> --if-exists=add.
>>
>
> Yesterday, Christian Couder and I had already discussed this issue:
> Your idea is correct, I should not add "--if-exists = add",  this will destroy
> the user's rights to configure by using `git -c trailer.if-exist`.
>
>> So it would be good to update these tests so you test:
>>
>>  * For the --if-exists=add case at all, there's no tests for it
>>    now. I.e. add some trailers manually to the commit (via -F or
>>    whatever) and then see if they get added to, replacet etc.
>>
>>  * Ditto but for the user having configured trailer.separators (see the
>>    test_config helper for how to set config in a test). I.e. if it's "="
>>    does adding trailers work, how about if it's "=" on the CLI but the
>>    config/commit message has ";" instead of ":" or something?
>>
>
> As mentioned above, it works normally.
>
>>  * Hrm, actually I think tweaking "-c trailer.ifexists" won't work at
>>    all, since the CLI switch would override it. I honestly don't know,
>>    but why not not supply it and keep the addIfDifferentNeighbor
>>    default?
>>
>>    If it's essential that seems like a good test / documentation
>>    addition...
>>
>>  * For the above -c ... case I can't think of a good way to deal with it
>>    that doesn't involve pulling in git_trailer_config() into
>>    git_commit_config(), but perhaps the least nasty way is to just set a
>>    flag in git_commit_config() if we see a "trailer.ifexists" flag, and
>>    if so don't provide "--if-exists=add", if there's no config (this
>>    will include "git -c ... commit" we set provide "--if-exists=add" )
>>    or as noted above, maybe we can skip the whole thing and use the
>>    addIfDifferentNeighbor default.
>>
>
> Has been restored to the default settings.

To clarify: What I really mean is for all these things you've tested:
let's add those to the tests as part of the patch.

>> And, not needed for this patch but worth thinking about:
>>
>>  * We pass through --trailer to git-interpret-trailers, what should we
>>    do about the other options? Should git-commit eventually support
>>    --trailer-where and pass it along as --where to
>>    git-interpret-trailers, or is "git -c trailer.where=... commit" good
>>    enough?
>>
> Logically speaking, `interpret_trailers` should be dedicated to `commit`
> or other sub-commands that require trailers.
>
> But I think that in the later stage, the parse_options of the `cmd_commit`
> can keep the unrecognized options, and then these choices can be directly
> passed to the `interpret_trailers` backend.

We have this interaction with e.g. range-diff and "log", it's often
surprising. You add an option to one command and it appears in the
other.

>>  * It would be good to test for and document if that "-c trailer.*"
>>    trick works (no reason it shouldn't). I.e. to add something like this
>>    after what you have (along with tests, and check if it's even true):
>>
>
> I haven't tested them for the time being, but I will do it.
>
>>        Only the `--trailer` argument to
>>        linkgit:git-interpret-trailers[1] is supported. Other
>>        pass-through switches may be added in the future, but currently
>>        you'll need to pass arguments to
>>        linkgit:git-interpret-trailers[1] along as config, e.g. `git -c
>>        trailer.where=start commit [...] --trailer=[...]`.
>>
>
> I think this is worth writing in the documentation.
>
>>  * We have a longer-term goal of having the .mailmap apply to trailers,
>>    it would be nice if git-interpret-trailers had some fuzzy-matching to
>>    check if the RHS of a trailer is a name/E-Mail pair, and if so did
>>    stricter validation on it with the ident functions we use for fsck
>>    etc. (that's copied & subtly different in several different places in
>>    the codebase, unfortunately[1]).
>>
>
> I may not know much about fuzzy-matching, which may be worth studying later.
>
>> More thoughts:
>>
>>  * Having written all the above I checked how --signoff is implemented.
>>
>>    It seems to me to be a good idea to (at least for testing) convert
>>    the --signoff trailer to your implementation. We have plenty of tests
>>    for it, does migrating it over pass or fail those?
>>
> I don’t know how to migrating yet, it may take a long time.
> Even I think I can leave it as #leftoverbit later.

Sure, I mean (having looked at it) that at least for your own local
testing it would make sense to change it (even if just search-replacing
the --signoff in the test suite) to see if it behaves as you
expect. I.e. does the --trailer behavior mirror --signoff?

>>  * I also agree with Junio that we shouldn't have a --fixed-by or
>>    whatever and wouldn't add --signoff today, but it seems very useful
>>    to me to have a shortcut like:
>>
>>        --trailer "Signed-off-by"
>>
>>    I.e. omitting the value, or:
>>
>>       --trailer "Signed-off-by="
>>
>>    Or some other thing we deem sufficiently useful/sane
>>    syntax/unambiguous.n
>>
>>    Then the value would be provided by fmt_name(WANT_COMMITTER_IDENT)
>>    just as we do in append_signoff() now. I think a *very common* case
>>    for this would be something like:
>>
>>        git commit --amend -v --trailer "Reviewed-by"
>>
>>    And it would be useful to help that along and not have to do:
>>
>>        git commit --amend -v --trailer "Reviewed-by=$(git config user.name) <$(git config user.email)>"
>>
>>    Or worse yet, manually typo your name/e-mail address, as I'm sure I
>>    and many others will inevitably do when using this option...
>>
> I think this idea is very good and easy to implement.
> We only need to do a simple string match when we get the "trailer" string,
> If it can be completed, it can indeed bring great convenience to users.
>
>> 1. https://lore.kernel.org/git/87bld8ov9q.fsf@evledraar.gmail.com/
>
> Thanks, Ævar Arnfjörð Bjarmason!

And thanks for working on this.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v8 1/2] [GSOC] commit: add --trailer option
  2021-03-17  8:08                     ` Ævar Arnfjörð Bjarmason
@ 2021-03-17 13:54                       ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-17 13:54 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: ZheNing Hu via GitGitGadget, Git List, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Christian Couder,
	Rafael Silva

Ævar Arnfjörð Bjarmason <avarab@gmail.com> 于2021年3月17日周三 下午4:08写道:
> > Logically speaking, `interpret_trailers` should be dedicated to `commit`
> > or other sub-commands that require trailers.
> >
> > But I think that in the later stage, the parse_options of the `cmd_commit`
> > can keep the unrecognized options, and then these choices can be directly
> > passed to the `interpret_trailers` backend.
>
> We have this interaction with e.g. range-diff and "log", it's often
> surprising. You add an option to one command and it appears in the
> other.
>

All right, I'm wrong, I may have reference to an wrong experience
of `difftool`-->`diff`.

> >>    It seems to me to be a good idea to (at least for testing) convert
> >>    the --signoff trailer to your implementation. We have plenty of tests
> >>    for it, does migrating it over pass or fail those?
> >>
> > I don’t know how to migrating yet, it may take a long time.
> > Even I think I can leave it as #leftoverbit later.
>
> Sure, I mean (having looked at it) that at least for your own local
> testing it would make sense to change it (even if just search-replacing
> the --signoff in the test suite) to see if it behaves as you
> expect. I.e. does the --trailer behavior mirror --signoff?
>
> >>  * I also agree with Junio that we shouldn't have a --fixed-by or
> >>    whatever and wouldn't add --signoff today, but it seems very useful
> >>    to me to have a shortcut like:
> >>
> >>        --trailer "Signed-off-by"
> >>
> >>    I.e. omitting the value, or:
> >>
> >>       --trailer "Signed-off-by="
> >>
> >>    Or some other thing we deem sufficiently useful/sane
> >>    syntax/unambiguous.n
> >>
> >>    Then the value would be provided by fmt_name(WANT_COMMITTER_IDENT)
> >>    just as we do in append_signoff() now. I think a *very common* case
> >>    for this would be something like:
> >>
> >>        git commit --amend -v --trailer "Reviewed-by"
> >>
> >>    And it would be useful to help that along and not have to do:
> >>
> >>        git commit --amend -v --trailer "Reviewed-by=$(git config user.name) <$(git config user.email)>"
> >>
> >>    Or worse yet, manually typo your name/e-mail address, as I'm sure I
> >>    and many others will inevitably do when using this option...
> >>

Well, that's what I think here:

Now we can go through:

$ git -c trailer.signoff.key = "Signed-off-by" commit --trailer
"signoff = commiter <email>"

to get a trailer: "Signed-off-by: commiter <email>", this means we
can't just do simple string
matching in `cmd_commit` to replace `--trailer="Signed-off-by"` or
`--trailer="Reviewed-by"` to
user's own identity, to replace the trailers which have omitting value
 we passed in, but I think
we can provide a new option to `commit` which can mandatory that
trailers with no value can be
 replaced with the identity of the user.

e.g.

$ git -c trailer.signoff.key = "Signed-off-by" commit --trailer
"signoff" --trailer "Helped-by" \
 --trailer "Helped-by = C <E>" --own_ident

will output like this:

Signed-off-by: $(git config user.name) <$(git config user.email)>
Signed-off-by: $(git config user.name) <$(git config user.email)>
Helped-by: $(git config user.name) <$(git config user.email)>
Helped-by: C <E>

I don't know if this idea is good, I will try to do it first.

Thanks.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v10 0/3] [GSOC] commit: add --trailer option
  2021-03-16 10:39               ` [PATCH v9] [GSOC] commit: add --trailer option ZheNing Hu via GitGitGadget
  2021-03-17  5:26                 ` Shourya Shukla
@ 2021-03-18 11:15                 ` ZheNing Hu via GitGitGadget
  2021-03-18 11:15                   ` [PATCH v10 1/3] " ZheNing Hu via GitGitGadget
                                     ` (4 more replies)
  1 sibling, 5 replies; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-18 11:15 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva, ZheNing Hu

Now maintainers or developers can also use commit
--trailer="Signed-off-by:commiter<email>" from the command line to provide
trailers to commit messages. This solution may be more generalized than v1.

ZheNing Hu (3):
  [GSOC] commit: add --trailer option
  interpret-trailers: add own-identity option
  commit: add own-identity option

 Documentation/git-commit.txt             |  25 +-
 Documentation/git-interpret-trailers.txt |  14 +
 builtin/commit.c                         |  31 ++
 builtin/interpret-trailers.c             |   6 +-
 t/t7501-commit-basic-functionality.sh    |  91 ++++++
 t/t7502-commit-porcelain.sh              | 356 +++++++++++++++++++++++
 t/t7513-interpret-trailers.sh            |  12 +
 trailer.c                                |  18 +-
 trailer.h                                |   3 +-
 9 files changed, 550 insertions(+), 6 deletions(-)


base-commit: 13d7ab6b5d7929825b626f050b62a11241ea4945
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v10
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v10
Pull-Request: https://github.com/gitgitgadget/git/pull/901

Range-diff vs v9:

 1:  e524c4ba5dc1 ! 1:  949faf9ee56a [GSOC] commit: add --trailer option
     @@ Documentation/git-commit.txt: The `-m` option is mutually exclusive with `-c`, `
      +	<committer@example.com>" --trailer "Helped-by:C O Mitter \
      +	<committer@example.com>"` will add the "Signed-off-by" trailer
      +	and the "Helped-by" trailer in the commit message.)
     -+	See linkgit:git-interpret-trailers[1] for details.
     ++	Use `git -c trailer.* commit --trailer` to make the appropriate
     ++	configuration. See linkgit:git-interpret-trailers[1] for details.
       -n::
       --no-verify::
       	This option bypasses the pre-commit and commit-msg hooks.
     @@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const cha
      +			     "--in-place", git_path_commit_editmsg(), NULL);
      +		strvec_pushv(&run_trailer.args, trailer_args.v);
      +		run_trailer.git_cmd = 1;
     -+		if (run_command(&run_trailer))
     -+			strvec_clear(&run_trailer.args);
     ++		if (run_command(&run_trailer)) {
     ++			die(_("unable to pass tailers to --trailers"));
     ++		}
      +		strvec_clear(&trailer_args);
      +	}
      +
     @@ builtin/commit.c: int cmd_commit(int argc, const char **argv, const char *prefix
       		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("trailer(s) to add"), PARSE_OPT_NONEG, opt_pass_trailer),
     ++		OPT_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
       		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
       		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
       		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
     @@ t/t7502-commit-porcelain.sh: test_expect_success 'sign off' '
       
       '
       
     -+test_expect_success 'trailer' '
     -+	>file1 &&
     -+	git add file1 &&
     -+	git commit -s --trailer "Signed-off-by:C O Mitter1 <committer1@example.com>" \
     -+		--trailer "Helped-by:C O Mitter2 <committer2@example.com>"  \
     -+		--trailer "Reported-by:C O Mitter3 <committer3@example.com>" \
     -+		--trailer "Mentored-by:C O Mitter4 <committer4@example.com>" \
     ++test_expect_success 'commit --trailer without -c' '
     ++	echo "fun" >>file &&
     ++	git add file &&
     ++	cat >expected <<-\EOF &&
     ++
     ++	Signed-off-by: C O Mitter <committer@example.com>
     ++	Signed-off-by: C1 E1
     ++	Helped-by: C2 E2
     ++	Reported-by: C3 E3
     ++	Mentored-by: C4 E4
     ++	EOF
     ++	git commit -s --trailer "Signed-off-by:C1 E1 " \
     ++		--trailer "Helped-by:C2 E2 " \
     ++		--trailer "Reported-by:C3 E3" \
     ++		--trailer "Mentored-by:C4 E4" \
      +		-m "hello" &&
      +	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,7d" commit.msg >actual &&
     ++	sed -e "1,6d" commit.msg >actual &&
     ++	test_cmp expected actual
     ++'
     ++
     ++test_expect_success 'commit --trailer with -c and "replace" as ifexists' '
     ++	echo "fun" >>file1 &&
     ++	git add file1 &&
     ++	cat >expected <<-\EOF &&
     ++
     ++	Signed-off-by: C O Mitter <committer@example.com>
     ++	Signed-off-by: C1 E1
     ++	Reported-by: C3 E3
     ++	Mentored-by: C4 E4
     ++	Helped-by: C3 E3
     ++	EOF
     ++	git -c trailer.ifexists="replace" \
     ++		commit --trailer "Mentored-by: C4 E4" \
     ++		 --trailer "Helped-by: C3 E3" \
     ++		--amend &&
     ++	git cat-file commit HEAD >commit.msg &&
     ++	sed -e "1,6d" commit.msg >actual &&
     ++	test_cmp expected actual
     ++'
     ++
     ++test_expect_success 'commit --trailer with -c and "add" as ifexists' '
     ++	echo "fun" >>file1 &&
     ++	git add file1 &&
     ++	cat >expected <<-\EOF &&
     ++
     ++	Signed-off-by: C O Mitter <committer@example.com>
     ++	Signed-off-by: C1 E1
     ++	Reported-by: C3 E3
     ++	Mentored-by: C4 E4
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	EOF
     ++	git -c trailer.ifexists="add" \
     ++		commit --trailer "Helped-by: C3 E3" \
     ++		--trailer "Helped-by: C3 E3" \
     ++		--amend &&
     ++	git cat-file commit HEAD >commit.msg &&
     ++	sed -e "1,6d" commit.msg >actual &&
     ++	test_cmp expected actual
     ++'
     ++
     ++test_expect_success 'commit --trailer with -c and "donothing" as ifexists' '
     ++	echo "fun" >>file1 &&
     ++	git add file1 &&
      +	cat >expected <<-\EOF &&
     ++
      +	Signed-off-by: C O Mitter <committer@example.com>
     -+	Signed-off-by: C O Mitter1 <committer1@example.com>
     -+	Helped-by: C O Mitter2 <committer2@example.com>
     -+	Reported-by: C O Mitter3 <committer3@example.com>
     -+	Mentored-by: C O Mitter4 <committer4@example.com>
     ++	Signed-off-by: C1 E1
     ++	Reported-by: C3 E3
     ++	Mentored-by: C4 E4
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Reviewed-by: C6 E6
      +	EOF
     ++	git -c trailer.ifexists="donothing" \
     ++		commit --trailer "Mentored-by: C5 E5" \
     ++		--trailer "Reviewed-by: C6 E6" \
     ++		--amend &&
     ++	git cat-file commit HEAD >commit.msg &&
     ++	sed -e "1,6d" commit.msg >actual &&
     ++	test_cmp expected actual
     ++'
     ++
     ++test_expect_success 'commit --trailer with -c and "addIfDifferent" as ifexists' '
     ++	echo "fun" >>file1 &&
     ++	git add file1 &&
     ++	cat >expected <<-\EOF &&
     ++
     ++	Signed-off-by: C O Mitter <committer@example.com>
     ++	Signed-off-by: C1 E1
     ++	Reported-by: C3 E3
     ++	Mentored-by: C4 E4
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Reviewed-by: C6 E6
     ++	Reported-by: C5 E5
     ++	EOF
     ++	git -c trailer.ifexists="addIfDifferent" \
     ++		commit --trailer "Reviewed-by: C6 E6" \
     ++		--trailer "Reported-by: C5 E5" \
     ++		--amend &&
     ++	git cat-file commit HEAD >commit.msg &&
     ++	sed -e "1,6d" commit.msg >actual &&
     ++	test_cmp expected actual
     ++'
     ++
     ++test_expect_success 'commit --trailer with -c and "addIfDifferentNeighbor" as ifexists' '
     ++	echo "fun" >>file1 &&
     ++	git add file1 &&
     ++	cat >expected <<-\EOF &&
     ++
     ++	Signed-off-by: C O Mitter <committer@example.com>
     ++	Signed-off-by: C1 E1
     ++	Reported-by: C3 E3
     ++	Mentored-by: C4 E4
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Reviewed-by: C6 E6
     ++	Reported-by: C5 E5
     ++	EOF
     ++	git -c trailer.ifexists="addIfDifferent" \
     ++		commit --trailer "Reported-by: C5 E5" \
     ++		--trailer "Reviewed-by: C6 E6" \
     ++		--amend &&
     ++	git cat-file commit HEAD >commit.msg &&
     ++	sed -e "1,6d" commit.msg >actual &&
     ++	test_cmp expected actual
     ++'
     ++
     ++test_expect_success 'commit --trailer with -c and "end" as where' '
     ++	echo "fun" >>file1 &&
     ++	git add file1 &&
     ++	cat >expected <<-\EOF &&
     ++
     ++	Signed-off-by: C O Mitter <committer@example.com>
     ++	Signed-off-by: C1 E1
     ++	Reported-by: C3 E3
     ++	Mentored-by: C4 E4
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Reviewed-by: C6 E6
     ++	Reported-by: C5 E5
     ++	Reported-by: C7 E7
     ++	EOF
     ++	git -c trailer.where="end" \
     ++		commit --trailer "Reported-by: C5 E5" \
     ++		--trailer "Reported-by: C7 E7" \
     ++		--amend &&
     ++	git cat-file commit HEAD >commit.msg &&
     ++	sed -e "1,6d" commit.msg >actual &&
     ++	test_cmp expected actual
     ++'
     ++
     ++test_expect_success 'commit --trailer with -c and "start" as where' '
     ++	echo "fun" >>file1 &&
     ++	git add file1 &&
     ++	cat >expected <<-\EOF &&
     ++
     ++	Signed-off-by: C8 E8
     ++	Signed-off-by: C O Mitter <committer@example.com>
     ++	Signed-off-by: C1 E1
     ++	Reported-by: C3 E3
     ++	Mentored-by: C4 E4
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Reviewed-by: C6 E6
     ++	Reported-by: C5 E5
     ++	Reported-by: C7 E7
     ++	EOF
     ++	git -c trailer.where="start" \
     ++		commit --trailer "Signed-off-by: C8 E8" \
     ++		--trailer "Signed-off-by: C8 E8" \
     ++		--amend &&
     ++	git cat-file commit HEAD >commit.msg &&
     ++	sed -e "1,6d" commit.msg >actual &&
     ++	test_cmp expected actual
     ++'
     ++
     ++test_expect_success 'commit --trailer with -c and "after" as where' '
     ++	echo "fun" >>file1 &&
     ++	git add file1 &&
     ++	cat >expected <<-\EOF &&
     ++
     ++	Signed-off-by: C8 E8
     ++	Signed-off-by: C O Mitter <committer@example.com>
     ++	Signed-off-by: C1 E1
     ++	Reported-by: C3 E3
     ++	Mentored-by: C4 E4
     ++	Mentored-by: C9 E9
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Reviewed-by: C6 E6
     ++	Reported-by: C5 E5
     ++	Reported-by: C7 E7
     ++	Reported-by: C10 E10
     ++	EOF
     ++	git -c trailer.where="after" \
     ++		commit --trailer "Mentored-by: C9 E9" \
     ++		--trailer "Reported-by: C10 E10" \
     ++		--amend &&
     ++	git cat-file commit HEAD >commit.msg &&
     ++	sed -e "1,6d" commit.msg >actual &&
     ++	test_cmp expected actual
     ++'
     ++
     ++test_expect_success 'commit --trailer with -c and "before" as where' '
     ++	echo "fun" >>file1 &&
     ++	git add file1 &&
     ++	cat >expected <<-\EOF &&
     ++
     ++	Signed-off-by: C8 E8
     ++	Signed-off-by: C O Mitter <committer@example.com>
     ++	Signed-off-by: C1 E1
     ++	Reported-by: C11 E11
     ++	Reported-by: C3 E3
     ++	Mentored-by: C4 E4
     ++	Mentored-by: C9 E9
     ++	Helped-by: C12 E12
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Reviewed-by: C6 E6
     ++	Reported-by: C5 E5
     ++	Reported-by: C7 E7
     ++	Reported-by: C10 E10
     ++	EOF
     ++	git -c trailer.where="before" \
     ++		commit --trailer "Helped-by: C12 E12" \
     ++		--trailer "Reported-by: C11 E11" \
     ++		--amend &&
     ++	git cat-file commit HEAD >commit.msg &&
     ++	sed -e "1,6d" commit.msg >actual &&
     ++	test_cmp expected actual
     ++'
     ++
     ++test_expect_success 'commit --trailer with -c and "donothing" as ifmissing' '
     ++	echo "fun" >>file1 &&
     ++	git add file1 &&
     ++	cat >expected <<-\EOF &&
     ++
     ++	Signed-off-by: C8 E8
     ++	Signed-off-by: C O Mitter <committer@example.com>
     ++	Signed-off-by: C1 E1
     ++	Reported-by: C11 E11
     ++	Reported-by: C3 E3
     ++	Mentored-by: C4 E4
     ++	Mentored-by: C9 E9
     ++	Helped-by: C12 E12
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Reviewed-by: C6 E6
     ++	Reported-by: C5 E5
     ++	Reported-by: C7 E7
     ++	Reported-by: C10 E10
     ++	Helped-by: C12 E12
     ++	EOF
     ++	git -c trailer.ifmissing="donothing" \
     ++		commit --trailer "Helped-by: C12 E12" \
     ++		--trailer "Based-by: C13 E13" \
     ++		--amend &&
     ++	git cat-file commit HEAD >commit.msg &&
     ++	sed -e "1,6d" commit.msg >actual &&
     ++	test_cmp expected actual
     ++'
     ++
     ++test_expect_success 'commit --trailer with -c and "add" as ifmissing' '
     ++	echo "fun" >>file1 &&
     ++	git add file1 &&
     ++	cat >expected <<-\EOF &&
     ++
     ++	Signed-off-by: C8 E8
     ++	Signed-off-by: C O Mitter <committer@example.com>
     ++	Signed-off-by: C1 E1
     ++	Reported-by: C11 E11
     ++	Reported-by: C3 E3
     ++	Mentored-by: C4 E4
     ++	Mentored-by: C9 E9
     ++	Helped-by: C12 E12
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Helped-by: C3 E3
     ++	Reviewed-by: C6 E6
     ++	Reported-by: C5 E5
     ++	Reported-by: C7 E7
     ++	Reported-by: C10 E10
     ++	Helped-by: C12 E12
     ++	Based-by: C13 E13
     ++	EOF
     ++	git -c trailer.ifmissing="add" \
     ++		commit --trailer "Helped-by: C12 E12" \
     ++		--trailer "Based-by: C13 E13" \
     ++		--amend &&
     ++	git cat-file commit HEAD >commit.msg &&
     ++	sed -e "1,6d" commit.msg >actual &&
     ++	test_cmp expected actual
     ++'
     ++
     ++test_expect_success 'commit --trailer with -c and "=" as separators' '
     ++	echo "fun" >>file1 &&
     ++	git add file1 &&
     ++	cat >expected <<-\EOF &&
     ++
     ++		Acked-by= Peff
     ++	EOF
     ++	git -c trailer.separators="=" \
     ++		-c trailer.ack.key="Acked-by= " \
     ++		commit --trailer "ack = Peff" -m "hello" &&
     ++	git cat-file commit HEAD >commit.msg &&
     ++	sed -e "1,6d" commit.msg >actual &&
     ++	test_cmp expected actual
     ++'
     ++
     ++test_expect_success 'commit --trailer with -c and ":=#" as separators' '
     ++	echo "fun" >>file1 &&
     ++	git add file1 &&
     ++	cat >expected <<-\EOF &&
     ++
     ++		Bug #42
     ++	EOF
     ++	git -c trailer.separators=":=#" \
     ++		-c trailer.bug.key="Bug #" \
     ++		commit --trailer "bug = 42" -m "I hate bug" &&
     ++	git cat-file commit HEAD >commit.msg &&
     ++	sed -e "1,6d" commit.msg >actual &&
      +	test_cmp expected actual
      +'
      +
 -:  ------------ > 2:  42590e95deee interpret-trailers: add own-identity option
 -:  ------------ > 3:  2dfcc20f0e9f commit: add own-identity option

-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v10 1/3] [GSOC] commit: add --trailer option
  2021-03-18 11:15                 ` [PATCH v10 0/3] " ZheNing Hu via GitGitGadget
@ 2021-03-18 11:15                   ` ZheNing Hu via GitGitGadget
  2021-03-18 16:29                     ` Đoàn Trần Công Danh
  2021-03-18 11:15                   ` [PATCH v10 2/3] interpret-trailers: add own-identity option ZheNing Hu via GitGitGadget
                                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-18 11:15 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Historically, Git has supported the 'Signed-off-by' commit trailer
using the '--signoff' and the '-s' option from the command line.
But users may need to provide other trailer information from the
command line such as "Helped-by", "Reported-by", "Mentored-by",

Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
other trailers to `interpret-trailers` and insert them into commit
messages.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
 Documentation/git-commit.txt |  10 +-
 builtin/commit.c             |  23 +++
 t/t7502-commit-porcelain.sh  | 336 +++++++++++++++++++++++++++++++++++
 3 files changed, 368 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 17150fa7eabe..c5de981cd40d 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -14,7 +14,7 @@ SYNOPSIS
 	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
 	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
 	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
-	   [-S[<keyid>]] [--] [<pathspec>...]
+	   [-S[<keyid>]] [--] [<pathspec>...] [(--trailer <token>[(=|:)<value>])...]
 
 DESCRIPTION
 -----------
@@ -166,6 +166,14 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
 
 include::signoff-option.txt[]
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
+	<committer@example.com>" --trailer "Helped-by:C O Mitter \
+	<committer@example.com>"` will add the "Signed-off-by" trailer
+	and the "Helped-by" trailer in the commit message.)
+	Use `git -c trailer.* commit --trailer` to make the appropriate
+	configuration. See linkgit:git-interpret-trailers[1] for details.
 -n::
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
diff --git a/builtin/commit.c b/builtin/commit.c
index 739110c5a7f6..7a79aae48f43 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,6 +113,7 @@ static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit, *pathspec_from_file;
+static struct strvec trailer_args = STRVEC_INIT;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -131,6 +132,14 @@ static struct strbuf message = STRBUF_INIT;
 
 static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
 
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+
+	strvec_pushl(&trailer_args, "--trailer", arg, NULL);
+	return 0;
+}
+
 static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
 {
 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -958,6 +967,19 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
 	fclose(s->fp);
 
+	if (trailer_args.nr) {
+		struct child_process run_trailer = CHILD_PROCESS_INIT;
+
+		strvec_pushl(&run_trailer.args, "interpret-trailers",
+			     "--in-place", git_path_commit_editmsg(), NULL);
+		strvec_pushv(&run_trailer.args, trailer_args.v);
+		run_trailer.git_cmd = 1;
+		if (run_command(&run_trailer)) {
+			die(_("unable to pass tailers to --trailers"));
+		}
+		strvec_clear(&trailer_args);
+	}
+
 	/*
 	 * Reject an attempt to record a non-merge empty commit without
 	 * explicit --allow-empty. In the cherry-pick case, it may be
@@ -1507,6 +1529,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 6396897cc818..6df71fa00bcb 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -154,6 +154,342 @@ test_expect_success 'sign off' '
 
 '
 
+test_expect_success 'commit --trailer without -c' '
+	echo "fun" >>file &&
+	git add file &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git commit -s --trailer "Signed-off-by:C1 E1 " \
+		--trailer "Helped-by:C2 E2 " \
+		--trailer "Reported-by:C3 E3" \
+		--trailer "Mentored-by:C4 E4" \
+		-m "hello" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "replace" as ifexists' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	EOF
+	git -c trailer.ifexists="replace" \
+		commit --trailer "Mentored-by: C4 E4" \
+		 --trailer "Helped-by: C3 E3" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "add" as ifexists' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	EOF
+	git -c trailer.ifexists="add" \
+		commit --trailer "Helped-by: C3 E3" \
+		--trailer "Helped-by: C3 E3" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "donothing" as ifexists' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	EOF
+	git -c trailer.ifexists="donothing" \
+		commit --trailer "Mentored-by: C5 E5" \
+		--trailer "Reviewed-by: C6 E6" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "addIfDifferent" as ifexists' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	EOF
+	git -c trailer.ifexists="addIfDifferent" \
+		commit --trailer "Reviewed-by: C6 E6" \
+		--trailer "Reported-by: C5 E5" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "addIfDifferentNeighbor" as ifexists' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	EOF
+	git -c trailer.ifexists="addIfDifferent" \
+		commit --trailer "Reported-by: C5 E5" \
+		--trailer "Reviewed-by: C6 E6" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "end" as where' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	Reported-by: C7 E7
+	EOF
+	git -c trailer.where="end" \
+		commit --trailer "Reported-by: C5 E5" \
+		--trailer "Reported-by: C7 E7" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "start" as where' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C8 E8
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	Reported-by: C7 E7
+	EOF
+	git -c trailer.where="start" \
+		commit --trailer "Signed-off-by: C8 E8" \
+		--trailer "Signed-off-by: C8 E8" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "after" as where' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C8 E8
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Mentored-by: C9 E9
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	Reported-by: C7 E7
+	Reported-by: C10 E10
+	EOF
+	git -c trailer.where="after" \
+		commit --trailer "Mentored-by: C9 E9" \
+		--trailer "Reported-by: C10 E10" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "before" as where' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C8 E8
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C11 E11
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Mentored-by: C9 E9
+	Helped-by: C12 E12
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	Reported-by: C7 E7
+	Reported-by: C10 E10
+	EOF
+	git -c trailer.where="before" \
+		commit --trailer "Helped-by: C12 E12" \
+		--trailer "Reported-by: C11 E11" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "donothing" as ifmissing' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C8 E8
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C11 E11
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Mentored-by: C9 E9
+	Helped-by: C12 E12
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	Reported-by: C7 E7
+	Reported-by: C10 E10
+	Helped-by: C12 E12
+	EOF
+	git -c trailer.ifmissing="donothing" \
+		commit --trailer "Helped-by: C12 E12" \
+		--trailer "Based-by: C13 E13" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "add" as ifmissing' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C8 E8
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C11 E11
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Mentored-by: C9 E9
+	Helped-by: C12 E12
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	Reported-by: C7 E7
+	Reported-by: C10 E10
+	Helped-by: C12 E12
+	Based-by: C13 E13
+	EOF
+	git -c trailer.ifmissing="add" \
+		commit --trailer "Helped-by: C12 E12" \
+		--trailer "Based-by: C13 E13" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "=" as separators' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+		Acked-by= Peff
+	EOF
+	git -c trailer.separators="=" \
+		-c trailer.ack.key="Acked-by= " \
+		commit --trailer "ack = Peff" -m "hello" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and ":=#" as separators' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+		Bug #42
+	EOF
+	git -c trailer.separators=":=#" \
+		-c trailer.bug.key="Bug #" \
+		commit --trailer "bug = 42" -m "I hate bug" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v10 2/3] interpret-trailers: add own-identity option
  2021-03-18 11:15                 ` [PATCH v10 0/3] " ZheNing Hu via GitGitGadget
  2021-03-18 11:15                   ` [PATCH v10 1/3] " ZheNing Hu via GitGitGadget
@ 2021-03-18 11:15                   ` ZheNing Hu via GitGitGadget
  2021-03-18 16:45                     ` Đoàn Trần Công Danh
  2021-03-18 19:20                     ` Junio C Hamano
  2021-03-18 11:15                   ` [PATCH v10 3/3] commit: " ZheNing Hu via GitGitGadget
                                     ` (2 subsequent siblings)
  4 siblings, 2 replies; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-18 11:15 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Beacuse `git commit --trailer="Signed-off-by: \
$(git config user.name) <$(git config user.email)>"`
is difficult for users to add their own identities,
so teach interpret-trailers a new option `--own-identity`
which allow those trailers with no value add the user’s own
identity. This will help the use of `commit --trailer` as
easy as `--signoff`.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
 Documentation/git-interpret-trailers.txt | 14 ++++++++++++++
 builtin/interpret-trailers.c             |  6 ++++--
 t/t7513-interpret-trailers.sh            | 12 ++++++++++++
 trailer.c                                | 18 ++++++++++++++++--
 trailer.h                                |  3 ++-
 5 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index 96ec6499f001..ace44446c3e3 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -84,6 +84,19 @@ OPTIONS
 	trailer to the input messages. See the description of this
 	command.
 
+--own-identity::
+	Used with `--trailer`. Those trailers without value with the
+	`--own-identity` option all will add the user's own identity.
+	For example,` git interpret-trailers --trailer "A:B" --trailer \
+	"Signed-off-by" --trailer "Helped-by" --own-identity --inplace a.txt`
+	will output:
+	"
+	A:B
+	Signed-off-by: C O Mitter <committer@example.com>
+	Helped-by: C O Mitter <committer@example.com>
+	"
+	in `a.txt`.
+
 --where <placement>::
 --no-where::
 	Specify where all new trailers will be added.  A setting
@@ -131,6 +144,7 @@ OPTIONS
 	when you know your input contains just the commit message itself
 	(and not an email or the output of `git format-patch`).
 
+
 CONFIGURATION VARIABLES
 -----------------------
 
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 84748eafc01b..be7f502a58d7 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -47,6 +47,7 @@ static void new_trailers_clear(struct list_head *trailers)
 	list_for_each_safe(pos, tmp, trailers) {
 		item = list_entry(pos, struct new_trailer_item, list);
 		list_del(pos);
+		free(item->text);
 		free(item);
 	}
 }
@@ -66,7 +67,7 @@ static int option_parse_trailer(const struct option *opt,
 		return -1;
 
 	item = xmalloc(sizeof(*item));
-	item->text = arg;
+	item->text = xstrdup(arg);
 	item->where = where;
 	item->if_exists = if_exists;
 	item->if_missing = if_missing;
@@ -94,7 +95,8 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
 	struct option options[] = {
 		OPT_BOOL(0, "in-place", &opts.in_place, N_("edit files in place")),
 		OPT_BOOL(0, "trim-empty", &opts.trim_empty, N_("trim empty trailers")),
-
+		OPT_BOOL(0, "own-identity", &opts.own_identity,
+			     N_("specify the user's own identity for omitted trailers value")),
 		OPT_CALLBACK(0, "where", NULL, N_("action"),
 			     N_("where to place the new trailer"), option_parse_where),
 		OPT_CALLBACK(0, "if-exists", NULL, N_("action"),
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 6602790b5f4c..f82cee93bfb2 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -63,6 +63,18 @@ test_expect_success 'without config' '
 	test_cmp expected actual
 '
 
+test_expect_success 'without config with --own-identity' '
+	cat >expected <<-\EOF &&
+
+	Acked-by: A B <C>
+	Helped-by: C O Mitter <committer@example.com>
+	Signed-off-by: C O Mitter <committer@example.com>
+	EOF
+	git interpret-trailers --trailer "Acked-by: A B <C>" --trailer "Helped-by" \
+	--trailer "Signed-off-by" --own-identity empty >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'without config in another order' '
 	sed -e "s/ Z\$/ /" >expected <<-\EOF &&
 
diff --git a/trailer.c b/trailer.c
index 249ed618ed8e..cd4f85e71c9a 100644
--- a/trailer.c
+++ b/trailer.c
@@ -690,8 +690,18 @@ static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
 	list_add_tail(&new_item->list, arg_head);
 }
 
+static void add_user_own_identity(struct new_trailer_item *item)
+{
+	struct strbuf buf = STRBUF_INIT;
+	strbuf_addstr(&buf, item->text);
+	strbuf_add(&buf, "=", 1);
+	strbuf_addstr(&buf, fmt_name(WANT_COMMITTER_IDENT));
+	free(item->text);
+	item->text = buf.buf;
+}
+
 static void process_command_line_args(struct list_head *arg_head,
-				      struct list_head *new_trailer_head)
+				      struct list_head *new_trailer_head, int own_identity)
 {
 	struct arg_item *item;
 	struct strbuf tok = STRBUF_INIT;
@@ -728,6 +738,10 @@ static void process_command_line_args(struct list_head *arg_head,
 			error(_("empty trailer token in trailer '%.*s'"),
 			      (int) sb.len, sb.buf);
 			strbuf_release(&sb);
+		} else if (separator_pos == -1 && own_identity) {
+				add_user_own_identity(tr);
+				pos = pos->prev;
+				continue;
 		} else {
 			parse_trailer(&tok, &val, &conf, tr->text,
 				      separator_pos);
@@ -1048,7 +1062,7 @@ void process_trailers(const char *file,
 
 	if (!opts->only_input) {
 		LIST_HEAD(arg_head);
-		process_command_line_args(&arg_head, new_trailer_head);
+		process_command_line_args(&arg_head, new_trailer_head ,opts->own_identity);
 		process_trailers_lists(&head, &arg_head);
 	}
 
diff --git a/trailer.h b/trailer.h
index 795d2fccfd95..235dfdfa1978 100644
--- a/trailer.h
+++ b/trailer.h
@@ -57,7 +57,7 @@ struct trailer_info {
 struct new_trailer_item {
 	struct list_head list;
 
-	const char *text;
+	char *text;
 
 	enum trailer_where where;
 	enum trailer_if_exists if_exists;
@@ -73,6 +73,7 @@ struct process_trailer_options {
 	int no_divider;
 	int key_only;
 	int value_only;
+	int own_identity;
 	const struct strbuf *separator;
 	const struct strbuf *key_value_separator;
 	int (*filter)(const struct strbuf *, void *);
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v10 3/3] commit: add own-identity option
  2021-03-18 11:15                 ` [PATCH v10 0/3] " ZheNing Hu via GitGitGadget
  2021-03-18 11:15                   ` [PATCH v10 1/3] " ZheNing Hu via GitGitGadget
  2021-03-18 11:15                   ` [PATCH v10 2/3] interpret-trailers: add own-identity option ZheNing Hu via GitGitGadget
@ 2021-03-18 11:15                   ` ZheNing Hu via GitGitGadget
  2021-03-18 13:47                   ` [PATCH v10 0/3] [GSOC] commit: add --trailer option Christian Couder
  2021-03-19 12:05                   ` [PATCH v11] " ZheNing Hu via GitGitGadget
  4 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-18 11:15 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Use the newly added option `--own-identity` in `interpret-trailers`,
implement new commit option `--own-identity` to allow those trailers
with no value add the user’s own identity. Using the `--own-identity`
option, users can directly use `--trailer="Signed-off-by"` to generate
a signoff trailer with their own identities in commit messages,

The effect is basically the same as the `--signoff` option. However, users
can add more useful options at the same time. e.g. `--trailer="Helped-by"
--own-identity` can general `Helped-by: C O Mitter <committer@example.com>`;
Or through appropriate configuration:
`git -c trailer.signoff.key="Signed-off-by" commit --trailer="signoff" \
--own-identity` can also general their needs trailers with their
favorite keys and their own identities.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
 Documentation/git-commit.txt          | 15 +++++
 builtin/commit.c                      |  8 +++
 t/t7501-commit-basic-functionality.sh | 91 +++++++++++++++++++++++++++
 t/t7502-commit-porcelain.sh           | 20 ++++++
 4 files changed, 134 insertions(+)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index c5de981cd40d..393c424e8184 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -174,6 +174,21 @@ include::signoff-option.txt[]
 	and the "Helped-by" trailer in the commit message.)
 	Use `git -c trailer.* commit --trailer` to make the appropriate
 	configuration. See linkgit:git-interpret-trailers[1] for details.
+
+--own-identity::
+	Used with `--trailer`. Those trailers without value with the
+	`--own-identity` option all will add the user's own identity.
+	For example, `git commit --trailer \
+	"A:B" --trailer	"Signed-off-by" --trailer "Helped-by" --own-identity`,
+	will output:
+	"
+	A:B
+	Signed-off-by: C O Mitter <committer@example.com>
+	Helped-by: C O Mitter <committer@example.com>
+	"
+	in commit messages.
+	See linkgit:git-interpret-trailers[1]for details.
+
 -n::
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
diff --git a/builtin/commit.c b/builtin/commit.c
index 7a79aae48f43..0310c187ec07 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -114,6 +114,7 @@ static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit, *pathspec_from_file;
 static struct strvec trailer_args = STRVEC_INIT;
+static int own_identity;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -972,6 +973,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
 		strvec_pushl(&run_trailer.args, "interpret-trailers",
 			     "--in-place", git_path_commit_editmsg(), NULL);
+		if (own_identity)
+			strvec_push(&run_trailer.args, "--own-identity");
 		strvec_pushv(&run_trailer.args, trailer_args.v);
 		run_trailer.git_cmd = 1;
 		if (run_command(&run_trailer)) {
@@ -1530,6 +1533,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
+		OPT_BOOL(0, "own-identity", &own_identity,
+			     N_("specify the user's own identity for omitted trailers value")),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
@@ -1606,6 +1611,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	if (verbose == -1)
 		verbose = (config_commit_verbose < 0) ? 0 : config_commit_verbose;
 
+	if (own_identity && !trailer_args.nr)
+		die(_("--own_identity requires --trailer"));
+
 	if (dry_run)
 		return dry_run_commit(argv, prefix, current_head, &s);
 	index_file = prepare_index(argv, prefix, current_head, 0);
diff --git a/t/t7501-commit-basic-functionality.sh b/t/t7501-commit-basic-functionality.sh
index 512ae2781fe2..7ff74cf81e75 100755
--- a/t/t7501-commit-basic-functionality.sh
+++ b/t/t7501-commit-basic-functionality.sh
@@ -423,6 +423,25 @@ test_expect_success 'sign off (1)' '
 
 '
 
+test_expect_success '--trailer="signoff" (1)' '
+
+	echo 1 >>positive &&
+	git add positive &&
+	git -c trailer.signoff.key="Signed-off-by" \
+		commit --trailer="signoff" --own-identity \
+		-m "thank you" &&
+	git cat-file commit HEAD >commit &&
+	sed -e "1,/^\$/d" commit >actual &&
+	(
+		echo thank you &&
+		echo &&
+		git var GIT_COMMITTER_IDENT >ident &&
+		sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /" ident
+	) >expected &&
+	test_cmp expected actual
+
+'
+
 test_expect_success 'sign off (2)' '
 
 	echo 2 >positive &&
@@ -444,6 +463,30 @@ $existing" &&
 
 '
 
+test_expect_success '--trailer="signoff" (2)' '
+
+	echo 2 >>positive &&
+	git add positive &&
+	existing="Signed-off-by: Watch This <watchthis@example.com>" &&
+	git -c trailer.signoff.key="Signed-off-by" \
+		commit --trailer="signoff" --own-identity \
+		-m "thank you
+
+$existing" &&
+	git cat-file commit HEAD >commit &&
+	sed -e "1,/^\$/d" commit >actual &&
+	(
+		echo thank you &&
+		echo &&
+		echo $existing &&
+		git var GIT_COMMITTER_IDENT >ident &&
+		sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /" ident
+	) >expected &&
+	test_cmp expected actual
+
+'
+
+
 test_expect_success 'signoff gap' '
 
 	echo 3 >positive &&
@@ -464,6 +507,29 @@ $alt" &&
 	test_cmp expected actual
 '
 
+test_expect_success '--trailer="signoff" gap' '
+
+	echo 3 >>positive &&
+	git add positive &&
+	alt="Alt-RFC-822-Header: Value" &&
+	git -c trailer.signoff.key="Signed-off-by" \
+		commit --trailer="signoff" --own-identity \
+		-m "welcome
+
+$alt" &&
+	git cat-file commit HEAD >commit &&
+	sed -e "1,/^\$/d" commit >actual &&
+	(
+		echo welcome &&
+		echo &&
+		echo $alt &&
+		git var GIT_COMMITTER_IDENT >ident &&
+		sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /" ident
+	) >expected &&
+	test_cmp expected actual
+'
+
+
 test_expect_success 'signoff gap 2' '
 
 	echo 4 >positive &&
@@ -487,6 +553,31 @@ $alt" &&
 	test_cmp expected actual
 '
 
+test_expect_success '--trailer="signoff" gap 2' '
+
+	echo 4 >>positive &&
+	git add positive &&
+	alt="fixed: 34" &&
+	git -c trailer.signoff.key="Signed-off-by" \
+		commit --trailer="signoff" --own-identity \
+		-m "welcome
+
+We have now
+$alt" &&
+	git cat-file commit HEAD >commit &&
+	sed -e "1,/^\$/d" commit >actual &&
+	(
+		echo welcome &&
+		echo &&
+		echo We have now &&
+		echo $alt &&
+		echo &&
+		git var GIT_COMMITTER_IDENT >ident &&
+		sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /" ident
+	) >expected &&
+	test_cmp expected actual
+'
+
 test_expect_success 'signoff respects trailer config' '
 
 	echo 5 >positive &&
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 6df71fa00bcb..1628a1c1e7d6 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -490,6 +490,26 @@ test_expect_success 'commit --trailer with -c and ":=#" as separators' '
 	test_cmp expected actual
 '
 
+test_expect_success 'commit --trailer with -c and --own-identity' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	EOF
+	git -c trailer.signoff.key="Signed-off-by: " \
+		commit --trailer "signoff" --own-identity -m "abc" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --own-identity without --trailer' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	test_must_fail git -c commit --own-identity -m "abc"
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 0/3] [GSOC] commit: add --trailer option
  2021-03-18 11:15                 ` [PATCH v10 0/3] " ZheNing Hu via GitGitGadget
                                     ` (2 preceding siblings ...)
  2021-03-18 11:15                   ` [PATCH v10 3/3] commit: " ZheNing Hu via GitGitGadget
@ 2021-03-18 13:47                   ` Christian Couder
  2021-03-18 15:27                     ` ZheNing Hu
  2021-03-19 12:05                   ` [PATCH v11] " ZheNing Hu via GitGitGadget
  4 siblings, 1 reply; 84+ messages in thread
From: Christian Couder @ 2021-03-18 13:47 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Junio C Hamano, Brandon Casey,
	Shourya Shukla, Rafael Silva, ZheNing Hu

On Thu, Mar 18, 2021 at 12:15 PM ZheNing Hu via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> Now maintainers or developers can also use commit
> --trailer="Signed-off-by:commiter<email>" from the command line to provide
> trailers to commit messages. This solution may be more generalized than v1.
>
> ZheNing Hu (3):
>   [GSOC] commit: add --trailer option
>   interpret-trailers: add own-identity option
>   commit: add own-identity option

I see that you discussed an own-identity option in this thread
recently, but this is version 10 of a patch series, so I don't think
it's a good idea to introduce new features at this point. I think it's
better to not tie the work that has already been done on polishing the
first patch to new patches implementing an additional feature. The
additional feature can be discussed and worked on in its own patch
series building on top of this one.

Also it feels strange that only the first patch has "[GSOC]".

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 0/3] [GSOC] commit: add --trailer option
  2021-03-18 13:47                   ` [PATCH v10 0/3] [GSOC] commit: add --trailer option Christian Couder
@ 2021-03-18 15:27                     ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-18 15:27 UTC (permalink / raw)
  To: Christian Couder
  Cc: ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Rafael Silva

Christian Couder <christian.couder@gmail.com> 于2021年3月18日周四 下午9:48写道:
>
> On Thu, Mar 18, 2021 at 12:15 PM ZheNing Hu via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
> >
> > Now maintainers or developers can also use commit
> > --trailer="Signed-off-by:commiter<email>" from the command line to provide
> > trailers to commit messages. This solution may be more generalized than v1.
> >
> > ZheNing Hu (3):
> >   [GSOC] commit: add --trailer option
> >   interpret-trailers: add own-identity option
> >   commit: add own-identity option
>
> I see that you discussed an own-identity option in this thread
> recently, but this is version 10 of a patch series, so I don't think
> it's a good idea to introduce new features at this point. I think it's
> better to not tie the work that has already been done on polishing the
> first patch to new patches implementing an additional feature. The
> additional feature can be discussed and worked on in its own patch
> series building on top of this one.
>
OK, I understand that now. I will divide this patch series into two parts.
> Also it feels strange that only the first patch has "[GSOC]".
I forgot to add [GSOC]...

Thanks.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 1/3] [GSOC] commit: add --trailer option
  2021-03-18 11:15                   ` [PATCH v10 1/3] " ZheNing Hu via GitGitGadget
@ 2021-03-18 16:29                     ` Đoàn Trần Công Danh
  2021-03-19  7:56                       ` ZheNing Hu
  0 siblings, 1 reply; 84+ messages in thread
From: Đoàn Trần Công Danh @ 2021-03-18 16:29 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Junio C Hamano, Brandon Casey,
	Shourya Shukla, Christian Couder, Rafael Silva, ZheNing Hu

On 2021-03-18 11:15:54+0000, ZheNing Hu via GitGitGadget <gitgitgadget@gmail.com> wrote:
> From: ZheNing Hu <adlternative@gmail.com>
> 
> Historically, Git has supported the 'Signed-off-by' commit trailer
> using the '--signoff' and the '-s' option from the command line.
> But users may need to provide other trailer information from the
> command line such as "Helped-by", "Reported-by", "Mentored-by",
> 
> Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
> other trailers to `interpret-trailers` and insert them into commit
> messages.
> 
> Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> ---
>  Documentation/git-commit.txt |  10 +-
>  builtin/commit.c             |  23 +++
>  t/t7502-commit-porcelain.sh  | 336 +++++++++++++++++++++++++++++++++++
>  3 files changed, 368 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
> index 17150fa7eabe..c5de981cd40d 100644
> --- a/Documentation/git-commit.txt
> +++ b/Documentation/git-commit.txt
> @@ -14,7 +14,7 @@ SYNOPSIS
>  	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
>  	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
>  	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
> -	   [-S[<keyid>]] [--] [<pathspec>...]
> +	   [-S[<keyid>]] [--] [<pathspec>...] [(--trailer <token>[(=|:)<value>])...]

Please move all options before non-option arguments.
In other words, please move --trailer before [--].

This form implies that there are no way to specify pathspec "--trailer"

>  
>  DESCRIPTION
>  -----------
> @@ -166,6 +166,14 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
>  
>  include::signoff-option.txt[]
>  
> +--trailer <token>[(=|:)<value>]::
> +	Specify a (<token>, <value>) pair that should be applied as a
> +	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
> +	<committer@example.com>" --trailer "Helped-by:C O Mitter \
> +	<committer@example.com>"` will add the "Signed-off-by" trailer
> +	and the "Helped-by" trailer in the commit message.)
> +	Use `git -c trailer.* commit --trailer` to make the appropriate
> +	configuration. See linkgit:git-interpret-trailers[1] for details.
>  -n::
>  --no-verify::
>  	This option bypasses the pre-commit and commit-msg hooks.
> diff --git a/builtin/commit.c b/builtin/commit.c
> index 739110c5a7f6..7a79aae48f43 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -113,6 +113,7 @@ static int config_commit_verbose = -1; /* unspecified */
>  static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
>  static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
>  static char *sign_commit, *pathspec_from_file;
> +static struct strvec trailer_args = STRVEC_INIT;
>  
>  /*
>   * The default commit message cleanup mode will remove the lines
> @@ -131,6 +132,14 @@ static struct strbuf message = STRBUF_INIT;
>  
>  static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
>  
> +static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
> +{
> +	BUG_ON_OPT_NEG(unset);
> +
> +	strvec_pushl(&trailer_args, "--trailer", arg, NULL);
> +	return 0;
> +}
> +
>  static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
>  {
>  	enum wt_status_format *value = (enum wt_status_format *)opt->value;
> @@ -958,6 +967,19 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
>  
>  	fclose(s->fp);
>  
> +	if (trailer_args.nr) {
> +		struct child_process run_trailer = CHILD_PROCESS_INIT;
> +
> +		strvec_pushl(&run_trailer.args, "interpret-trailers",
> +			     "--in-place", git_path_commit_editmsg(), NULL);
> +		strvec_pushv(&run_trailer.args, trailer_args.v);
> +		run_trailer.git_cmd = 1;
> +		if (run_command(&run_trailer)) {
> +			die(_("unable to pass tailers to --trailers"));

s/tailers/trailers/ perhap?
Also we usually not put {} around single statement.

> +		}
> +		strvec_clear(&trailer_args);
> +	}
> +
>  	/*
>  	 * Reject an attempt to record a non-merge empty commit without
>  	 * explicit --allow-empty. In the cherry-pick case, it may be
> @@ -1507,6 +1529,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>  		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
>  		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
>  		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
>  		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
> diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
> index 6396897cc818..6df71fa00bcb 100755
> --- a/t/t7502-commit-porcelain.sh
> +++ b/t/t7502-commit-porcelain.sh
> @@ -154,6 +154,342 @@ test_expect_success 'sign off' '
>  
>  '
>  
> +test_expect_success 'commit --trailer without -c' '
> +	echo "fun" >>file &&
> +	git add file &&
> +	cat >expected <<-\EOF &&
> +
> +	Signed-off-by: C O Mitter <committer@example.com>
> +	Signed-off-by: C1 E1
> +	Helped-by: C2 E2
> +	Reported-by: C3 E3
> +	Mentored-by: C4 E4
> +	EOF
> +	git commit -s --trailer "Signed-off-by:C1 E1 " \
> +		--trailer "Helped-by:C2 E2 " \
> +		--trailer "Reported-by:C3 E3" \
> +		--trailer "Mentored-by:C4 E4" \
> +		-m "hello" &&

It's documented that we're supporting --trailer <token>[(=|:)<value>]
However, only --trailer <token>:<value> is tested.
I think it's better to have 
	
	--trailer "Helped-by=C2 E2" --trailer "Reported-by"


-- 
Danh

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 2/3] interpret-trailers: add own-identity option
  2021-03-18 11:15                   ` [PATCH v10 2/3] interpret-trailers: add own-identity option ZheNing Hu via GitGitGadget
@ 2021-03-18 16:45                     ` Đoàn Trần Công Danh
  2021-03-19  8:04                       ` ZheNing Hu
  2021-03-18 19:20                     ` Junio C Hamano
  1 sibling, 1 reply; 84+ messages in thread
From: Đoàn Trần Công Danh @ 2021-03-18 16:45 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Junio C Hamano, Brandon Casey,
	Shourya Shukla, Christian Couder, Rafael Silva, ZheNing Hu

On 2021-03-18 11:15:55+0000, ZheNing Hu via GitGitGadget <gitgitgadget@gmail.com> wrote:
> From: ZheNing Hu <adlternative@gmail.com>
> 
> Beacuse `git commit --trailer="Signed-off-by: \

s/Beacuse/Because/

And I think, it's easier to read if we write the command in its own
(indented) line.

> $(git config user.name) <$(git config user.email)>"`
> is difficult for users to add their own identities,
> so teach interpret-trailers a new option `--own-identity`
> which allow those trailers with no value add the user’s own
> identity. This will help the use of `commit --trailer` as
> easy as `--signoff`.

Perhap, saying that we're optionalise <value> in --trailer, by
substitute user's identity if missing instead?

> @@ -131,6 +144,7 @@ OPTIONS
>  	when you know your input contains just the commit message itself
>  	(and not an email or the output of `git format-patch`).
>  
> +

I think it's better to not add this line change

>  CONFIGURATION VARIABLES
>  -----------------------
>  
> diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
> index 84748eafc01b..be7f502a58d7 100644
> --- a/builtin/interpret-trailers.c
> +++ b/builtin/interpret-trailers.c

-- 
Danh

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 2/3] interpret-trailers: add own-identity option
  2021-03-18 11:15                   ` [PATCH v10 2/3] interpret-trailers: add own-identity option ZheNing Hu via GitGitGadget
  2021-03-18 16:45                     ` Đoàn Trần Công Danh
@ 2021-03-18 19:20                     ` Junio C Hamano
  2021-03-19  9:33                       ` ZheNing Hu
  1 sibling, 1 reply; 84+ messages in thread
From: Junio C Hamano @ 2021-03-18 19:20 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva, ZheNing Hu

"ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: ZheNing Hu <adlternative@gmail.com>
>
> Beacuse `git commit --trailer="Signed-off-by: \
> $(git config user.name) <$(git config user.email)>"`
> is difficult for users to add their own identities,
> so teach interpret-trailers a new option `--own-identity`
> which allow those trailers with no value add the user’s own
> identity. This will help the use of `commit --trailer` as
> easy as `--signoff`.

I have a suspicion that this is too narrowly focused to be useful in
practice, and I find that the proposed "--own-identity" is quite a
mouthful.

> +--own-identity::
> +	Used with `--trailer`. Those trailers without value with the
> +	`--own-identity` option all will add the user's own identity.

So, the assumption here is that the name of the trailer tag alone,
without the ':' separator, can identify which trailer the user is
talking about, and it can be distinguished from the tag name plus ':'
and nothing else which is calling for a trailer entry with an empty
string as its value?

OK.

The reason why this looks too narrowly focused on oneself alone to
be useful to me is because I often need to add various -by trailers
to incoming patches, and have a script to do exactly that (which
does not use interpret-trailers, as I do not think
interpret-trailers can accept a patch email as its input, and the
script predates interpret-trailers) but it will be useless if that
script were limited to add -by for myself.

Wouldn't it be a lot more useful if

	git commit --trailer="Helped-by:@Ch.*Couder"

is expanded (note: I am not married to the syntax, but only for
illustration purposes, I am using "a value prefixed by @ triggers
the 'name expansion'" convention in this example.  People can come
up with better convention) by finding an author or a committer whose
name matches the given pattern from "git log"?  Then, instead of

	git commit --own-identity --trailer=Signed-off-by

I can say

	git commit --trailer=Signed-off-by:@gitster

and I can even add more than one, e.g.

	git commit --trailer=Helped-by:@peff --trailer=Signed-off-by:@gitster


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 1/3] [GSOC] commit: add --trailer option
  2021-03-18 16:29                     ` Đoàn Trần Công Danh
@ 2021-03-19  7:56                       ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-19  7:56 UTC (permalink / raw)
  To: Đoàn Trần Công Danh
  Cc: ZheNing Hu via GitGitGadget, Git List, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Christian Couder,
	Rafael Silva

Đoàn Trần Công Danh <congdanhqx@gmail.com> 于2021年3月19日周五 上午12:29写道:
>
> On 2021-03-18 11:15:54+0000, ZheNing Hu via GitGitGadget <gitgitgadget@gmail.com> wrote:
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > Historically, Git has supported the 'Signed-off-by' commit trailer
> > using the '--signoff' and the '-s' option from the command line.
> > But users may need to provide other trailer information from the
> > command line such as "Helped-by", "Reported-by", "Mentored-by",
> >
> > Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
> > other trailers to `interpret-trailers` and insert them into commit
> > messages.
> >
> > Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> > ---
> >  Documentation/git-commit.txt |  10 +-
> >  builtin/commit.c             |  23 +++
> >  t/t7502-commit-porcelain.sh  | 336 +++++++++++++++++++++++++++++++++++
> >  3 files changed, 368 insertions(+), 1 deletion(-)
> >
> > diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
> > index 17150fa7eabe..c5de981cd40d 100644
> > --- a/Documentation/git-commit.txt
> > +++ b/Documentation/git-commit.txt
> > @@ -14,7 +14,7 @@ SYNOPSIS
> >          [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
> >          [--date=<date>] [--cleanup=<mode>] [--[no-]status]
> >          [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
> > -        [-S[<keyid>]] [--] [<pathspec>...]
> > +        [-S[<keyid>]] [--] [<pathspec>...] [(--trailer <token>[(=|:)<value>])...]
>
> Please move all options before non-option arguments.
> In other words, please move --trailer before [--].
>
> This form implies that there are no way to specify pathspec "--trailer"

Thanks, I didn't pay attention to this little detail before.

>
> >
> >  DESCRIPTION
> >  -----------
> > @@ -166,6 +166,14 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
> >
> >  include::signoff-option.txt[]
> >
> > +--trailer <token>[(=|:)<value>]::
> > +     Specify a (<token>, <value>) pair that should be applied as a
> > +     trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
> > +     <committer@example.com>" --trailer "Helped-by:C O Mitter \
> > +     <committer@example.com>"` will add the "Signed-off-by" trailer
> > +     and the "Helped-by" trailer in the commit message.)
> > +     Use `git -c trailer.* commit --trailer` to make the appropriate
> > +     configuration. See linkgit:git-interpret-trailers[1] for details.
> >  -n::
> >  --no-verify::
> >       This option bypasses the pre-commit and commit-msg hooks.
> > diff --git a/builtin/commit.c b/builtin/commit.c
> > index 739110c5a7f6..7a79aae48f43 100644
> > --- a/builtin/commit.c
> > +++ b/builtin/commit.c
> > @@ -113,6 +113,7 @@ static int config_commit_verbose = -1; /* unspecified */
> >  static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
> >  static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
> >  static char *sign_commit, *pathspec_from_file;
> > +static struct strvec trailer_args = STRVEC_INIT;
> >
> >  /*
> >   * The default commit message cleanup mode will remove the lines
> > @@ -131,6 +132,14 @@ static struct strbuf message = STRBUF_INIT;
> >
> >  static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
> >
> > +static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
> > +{
> > +     BUG_ON_OPT_NEG(unset);
> > +
> > +     strvec_pushl(&trailer_args, "--trailer", arg, NULL);
> > +     return 0;
> > +}
> > +
> >  static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
> >  {
> >       enum wt_status_format *value = (enum wt_status_format *)opt->value;
> > @@ -958,6 +967,19 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
> >
> >       fclose(s->fp);
> >
> > +     if (trailer_args.nr) {
> > +             struct child_process run_trailer = CHILD_PROCESS_INIT;
> > +
> > +             strvec_pushl(&run_trailer.args, "interpret-trailers",
> > +                          "--in-place", git_path_commit_editmsg(), NULL);
> > +             strvec_pushv(&run_trailer.args, trailer_args.v);
> > +             run_trailer.git_cmd = 1;
> > +             if (run_command(&run_trailer)) {
> > +                     die(_("unable to pass tailers to --trailers"));
>
> s/tailers/trailers/ perhap?
> Also we usually not put {} around single statement.
>

OK.

> > +             }
> > +             strvec_clear(&trailer_args);
> > +     }
> > +
> >       /*
> >        * Reject an attempt to record a non-merge empty commit without
> >        * explicit --allow-empty. In the cherry-pick case, it may be
> > @@ -1507,6 +1529,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
> >               OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
> >               OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
> >               OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
> >               OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
> > diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
> > index 6396897cc818..6df71fa00bcb 100755
> > --- a/t/t7502-commit-porcelain.sh
> > +++ b/t/t7502-commit-porcelain.sh
> > @@ -154,6 +154,342 @@ test_expect_success 'sign off' '
> >
> >  '
> >
> > +test_expect_success 'commit --trailer without -c' '
> > +     echo "fun" >>file &&
> > +     git add file &&
> > +     cat >expected <<-\EOF &&
> > +
> > +     Signed-off-by: C O Mitter <committer@example.com>
> > +     Signed-off-by: C1 E1
> > +     Helped-by: C2 E2
> > +     Reported-by: C3 E3
> > +     Mentored-by: C4 E4
> > +     EOF
> > +     git commit -s --trailer "Signed-off-by:C1 E1 " \
> > +             --trailer "Helped-by:C2 E2 " \
> > +             --trailer "Reported-by:C3 E3" \
> > +             --trailer "Mentored-by:C4 E4" \
> > +             -m "hello" &&
>
> It's documented that we're supporting --trailer <token>[(=|:)<value>]
> However, only --trailer <token>:<value> is tested.
> I think it's better to have
>
>         --trailer "Helped-by=C2 E2" --trailer "Reported-by"
>

In fact, I want to test in `test_expect_success'commit --trailer with
-c and "=" as separators'`,
but some changes are needed.

>
> --
> Danh

-- 
ZheNing Hu

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 2/3] interpret-trailers: add own-identity option
  2021-03-18 16:45                     ` Đoàn Trần Công Danh
@ 2021-03-19  8:04                       ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-19  8:04 UTC (permalink / raw)
  To: Đoàn Trần Công Danh
  Cc: ZheNing Hu via GitGitGadget, Git List, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Christian Couder,
	Rafael Silva

Đoàn Trần Công Danh <congdanhqx@gmail.com> 于2021年3月19日周五 上午12:45写道:
>
> On 2021-03-18 11:15:55+0000, ZheNing Hu via GitGitGadget <gitgitgadget@gmail.com> wrote:
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > Beacuse `git commit --trailer="Signed-off-by: \
>
> s/Beacuse/Because/
>
> And I think, it's easier to read if we write the command in its own
> (indented) line.
>
> > $(git config user.name) <$(git config user.email)>"`
> > is difficult for users to add their own identities,
> > so teach interpret-trailers a new option `--own-identity`
> > which allow those trailers with no value add the user’s own
> > identity. This will help the use of `commit --trailer` as
> > easy as `--signoff`.
>
> Perhap, saying that we're optionalise <value> in --trailer, by
> substitute user's identity if missing instead?

Indeed so.

>
> > @@ -131,6 +144,7 @@ OPTIONS
> >       when you know your input contains just the commit message itself
> >       (and not an email or the output of `git format-patch`).
> >
> > +
>
> I think it's better to not add this line change
>
> >  CONFIGURATION VARIABLES
> >  -----------------------
> >
> > diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
> > index 84748eafc01b..be7f502a58d7 100644
> > --- a/builtin/interpret-trailers.c
> > +++ b/builtin/interpret-trailers.c
>

Thanks for these kindful advices.

> --
> Danh

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 2/3] interpret-trailers: add own-identity option
  2021-03-18 19:20                     ` Junio C Hamano
@ 2021-03-19  9:33                       ` ZheNing Hu
  2021-03-19 15:36                         ` Junio C Hamano
  0 siblings, 1 reply; 84+ messages in thread
From: ZheNing Hu @ 2021-03-19  9:33 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: ZheNing Hu via GitGitGadget, Git List, Bradley M. Kuhn,
	Brandon Casey, Shourya Shukla, Christian Couder, Rafael Silva

Junio C Hamano <gitster@pobox.com> 于2021年3月19日周五 上午3:20写道:
>
> "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > Beacuse `git commit --trailer="Signed-off-by: \
> > $(git config user.name) <$(git config user.email)>"`
> > is difficult for users to add their own identities,
> > so teach interpret-trailers a new option `--own-identity`
> > which allow those trailers with no value add the user’s own
> > identity. This will help the use of `commit --trailer` as
> > easy as `--signoff`.
>
> I have a suspicion that this is too narrowly focused to be useful in
> practice, and I find that the proposed "--own-identity" is quite a
> mouthful.
>

Well, the original meaning of this `--trailer`and`--own-identity` option is to
imitate the behavior of `--signoff` to create other trailers, For the
time being,
it can only "imitation", it does not yet support the ability to provide multiple
identities instead of "own-identity".

If `--own-identity` is mouthful, is there a better name?

> > +--own-identity::
> > +     Used with `--trailer`. Those trailers without value with the
> > +     `--own-identity` option all will add the user's own identity.
>
> So, the assumption here is that the name of the trailer tag alone,
> without the ':' separator, can identify which trailer the user is
> talking about, and it can be distinguished from the tag name plus ':'
> and nothing else which is calling for a trailer entry with an empty
> string as its value?
>

Yes. `--own-ident` is for trailers without a separator.

> OK.
>
> The reason why this looks too narrowly focused on oneself alone to
> be useful to me is because I often need to add various -by trailers
> to incoming patches, and have a script to do exactly that (which
> does not use interpret-trailers, as I do not think
> interpret-trailers can accept a patch email as its input, and the
> script predates interpret-trailers) but it will be useless if that
> script were limited to add -by for myself.
>

I understand it. But I think `--own-ident` is more inclined to add
identities for users. If users want to add other people’s identities,
they need to use another method.

> Wouldn't it be a lot more useful if
>
>         git commit --trailer="Helped-by:@Ch.*Couder"
>
> is expanded (note: I am not married to the syntax, but only for
> illustration purposes, I am using "a value prefixed by @ triggers
> the 'name expansion'" convention in this example.  People can come
> up with better convention) by finding an author or a committer whose
> name matches the given pattern from "git log"?  Then, instead of
>
>         git commit --own-identity --trailer=Signed-off-by
>
> I can say
>
>         git commit --trailer=Signed-off-by:@gitster
>
> and I can even add more than one, e.g.
>
>         git commit --trailer=Helped-by:@peff --trailer=Signed-off-by:@gitster
>

I agree that this idea can be extended again from "own-identity "
mode to "multiple-identity mode",‘@’ seems to have a lot of
 important meanings in git, but I don’t know how to design a solution
 that is more suitable than yours for the time being.

 Another doubt  is what happens if one person corresponds to
 multiple mailboxes or one mailbox corresponds to multiple people?

I think `--own-ident` may be a somewhat "narrow" option that can be
kept. Also add a new option for interpret-trailers to resolve the identity
of other people?

Thanks for suggestions.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v11] [GSOC] commit: add --trailer option
  2021-03-18 11:15                 ` [PATCH v10 0/3] " ZheNing Hu via GitGitGadget
                                     ` (3 preceding siblings ...)
  2021-03-18 13:47                   ` [PATCH v10 0/3] [GSOC] commit: add --trailer option Christian Couder
@ 2021-03-19 12:05                   ` ZheNing Hu via GitGitGadget
  2021-03-19 17:48                     ` Junio C Hamano
  2021-03-20 13:41                     ` [PATCH v12] " ZheNing Hu via GitGitGadget
  4 siblings, 2 replies; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-19 12:05 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva,
	Đoàn Trần Công Danh, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Historically, Git has supported the 'Signed-off-by' commit trailer
using the '--signoff' and the '-s' option from the command line.
But users may need to provide other trailer information from the
command line such as "Helped-by", "Reported-by", "Mentored-by",

Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
other trailers to `interpret-trailers` and insert them into commit
messages.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC] commit: add --trailer option
    
    Now maintainers or developers can also use commit
    --trailer="Signed-off-by:commiter<email>" from the command line to
    provide trailers to commit messages. This solution may be more
    generalized than v1.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v11
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v11
Pull-Request: https://github.com/gitgitgadget/git/pull/901

Range-diff vs v10:

 1:  949faf9ee56a ! 1:  0db1210ffd6d [GSOC] commit: add --trailer option
     @@ Documentation/git-commit.txt: SYNOPSIS
       	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
       	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
      -	   [-S[<keyid>]] [--] [<pathspec>...]
     -+	   [-S[<keyid>]] [--] [<pathspec>...] [(--trailer <token>[(=|:)<value>])...]
     ++	   [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]
     ++	   [--] [<pathspec>...]
       
       DESCRIPTION
       -----------
     @@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const cha
      +			     "--in-place", git_path_commit_editmsg(), NULL);
      +		strvec_pushv(&run_trailer.args, trailer_args.v);
      +		run_trailer.git_cmd = 1;
     -+		if (run_command(&run_trailer)) {
     -+			die(_("unable to pass tailers to --trailers"));
     -+		}
     ++		if (run_command(&run_trailer))
     ++			die(_("unable to pass trailers to --trailers"));
      +		strvec_clear(&trailer_args);
      +	}
      +
     @@ t/t7502-commit-porcelain.sh: test_expect_success 'sign off' '
      +	test_cmp expected actual
      +'
      +
     -+test_expect_success 'commit --trailer with -c and "=" as separators' '
     ++test_expect_success 'commit --trailer with "=" ' '
      +	echo "fun" >>file1 &&
      +	git add file1 &&
      +	cat >expected <<-\EOF &&
      +
     -+		Acked-by= Peff
     ++		Acked-by: Peff
      +	EOF
     -+	git -c trailer.separators="=" \
     -+		-c trailer.ack.key="Acked-by= " \
     ++	git -c trailer.ack.key="Acked-by" \
      +		commit --trailer "ack = Peff" -m "hello" &&
      +	git cat-file commit HEAD >commit.msg &&
      +	sed -e "1,6d" commit.msg >actual &&
 2:  42590e95deee < -:  ------------ interpret-trailers: add own-identity option
 3:  2dfcc20f0e9f < -:  ------------ commit: add own-identity option


 Documentation/git-commit.txt |  11 +-
 builtin/commit.c             |  22 +++
 t/t7502-commit-porcelain.sh  | 335 +++++++++++++++++++++++++++++++++++
 3 files changed, 367 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 17150fa7eabe..2ba2fe0dd10e 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -14,7 +14,8 @@ SYNOPSIS
 	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
 	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
 	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
-	   [-S[<keyid>]] [--] [<pathspec>...]
+	   [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]
+	   [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -166,6 +167,14 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
 
 include::signoff-option.txt[]
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
+	<committer@example.com>" --trailer "Helped-by:C O Mitter \
+	<committer@example.com>"` will add the "Signed-off-by" trailer
+	and the "Helped-by" trailer in the commit message.)
+	Use `git -c trailer.* commit --trailer` to make the appropriate
+	configuration. See linkgit:git-interpret-trailers[1] for details.
 -n::
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
diff --git a/builtin/commit.c b/builtin/commit.c
index 739110c5a7f6..4b06672bd07d 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,6 +113,7 @@ static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit, *pathspec_from_file;
+static struct strvec trailer_args = STRVEC_INIT;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -131,6 +132,14 @@ static struct strbuf message = STRBUF_INIT;
 
 static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
 
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+
+	strvec_pushl(&trailer_args, "--trailer", arg, NULL);
+	return 0;
+}
+
 static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
 {
 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -958,6 +967,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
 	fclose(s->fp);
 
+	if (trailer_args.nr) {
+		struct child_process run_trailer = CHILD_PROCESS_INIT;
+
+		strvec_pushl(&run_trailer.args, "interpret-trailers",
+			     "--in-place", git_path_commit_editmsg(), NULL);
+		strvec_pushv(&run_trailer.args, trailer_args.v);
+		run_trailer.git_cmd = 1;
+		if (run_command(&run_trailer))
+			die(_("unable to pass trailers to --trailers"));
+		strvec_clear(&trailer_args);
+	}
+
 	/*
 	 * Reject an attempt to record a non-merge empty commit without
 	 * explicit --allow-empty. In the cherry-pick case, it may be
@@ -1507,6 +1528,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 6396897cc818..024cf3c81b18 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -154,6 +154,341 @@ test_expect_success 'sign off' '
 
 '
 
+test_expect_success 'commit --trailer without -c' '
+	echo "fun" >>file &&
+	git add file &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git commit -s --trailer "Signed-off-by:C1 E1 " \
+		--trailer "Helped-by:C2 E2 " \
+		--trailer "Reported-by:C3 E3" \
+		--trailer "Mentored-by:C4 E4" \
+		-m "hello" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "replace" as ifexists' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	EOF
+	git -c trailer.ifexists="replace" \
+		commit --trailer "Mentored-by: C4 E4" \
+		 --trailer "Helped-by: C3 E3" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "add" as ifexists' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	EOF
+	git -c trailer.ifexists="add" \
+		commit --trailer "Helped-by: C3 E3" \
+		--trailer "Helped-by: C3 E3" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "donothing" as ifexists' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	EOF
+	git -c trailer.ifexists="donothing" \
+		commit --trailer "Mentored-by: C5 E5" \
+		--trailer "Reviewed-by: C6 E6" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "addIfDifferent" as ifexists' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	EOF
+	git -c trailer.ifexists="addIfDifferent" \
+		commit --trailer "Reviewed-by: C6 E6" \
+		--trailer "Reported-by: C5 E5" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "addIfDifferentNeighbor" as ifexists' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	EOF
+	git -c trailer.ifexists="addIfDifferent" \
+		commit --trailer "Reported-by: C5 E5" \
+		--trailer "Reviewed-by: C6 E6" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "end" as where' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	Reported-by: C7 E7
+	EOF
+	git -c trailer.where="end" \
+		commit --trailer "Reported-by: C5 E5" \
+		--trailer "Reported-by: C7 E7" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "start" as where' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C8 E8
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	Reported-by: C7 E7
+	EOF
+	git -c trailer.where="start" \
+		commit --trailer "Signed-off-by: C8 E8" \
+		--trailer "Signed-off-by: C8 E8" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "after" as where' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C8 E8
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Mentored-by: C9 E9
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	Reported-by: C7 E7
+	Reported-by: C10 E10
+	EOF
+	git -c trailer.where="after" \
+		commit --trailer "Mentored-by: C9 E9" \
+		--trailer "Reported-by: C10 E10" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "before" as where' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C8 E8
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C11 E11
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Mentored-by: C9 E9
+	Helped-by: C12 E12
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	Reported-by: C7 E7
+	Reported-by: C10 E10
+	EOF
+	git -c trailer.where="before" \
+		commit --trailer "Helped-by: C12 E12" \
+		--trailer "Reported-by: C11 E11" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "donothing" as ifmissing' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C8 E8
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C11 E11
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Mentored-by: C9 E9
+	Helped-by: C12 E12
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	Reported-by: C7 E7
+	Reported-by: C10 E10
+	Helped-by: C12 E12
+	EOF
+	git -c trailer.ifmissing="donothing" \
+		commit --trailer "Helped-by: C12 E12" \
+		--trailer "Based-by: C13 E13" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "add" as ifmissing' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+	Signed-off-by: C8 E8
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C11 E11
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Mentored-by: C9 E9
+	Helped-by: C12 E12
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Helped-by: C3 E3
+	Reviewed-by: C6 E6
+	Reported-by: C5 E5
+	Reported-by: C7 E7
+	Reported-by: C10 E10
+	Helped-by: C12 E12
+	Based-by: C13 E13
+	EOF
+	git -c trailer.ifmissing="add" \
+		commit --trailer "Helped-by: C12 E12" \
+		--trailer "Based-by: C13 E13" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with "=" ' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+		Acked-by: Peff
+	EOF
+	git -c trailer.ack.key="Acked-by" \
+		commit --trailer "ack = Peff" -m "hello" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and ":=#" as separators' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+
+		Bug #42
+	EOF
+	git -c trailer.separators=":=#" \
+		-c trailer.bug.key="Bug #" \
+		commit --trailer "bug = 42" -m "I hate bug" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,6d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&

base-commit: 13d7ab6b5d7929825b626f050b62a11241ea4945
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 2/3] interpret-trailers: add own-identity option
  2021-03-19  9:33                       ` ZheNing Hu
@ 2021-03-19 15:36                         ` Junio C Hamano
  2021-03-20  2:54                           ` ZheNing Hu
  0 siblings, 1 reply; 84+ messages in thread
From: Junio C Hamano @ 2021-03-19 15:36 UTC (permalink / raw)
  To: ZheNing Hu
  Cc: ZheNing Hu via GitGitGadget, Git List, Bradley M. Kuhn,
	Brandon Casey, Shourya Shukla, Christian Couder, Rafael Silva

ZheNing Hu <adlternative@gmail.com> writes:

> If `--own-identity` is mouthful, is there a better name?

I originally had "perhaps XXXX?" in the message you are responding
to, but I hoped that the message, especially the examples at the
end, would be sufficient to make you realize that the option itself
is not such a great idea (an additional ":@gitster" in whatever
syntax used would be even shorter than "--own-identity", and
obviously more flexible in that it can name other people).

If you really want to have this option, perhaps call it "--self"?  I
still do not think it is a good idea, though.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v11] [GSOC] commit: add --trailer option
  2021-03-19 12:05                   ` [PATCH v11] " ZheNing Hu via GitGitGadget
@ 2021-03-19 17:48                     ` Junio C Hamano
  2021-03-20 13:41                     ` [PATCH v12] " ZheNing Hu via GitGitGadget
  1 sibling, 0 replies; 84+ messages in thread
From: Junio C Hamano @ 2021-03-19 17:48 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva,
	Đoàn Trần Công Danh, ZheNing Hu

"ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:

> +--trailer <token>[(=|:)<value>]::
> +	Specify a (<token>, <value>) pair that should be applied as a
> +	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
> +	<committer@example.com>" --trailer "Helped-by:C O Mitter \
> +	<committer@example.com>"` will add the "Signed-off-by" trailer
> +	and the "Helped-by" trailer in the commit message.)

s/in the commit message/to the commit message/ probably.

> +	Use `git -c trailer.* commit --trailer` to make the appropriate
> +	configuration. See linkgit:git-interpret-trailers[1] for details.

I doubt this is a good advice for a few reasons.

 (1) The "git -c var=val" is meant to be used as a single-shot
     oddball configuration.  If the user will be working on the
     project long enough to be worth using the --trailer option
     (otherwise a single-shot drive-by patch can just add these
     trailers while editing the log message in the editor), the user
     would not want to use "git -c var=val" mechanism to use
     different configuration every time the --trailer option is
     used.

 (2) The "appropriate configuration" is too vague and does not give
     enough incentive to the reader to go look in the other manual
     page.  At least there should be a cursory mention of what kind
     of things are possible by the configuration.

Prehaps

    The `trailer.*` configuration variables (see linkgit:...) can be
    used to define if a duplicated trailer is omitted, where in the
    run of trailers each trailer would appear, and other details.

or something along the line (Christian would be a better person to
suggest what good examples are than I am, though).

> diff --git a/builtin/commit.c b/builtin/commit.c
> index 739110c5a7f6..4b06672bd07d 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -113,6 +113,7 @@ static int config_commit_verbose = -1; /* unspecified */
>  static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
>  static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
>  static char *sign_commit, *pathspec_from_file;
> +static struct strvec trailer_args = STRVEC_INIT;
>  
>  /*
>   * The default commit message cleanup mode will remove the lines
> @@ -131,6 +132,14 @@ static struct strbuf message = STRBUF_INIT;
>  
>  static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
>  
> +static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
> +{
> +	BUG_ON_OPT_NEG(unset);
> +
> +	strvec_pushl(&trailer_args, "--trailer", arg, NULL);
> +	return 0;
> +}
> +
>  static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
>  {
>  	enum wt_status_format *value = (enum wt_status_format *)opt->value;
> @@ -958,6 +967,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
>  
>  	fclose(s->fp);
>  
> +	if (trailer_args.nr) {
> +		struct child_process run_trailer = CHILD_PROCESS_INIT;
> +
> +		strvec_pushl(&run_trailer.args, "interpret-trailers",
> +			     "--in-place", git_path_commit_editmsg(), NULL);
> +		strvec_pushv(&run_trailer.args, trailer_args.v);
> +		run_trailer.git_cmd = 1;
> +		if (run_command(&run_trailer))
> +			die(_("unable to pass trailers to --trailers"));
> +		strvec_clear(&trailer_args);

OK.  run_command() cleans the run_trailer.args when it returns, so
we only need to clear our own array here.

> +	}
> +

> diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
> index 6396897cc818..024cf3c81b18 100755
> --- a/t/t7502-commit-porcelain.sh
> +++ b/t/t7502-commit-porcelain.sh
> @@ -154,6 +154,341 @@ test_expect_success 'sign off' '
>  
>  '
>  
> +test_expect_success 'commit --trailer without -c' '
> +	echo "fun" >>file &&
> +	git add file &&
> +	cat >expected <<-\EOF &&
> +
> +	Signed-off-by: C O Mitter <committer@example.com>
> +	Signed-off-by: C1 E1
> +	Helped-by: C2 E2
> +	Reported-by: C3 E3
> +	Mentored-by: C4 E4
> +	EOF
> +	git commit -s --trailer "Signed-off-by:C1 E1 " \
> +		--trailer "Helped-by:C2 E2 " \
> +		--trailer "Reported-by:C3 E3" \
> +		--trailer "Mentored-by:C4 E4" \
> +		-m "hello" &&
> +	git cat-file commit HEAD >commit.msg &&
> +	sed -e "1,6d" commit.msg >actual &&

This 1,6d depends on the exact line organization of the underlying
object detail, which is not good.  You'd want to grab the run of the
consecutive non-empty lines at the end, so that commit object headers
and the log message "fun" can change over time by changes to Git and
changes to this test, without breaking this test.

> +	test_cmp expected actual
> +'
> +
> +test_expect_success 'commit --trailer with -c and "replace" as ifexists' '
> +	echo "fun" >>file1 &&
> +	git add file1 &&
> +	cat >expected <<-\EOF &&
> +
> +	Signed-off-by: C O Mitter <committer@example.com>
> +	Signed-off-by: C1 E1
> +	Reported-by: C3 E3
> +	Mentored-by: C4 E4
> +	Helped-by: C3 E3
> +	EOF
> +	git -c trailer.ifexists="replace" \
> +		commit --trailer "Mentored-by: C4 E4" \
> +		 --trailer "Helped-by: C3 E3" \
> +		--amend &&
> +	git cat-file commit HEAD >commit.msg &&
> +	sed -e "1,6d" commit.msg >actual &&
> +	test_cmp expected actual

The same comment applies, and also by using "--amend", this relies
on the outcome of the previous test, which is not great.

> +'
> +
> +test_expect_success 'commit --trailer with -c and "add" as ifexists' '
> +	echo "fun" >>file1 &&
> +	git add file1 &&
> +	cat >expected <<-\EOF &&
> +
> +	Signed-off-by: C O Mitter <committer@example.com>
> +	Signed-off-by: C1 E1
> +	Reported-by: C3 E3
> +	Mentored-by: C4 E4
> +	Helped-by: C3 E3
> +	Helped-by: C3 E3
> +	Helped-by: C3 E3
> +	EOF
> +	git -c trailer.ifexists="add" \
> +		commit --trailer "Helped-by: C3 E3" \
> +		--trailer "Helped-by: C3 E3" \
> +		--amend &&

And it makes things worse by keep amending.

At least, establish a baseline commit that has known set of
trailers, tag it, and reset the HEAD to that commit at the beginning
of each test that tries to amend an existing commit.  That way, the
correctness of each individual test would depend only on the test
that creates the baseline commit and tags it.


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 2/3] interpret-trailers: add own-identity option
  2021-03-19 15:36                         ` Junio C Hamano
@ 2021-03-20  2:54                           ` ZheNing Hu
  2021-03-20  5:06                             ` Jeff King
  0 siblings, 1 reply; 84+ messages in thread
From: ZheNing Hu @ 2021-03-20  2:54 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: ZheNing Hu via GitGitGadget, Git List, Bradley M. Kuhn,
	Brandon Casey, Shourya Shukla, Christian Couder, Rafael Silva

Junio C Hamano <gitster@pobox.com> 于2021年3月19日周五 下午11:36写道:
>
> ZheNing Hu <adlternative@gmail.com> writes:
>
> > If `--own-identity` is mouthful, is there a better name?
>
> I originally had "perhaps XXXX?" in the message you are responding
> to, but I hoped that the message, especially the examples at the
> end, would be sufficient to make you realize that the option itself
> is not such a great idea (an additional ":@gitster" in whatever
> syntax used would be even shorter than "--own-identity", and
> obviously more flexible in that it can name other people).
>
> If you really want to have this option, perhaps call it "--self"?  I
> still do not think it is a good idea, though.

Fine, If this option is really not particularly useful, I am willing
to give it up.

I am looking for how to extract each author or committer from the log.
I just know I can use:

$ git log --pretty="%an %ae" | sort | uniq

get all unique author <email> from a git repo.
Is there any function in the source code of git to get them?

I notice Ævar Arnfjörð Bjarmason mentioned `.mailmap`, I have seen the
 `.mailmap` under git repo. Would it be better to extract the author
or committer
 from  the .mailmap than to extract it from git log?
If so, the operation of the trailer may depend on the contents of `.mailmap`.
If there is no `.mailmap` in the user git repo, then the identity in the trailer
cannot be parsed.

Thanks.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 2/3] interpret-trailers: add own-identity option
  2021-03-20  2:54                           ` ZheNing Hu
@ 2021-03-20  5:06                             ` Jeff King
  2021-03-20  5:50                               ` Junio C Hamano
  0 siblings, 1 reply; 84+ messages in thread
From: Jeff King @ 2021-03-20  5:06 UTC (permalink / raw)
  To: ZheNing Hu
  Cc: Junio C Hamano, ZheNing Hu via GitGitGadget, Git List,
	Bradley M. Kuhn, Brandon Casey, Shourya Shukla, Christian Couder,
	Rafael Silva

On Sat, Mar 20, 2021 at 10:54:38AM +0800, ZheNing Hu wrote:

> I am looking for how to extract each author or committer from the log.
> I just know I can use:
> 
> $ git log --pretty="%an %ae" | sort | uniq
> 
> get all unique author <email> from a git repo.
> Is there any function in the source code of git to get them?

If I understand Junio's suggestion correctly, it is very similar to how
"commit --author" works. See how it calls find_author_by_nickname(),
which finds the first commit matching the name, and then pulls out the
full name from format_commit_message().

-Peff

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 2/3] interpret-trailers: add own-identity option
  2021-03-20  5:06                             ` Jeff King
@ 2021-03-20  5:50                               ` Junio C Hamano
  2021-03-20  6:16                                 ` ZheNing Hu
  0 siblings, 1 reply; 84+ messages in thread
From: Junio C Hamano @ 2021-03-20  5:50 UTC (permalink / raw)
  To: Jeff King
  Cc: ZheNing Hu, ZheNing Hu via GitGitGadget, Git List,
	Bradley M. Kuhn, Brandon Casey, Shourya Shukla, Christian Couder,
	Rafael Silva

Jeff King <peff@peff.net> writes:

> If I understand Junio's suggestion correctly, it is very similar to how
> "commit --author" works. See how it calls find_author_by_nickname(),
> which finds the first commit matching the name, and then pulls out the
> full name from format_commit_message().

Yup.

But I have to warn readers that it would not be a sane approach to
simply expose find_author_by_nickname() from builtin/commit.c as if
it is a generally reusable helper function.  In a very limited
context of "git commit", what the helper does is OK to run a single
revision traversal without cleaning the parsed commit objects and
object flag bits after finding a single commit.

But generally, it would not be an approach that would scale (e.g. I
do not know if we can expect to be able to call the helper function
twice and get sensible results out of it).

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 2/3] interpret-trailers: add own-identity option
  2021-03-20  5:50                               ` Junio C Hamano
@ 2021-03-20  6:16                                 ` ZheNing Hu
  2021-03-20  6:38                                   ` ZheNing Hu
  0 siblings, 1 reply; 84+ messages in thread
From: ZheNing Hu @ 2021-03-20  6:16 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Jeff King, ZheNing Hu via GitGitGadget, Git List,
	Bradley M. Kuhn, Brandon Casey, Shourya Shukla, Christian Couder,
	Rafael Silva

Junio C Hamano <gitster@pobox.com> 于2021年3月20日周六 下午1:50写道:
>
> Jeff King <peff@peff.net> writes:
>
> > If I understand Junio's suggestion correctly, it is very similar to how
> > "commit --author" works. See how it calls find_author_by_nickname(),
> > which finds the first commit matching the name, and then pulls out the
> > full name from format_commit_message().
>
> Yup.
>
> But I have to warn readers that it would not be a sane approach to
> simply expose find_author_by_nickname() from builtin/commit.c as if
> it is a generally reusable helper function.  In a very limited
> context of "git commit", what the helper does is OK to run a single
> revision traversal without cleaning the parsed commit objects and
> object flag bits after finding a single commit.
>
> But generally, it would not be an approach that would scale (e.g. I
> do not know if we can expect to be able to call the helper function
> twice and get sensible results out of it).

Hi, Jeff and Junio,

`find_author_by_nickname` is indeed a good way to find
 the `author <email>` in the "last" commit that matches
the specified name, but after testing, `find_author_by_nickname` is
as Junio say, it is not reusable(use it twice, git will call die), but why?

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 2/3] interpret-trailers: add own-identity option
  2021-03-20  6:16                                 ` ZheNing Hu
@ 2021-03-20  6:38                                   ` ZheNing Hu
  2021-03-20  6:53                                     ` Junio C Hamano
  0 siblings, 1 reply; 84+ messages in thread
From: ZheNing Hu @ 2021-03-20  6:38 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Jeff King, ZheNing Hu via GitGitGadget, Git List,
	Bradley M. Kuhn, Brandon Casey, Shourya Shukla, Christian Couder,
	Rafael Silva

In actually ,

@@ -1071,6 +1071,7 @@ static const char *find_author_by_nickname(const
char *name)
                strbuf_release(&buf);
                format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
                clear_mailmap(&mailmap);
+               reset_revision_walk();
                return strbuf_detach(&buf, NULL);
        }

then we can reuse this function.

But I think I can give find_author_by_nickname another arg for choice if we want
 `reset_revision_walk()`.

Thanks, Junio and Jeff.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 2/3] interpret-trailers: add own-identity option
  2021-03-20  6:38                                   ` ZheNing Hu
@ 2021-03-20  6:53                                     ` Junio C Hamano
  2021-03-20  8:43                                       ` ZheNing Hu
  0 siblings, 1 reply; 84+ messages in thread
From: Junio C Hamano @ 2021-03-20  6:53 UTC (permalink / raw)
  To: ZheNing Hu
  Cc: Jeff King, ZheNing Hu via GitGitGadget, Git List,
	Bradley M. Kuhn, Brandon Casey, Shourya Shukla, Christian Couder,
	Rafael Silva

ZheNing Hu <adlternative@gmail.com> writes:

> In actually ,
>
> @@ -1071,6 +1071,7 @@ static const char *find_author_by_nickname(const
> char *name)
>                 strbuf_release(&buf);
>                 format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
>                 clear_mailmap(&mailmap);
> +               reset_revision_walk();
>                 return strbuf_detach(&buf, NULL);
>         }
>
> then we can reuse this function.
>
> But I think I can give find_author_by_nickname another arg for choice if we want
>  `reset_revision_walk()`.

That is half fixing and half breaking.  It would allow us to call
the helper number of times as long as there is no other revision
traversal is in progress; calling reset_revision_walk() would mean
any and all revision traversal in progress will be broken.  So we
cannot use the helper to tweak each and every commit we encounter
while running "git log", for example.  Imagine adding an option to
"git format-patch" to allow each commit it formats to be tweaked by
adding "--trailers=foo:@ZheNing" and the like.  There is one primary
traversal that is used to list which commit to format in what order,
and every time that primary traversal yields a commit, if we run
find_author_by_nickname(), we end up initiating another traversal,
and then by calling reset, we clear all object flags that are used
for revision traversal, thereby breaking the primary traversal.

The only safe way to introduce a generally usable helper (without
rewriting the revision traversal machinery) is to spawn a subprocess
and do an equivalent of find_author_by_nickname() in it.

A standalone "interpret-trailers" command, as long as it won't do
any other revision traversal, would not have such a problem, and
calling reset every time find_author_by_nickname() is called may be
sufficient.  The only thing I care about is *not* to pretend that
find_author_by_nickname() plus reset() is the generally reusable
helper function and advertise it as such, which will mislead future
developers into misusing the function in a context they shouldn't
(i.e. while they are performing their own revision traversal).

Thanks.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v10 2/3] interpret-trailers: add own-identity option
  2021-03-20  6:53                                     ` Junio C Hamano
@ 2021-03-20  8:43                                       ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-20  8:43 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Jeff King, ZheNing Hu via GitGitGadget, Git List,
	Bradley M. Kuhn, Brandon Casey, Shourya Shukla, Christian Couder,
	Rafael Silva

Junio C Hamano <gitster@pobox.com> 于2021年3月20日周六 下午2:53写道:
>
> ZheNing Hu <adlternative@gmail.com> writes:
>
> > In actually ,
> >
> > @@ -1071,6 +1071,7 @@ static const char *find_author_by_nickname(const
> > char *name)
> >                 strbuf_release(&buf);
> >                 format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
> >                 clear_mailmap(&mailmap);
> > +               reset_revision_walk();
> >                 return strbuf_detach(&buf, NULL);
> >         }
> >
> > then we can reuse this function.
> >
> > But I think I can give find_author_by_nickname another arg for choice if we want
> >  `reset_revision_walk()`.
>
> That is half fixing and half breaking.  It would allow us to call
> the helper number of times as long as there is no other revision
> traversal is in progress; calling reset_revision_walk() would mean
> any and all revision traversal in progress will be broken.  So we
> cannot use the helper to tweak each and every commit we encounter
> while running "git log", for example.  Imagine adding an option to
> "git format-patch" to allow each commit it formats to be tweaked by
> adding "--trailers=foo:@ZheNing" and the like.  There is one primary
> traversal that is used to list which commit to format in what order,
> and every time that primary traversal yields a commit, if we run
> find_author_by_nickname(), we end up initiating another traversal,
> and then by calling reset, we clear all object flags that are used
> for revision traversal, thereby breaking the primary traversal.
>

I understand what you meaning. I may not be very thorough in considering
 the potential problems. This is as dangerous as calling `list_del` in the
 middle of `list_for_each`.

> The only safe way to introduce a generally usable helper (without
> rewriting the revision traversal machinery) is to spawn a subprocess
> and do an equivalent of find_author_by_nickname() in it.
>

If I didn't notice what you mentioned later, I might think about it
for a long time.
Execute a child process specifically to execute `find_author_by_nickname()`,
which feels a bit wasteful.

> A standalone "interpret-trailers" command, as long as it won't do
> any other revision traversal, would not have such a problem, and
> calling reset every time find_author_by_nickname() is called may be
> sufficient.  The only thing I care about is *not* to pretend that
> find_author_by_nickname() plus reset() is the generally reusable
> helper function and advertise it as such, which will mislead future
> developers into misusing the function in a context they shouldn't
> (i.e. while they are performing their own revision traversal).
>
> Thanks.

After listening to your previous explanation, I agree with this view.
`interpret-trailers` alone as a process, as long as it does not traverse
the revision externally, It is safe for us to call `reset_revision_walk()`
after `find_author_by_nickname()` inside interpret-trailers.

Thank you for your thoughts and answers.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v12] [GSOC] commit: add --trailer option
  2021-03-19 12:05                   ` [PATCH v11] " ZheNing Hu via GitGitGadget
  2021-03-19 17:48                     ` Junio C Hamano
@ 2021-03-20 13:41                     ` ZheNing Hu via GitGitGadget
  2021-03-22  4:24                       ` [PATCH v13] " ZheNing Hu via GitGitGadget
  1 sibling, 1 reply; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-20 13:41 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King, ZheNing Hu,
	ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Historically, Git has supported the 'Signed-off-by' commit trailer
using the '--signoff' and the '-s' option from the command line.
But users may need to provide other trailer information from the
command line such as "Helped-by", "Reported-by", "Mentored-by",

Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
other trailers to `interpret-trailers` and insert them into commit
messages.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC] commit: add --trailer option
    
    Now maintainers or developers can also use commit
    --trailer="Signed-off-by:commiter<email>" from the command line to
    provide trailers to commit messages. This solution may be more
    generalized than v1.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v12
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v12
Pull-Request: https://github.com/gitgitgadget/git/pull/901

Range-diff vs v11:

 1:  0db1210ffd6d ! 1:  2378e3b4c1ae [GSOC] commit: add --trailer option
     @@ Documentation/git-commit.txt: The `-m` option is mutually exclusive with `-c`, `
      +	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
      +	<committer@example.com>" --trailer "Helped-by:C O Mitter \
      +	<committer@example.com>"` will add the "Signed-off-by" trailer
     -+	and the "Helped-by" trailer in the commit message.)
     -+	Use `git -c trailer.* commit --trailer` to make the appropriate
     -+	configuration. See linkgit:git-interpret-trailers[1] for details.
     ++	and the "Helped-by" trailer to the commit message.)
     ++	The `trailer.*` configuration variables
     ++	(linkgit:git-interpret-trailers[1]) can be used to define if
     ++	a duplicated trailer is omitted, where in the run of trailers
     ++	each trailer would appear, and other details.
     ++
       -n::
       --no-verify::
       	This option bypasses the pre-commit and commit-msg hooks.
     @@ builtin/commit.c: int cmd_commit(int argc, const char **argv, const char *prefix
       		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
      
       ## t/t7502-commit-porcelain.sh ##
     +@@ t/t7502-commit-porcelain.sh: check_summary_oneline() {
     + 	test_cmp exp act
     + }
     + 
     ++trailer_commit_base () {
     ++	echo "fun" >>file &&
     ++	git add file &&
     ++	git commit -s --trailer "Signed-off-by=C1 E1 " \
     ++		--trailer "Helped-by:C2 E2 " \
     ++		--trailer "Reported-by=C3 E3" \
     ++		--trailer "Mentored-by:C4 E4" \
     ++		-m "hello"
     ++}
     ++
     + test_expect_success 'output summary format' '
     + 
     + 	echo new >file1 &&
      @@ t/t7502-commit-porcelain.sh: test_expect_success 'sign off' '
       
       '
       
     -+test_expect_success 'commit --trailer without -c' '
     -+	echo "fun" >>file &&
     -+	git add file &&
     ++test_expect_success 'commit --trailer with "="' '
     ++	trailer_commit_base &&
      +	cat >expected <<-\EOF &&
     ++	hello
      +
      +	Signed-off-by: C O Mitter <committer@example.com>
      +	Signed-off-by: C1 E1
     @@ t/t7502-commit-porcelain.sh: test_expect_success 'sign off' '
      +	Reported-by: C3 E3
      +	Mentored-by: C4 E4
      +	EOF
     -+	git commit -s --trailer "Signed-off-by:C1 E1 " \
     -+		--trailer "Helped-by:C2 E2 " \
     -+		--trailer "Reported-by:C3 E3" \
     -+		--trailer "Mentored-by:C4 E4" \
     -+		-m "hello" &&
      +	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,6d" commit.msg >actual &&
     ++	sed -e "1,/^\$/d" commit.msg >actual &&
      +	test_cmp expected actual
      +'
      +
      +test_expect_success 'commit --trailer with -c and "replace" as ifexists' '
     -+	echo "fun" >>file1 &&
     -+	git add file1 &&
     ++	trailer_commit_base &&
      +	cat >expected <<-\EOF &&
     ++	hello
      +
      +	Signed-off-by: C O Mitter <committer@example.com>
      +	Signed-off-by: C1 E1
     @@ t/t7502-commit-porcelain.sh: test_expect_success 'sign off' '
      +		 --trailer "Helped-by: C3 E3" \
      +		--amend &&
      +	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,6d" commit.msg >actual &&
     ++	sed -e "1,/^\$/d"  commit.msg >actual &&
      +	test_cmp expected actual
      +'
      +
      +test_expect_success 'commit --trailer with -c and "add" as ifexists' '
     -+	echo "fun" >>file1 &&
     -+	git add file1 &&
     ++	trailer_commit_base &&
      +	cat >expected <<-\EOF &&
     ++	hello
      +
      +	Signed-off-by: C O Mitter <committer@example.com>
      +	Signed-off-by: C1 E1
     ++	Helped-by: C2 E2
     ++	Reported-by: C3 E3
     ++	Mentored-by: C4 E4
      +	Reported-by: C3 E3
      +	Mentored-by: C4 E4
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
      +	EOF
      +	git -c trailer.ifexists="add" \
     -+		commit --trailer "Helped-by: C3 E3" \
     -+		--trailer "Helped-by: C3 E3" \
     ++		commit --trailer "Reported-by: C3 E3" \
     ++		--trailer "Mentored-by: C4 E4" \
      +		--amend &&
      +	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,6d" commit.msg >actual &&
     ++	sed -e "1,/^\$/d"  commit.msg >actual &&
      +	test_cmp expected actual
      +'
      +
      +test_expect_success 'commit --trailer with -c and "donothing" as ifexists' '
     -+	echo "fun" >>file1 &&
     -+	git add file1 &&
     ++	trailer_commit_base &&
      +	cat >expected <<-\EOF &&
     ++	hello
      +
      +	Signed-off-by: C O Mitter <committer@example.com>
      +	Signed-off-by: C1 E1
     ++	Helped-by: C2 E2
      +	Reported-by: C3 E3
      +	Mentored-by: C4 E4
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
      +	Reviewed-by: C6 E6
      +	EOF
      +	git -c trailer.ifexists="donothing" \
     @@ t/t7502-commit-porcelain.sh: test_expect_success 'sign off' '
      +		--trailer "Reviewed-by: C6 E6" \
      +		--amend &&
      +	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,6d" commit.msg >actual &&
     ++	sed -e "1,/^\$/d"  commit.msg >actual &&
      +	test_cmp expected actual
      +'
      +
      +test_expect_success 'commit --trailer with -c and "addIfDifferent" as ifexists' '
     -+	echo "fun" >>file1 &&
     -+	git add file1 &&
     ++	trailer_commit_base &&
      +	cat >expected <<-\EOF &&
     ++	hello
      +
      +	Signed-off-by: C O Mitter <committer@example.com>
      +	Signed-off-by: C1 E1
     ++	Helped-by: C2 E2
      +	Reported-by: C3 E3
      +	Mentored-by: C4 E4
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Reviewed-by: C6 E6
     -+	Reported-by: C5 E5
     ++	Mentored-by: C5 E5
      +	EOF
      +	git -c trailer.ifexists="addIfDifferent" \
     -+		commit --trailer "Reviewed-by: C6 E6" \
     -+		--trailer "Reported-by: C5 E5" \
     ++		commit --trailer "Reported-by: C3 E3" \
     ++		--trailer "Mentored-by: C5 E5" \
      +		--amend &&
      +	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,6d" commit.msg >actual &&
     ++	sed -e "1,/^\$/d"  commit.msg >actual &&
      +	test_cmp expected actual
      +'
      +
      +test_expect_success 'commit --trailer with -c and "addIfDifferentNeighbor" as ifexists' '
     -+	echo "fun" >>file1 &&
     -+	git add file1 &&
     ++	trailer_commit_base &&
      +	cat >expected <<-\EOF &&
     ++	hello
      +
      +	Signed-off-by: C O Mitter <committer@example.com>
      +	Signed-off-by: C1 E1
     ++	Helped-by: C2 E2
      +	Reported-by: C3 E3
      +	Mentored-by: C4 E4
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Reviewed-by: C6 E6
     -+	Reported-by: C5 E5
     ++	Reported-by: C3 E3
      +	EOF
     -+	git -c trailer.ifexists="addIfDifferent" \
     -+		commit --trailer "Reported-by: C5 E5" \
     -+		--trailer "Reviewed-by: C6 E6" \
     ++	git -c trailer.ifexists="addIfDifferentNeighbor" \
     ++		commit --trailer "Mentored-by: C4 E4" \
     ++		--trailer "Reported-by: C3 E3" \
      +		--amend &&
      +	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,6d" commit.msg >actual &&
     ++	sed -e "1,/^\$/d"  commit.msg >actual &&
      +	test_cmp expected actual
      +'
      +
      +test_expect_success 'commit --trailer with -c and "end" as where' '
     -+	echo "fun" >>file1 &&
     -+	git add file1 &&
     ++	trailer_commit_base &&
      +	cat >expected <<-\EOF &&
     ++	hello
      +
      +	Signed-off-by: C O Mitter <committer@example.com>
      +	Signed-off-by: C1 E1
     ++	Helped-by: C2 E2
     ++	Reported-by: C3 E3
     ++	Mentored-by: C4 E4
      +	Reported-by: C3 E3
      +	Mentored-by: C4 E4
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Reviewed-by: C6 E6
     -+	Reported-by: C5 E5
     -+	Reported-by: C7 E7
      +	EOF
      +	git -c trailer.where="end" \
     -+		commit --trailer "Reported-by: C5 E5" \
     -+		--trailer "Reported-by: C7 E7" \
     ++		commit --trailer "Reported-by: C3 E3" \
     ++		--trailer "Mentored-by: C4 E4" \
      +		--amend &&
      +	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,6d" commit.msg >actual &&
     ++	sed -e "1,/^\$/d" commit.msg >actual &&
      +	test_cmp expected actual
      +'
      +
      +test_expect_success 'commit --trailer with -c and "start" as where' '
     -+	echo "fun" >>file1 &&
     -+	git add file1 &&
     ++	trailer_commit_base &&
      +	cat >expected <<-\EOF &&
     ++	hello
      +
     -+	Signed-off-by: C8 E8
     ++	Signed-off-by: C1 E1
      +	Signed-off-by: C O Mitter <committer@example.com>
      +	Signed-off-by: C1 E1
     ++	Helped-by: C2 E2
      +	Reported-by: C3 E3
      +	Mentored-by: C4 E4
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Reviewed-by: C6 E6
     -+	Reported-by: C5 E5
     -+	Reported-by: C7 E7
      +	EOF
      +	git -c trailer.where="start" \
     -+		commit --trailer "Signed-off-by: C8 E8" \
     -+		--trailer "Signed-off-by: C8 E8" \
     ++		commit --trailer "Signed-off-by: C O Mitter <committer@example.com>" \
     ++		--trailer "Signed-off-by: C1 E1" \
      +		--amend &&
      +	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,6d" commit.msg >actual &&
     ++	sed -e "1,/^\$/d" commit.msg >actual &&
      +	test_cmp expected actual
      +'
      +
      +test_expect_success 'commit --trailer with -c and "after" as where' '
     -+	echo "fun" >>file1 &&
     -+	git add file1 &&
     ++	trailer_commit_base &&
      +	cat >expected <<-\EOF &&
     ++	hello
      +
     -+	Signed-off-by: C8 E8
      +	Signed-off-by: C O Mitter <committer@example.com>
      +	Signed-off-by: C1 E1
     ++	Helped-by: C2 E2
      +	Reported-by: C3 E3
      +	Mentored-by: C4 E4
     -+	Mentored-by: C9 E9
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Reviewed-by: C6 E6
     -+	Reported-by: C5 E5
     -+	Reported-by: C7 E7
     -+	Reported-by: C10 E10
     ++	Mentored-by: C5 E5
      +	EOF
      +	git -c trailer.where="after" \
     -+		commit --trailer "Mentored-by: C9 E9" \
     -+		--trailer "Reported-by: C10 E10" \
     ++		commit --trailer "Mentored-by: C4 E4" \
     ++		--trailer "Mentored-by: C5 E5" \
      +		--amend &&
      +	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,6d" commit.msg >actual &&
     ++	sed -e "1,/^\$/d" commit.msg >actual &&
      +	test_cmp expected actual
      +'
      +
      +test_expect_success 'commit --trailer with -c and "before" as where' '
     -+	echo "fun" >>file1 &&
     -+	git add file1 &&
     ++	trailer_commit_base &&
      +	cat >expected <<-\EOF &&
     ++	hello
      +
     -+	Signed-off-by: C8 E8
      +	Signed-off-by: C O Mitter <committer@example.com>
      +	Signed-off-by: C1 E1
     -+	Reported-by: C11 E11
     ++	Helped-by: C2 E2
      +	Reported-by: C3 E3
     ++	Mentored-by: C2 E2
     ++	Mentored-by: C3 E3
      +	Mentored-by: C4 E4
     -+	Mentored-by: C9 E9
     -+	Helped-by: C12 E12
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Reviewed-by: C6 E6
     -+	Reported-by: C5 E5
     -+	Reported-by: C7 E7
     -+	Reported-by: C10 E10
      +	EOF
      +	git -c trailer.where="before" \
     -+		commit --trailer "Helped-by: C12 E12" \
     -+		--trailer "Reported-by: C11 E11" \
     ++		commit --trailer "Mentored-by: C3 E3" \
     ++		--trailer "Mentored-by: C2 E2" \
      +		--amend &&
      +	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,6d" commit.msg >actual &&
     ++	sed -e "1,/^\$/d" commit.msg >actual &&
      +	test_cmp expected actual
      +'
      +
      +test_expect_success 'commit --trailer with -c and "donothing" as ifmissing' '
     -+	echo "fun" >>file1 &&
     -+	git add file1 &&
     ++	trailer_commit_base &&
      +	cat >expected <<-\EOF &&
     ++	hello
      +
     -+	Signed-off-by: C8 E8
      +	Signed-off-by: C O Mitter <committer@example.com>
      +	Signed-off-by: C1 E1
     -+	Reported-by: C11 E11
     ++	Helped-by: C2 E2
      +	Reported-by: C3 E3
      +	Mentored-by: C4 E4
     -+	Mentored-by: C9 E9
     -+	Helped-by: C12 E12
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Reviewed-by: C6 E6
     -+	Reported-by: C5 E5
     -+	Reported-by: C7 E7
     -+	Reported-by: C10 E10
     -+	Helped-by: C12 E12
     ++	Helped-by: C5 E5
      +	EOF
      +	git -c trailer.ifmissing="donothing" \
     -+		commit --trailer "Helped-by: C12 E12" \
     -+		--trailer "Based-by: C13 E13" \
     ++		commit --trailer "Helped-by: C5 E5" \
     ++		--trailer "Based-by: C6 E6" \
      +		--amend &&
      +	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,6d" commit.msg >actual &&
     ++	sed -e "1,/^\$/d" commit.msg >actual &&
      +	test_cmp expected actual
      +'
      +
      +test_expect_success 'commit --trailer with -c and "add" as ifmissing' '
     -+	echo "fun" >>file1 &&
     -+	git add file1 &&
     ++	trailer_commit_base &&
      +	cat >expected <<-\EOF &&
     ++	hello
      +
     -+	Signed-off-by: C8 E8
      +	Signed-off-by: C O Mitter <committer@example.com>
      +	Signed-off-by: C1 E1
     -+	Reported-by: C11 E11
     ++	Helped-by: C2 E2
      +	Reported-by: C3 E3
      +	Mentored-by: C4 E4
     -+	Mentored-by: C9 E9
     -+	Helped-by: C12 E12
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Helped-by: C3 E3
     -+	Reviewed-by: C6 E6
     -+	Reported-by: C5 E5
     -+	Reported-by: C7 E7
     -+	Reported-by: C10 E10
     -+	Helped-by: C12 E12
     -+	Based-by: C13 E13
     ++	Helped-by: C5 E5
     ++	Based-by: C6 E6
      +	EOF
      +	git -c trailer.ifmissing="add" \
     -+		commit --trailer "Helped-by: C12 E12" \
     -+		--trailer "Based-by: C13 E13" \
     ++		commit --trailer "Helped-by: C5 E5" \
     ++		--trailer "Based-by: C6 E6" \
      +		--amend &&
      +	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,6d" commit.msg >actual &&
     ++	sed -e "1,/^\$/d" commit.msg >actual &&
      +	test_cmp expected actual
      +'
      +
     -+test_expect_success 'commit --trailer with "=" ' '
     ++test_expect_success 'commit --trailer with -c ack.key ' '
      +	echo "fun" >>file1 &&
      +	git add file1 &&
      +	cat >expected <<-\EOF &&
     ++		hello
      +
      +		Acked-by: Peff
      +	EOF
      +	git -c trailer.ack.key="Acked-by" \
      +		commit --trailer "ack = Peff" -m "hello" &&
      +	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,6d" commit.msg >actual &&
     ++	sed -e "1,/^\$/d" commit.msg >actual &&
      +	test_cmp expected actual
      +'
      +
     @@ t/t7502-commit-porcelain.sh: test_expect_success 'sign off' '
      +	echo "fun" >>file1 &&
      +	git add file1 &&
      +	cat >expected <<-\EOF &&
     ++		I hate bug
      +
      +		Bug #42
      +	EOF
     @@ t/t7502-commit-porcelain.sh: test_expect_success 'sign off' '
      +		-c trailer.bug.key="Bug #" \
      +		commit --trailer "bug = 42" -m "I hate bug" &&
      +	git cat-file commit HEAD >commit.msg &&
     -+	sed -e "1,6d" commit.msg >actual &&
     ++	sed -e "1,/^\$/d" commit.msg >actual &&
      +	test_cmp expected actual
      +'
      +


 Documentation/git-commit.txt |  14 +-
 builtin/commit.c             |  22 +++
 t/t7502-commit-porcelain.sh  | 291 +++++++++++++++++++++++++++++++++++
 3 files changed, 326 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 17150fa7eabe..3fe7ef33cb07 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -14,7 +14,8 @@ SYNOPSIS
 	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
 	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
 	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
-	   [-S[<keyid>]] [--] [<pathspec>...]
+	   [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]
+	   [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -166,6 +167,17 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
 
 include::signoff-option.txt[]
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
+	<committer@example.com>" --trailer "Helped-by:C O Mitter \
+	<committer@example.com>"` will add the "Signed-off-by" trailer
+	and the "Helped-by" trailer to the commit message.)
+	The `trailer.*` configuration variables
+	(linkgit:git-interpret-trailers[1]) can be used to define if
+	a duplicated trailer is omitted, where in the run of trailers
+	each trailer would appear, and other details.
+
 -n::
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
diff --git a/builtin/commit.c b/builtin/commit.c
index 739110c5a7f6..4b06672bd07d 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,6 +113,7 @@ static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit, *pathspec_from_file;
+static struct strvec trailer_args = STRVEC_INIT;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -131,6 +132,14 @@ static struct strbuf message = STRBUF_INIT;
 
 static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
 
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+
+	strvec_pushl(&trailer_args, "--trailer", arg, NULL);
+	return 0;
+}
+
 static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
 {
 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -958,6 +967,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
 	fclose(s->fp);
 
+	if (trailer_args.nr) {
+		struct child_process run_trailer = CHILD_PROCESS_INIT;
+
+		strvec_pushl(&run_trailer.args, "interpret-trailers",
+			     "--in-place", git_path_commit_editmsg(), NULL);
+		strvec_pushv(&run_trailer.args, trailer_args.v);
+		run_trailer.git_cmd = 1;
+		if (run_command(&run_trailer))
+			die(_("unable to pass trailers to --trailers"));
+		strvec_clear(&trailer_args);
+	}
+
 	/*
 	 * Reject an attempt to record a non-merge empty commit without
 	 * explicit --allow-empty. In the cherry-pick case, it may be
@@ -1507,6 +1528,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 6396897cc818..74b1602c0ce6 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -38,6 +38,16 @@ check_summary_oneline() {
 	test_cmp exp act
 }
 
+trailer_commit_base () {
+	echo "fun" >>file &&
+	git add file &&
+	git commit -s --trailer "Signed-off-by=C1 E1 " \
+		--trailer "Helped-by:C2 E2 " \
+		--trailer "Reported-by=C3 E3" \
+		--trailer "Mentored-by:C4 E4" \
+		-m "hello"
+}
+
 test_expect_success 'output summary format' '
 
 	echo new >file1 &&
@@ -154,6 +164,287 @@ test_expect_success 'sign off' '
 
 '
 
+test_expect_success 'commit --trailer with "="' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "replace" as ifexists' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	EOF
+	git -c trailer.ifexists="replace" \
+		commit --trailer "Mentored-by: C4 E4" \
+		 --trailer "Helped-by: C3 E3" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d"  commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "add" as ifexists' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git -c trailer.ifexists="add" \
+		commit --trailer "Reported-by: C3 E3" \
+		--trailer "Mentored-by: C4 E4" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d"  commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "donothing" as ifexists' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Reviewed-by: C6 E6
+	EOF
+	git -c trailer.ifexists="donothing" \
+		commit --trailer "Mentored-by: C5 E5" \
+		--trailer "Reviewed-by: C6 E6" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d"  commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "addIfDifferent" as ifexists' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Mentored-by: C5 E5
+	EOF
+	git -c trailer.ifexists="addIfDifferent" \
+		commit --trailer "Reported-by: C3 E3" \
+		--trailer "Mentored-by: C5 E5" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d"  commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "addIfDifferentNeighbor" as ifexists' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Reported-by: C3 E3
+	EOF
+	git -c trailer.ifexists="addIfDifferentNeighbor" \
+		commit --trailer "Mentored-by: C4 E4" \
+		--trailer "Reported-by: C3 E3" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d"  commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "end" as where' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git -c trailer.where="end" \
+		commit --trailer "Reported-by: C3 E3" \
+		--trailer "Mentored-by: C4 E4" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "start" as where' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C1 E1
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git -c trailer.where="start" \
+		commit --trailer "Signed-off-by: C O Mitter <committer@example.com>" \
+		--trailer "Signed-off-by: C1 E1" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "after" as where' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Mentored-by: C5 E5
+	EOF
+	git -c trailer.where="after" \
+		commit --trailer "Mentored-by: C4 E4" \
+		--trailer "Mentored-by: C5 E5" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "before" as where' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C2 E2
+	Mentored-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git -c trailer.where="before" \
+		commit --trailer "Mentored-by: C3 E3" \
+		--trailer "Mentored-by: C2 E2" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "donothing" as ifmissing' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C5 E5
+	EOF
+	git -c trailer.ifmissing="donothing" \
+		commit --trailer "Helped-by: C5 E5" \
+		--trailer "Based-by: C6 E6" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "add" as ifmissing' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C5 E5
+	Based-by: C6 E6
+	EOF
+	git -c trailer.ifmissing="add" \
+		commit --trailer "Helped-by: C5 E5" \
+		--trailer "Based-by: C6 E6" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c ack.key ' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+		hello
+
+		Acked-by: Peff
+	EOF
+	git -c trailer.ack.key="Acked-by" \
+		commit --trailer "ack = Peff" -m "hello" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and ":=#" as separators' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+		I hate bug
+
+		Bug #42
+	EOF
+	git -c trailer.separators=":=#" \
+		-c trailer.bug.key="Bug #" \
+		commit --trailer "bug = 42" -m "I hate bug" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&

base-commit: 13d7ab6b5d7929825b626f050b62a11241ea4945
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v13] [GSOC] commit: add --trailer option
  2021-03-20 13:41                     ` [PATCH v12] " ZheNing Hu via GitGitGadget
@ 2021-03-22  4:24                       ` ZheNing Hu via GitGitGadget
  2021-03-22  7:43                         ` Christian Couder
  2021-03-23 13:55                         ` [PATCH v14] " ZheNing Hu via GitGitGadget
  0 siblings, 2 replies; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-22  4:24 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Junio C Hamano, Ævar Arnfjörð Bjarmason,
	ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Historically, Git has supported the 'Signed-off-by' commit trailer
using the '--signoff' and the '-s' option from the command line.
But users may need to provide other trailer information from the
command line such as "Helped-by", "Reported-by", "Mentored-by",

Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
other trailers to `interpret-trailers` and insert them into commit
messages.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
Reported-by:
---
    [GSOC] commit: add --trailer option
    
    Now maintainers or developers can also use commit
    --trailer="Signed-off-by:commiter<email>" from the command line to
    provide trailers to commit messages. This solution may be more
    generalized than v1.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v13
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v13
Pull-Request: https://github.com/gitgitgadget/git/pull/901

Range-diff vs v12:

 1:  2378e3b4c1ae ! 1:  98b0c470e141 [GSOC] commit: add --trailer option
     @@ Commit message
          messages.
      
          Signed-off-by: ZheNing Hu <adlternative@gmail.com>
     +    Reported-by:
      
       ## Documentation/git-commit.txt ##
      @@ Documentation/git-commit.txt: SYNOPSIS
     @@ t/t7502-commit-porcelain.sh: test_expect_success 'sign off' '
      +	sed -e "1,/^\$/d" commit.msg >actual &&
      +	test_cmp expected actual
      +'
     ++
     ++test_expect_success 'commit --trailer with -c and command' '
     ++	trailer_commit_base &&
     ++	cat >expected <<-\EOF &&
     ++	hello
     ++
     ++	Signed-off-by: C O Mitter <committer@example.com>
     ++	Signed-off-by: C1 E1
     ++	Helped-by: C2 E2
     ++	Mentored-by: C4 E4
     ++	Reported-by: A U Thor <author@example.com>
     ++	EOF
     ++	git -c trailer.report.key="Reported-by: " \
     ++		-c trailer.report.ifexists="replace" \
     ++		-c trailer.report.command="git log --author=\"\$ARG\" -1 \
     ++		--format=\"format:%aN <%aE>\"" \
     ++		commit --trailer "report = author" --amend &&
     ++	git cat-file commit HEAD >commit.msg &&
     ++	sed -e "1,/^\$/d" commit.msg >actual &&
     ++	test_cmp expected actual
     ++'
      +
       test_expect_success 'multiple -m' '
       


 Documentation/git-commit.txt |  14 +-
 builtin/commit.c             |  22 +++
 t/t7502-commit-porcelain.sh  | 312 +++++++++++++++++++++++++++++++++++
 3 files changed, 347 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 17150fa7eabe..3fe7ef33cb07 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -14,7 +14,8 @@ SYNOPSIS
 	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
 	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
 	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
-	   [-S[<keyid>]] [--] [<pathspec>...]
+	   [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]
+	   [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -166,6 +167,17 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
 
 include::signoff-option.txt[]
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
+	<committer@example.com>" --trailer "Helped-by:C O Mitter \
+	<committer@example.com>"` will add the "Signed-off-by" trailer
+	and the "Helped-by" trailer to the commit message.)
+	The `trailer.*` configuration variables
+	(linkgit:git-interpret-trailers[1]) can be used to define if
+	a duplicated trailer is omitted, where in the run of trailers
+	each trailer would appear, and other details.
+
 -n::
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
diff --git a/builtin/commit.c b/builtin/commit.c
index 739110c5a7f6..4b06672bd07d 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,6 +113,7 @@ static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit, *pathspec_from_file;
+static struct strvec trailer_args = STRVEC_INIT;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -131,6 +132,14 @@ static struct strbuf message = STRBUF_INIT;
 
 static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
 
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+
+	strvec_pushl(&trailer_args, "--trailer", arg, NULL);
+	return 0;
+}
+
 static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
 {
 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -958,6 +967,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
 	fclose(s->fp);
 
+	if (trailer_args.nr) {
+		struct child_process run_trailer = CHILD_PROCESS_INIT;
+
+		strvec_pushl(&run_trailer.args, "interpret-trailers",
+			     "--in-place", git_path_commit_editmsg(), NULL);
+		strvec_pushv(&run_trailer.args, trailer_args.v);
+		run_trailer.git_cmd = 1;
+		if (run_command(&run_trailer))
+			die(_("unable to pass trailers to --trailers"));
+		strvec_clear(&trailer_args);
+	}
+
 	/*
 	 * Reject an attempt to record a non-merge empty commit without
 	 * explicit --allow-empty. In the cherry-pick case, it may be
@@ -1507,6 +1528,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 6396897cc818..dd044fcea31c 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -38,6 +38,16 @@ check_summary_oneline() {
 	test_cmp exp act
 }
 
+trailer_commit_base () {
+	echo "fun" >>file &&
+	git add file &&
+	git commit -s --trailer "Signed-off-by=C1 E1 " \
+		--trailer "Helped-by:C2 E2 " \
+		--trailer "Reported-by=C3 E3" \
+		--trailer "Mentored-by:C4 E4" \
+		-m "hello"
+}
+
 test_expect_success 'output summary format' '
 
 	echo new >file1 &&
@@ -154,6 +164,308 @@ test_expect_success 'sign off' '
 
 '
 
+test_expect_success 'commit --trailer with "="' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "replace" as ifexists' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	EOF
+	git -c trailer.ifexists="replace" \
+		commit --trailer "Mentored-by: C4 E4" \
+		 --trailer "Helped-by: C3 E3" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d"  commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "add" as ifexists' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git -c trailer.ifexists="add" \
+		commit --trailer "Reported-by: C3 E3" \
+		--trailer "Mentored-by: C4 E4" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d"  commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "donothing" as ifexists' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Reviewed-by: C6 E6
+	EOF
+	git -c trailer.ifexists="donothing" \
+		commit --trailer "Mentored-by: C5 E5" \
+		--trailer "Reviewed-by: C6 E6" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d"  commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "addIfDifferent" as ifexists' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Mentored-by: C5 E5
+	EOF
+	git -c trailer.ifexists="addIfDifferent" \
+		commit --trailer "Reported-by: C3 E3" \
+		--trailer "Mentored-by: C5 E5" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d"  commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "addIfDifferentNeighbor" as ifexists' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Reported-by: C3 E3
+	EOF
+	git -c trailer.ifexists="addIfDifferentNeighbor" \
+		commit --trailer "Mentored-by: C4 E4" \
+		--trailer "Reported-by: C3 E3" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d"  commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "end" as where' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git -c trailer.where="end" \
+		commit --trailer "Reported-by: C3 E3" \
+		--trailer "Mentored-by: C4 E4" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "start" as where' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C1 E1
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git -c trailer.where="start" \
+		commit --trailer "Signed-off-by: C O Mitter <committer@example.com>" \
+		--trailer "Signed-off-by: C1 E1" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "after" as where' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Mentored-by: C5 E5
+	EOF
+	git -c trailer.where="after" \
+		commit --trailer "Mentored-by: C4 E4" \
+		--trailer "Mentored-by: C5 E5" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "before" as where' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C2 E2
+	Mentored-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git -c trailer.where="before" \
+		commit --trailer "Mentored-by: C3 E3" \
+		--trailer "Mentored-by: C2 E2" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "donothing" as ifmissing' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C5 E5
+	EOF
+	git -c trailer.ifmissing="donothing" \
+		commit --trailer "Helped-by: C5 E5" \
+		--trailer "Based-by: C6 E6" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "add" as ifmissing' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C5 E5
+	Based-by: C6 E6
+	EOF
+	git -c trailer.ifmissing="add" \
+		commit --trailer "Helped-by: C5 E5" \
+		--trailer "Based-by: C6 E6" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c ack.key ' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+		hello
+
+		Acked-by: Peff
+	EOF
+	git -c trailer.ack.key="Acked-by" \
+		commit --trailer "ack = Peff" -m "hello" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and ":=#" as separators' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+		I hate bug
+
+		Bug #42
+	EOF
+	git -c trailer.separators=":=#" \
+		-c trailer.bug.key="Bug #" \
+		commit --trailer "bug = 42" -m "I hate bug" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and command' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Mentored-by: C4 E4
+	Reported-by: A U Thor <author@example.com>
+	EOF
+	git -c trailer.report.key="Reported-by: " \
+		-c trailer.report.ifexists="replace" \
+		-c trailer.report.command="git log --author=\"\$ARG\" -1 \
+		--format=\"format:%aN <%aE>\"" \
+		commit --trailer "report = author" --amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&

base-commit: 13d7ab6b5d7929825b626f050b62a11241ea4945
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v13] [GSOC] commit: add --trailer option
  2021-03-22  4:24                       ` [PATCH v13] " ZheNing Hu via GitGitGadget
@ 2021-03-22  7:43                         ` Christian Couder
  2021-03-22 10:23                           ` ZheNing Hu
  2021-03-23 13:55                         ` [PATCH v14] " ZheNing Hu via GitGitGadget
  1 sibling, 1 reply; 84+ messages in thread
From: Christian Couder @ 2021-03-22  7:43 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget
  Cc: git, Bradley M. Kuhn, Junio C Hamano, Brandon Casey,
	Shourya Shukla, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Ævar Arnfjörð Bjarmason, ZheNing Hu

On Mon, Mar 22, 2021 at 5:24 AM ZheNing Hu via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> From: ZheNing Hu <adlternative@gmail.com>
>
> Historically, Git has supported the 'Signed-off-by' commit trailer
> using the '--signoff' and the '-s' option from the command line.
> But users may need to provide other trailer information from the
> command line such as "Helped-by", "Reported-by", "Mentored-by",
>
> Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
> other trailers to `interpret-trailers` and insert them into commit
> messages.
>
> Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> Reported-by:

Why is there this "Reported-by:" trailer with an empty value? If you
are looking to add trailers to this commit message, you might want to
add them before your "Signed-off-by".

> ---
>     [GSOC] commit: add --trailer option
>
>     Now maintainers or developers can also use commit
>     --trailer="Signed-off-by:commiter<email>" from the command line to
>     provide trailers to commit messages. This solution may be more
>     generalized than v1.

It's not a big deal as this is not going into the commit message, but
at this point (v13) you might want to tell explicitly what solution v1
implemented, instead of referring to it.

>      @@ t/t7502-commit-porcelain.sh: test_expect_success 'sign off' '>       + sed -e "1,/^\$/d" commit.msg >actual &&
>       + test_cmp expected actual
>       +'
>      ++
>      ++test_expect_success 'commit --trailer with -c and command' '
>      ++ trailer_commit_base &&
>      ++ cat >expected <<-\EOF &&
>      ++ hello
>      ++
>      ++ Signed-off-by: C O Mitter <committer@example.com>
>      ++ Signed-off-by: C1 E1
>      ++ Helped-by: C2 E2
>      ++ Mentored-by: C4 E4
>      ++ Reported-by: A U Thor <author@example.com>
>      ++ EOF
>      ++ git -c trailer.report.key="Reported-by: " \
>      ++         -c trailer.report.ifexists="replace" \
>      ++         -c trailer.report.command="git log --author=\"\$ARG\" -1 \
>      ++         --format=\"format:%aN <%aE>\"" \
>      ++         commit --trailer "report = author" --amend &&
>      ++ git cat-file commit HEAD >commit.msg &&
>      ++ sed -e "1,/^\$/d" commit.msg >actual &&
>      ++ test_cmp expected actual
>      ++'

Nice that you have added such a test!

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v13] [GSOC] commit: add --trailer option
  2021-03-22  7:43                         ` Christian Couder
@ 2021-03-22 10:23                           ` ZheNing Hu
  2021-03-22 21:34                             ` Christian Couder
  2021-03-22 21:55                             ` Christian Couder
  0 siblings, 2 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-22 10:23 UTC (permalink / raw)
  To: Christian Couder
  Cc: ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Ævar Arnfjörð Bjarmason

Christian Couder <christian.couder@gmail.com> 于2021年3月22日周一 下午3:43写道:
>
> On Mon, Mar 22, 2021 at 5:24 AM ZheNing Hu via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
> >
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > Historically, Git has supported the 'Signed-off-by' commit trailer
> > using the '--signoff' and the '-s' option from the command line.
> > But users may need to provide other trailer information from the
> > command line such as "Helped-by", "Reported-by", "Mentored-by",
> >
> > Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
> > other trailers to `interpret-trailers` and insert them into commit
> > messages.
> >
> > Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> > Reported-by:
>
> Why is there this "Reported-by:" trailer with an empty value? If you
> are looking to add trailers to this commit message, you might want to
> add them before your "Signed-off-by".
>

Sorry, It was purely a small accident during testing and I didn't notice it.

> > ---
> >     [GSOC] commit: add --trailer option
> >
> >     Now maintainers or developers can also use commit
> >     --trailer="Signed-off-by:commiter<email>" from the command line to
> >     provide trailers to commit messages. This solution may be more
> >     generalized than v1.
>
> It's not a big deal as this is not going into the commit message, but
> at this point (v13) you might want to tell explicitly what solution v1
> implemented, instead of referring to it.

Ok.

>
> >      @@ t/t7502-commit-porcelain.sh: test_expect_success 'sign off' '>       + sed -e "1,/^\$/d" commit.msg >actual &&
> >       + test_cmp expected actual
> >       +'
> >      ++
> >      ++test_expect_success 'commit --trailer with -c and command' '
> >      ++ trailer_commit_base &&
> >      ++ cat >expected <<-\EOF &&
> >      ++ hello
> >      ++
> >      ++ Signed-off-by: C O Mitter <committer@example.com>
> >      ++ Signed-off-by: C1 E1
> >      ++ Helped-by: C2 E2
> >      ++ Mentored-by: C4 E4
> >      ++ Reported-by: A U Thor <author@example.com>
> >      ++ EOF
> >      ++ git -c trailer.report.key="Reported-by: " \
> >      ++         -c trailer.report.ifexists="replace" \
> >      ++         -c trailer.report.command="git log --author=\"\$ARG\" -1 \
> >      ++         --format=\"format:%aN <%aE>\"" \
> >      ++         commit --trailer "report = author" --amend &&
> >      ++ git cat-file commit HEAD >commit.msg &&
> >      ++ sed -e "1,/^\$/d" commit.msg >actual &&
> >      ++ test_cmp expected actual
> >      ++'
>
> Nice that you have added such a test!

Thanks.

But at the same time I have two little doubt.

1.
If we have your config:

$ git config trailer.sign.key "Signed-off-by: "
$ git config trailer.sign.ifexists replace
$ git config trailer.sign.command "git log --author='\$ARG' -1
--format='format:%aN <%aE>'"

Then I touch a test.c and use:

$ git interpret-trailers --in-place test.c

without `--trailer`, See what is happen:

It seem like your local repo last commit "name <email>" pair
have been record in `test.c`.

Could this be considered a bug?

2.
`git interpret-trailers --in-place`  seem like work on git top-dir,
If I am in a sub-dir `b` and I want to change a file such as `d.c`,
then I must use `git interpret-trailers --in-place b/d.c` to add some
trailers.

I think the original intention of `--in-place` is to modify a file similar to
"$COMMIT_MSG_FILE", so make it run at top-dir, but this is not reflected
in the git documentation. This at least confuses people who use this
option for the first time. Is it worth modifying? Or is there something
wrong with the design of `--in-place`?

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v13] [GSOC] commit: add --trailer option
  2021-03-22 10:23                           ` ZheNing Hu
@ 2021-03-22 21:34                             ` Christian Couder
  2021-03-23  6:11                               ` ZheNing Hu
  2021-03-23  6:19                               ` Junio C Hamano
  2021-03-22 21:55                             ` Christian Couder
  1 sibling, 2 replies; 84+ messages in thread
From: Christian Couder @ 2021-03-22 21:34 UTC (permalink / raw)
  To: ZheNing Hu
  Cc: ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Ævar Arnfjörð Bjarmason

On Mon, Mar 22, 2021 at 11:23 AM ZheNing Hu <adlternative@gmail.com> wrote:
>
> Christian Couder <christian.couder@gmail.com> 于2021年3月22日周一 下午3:43写道:

> > Nice that you have added such a test!
>
> Thanks.
>
> But at the same time I have two little doubt.
>
> 1.
> If we have your config:
>
> $ git config trailer.sign.key "Signed-off-by: "
> $ git config trailer.sign.ifexists replace
> $ git config trailer.sign.command "git log --author='\$ARG' -1
> --format='format:%aN <%aE>'"
>
> Then I touch a test.c and use:
>
> $ git interpret-trailers --in-place test.c
>
> without `--trailer`, See what is happen:
>
> It seem like your local repo last commit "name <email>" pair
> have been record in `test.c`.
>
> Could this be considered a bug?

First it seems strange to use `git interpret-trailers` on a "test.c"
file. It's supposed to be used on commit messages.

Then, as the doc says, every command specified by any
"trailer.<token>.command" config option is run at least once when `git
interpret-trailers` is run. This is because users might want to
automatically add some trailers all the time.

If you want nothing to happen when $ARG isn't set, you can change the
config option to something like:

$ git config trailer.sign.command "NAME='\$ARG'; test -n \"\$NAME\" &&
git log --author=\"\$NAME\" -1 --format='format:%aN <%aE>' || true"

(This is because it looks like $ARG is replaced only once with the
actual value, which is perhaps a bug. Otherwise something like the
following might work:

git config trailer.sign.command "test -n '\$ARG' && git log
--author='\$ARG' -1 --format='format:%aN <%aE>' || true")

Then you can run `git interpret-trailers` with the --trim-empty option
like this:

------
$ git interpret-trailers --trim-empty --trailer sign=Linus<<EOF
EOF

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
------

or like:

------
$ git interpret-trailers --trim-empty<<EOF
> EOF

------

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v13] [GSOC] commit: add --trailer option
  2021-03-22 10:23                           ` ZheNing Hu
  2021-03-22 21:34                             ` Christian Couder
@ 2021-03-22 21:55                             ` Christian Couder
  2021-03-23  6:29                               ` ZheNing Hu
  1 sibling, 1 reply; 84+ messages in thread
From: Christian Couder @ 2021-03-22 21:55 UTC (permalink / raw)
  To: ZheNing Hu
  Cc: ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Ævar Arnfjörð Bjarmason

On Mon, Mar 22, 2021 at 11:23 AM ZheNing Hu <adlternative@gmail.com> wrote:

> 2.
> `git interpret-trailers --in-place`  seem like work on git top-dir,
> If I am in a sub-dir `b` and I want to change a file such as `d.c`,
> then I must use `git interpret-trailers --in-place b/d.c` to add some
> trailers.

What happens without --in-place? Are the input files read correctly?

> I think the original intention of `--in-place` is to modify a file similar to
> "$COMMIT_MSG_FILE", so make it run at top-dir, but this is not reflected
> in the git documentation. This at least confuses people who use this
> option for the first time. Is it worth modifying? Or is there something
> wrong with the design of `--in-place`?

I haven't checked but there is perhaps a bug in
create_in_place_tempfile() in "trailer.c".

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v13] [GSOC] commit: add --trailer option
  2021-03-22 21:34                             ` Christian Couder
@ 2021-03-23  6:11                               ` ZheNing Hu
  2021-03-23  6:19                               ` Junio C Hamano
  1 sibling, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-23  6:11 UTC (permalink / raw)
  To: Christian Couder
  Cc: ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Ævar Arnfjörð Bjarmason

Christian Couder <christian.couder@gmail.com> 于2021年3月23日周二 上午5:34写道:
>
> On Mon, Mar 22, 2021 at 11:23 AM ZheNing Hu <adlternative@gmail.com> wrote:
> >
> > Christian Couder <christian.couder@gmail.com> 于2021年3月22日周一 下午3:43写道:
>
> > > Nice that you have added such a test!
> >
> > Thanks.
> >
> > But at the same time I have two little doubt.
> >
> > 1.
> > If we have your config:
> >
> > $ git config trailer.sign.key "Signed-off-by: "
> > $ git config trailer.sign.ifexists replace
> > $ git config trailer.sign.command "git log --author='\$ARG' -1
> > --format='format:%aN <%aE>'"
> >
> > Then I touch a test.c and use:
> >
> > $ git interpret-trailers --in-place test.c
> >
> > without `--trailer`, See what is happen:
> >
> > It seem like your local repo last commit "name <email>" pair
> > have been record in `test.c`.
> >
> > Could this be considered a bug?
>
> First it seems strange to use `git interpret-trailers` on a "test.c"
> file. It's supposed to be used on commit messages.
>
> Then, as the doc says, every command specified by any
> "trailer.<token>.command" config option is run at least once when `git
> interpret-trailers` is run. This is because users might want to
> automatically add some trailers all the time.
>

Well, I understand it now.

> If you want nothing to happen when $ARG isn't set, you can change the
> config option to something like:
>
> $ git config trailer.sign.command "NAME='\$ARG'; test -n \"\$NAME\" &&
> git log --author=\"\$NAME\" -1 --format='format:%aN <%aE>' || true"
>
> (This is because it looks like $ARG is replaced only once with the
> actual value, which is perhaps a bug. Otherwise something like the
> following might work:

this is because `$ARG` is replaced in "trailer.c" by "strbuf_replace" which
only replcae the specified string for only one time,
I think `strbuf_replace()` can be changed like:

@@ -110,8 +110,9 @@ static inline int is_blank_line(const char *str)

 static inline void strbuf_replace(struct strbuf *sb, const char *a,
const char *b)
 {
-       const char *ptr = strstr(sb->buf, a);
-       if (ptr)
+       char *ptr = sb->buf;
+
+       while ((ptr = strstr(ptr, a)))
                strbuf_splice(sb, ptr - sb->buf, strlen(a), b, strlen(b));
 }

>
> git config trailer.sign.command "test -n '\$ARG' && git log
> --author='\$ARG' -1 --format='format:%aN <%aE>' || true")
>
> Then you can run `git interpret-trailers` with the --trim-empty option
> like this:
>
> ------
> $ git interpret-trailers --trim-empty --trailer sign=Linus<<EOF
> EOF
>
> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
> ------
>
> or like:
>
> ------
> $ git interpret-trailers --trim-empty<<EOF
> > EOF
>
> ------

Thanks for effective solution.
Maybe I should also correct this part of the test.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v13] [GSOC] commit: add --trailer option
  2021-03-22 21:34                             ` Christian Couder
  2021-03-23  6:11                               ` ZheNing Hu
@ 2021-03-23  6:19                               ` Junio C Hamano
  2021-03-23  7:57                                 ` Christian Couder
  2021-03-23 10:35                                 ` ZheNing Hu
  1 sibling, 2 replies; 84+ messages in thread
From: Junio C Hamano @ 2021-03-23  6:19 UTC (permalink / raw)
  To: Christian Couder
  Cc: ZheNing Hu, ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Brandon Casey, Shourya Shukla, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Ævar Arnfjörð Bjarmason

Christian Couder <christian.couder@gmail.com> writes:

> If you want nothing to happen when $ARG isn't set, you can change the
> config option to something like:
>
> $ git config trailer.sign.command "NAME='\$ARG'; test -n \"\$NAME\" &&
> git log --author=\"\$NAME\" -1 --format='format:%aN <%aE>' || true"
>
> (This is because it looks like $ARG is replaced only once with the
> actual value, which is perhaps a bug. Otherwise something like the
> following might work:

I do not know the origin of that code in trailers.c but it feels
quite confused and error prone to use textual replacement with
strbuf_replace().  Why doesn't the code, which knows it will use
shell to execute the command line given by the end user in the
configuration, to just export ARG as an environment variable and
be done with it?  It would also avoid quoting problem etc.


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v13] [GSOC] commit: add --trailer option
  2021-03-22 21:55                             ` Christian Couder
@ 2021-03-23  6:29                               ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-23  6:29 UTC (permalink / raw)
  To: Christian Couder
  Cc: ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Junio C Hamano, Brandon Casey, Shourya Shukla, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Ævar Arnfjörð Bjarmason

Christian Couder <christian.couder@gmail.com> 于2021年3月23日周二 上午5:55写道:
>
> On Mon, Mar 22, 2021 at 11:23 AM ZheNing Hu <adlternative@gmail.com> wrote:
>
> > 2.
> > `git interpret-trailers --in-place`  seem like work on git top-dir,
> > If I am in a sub-dir `b` and I want to change a file such as `d.c`,
> > then I must use `git interpret-trailers --in-place b/d.c` to add some
> > trailers.
>
> What happens without --in-place? Are the input files read correctly?

It's still wrong.
the git die() in `read_input_file` of "trailer.c".

>
> > I think the original intention of `--in-place` is to modify a file similar to
> > "$COMMIT_MSG_FILE", so make it run at top-dir, but this is not reflected
> > in the git documentation. This at least confuses people who use this
> > option for the first time. Is it worth modifying? Or is there something
> > wrong with the design of `--in-place`?
>
> I haven't checked but there is perhaps a bug in
> create_in_place_tempfile() in "trailer.c".

I haven't check finished. But I think it may do something like `chdir()`.

Thanks.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v13] [GSOC] commit: add --trailer option
  2021-03-23  6:19                               ` Junio C Hamano
@ 2021-03-23  7:57                                 ` Christian Couder
  2021-03-23 17:11                                   ` Junio C Hamano
  2021-03-23 10:35                                 ` ZheNing Hu
  1 sibling, 1 reply; 84+ messages in thread
From: Christian Couder @ 2021-03-23  7:57 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: ZheNing Hu, ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Brandon Casey, Shourya Shukla, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Ævar Arnfjörð Bjarmason

On Tue, Mar 23, 2021 at 7:19 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Christian Couder <christian.couder@gmail.com> writes:
>
> > If you want nothing to happen when $ARG isn't set, you can change the
> > config option to something like:
> >
> > $ git config trailer.sign.command "NAME='\$ARG'; test -n \"\$NAME\" &&
> > git log --author=\"\$NAME\" -1 --format='format:%aN <%aE>' || true"
> >
> > (This is because it looks like $ARG is replaced only once with the
> > actual value, which is perhaps a bug. Otherwise something like the
> > following might work:
>
> I do not know the origin of that code in trailers.c but it feels
> quite confused and error prone to use textual replacement with
> strbuf_replace().  Why doesn't the code, which knows it will use
> shell to execute the command line given by the end user in the
> configuration, to just export ARG as an environment variable and
> be done with it?  It would also avoid quoting problem etc.

Yeah, I agree that would be better.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v13] [GSOC] commit: add --trailer option
  2021-03-23  6:19                               ` Junio C Hamano
  2021-03-23  7:57                                 ` Christian Couder
@ 2021-03-23 10:35                                 ` ZheNing Hu
  2021-03-23 12:41                                   ` Christian Couder
  2021-03-23 17:12                                   ` Junio C Hamano
  1 sibling, 2 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-23 10:35 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Christian Couder, ZheNing Hu via GitGitGadget, git,
	Bradley M. Kuhn, Brandon Casey, Shourya Shukla, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Ævar Arnfjörð Bjarmason

Junio C Hamano <gitster@pobox.com> 于2021年3月23日周二 下午2:19写道:
>
> Christian Couder <christian.couder@gmail.com> writes:
>
> > If you want nothing to happen when $ARG isn't set, you can change the
> > config option to something like:
> >
> > $ git config trailer.sign.command "NAME='\$ARG'; test -n \"\$NAME\" &&
> > git log --author=\"\$NAME\" -1 --format='format:%aN <%aE>' || true"
> >
> > (This is because it looks like $ARG is replaced only once with the
> > actual value, which is perhaps a bug. Otherwise something like the
> > following might work:
>
> I do not know the origin of that code in trailers.c but it feels
> quite confused and error prone to use textual replacement with
> strbuf_replace().  Why doesn't the code, which knows it will use
> shell to execute the command line given by the end user in the
> configuration, to just export ARG as an environment variable and
> be done with it?  It would also avoid quoting problem etc.
>

Maybe like this?

-#define TRAILER_ARG_STRING "$ARG"
-
static const char *git_generated_prefixes[] = {
       "Signed-off-by: ",
       "(cherry picked from commit ",
@@ -222,13 +220,17 @@ static char *apply_command(const char *command,
const char *arg)
       struct strbuf buf = STRBUF_INIT;
       struct child_process cp = CHILD_PROCESS_INIT;
       char *result;
+       const char *const *var;

       strbuf_addstr(&cmd, command);
-       if (arg)
-               strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
+       for (var = local_repo_env; *var; var++)
+               strvec_push(&cp.env_array, *var);
+       if (arg) {
+               strvec_pushf(&cp.env_array, "ARG=%s", arg);
+       }

       strvec_push(&cp.args, cmd.buf);
-       cp.env = local_repo_env;
+
       cp.no_stdin = 1;
       cp.use_shell = 1;

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v13] [GSOC] commit: add --trailer option
  2021-03-23 10:35                                 ` ZheNing Hu
@ 2021-03-23 12:41                                   ` Christian Couder
  2021-03-23 17:12                                   ` Junio C Hamano
  1 sibling, 0 replies; 84+ messages in thread
From: Christian Couder @ 2021-03-23 12:41 UTC (permalink / raw)
  To: ZheNing Hu
  Cc: Junio C Hamano, ZheNing Hu via GitGitGadget, git,
	Bradley M. Kuhn, Brandon Casey, Shourya Shukla, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Ævar Arnfjörð Bjarmason

On Tue, Mar 23, 2021 at 11:35 AM ZheNing Hu <adlternative@gmail.com> wrote:
>
> Junio C Hamano <gitster@pobox.com> 于2021年3月23日周二 下午2:19写道:

> Maybe like this?

Yeah, it's a good idea to work on this, but please make it an
independent patch unrelated to adding --trailer to `git commit`.

Also the documentation might need a bit of tweeking to tell that ARG
is now an exported environment variable.

> -#define TRAILER_ARG_STRING "$ARG"

It might still be better to use a #define or a const char [] to avoid
hard coding "ARG" below.

> static const char *git_generated_prefixes[] = {
>        "Signed-off-by: ",
>        "(cherry picked from commit ",
> @@ -222,13 +220,17 @@ static char *apply_command(const char *command,
> const char *arg)
>        struct strbuf buf = STRBUF_INIT;
>        struct child_process cp = CHILD_PROCESS_INIT;
>        char *result;
> +       const char *const *var;
>
>        strbuf_addstr(&cmd, command);
> -       if (arg)
> -               strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
> +       for (var = local_repo_env; *var; var++)
> +               strvec_push(&cp.env_array, *var);
> +       if (arg) {
> +               strvec_pushf(&cp.env_array, "ARG=%s", arg);
> +       }

You can drop the "{" and "}" above.

>        strvec_push(&cp.args, cmd.buf);
> -       cp.env = local_repo_env;
> +
>        cp.no_stdin = 1;
>        cp.use_shell = 1;

Thanks!

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v14] [GSOC] commit: add --trailer option
  2021-03-22  4:24                       ` [PATCH v13] " ZheNing Hu via GitGitGadget
  2021-03-22  7:43                         ` Christian Couder
@ 2021-03-23 13:55                         ` ZheNing Hu via GitGitGadget
  1 sibling, 0 replies; 84+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-23 13:55 UTC (permalink / raw)
  To: git
  Cc: Bradley M. Kuhn, Junio C Hamano, Brandon Casey, Shourya Shukla,
	Christian Couder, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Junio C Hamano, Ævar Arnfjörð Bjarmason,
	ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Historically, Git has supported the 'Signed-off-by' commit trailer
using the '--signoff' and the '-s' option from the command line.
But users may need to provide other trailer information from the
command line such as "Helped-by", "Reported-by", "Mentored-by",

Now implement a new `--trailer <token>[(=|:)<value>]` option to pass
other trailers to `interpret-trailers` and insert them into commit
messages.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC] commit: add --trailer option
    
    commit --trailer connecting to the interpret-trailers backend, which can
    directly generate trailers similar to "Signed-off-by". Hope this will
    make it easier for programmers and maintainers :)

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-901%2Fadlternative%2Fcommit-with-multiple-signatures-v14
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-901/adlternative/commit-with-multiple-signatures-v14
Pull-Request: https://github.com/gitgitgadget/git/pull/901

Range-diff vs v13:

 1:  98b0c470e141 ! 1:  bb36af7c4827 [GSOC] commit: add --trailer option
     @@ Commit message
          messages.
      
          Signed-off-by: ZheNing Hu <adlternative@gmail.com>
     -    Reported-by:
      
       ## Documentation/git-commit.txt ##
      @@ Documentation/git-commit.txt: SYNOPSIS
     @@ t/t7502-commit-porcelain.sh: test_expect_success 'sign off' '
      +	EOF
      +	git -c trailer.report.key="Reported-by: " \
      +		-c trailer.report.ifexists="replace" \
     -+		-c trailer.report.command="git log --author=\"\$ARG\" -1 \
     -+		--format=\"format:%aN <%aE>\"" \
     ++		-c trailer.report.command="NAME=\"\$ARG\"; test -n \"\$NAME\" && \
     ++		git log --author=\"\$NAME\" -1 --format=\"format:%aN <%aE>\" || true" \
      +		commit --trailer "report = author" --amend &&
      +	git cat-file commit HEAD >commit.msg &&
      +	sed -e "1,/^\$/d" commit.msg >actual &&


 Documentation/git-commit.txt |  14 +-
 builtin/commit.c             |  22 +++
 t/t7502-commit-porcelain.sh  | 312 +++++++++++++++++++++++++++++++++++
 3 files changed, 347 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 17150fa7eabe..3fe7ef33cb07 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -14,7 +14,8 @@ SYNOPSIS
 	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
 	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
 	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
-	   [-S[<keyid>]] [--] [<pathspec>...]
+	   [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]
+	   [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -166,6 +167,17 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
 
 include::signoff-option.txt[]
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g. `git commit --trailer "Signed-off-by:C O Mitter \
+	<committer@example.com>" --trailer "Helped-by:C O Mitter \
+	<committer@example.com>"` will add the "Signed-off-by" trailer
+	and the "Helped-by" trailer to the commit message.)
+	The `trailer.*` configuration variables
+	(linkgit:git-interpret-trailers[1]) can be used to define if
+	a duplicated trailer is omitted, where in the run of trailers
+	each trailer would appear, and other details.
+
 -n::
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
diff --git a/builtin/commit.c b/builtin/commit.c
index 739110c5a7f6..4b06672bd07d 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,6 +113,7 @@ static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit, *pathspec_from_file;
+static struct strvec trailer_args = STRVEC_INIT;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -131,6 +132,14 @@ static struct strbuf message = STRBUF_INIT;
 
 static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
 
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+
+	strvec_pushl(&trailer_args, "--trailer", arg, NULL);
+	return 0;
+}
+
 static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
 {
 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -958,6 +967,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
 	fclose(s->fp);
 
+	if (trailer_args.nr) {
+		struct child_process run_trailer = CHILD_PROCESS_INIT;
+
+		strvec_pushl(&run_trailer.args, "interpret-trailers",
+			     "--in-place", git_path_commit_editmsg(), NULL);
+		strvec_pushv(&run_trailer.args, trailer_args.v);
+		run_trailer.git_cmd = 1;
+		if (run_command(&run_trailer))
+			die(_("unable to pass trailers to --trailers"));
+		strvec_clear(&trailer_args);
+	}
+
 	/*
 	 * Reject an attempt to record a non-merge empty commit without
 	 * explicit --allow-empty. In the cherry-pick case, it may be
@@ -1507,6 +1528,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup 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_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 6396897cc818..38a532d81ccd 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -38,6 +38,16 @@ check_summary_oneline() {
 	test_cmp exp act
 }
 
+trailer_commit_base () {
+	echo "fun" >>file &&
+	git add file &&
+	git commit -s --trailer "Signed-off-by=C1 E1 " \
+		--trailer "Helped-by:C2 E2 " \
+		--trailer "Reported-by=C3 E3" \
+		--trailer "Mentored-by:C4 E4" \
+		-m "hello"
+}
+
 test_expect_success 'output summary format' '
 
 	echo new >file1 &&
@@ -154,6 +164,308 @@ test_expect_success 'sign off' '
 
 '
 
+test_expect_success 'commit --trailer with "="' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "replace" as ifexists' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C3 E3
+	EOF
+	git -c trailer.ifexists="replace" \
+		commit --trailer "Mentored-by: C4 E4" \
+		 --trailer "Helped-by: C3 E3" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d"  commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "add" as ifexists' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git -c trailer.ifexists="add" \
+		commit --trailer "Reported-by: C3 E3" \
+		--trailer "Mentored-by: C4 E4" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d"  commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "donothing" as ifexists' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Reviewed-by: C6 E6
+	EOF
+	git -c trailer.ifexists="donothing" \
+		commit --trailer "Mentored-by: C5 E5" \
+		--trailer "Reviewed-by: C6 E6" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d"  commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "addIfDifferent" as ifexists' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Mentored-by: C5 E5
+	EOF
+	git -c trailer.ifexists="addIfDifferent" \
+		commit --trailer "Reported-by: C3 E3" \
+		--trailer "Mentored-by: C5 E5" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d"  commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "addIfDifferentNeighbor" as ifexists' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Reported-by: C3 E3
+	EOF
+	git -c trailer.ifexists="addIfDifferentNeighbor" \
+		commit --trailer "Mentored-by: C4 E4" \
+		--trailer "Reported-by: C3 E3" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d"  commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "end" as where' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git -c trailer.where="end" \
+		commit --trailer "Reported-by: C3 E3" \
+		--trailer "Mentored-by: C4 E4" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "start" as where' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C1 E1
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git -c trailer.where="start" \
+		commit --trailer "Signed-off-by: C O Mitter <committer@example.com>" \
+		--trailer "Signed-off-by: C1 E1" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "after" as where' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Mentored-by: C5 E5
+	EOF
+	git -c trailer.where="after" \
+		commit --trailer "Mentored-by: C4 E4" \
+		--trailer "Mentored-by: C5 E5" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "before" as where' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C2 E2
+	Mentored-by: C3 E3
+	Mentored-by: C4 E4
+	EOF
+	git -c trailer.where="before" \
+		commit --trailer "Mentored-by: C3 E3" \
+		--trailer "Mentored-by: C2 E2" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "donothing" as ifmissing' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C5 E5
+	EOF
+	git -c trailer.ifmissing="donothing" \
+		commit --trailer "Helped-by: C5 E5" \
+		--trailer "Based-by: C6 E6" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and "add" as ifmissing' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Reported-by: C3 E3
+	Mentored-by: C4 E4
+	Helped-by: C5 E5
+	Based-by: C6 E6
+	EOF
+	git -c trailer.ifmissing="add" \
+		commit --trailer "Helped-by: C5 E5" \
+		--trailer "Based-by: C6 E6" \
+		--amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c ack.key ' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+		hello
+
+		Acked-by: Peff
+	EOF
+	git -c trailer.ack.key="Acked-by" \
+		commit --trailer "ack = Peff" -m "hello" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and ":=#" as separators' '
+	echo "fun" >>file1 &&
+	git add file1 &&
+	cat >expected <<-\EOF &&
+		I hate bug
+
+		Bug #42
+	EOF
+	git -c trailer.separators=":=#" \
+		-c trailer.bug.key="Bug #" \
+		commit --trailer "bug = 42" -m "I hate bug" &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with -c and command' '
+	trailer_commit_base &&
+	cat >expected <<-\EOF &&
+	hello
+
+	Signed-off-by: C O Mitter <committer@example.com>
+	Signed-off-by: C1 E1
+	Helped-by: C2 E2
+	Mentored-by: C4 E4
+	Reported-by: A U Thor <author@example.com>
+	EOF
+	git -c trailer.report.key="Reported-by: " \
+		-c trailer.report.ifexists="replace" \
+		-c trailer.report.command="NAME=\"\$ARG\"; test -n \"\$NAME\" && \
+		git log --author=\"\$NAME\" -1 --format=\"format:%aN <%aE>\" || true" \
+		commit --trailer "report = author" --amend &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&

base-commit: 13d7ab6b5d7929825b626f050b62a11241ea4945
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v13] [GSOC] commit: add --trailer option
  2021-03-23  7:57                                 ` Christian Couder
@ 2021-03-23 17:11                                   ` Junio C Hamano
  2021-03-24  5:21                                     ` ZheNing Hu
  0 siblings, 1 reply; 84+ messages in thread
From: Junio C Hamano @ 2021-03-23 17:11 UTC (permalink / raw)
  To: Christian Couder
  Cc: ZheNing Hu, ZheNing Hu via GitGitGadget, git, Bradley M. Kuhn,
	Brandon Casey, Shourya Shukla, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Ævar Arnfjörð Bjarmason

Christian Couder <christian.couder@gmail.com> writes:

> On Tue, Mar 23, 2021 at 7:19 AM Junio C Hamano <gitster@pobox.com> wrote:
>>
>> Christian Couder <christian.couder@gmail.com> writes:
>>
>> > If you want nothing to happen when $ARG isn't set, you can change the
>> > config option to something like:
>> >
>> > $ git config trailer.sign.command "NAME='\$ARG'; test -n \"\$NAME\" &&
>> > git log --author=\"\$NAME\" -1 --format='format:%aN <%aE>' || true"
>> >
>> > (This is because it looks like $ARG is replaced only once with the
>> > actual value, which is perhaps a bug. Otherwise something like the
>> > following might work:
>>
>> I do not know the origin of that code in trailers.c but it feels
>> quite confused and error prone to use textual replacement with
>> strbuf_replace().  Why doesn't the code, which knows it will use
>> shell to execute the command line given by the end user in the
>> configuration, to just export ARG as an environment variable and
>> be done with it?  It would also avoid quoting problem etc.
>
> Yeah, I agree that would be better.

It probably would have been better to do so before the feature got
unleased to the public, but doing such a change retroactively would
introduce regression for those who were using ARG that happens to be
safe from shell quoting rules.

For example, if the trailer.*.command were

	echo '$ARG'

and the argument 'h e l l o' were to be given to it, then the
current code would have textually expanded $ARG with the argument
and caused

	echo 'h e l l o'

to run, which would have been "fine" [*1*].

But exporting the environment ARG would "break" such a setting that
has been "working perfectly well" for the user.  Because of the
single-quotes around $ARG, the command now will give literal four
letter string $ARG and not 'h e l l o'.

We should think such potential ramifications of changing it (and
also not changing it) through before deciding what to do about it.

Although I have a feeling that not many people would miss '$ARG'
inside a pair of single-quotes to be replaced textually and it would
be OK to make a backward incompatible bugfix, the safer and better
way is not all that difficult, so I am inclined to suggest going the
usual "deprecate and replace and then later remove" dance.

The normal sequence of replacing a "sort of works but not
recommended" feature with a "better and safer, but can break a
setting that has been 'working'" feature is:

 - Announce deprecation of trailer.x.command and add and advertise a
   similar traier.x.cmd that (1) exports environment variable ARG,
   or (2) passes the argument as a positional parameter [*], as a
   replacement.  Explain the reason for deprecation (i.e. unsafe
   substitution that works only once).  When .cmd exists, .command
   is ignored for the corresponding trailer.x

 - Wait for a few releases and then remove trailer.x.command.

and that is the safest way to fix this "bug".


[Footnotes]

*1* If the argument were 

	';rm -rf .;'

    then it wouldn't have been fine, though, and that is how the
    current code solicited "Huh?"  reaction out of me.


*2* If we passed the argument as a positional parameter, the example
    you gave in the quoted part of the message would become
    something like this:

      [trailer "sign"]
        cmd = test -n "$1" && git log --author="$1" -1 --format='%aN <%aE>'

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v13] [GSOC] commit: add --trailer option
  2021-03-23 10:35                                 ` ZheNing Hu
  2021-03-23 12:41                                   ` Christian Couder
@ 2021-03-23 17:12                                   ` Junio C Hamano
  2021-03-24  5:25                                     ` ZheNing Hu
  1 sibling, 1 reply; 84+ messages in thread
From: Junio C Hamano @ 2021-03-23 17:12 UTC (permalink / raw)
  To: ZheNing Hu
  Cc: Christian Couder, ZheNing Hu via GitGitGadget, git,
	Bradley M. Kuhn, Brandon Casey, Shourya Shukla, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Ævar Arnfjörð Bjarmason

ZheNing Hu <adlternative@gmail.com> writes:

> Junio C Hamano <gitster@pobox.com> 于2021年3月23日周二 下午2:19写道:
>>
>> Christian Couder <christian.couder@gmail.com> writes:
>>
>> > If you want nothing to happen when $ARG isn't set, you can change the
>> > config option to something like:
>> >
>> > $ git config trailer.sign.command "NAME='\$ARG'; test -n \"\$NAME\" &&
>> > git log --author=\"\$NAME\" -1 --format='format:%aN <%aE>' || true"
>> >
>> > (This is because it looks like $ARG is replaced only once with the
>> > actual value, which is perhaps a bug. Otherwise something like the
>> > following might work:
>>
>> I do not know the origin of that code in trailers.c but it feels
>> quite confused and error prone to use textual replacement with
>> strbuf_replace().  Why doesn't the code, which knows it will use
>> shell to execute the command line given by the end user in the
>> configuration, to just export ARG as an environment variable and
>> be done with it?  It would also avoid quoting problem etc.
>>
>
> Maybe like this?

Code is not an important part.  We should think through
ramifications of making (and not making) such a change first.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v13] [GSOC] commit: add --trailer option
  2021-03-23 17:11                                   ` Junio C Hamano
@ 2021-03-24  5:21                                     ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-24  5:21 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Christian Couder, ZheNing Hu via GitGitGadget, git,
	Bradley M. Kuhn, Brandon Casey, Shourya Shukla, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Ævar Arnfjörð Bjarmason

>
> It probably would have been better to do so before the feature got
> unleased to the public, but doing such a change retroactively would
> introduce regression for those who were using ARG that happens to be
> safe from shell quoting rules.
>
> For example, if the trailer.*.command were
>
>         echo '$ARG'
>
> and the argument 'h e l l o' were to be given to it, then the
> current code would have textually expanded $ARG with the argument
> and caused
>
>         echo 'h e l l o'
>
> to run, which would have been "fine" [*1*].
>
> But exporting the environment ARG would "break" such a setting that
> has been "working perfectly well" for the user.  Because of the
> single-quotes around $ARG, the command now will give literal four
> letter string $ARG and not 'h e l l o'.
>
> We should think such potential ramifications of changing it (and
> also not changing it) through before deciding what to do about it.
>
> Although I have a feeling that not many people would miss '$ARG'
> inside a pair of single-quotes to be replaced textually and it would
> be OK to make a backward incompatible bugfix, the safer and better
> way is not all that difficult, so I am inclined to suggest going the
> usual "deprecate and replace and then later remove" dance.
>

I think what you mean is that my patch breaks the principle of
"forward compatibility", which may make it impossible for users who
previously worked with `'$ARG'`.

> The normal sequence of replacing a "sort of works but not
> recommended" feature with a "better and safer, but can break a
> setting that has been 'working'" feature is:
>
>  - Announce deprecation of trailer.x.command and add and advertise a
>    similar traier.x.cmd that (1) exports environment variable ARG,
>    or (2) passes the argument as a positional parameter [*], as a
>    replacement.  Explain the reason for deprecation (i.e. unsafe
>    substitution that works only once).  When .cmd exists, .command
>    is ignored for the corresponding trailer.x
>

As your example below:

cmd = test -n "$1" && git log --author="$1" -1 --format='%aN <%aE>'

I would like to use this way.

It may be a bit cumbersome to do "deprecate and replace and
then later remove", Should documents tell users that the old method
"trailer.command" has been replaced by "trialer.cmd"? Or tell users that
"trailer.cmd" is a new feature?

Keep these doubts, I will try go to code it.

>  - Wait for a few releases and then remove trailer.x.command.
>
> and that is the safest way to fix this "bug".
>
>
> [Footnotes]
>
> *1* If the argument were
>
>         ';rm -rf .;'
>
>     then it wouldn't have been fine, though, and that is how the
>     current code solicited "Huh?"  reaction out of me.
>

A little scary. :-)

>
> *2* If we passed the argument as a positional parameter, the example
>     you gave in the quoted part of the message would become
>     something like this:
>
>       [trailer "sign"]
>         cmd = test -n "$1" && git log --author="$1" -1 --format='%aN <%aE>'

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v13] [GSOC] commit: add --trailer option
  2021-03-23 17:12                                   ` Junio C Hamano
@ 2021-03-24  5:25                                     ` ZheNing Hu
  0 siblings, 0 replies; 84+ messages in thread
From: ZheNing Hu @ 2021-03-24  5:25 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Christian Couder, ZheNing Hu via GitGitGadget, git,
	Bradley M. Kuhn, Brandon Casey, Shourya Shukla, Rafael Silva,
	Đoàn Trần Công Danh, Jeff King,
	Ævar Arnfjörð Bjarmason

> > Maybe like this?
>
> Code is not an important part.  We should think through
> ramifications of making (and not making) such a change first.

Sorry for without careful consideration.
Thank for this valuable suggestion.

^ permalink raw reply	[flat|nested] 84+ messages in thread

end of thread, other threads:[~2021-03-24  5:26 UTC | newest]

Thread overview: 84+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-11  7:16 [PATCH] [GSOC] commit: provides multiple common signatures ZheNing Hu via GitGitGadget
2021-03-11 15:03 ` Shourya Shukla
2021-03-12 11:41   ` ZheNing Hu
2021-03-11 17:28 ` Junio C Hamano
2021-03-12 12:01   ` ZheNing Hu
2021-03-12 13:22   ` ZheNing Hu
2021-03-12 15:54 ` [PATCH v2] [GSOC] commit: add trailer command ZheNing Hu via GitGitGadget
2021-03-14  4:19   ` Christian Couder
2021-03-14  7:09     ` ZheNing Hu
2021-03-14 22:45     ` Junio C Hamano
2021-03-14 13:02   ` [PATCH v3] [GSOC] commit: add --trailer option ZheNing Hu via GitGitGadget
2021-03-14 13:10     ` Rafael Silva
2021-03-14 14:13       ` ZheNing Hu
2021-03-14 15:58     ` [PATCH v4] " ZheNing Hu via GitGitGadget
2021-03-14 23:52       ` Junio C Hamano
2021-03-15  1:27         ` ZheNing Hu
2021-03-15  4:42           ` Junio C Hamano
2021-03-15  5:14             ` ZheNing Hu
2021-03-15  3:24       ` [PATCH v5] " ZheNing Hu via GitGitGadget
2021-03-15  5:33         ` Christian Couder
2021-03-15  5:41           ` Christian Couder
2021-03-15  5:46           ` ZheNing Hu
2021-03-15  6:35         ` [PATCH v6] " ZheNing Hu via GitGitGadget
2021-03-15  8:02           ` Christian Couder
2021-03-15  8:21             ` ZheNing Hu
2021-03-15  9:08           ` [PATCH v7] " ZheNing Hu via GitGitGadget
2021-03-15 10:00             ` Christian Couder
2021-03-15 10:14             ` Christian Couder
2021-03-15 11:32               ` ZheNing Hu
2021-03-16  5:37                 ` Christian Couder
2021-03-16  8:35                   ` ZheNing Hu
2021-03-15 13:07             ` [PATCH v8 0/2] " ZheNing Hu via GitGitGadget
2021-03-15 13:07               ` [PATCH v8 1/2] " ZheNing Hu via GitGitGadget
2021-03-16 12:52                 ` Ævar Arnfjörð Bjarmason
2021-03-17  2:01                   ` ZheNing Hu
2021-03-17  8:08                     ` Ævar Arnfjörð Bjarmason
2021-03-17 13:54                       ` ZheNing Hu
2021-03-15 13:07               ` [PATCH v8 2/2] interpret_trailers: for three options parse add warning ZheNing Hu via GitGitGadget
2021-03-16  5:53                 ` Christian Couder
2021-03-16  9:11                   ` ZheNing Hu
2021-03-16 10:39               ` [PATCH v9] [GSOC] commit: add --trailer option ZheNing Hu via GitGitGadget
2021-03-17  5:26                 ` Shourya Shukla
2021-03-17  6:06                   ` ZheNing Hu
2021-03-18 11:15                 ` [PATCH v10 0/3] " ZheNing Hu via GitGitGadget
2021-03-18 11:15                   ` [PATCH v10 1/3] " ZheNing Hu via GitGitGadget
2021-03-18 16:29                     ` Đoàn Trần Công Danh
2021-03-19  7:56                       ` ZheNing Hu
2021-03-18 11:15                   ` [PATCH v10 2/3] interpret-trailers: add own-identity option ZheNing Hu via GitGitGadget
2021-03-18 16:45                     ` Đoàn Trần Công Danh
2021-03-19  8:04                       ` ZheNing Hu
2021-03-18 19:20                     ` Junio C Hamano
2021-03-19  9:33                       ` ZheNing Hu
2021-03-19 15:36                         ` Junio C Hamano
2021-03-20  2:54                           ` ZheNing Hu
2021-03-20  5:06                             ` Jeff King
2021-03-20  5:50                               ` Junio C Hamano
2021-03-20  6:16                                 ` ZheNing Hu
2021-03-20  6:38                                   ` ZheNing Hu
2021-03-20  6:53                                     ` Junio C Hamano
2021-03-20  8:43                                       ` ZheNing Hu
2021-03-18 11:15                   ` [PATCH v10 3/3] commit: " ZheNing Hu via GitGitGadget
2021-03-18 13:47                   ` [PATCH v10 0/3] [GSOC] commit: add --trailer option Christian Couder
2021-03-18 15:27                     ` ZheNing Hu
2021-03-19 12:05                   ` [PATCH v11] " ZheNing Hu via GitGitGadget
2021-03-19 17:48                     ` Junio C Hamano
2021-03-20 13:41                     ` [PATCH v12] " ZheNing Hu via GitGitGadget
2021-03-22  4:24                       ` [PATCH v13] " ZheNing Hu via GitGitGadget
2021-03-22  7:43                         ` Christian Couder
2021-03-22 10:23                           ` ZheNing Hu
2021-03-22 21:34                             ` Christian Couder
2021-03-23  6:11                               ` ZheNing Hu
2021-03-23  6:19                               ` Junio C Hamano
2021-03-23  7:57                                 ` Christian Couder
2021-03-23 17:11                                   ` Junio C Hamano
2021-03-24  5:21                                     ` ZheNing Hu
2021-03-23 10:35                                 ` ZheNing Hu
2021-03-23 12:41                                   ` Christian Couder
2021-03-23 17:12                                   ` Junio C Hamano
2021-03-24  5:25                                     ` ZheNing Hu
2021-03-22 21:55                             ` Christian Couder
2021-03-23  6:29                               ` ZheNing Hu
2021-03-23 13:55                         ` [PATCH v14] " ZheNing Hu via GitGitGadget
2021-03-15  4:38       ` [PATCH v4] " Junio C Hamano
2021-03-15  5:11         ` ZheNing Hu

Code repositories for project(s) associated with this inbox:

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).