git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[RESEND PATCH v3 6/8] format-patch: read branch-specific To: and Cc: headers

From
Denton Liu <liu.denton@gmail.com>
Date
Jun 14, 2019, 21:56 UTC
Message-ID
<6ad1b258584d699aa49cdda39e41126ef5621343.1560547501.git.liu.denton@gmail.com>
In-Reply-To
<cover.1560547500.git.liu.denton@gmail.com>

If a user wishes to keep track of whom to Cc: on individual patchsets, they must manually keep track of each recipient and fill it in with the `--cc` option on git-format-patch each time. However, on the Git mailing list, Cc:'s are typically never dropped. As a result, it would be nice to have a method to keep track of recipients on a per-branch basis.

Currently, git-format-patch gets its To: headers from the `--to` options and the `format.to` config variable. The Cc: header is derived similarly.

In addition to the above, read To: and Cc: headers from `format.<branch-name>.to` and `format.<branch-name>.cc` so that users can have branch-specific configuration options.

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 Documentation/config/format.txt    |   7 +-
 Documentation/git-format-patch.txt |  12 +++-
 builtin/log.c                      |  63 +++++++++++++++--
 t/t4014-format-patch.sh            | 108 +++++++++++++++++++++++------
 4 files changed, 162 insertions(+), 28 deletions(-)
diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt
index c8b28fe47f..95e255347a 100644
--- a/Documentation/config/format.txt
+++ b/Documentation/config/format.txt
@@ -28,9 +28,12 @@ format.headers::
 
 format.to::
 format.cc::
+format.<branch-name>.to::
+format.<branch-name>.cc::
 	Additional recipients to include in a patch to be submitted
-	by mail.  See the --to and --cc options in
-	linkgit:git-format-patch[1].
+	by mail.  For the <branch-name> options, the recipients are only
+	included if patches are generated for the given <branch-name>.
+	See the --to and --cc options in linkgit:git-format-patch[1].
 
 format.subjectPrefix::
 	The default for format-patch is to output files with the '[PATCH]'
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index f54aecf4bf..76e61b746a 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -203,7 +203,7 @@ the description up to and excluding the first blank line.
 	Add a `To:` header to the email headers. This is in addition
 	to any configured headers, and may be used multiple times. The
 	emails given will be used along with any emails given by
-	`format.to` configurations.
+	`format.to` and `format.<branch-name>.to` configurations.
 	The negated form `--no-to` discards all `To:` headers added so
 	far (from config or command line).
 
@@ -211,7 +211,7 @@ the description up to and excluding the first blank line.
 	Add a `Cc:` header to the email headers. This is in addition
 	to any configured headers, and may be used multiple times. The
 	emails given will be used along with any emails given by
-	`format.cc` configurations.
+	`format.cc` and `format.<branch-name>.cc` configurations.
 	The negated form `--no-cc` discards all `Cc:` headers added so
 	far (from config or command line).
 
@@ -358,6 +358,14 @@ with configuration variables.
 	inferCoverSubject = true
 ------------
 
+In addition, for a specific branch, you can specify a custom cover
+letter subject, and add additional "To:" or "Cc:" headers.
+
+------------
+[format "branch-name"]
+	to = <email>
+	cc = <email>
+------------
 
 DISCUSSION
 ----------
diff --git a/builtin/log.c b/builtin/log.c
index 3f97f344df..cb9ccdc59c 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -738,6 +738,8 @@ static char *default_attach = NULL;
 static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;
 static struct string_list extra_to = STRING_LIST_INIT_NODUP;
 static struct string_list extra_cc = STRING_LIST_INIT_NODUP;
+int to_cleared;
+int cc_cleared;
 
 static void add_header(const char *value)
 {
@@ -1021,6 +1023,55 @@ static void show_diffstat(struct rev_info *rev,
 	fprintf(rev->diffopt.file, "\n");
 }
 
+static void add_branch_headers(struct rev_info *rev, const char *branch_name)
+{
+	struct strbuf buf = STRBUF_INIT;
+	const struct string_list *values;
+
+	if (!branch_name)
+		branch_name = find_branch_name(rev);
+
+	if (!branch_name || !*branch_name)
+		return;
+
+	/*
+	 * HACK: We only use branch-specific recipients iff the list has not
+	 * been cleared by an earlier --no-{to,cc} option on the command-line.
+	 *
+	 * When we get format.{to,cc} options, they can be cleared by
+	 * --no-{to,cc} options since the `git_config` call comes before the
+	 *  `parse_options` call.
+	 *
+	 *  However, in the case of branch.<name>.{to,cc}, this function needs
+	 *  to be called after `setup_revisions`, which must be called after
+	 *  `parse_options`. However, in order for the --no-{to,cc} logic to
+	 *  clear the extra_{to,cc} string_list, this function should actually
+	 *  be called _before_ `parse_options`. As a result, we have a circular
+	 *  dependency.
+	 *
+	 *  The {to,cc}_cleared flag lets us workaround this by just no
+	 *  including branch-specific recipients iff --no-{to,cc} has been
+	 *  specified on the command-line.
+	 */
+
+	if (!to_cleared) {
+		strbuf_addf(&buf, "format.%s.to", branch_name);
+		values = git_config_get_value_multi(buf.buf);
+		if (values)
+			string_list_append_all(&extra_to, values);
+	}
+
+	if (!cc_cleared) {
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "format.%s.cc", branch_name);
+		values = git_config_get_value_multi(buf.buf);
+		if (values)
+			string_list_append_all(&extra_cc, values);
+	}
+
+	strbuf_release(&buf);
+}
+
 static void make_cover_letter(struct rev_info *rev, int use_stdout,
 			      struct commit *origin,
 			      int nr, struct commit **list,
@@ -1293,18 +1344,20 @@ static int header_callback(const struct option *opt, const char *arg, int unset)
 
 static int to_callback(const struct option *opt, const char *arg, int unset)
 {
-	if (unset)
+	if (unset) {
+		to_cleared = 1;
 		string_list_clear(&extra_to, 0);
-	else
+	} else
 		string_list_append(&extra_to, arg);
 	return 0;
 }
 
 static int cc_callback(const struct option *opt, const char *arg, int unset)
 {
-	if (unset)
+	if (unset) {
+		cc_cleared = 1;
 		string_list_clear(&extra_cc, 0);
-	else
+	} else
 		string_list_append(&extra_cc, arg);
 	return 0;
 }
@@ -1769,6 +1822,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		}
 	}
 
+	add_branch_headers(&rev, branch_name);
+
 	for (i = 0; i < extra_hdr.nr; i++) {
 		strbuf_addstr(&buf, extra_hdr.items[i].string);
 		strbuf_addch(&buf, '\n');
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 4cb6b9edfa..23c467e95b 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -234,6 +234,65 @@ test_expect_failure 'configuration To: header (rfc2047)' '
 	grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" hdrs9
 '
 
+test_expect_success 'branch-specific configuration To: header (ascii)' '
+
+	test_unconfig format.to &&
+	git config format.side.to "R E Cipient <rcipient@example.com>" &&
+	git format-patch --stdout master..side >patch10 &&
+	sed -e "/^\$/q" patch10 >hdrs10 &&
+	grep "^To: R E Cipient <rcipient@example.com>\$" hdrs10
+'
+
+test_expect_failure 'branch-specific configuration To: header (rfc822)' '
+
+	git config format.side.to "R. E. Cipient <rcipient@example.com>" &&
+	git format-patch --stdout master..side >patch10 &&
+	sed -e "/^\$/q" patch10 >hdrs10 &&
+	grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" hdrs10
+'
+
+test_expect_failure 'branch-specific configuration To: header (rfc2047)' '
+
+	git config format.side.to "R Ä Cipient <rcipient@example.com>" &&
+	git format-patch --stdout master..side >patch10 &&
+	sed -e "/^\$/q" patch10 >hdrs10 &&
+	grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" hdrs10
+'
+
+test_expect_success 'all recipients included from all sources' '
+
+	git config format.to "Format To1 <formatto1@example.com>" &&
+	git config --add format.to "Format To2 <formatto2@example.com>" &&
+	git config format.cc "Format Cc1 <formatcc1@example.com>" &&
+	git config --add format.cc "Format Cc2 <formatcc2@example.com>" &&
+	git config format.side.to "Branch To1 <branchto1@example.com>" &&
+	git config --add format.side.to "Branch To2 <branchto2@example.com>" &&
+	git config format.side.cc "Branch Cc1 <branchcc1@example.com>" &&
+	git config --add format.side.cc "Branch Cc2 <branchcc2@example.com>" &&
+	cat <<-\EOF >expect &&
+	To: Format To1 <formatto1@example.com>,
+	    Format To2 <formatto2@example.com>,
+	    Command-line To1 <commandlineto1@example.com>,
+	    Command-line To2 <commandlineto2@example.com>,
+	    Branch To1 <branchto1@example.com>,
+	    Branch To2 <branchto2@example.com>
+	Cc: Format Cc1 <formatcc1@example.com>,
+	    Format Cc2 <formatcc2@example.com>,
+	    Command-line Cc1 <commandlinecc1@example.com>,
+	    Command-line Cc2 <commandlinecc2@example.com>,
+	    Branch Cc1 <branchcc1@example.com>,
+	    Branch Cc2 <branchcc2@example.com>
+
+	EOF
+	git format-patch --stdout \
+		--to="Command-line To1 <commandlineto1@example.com>" \
+		--to="Command-line To2 <commandlineto2@example.com>" \
+		--cc="Command-line Cc1 <commandlinecc1@example.com>" \
+		--cc="Command-line Cc2 <commandlinecc2@example.com>" \
+		master..side | sed -ne "/^To:/,/^$/p;/^$/q" >patch10 &&
+	test_cmp expect patch10
+'
+
 # check_patch <patch>: Verify that <patch> looks like a half-sane
 # patch email to avoid a false positive with !grep
 check_patch () {
@@ -286,42 +345,51 @@ test_expect_success '--no-to overrides config.to' '
 
 	git config --replace-all format.to \
 		"R E Cipient <rcipient@example.com>" &&
-	git format-patch --no-to --stdout master..side >patch10 &&
-	sed -e "/^\$/q" patch10 >hdrs10 &&
-	check_patch hdrs10 &&
-	! grep "^To: R E Cipient <rcipient@example.com>\$" hdrs10
+	git config --replace-all format.side.to \
+		"B R Anch <branch@example.com>" &&
+	git format-patch --no-to --stdout master..side >patch11 &&
+	sed -e "/^\$/q" patch11 >hdrs11 &&
+	check_patch hdrs11 &&
+	! grep "R E Cipient <rcipient@example.com>" hdrs11 &&
+	! grep "B R Anch <branch@example.com>" hdrs11
 '
 
 test_expect_success '--no-to and --to replaces config.to' '
 
 	git config --replace-all format.to \
 		"Someone <someone@out.there>" &&
+	git config --replace-all format.side.to \
+		"B R Anch2 <branch2@example.com>" &&
 	git format-patch --no-to --to="Someone Else <else@out.there>" \
-		--stdout master..side >patch11 &&
-	sed -e "/^\$/q" patch11 >hdrs11 &&
-	check_patch hdrs11 &&
-	! grep "^To: Someone <someone@out.there>\$" hdrs11 &&
-	grep "^To: Someone Else <else@out.there>\$" hdrs11
+		--stdout master..side >patch12 &&
+	sed -e "/^\$/q" patch12 >hdrs12 &&
+	check_patch hdrs12 &&
+	! grep "Someone <someone@out.there>" hdrs12 &&
+	! grep "B R Anch2 <branch2@example.com>" hdrs12 &&
+	grep "^To: Someone Else <else@out.there>\$" hdrs12
 '
 
 test_expect_success '--no-cc overrides config.cc' '
 
 	git config --replace-all format.cc \
 		"C E Cipient <rcipient@example.com>" &&
-	git format-patch --no-cc --stdout master..side >patch12 &&
-	sed -e "/^\$/q" patch12 >hdrs12 &&
-	check_patch hdrs12 &&
-	! grep "^Cc: C E Cipient <rcipient@example.com>\$" hdrs12
+	git config --replace-all format.side.cc \
+		"B R Anch3 <branch3@example.com>" &&
+	git format-patch --no-cc --stdout master..side >patch13 &&
+	sed -e "/^\$/q" patch13 >hdrs13 &&
+	check_patch hdrs13 &&
+	! grep "C E Cipient <rcipient@example.com>" hdrs13 &&
+	! grep "B R Anch3 <branch3@example.com>" hdrs13
 '
 
 test_expect_success '--no-add-header overrides config.headers' '
 
 	git config --replace-all format.headers \
 		"Header1: B E Cipient <rcipient@example.com>" &&
-	git format-patch --no-add-header --stdout master..side >patch13 &&
-	sed -e "/^\$/q" patch13 >hdrs13 &&
-	check_patch hdrs13 &&
-	! grep "^Header1: B E Cipient <rcipient@example.com>\$" hdrs13
+	git format-patch --no-add-header --stdout master..side >patch14 &&
+	sed -e "/^\$/q" patch14 >hdrs14 &&
+	check_patch hdrs14 &&
+	! grep "^Header1: B E Cipient <rcipient@example.com>\$" hdrs14
 '
 
 test_expect_success 'multiple files' '
@@ -963,7 +1031,7 @@ test_expect_success 'format-patch wraps extremely long subject (ascii)' '
 	git add file &&
 	git commit -m "$M512" &&
 	git format-patch --stdout -1 >patch &&
-	sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject &&
+	sed -n "/^Subject/p; /^ /p; /^Header1:/q" <patch >subject &&
 	test_cmp expect subject
 '
 
@@ -1002,7 +1070,7 @@ test_expect_success 'format-patch wraps extremely long subject (rfc2047)' '
 	git add file &&
 	git commit -m "$M512" &&
 	git format-patch --stdout -1 >patch &&
-	sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject &&
+	sed -n "/^Subject/p; /^ /p; /^Header1:/q" <patch >subject &&
 	test_cmp expect subject
 '
 
@@ -1011,7 +1079,7 @@ check_author() {
 	git add file &&
 	GIT_AUTHOR_NAME=$1 git commit -m author-check &&
 	git format-patch --stdout -1 >patch &&
-	sed -n "/^From: /p; /^ /p; /^$/q" <patch >actual &&
+	sed -n "/^From: /p; /^ /p; /^Date:/q" <patch >actual &&
 	test_cmp expect actual
 }
 
-- 
2.22.0.355.g3bfa262345
Previous: Denton LiuNext: Denton Liu
Message 51 of 59 in “teach branch-specific options for format-patch”
  1. 0/7 teach branch-specific options for format-patchDenton Liu, May 5, 2019
  2. 1/7 t4014: clean up styleDenton Liu, May 5, 2019
  3. 2/7 Doc: add more detail for git-format-patchDenton Liu, May 5, 2019
  4. 3/7 branch.c: extract read_branch_config functionDenton Liu, May 5, 2019
  5. 4/7 format-patch: make cover letter subject configurableDenton Liu, May 5, 2019
  6. 5/7 format-patch: move extra_headers logic laterDenton Liu, May 5, 2019
  7. 6/7 string-list: create string_list_append_allDenton Liu, May 5, 2019
  8. 7/7 format-patch: read branch-specific To: and Cc: headersDenton Liu, May 5, 2019
  9. Junio C HamanoMay 7, 2019
  10. Denton LiuMay 7, 2019
  11. Junio C HamanoMay 7, 2019
  12. Denton LiuMay 7, 2019
  13. Ævar Arnfjörð BjarmasonMay 7, 2019
  14. Junio C HamanoMay 8, 2019
  15. config: learn the "onbranch:" includeIf conditionDenton Liu, May 31, 2019
  16. Johannes SchindelinMay 31, 2019
  17. Denton LiuMay 31, 2019
  18. Johannes SchindelinMay 31, 2019
  19. Denton LiuMay 31, 2019
  20. config: learn the "onbranch:" includeIf conditionDenton Liu, May 31, 2019
  21. Johannes SchindelinMay 31, 2019
  22. Johannes SchindelinJun 5, 2019
  23. Duy NguyenJun 5, 2019
  24. config: learn the "onbranch:" includeIf conditionDenton Liu, Jun 5, 2019
  25. Johannes SchindelinJun 6, 2019
  26. 0/6 teach branch-specific options for format-patchDenton Liu, May 17, 2019
  27. 1/6 t4014: clean up styleDenton Liu, May 17, 2019
  28. 2/6 Doc: add more detail for git-format-patchDenton Liu, May 17, 2019
  29. 3/6 format-patch: make cover letter subject configurableDenton Liu, May 17, 2019
  30. 4/6 format-patch: move extra_headers logic laterDenton Liu, May 17, 2019
  31. 5/6 string-list: create string_list_append_allDenton Liu, May 17, 2019
  32. 6/6 format-patch: read branch-specific To: and Cc: headersDenton Liu, May 17, 2019
  33. Junio C HamanoMay 17, 2019
  34. Denton LiuMay 17, 2019
  35. Denton LiuMay 17, 2019
  36. 0/8 teach branch-specific options for format-patchDenton Liu, May 22, 2019
  37. 1/8 t4014: clean up styleDenton Liu, May 22, 2019
  38. 2/8 Doc: add more detail for git-format-patchDenton Liu, May 22, 2019
  39. 3/8 format-patch: infer cover letter from branch descriptionDenton Liu, May 22, 2019
  40. 4/8 format-patch: move extra_headers logic laterDenton Liu, May 22, 2019
  41. 5/8 string-list: create string_list_append_allDenton Liu, May 22, 2019
  42. 6/8 format-patch: read branch-specific To: and Cc: headersDenton Liu, May 22, 2019
  43. 7/8 format-patch: move output_directory logic laterDenton Liu, May 22, 2019
  44. 8/8 format-patch: read branch-specific output directoryDenton Liu, May 22, 2019
  45. Denton LiuMay 31, 2019
  46. 0/8 teach branch-specific options for format-patchDenton Liu, Jun 14, 2019
  47. 1/8 t4014: clean up styleDenton Liu, Jun 14, 2019
  48. 2/8 Doc: add more detail for git-format-patchDenton Liu, Jun 14, 2019
  49. 3/8 format-patch: infer cover letter from branch descriptionDenton Liu, Jun 14, 2019
  50. 4/8 format-patch: move extra_headers logic laterDenton Liu, Jun 14, 2019
  51. 6/8 format-patch: read branch-specific To: and Cc: headersDenton Liu, Jun 14, 2019
  52. 5/8 string-list: create string_list_append_allDenton Liu, Jun 14, 2019
  53. 8/8 format-patch: read branch-specific output directoryDenton Liu, Jun 14, 2019
  54. 7/8 format-patch: move output_directory logic laterDenton Liu, Jun 14, 2019
  55. 0/3 format-patch: learn --infer-cover-subject option (also t4014 cleanup)Denton Liu, Oct 15, 2019
  56. 1/3 format-patch: replace erroneous and conditionDenton Liu, Oct 15, 2019
  57. 2/3 format-patch: use enum variablesDenton Liu, Oct 15, 2019
  58. 3/3 format-patch: teach --cover-from-description optionDenton Liu, Oct 15, 2019
  59. Junio C HamanoOct 16, 2019

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.