{"thread":{"id":"51038","subject":"[PATCH 0/7] teach branch-specific options for format-patch","startedAt":"2019-05-05T16:24:32Z","lastAt":"2019-10-16T01:28:11Z","messageCount":59,"participants":["Denton Liu","Junio C Hamano","Ævar Arnfjörð Bjarmason","Johannes Schindelin","Duy Nguyen"],"isPatch":true,"patchVersion":1,"patchTotal":7},"messages":[{"id":"374916","messageId":"cover.1557072929.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":null,"subject":"[PATCH 0/7] teach branch-specific options for format-patch","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-05T16:24:26Z","receivedAt":"2019-05-05T16:24:32Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Currently, format-patch only accepts branch.<name>.description as a\nbranch-specific configuration variable. However, there are many other\noptions which would be useful to have on a branch-by-branch basis,\nnamely cover letter subject and To: and Cc: headers.\n\nTeach format-patch to recognise these branch-specific configuration\noptions.\n\nNote that this patchset[1] was created using these new configuration\noptions:\n\n\t[branch \"submitted/fix-revisions-txt\"]\n\t\tcoverSubject = \"cleanup revisions.txt\"\n\t\tcc = \"Andreas Heiduk <asheiduk@gmail.com>\"\n\t\tcc = \"Duy Nguyen <pclouds@gmail.com>\"\n\t\tcc = \"Junio C Hamano <gitster@pobox.com>\"\n\nSo this is a live example of this patchset working in practice.\n\n[1]: https://public-inbox.org/git/cover.1557072286.git.liu.denton@gmail.com/\n\nDenton Liu (7):\n  t4014: clean up style\n  Doc: add more detail for git-format-patch\n  branch.c: extract read_branch_config function\n  format-patch: make cover letter subject configurable\n  format-patch: move extra_headers logic later\n  string-list: create string_list_append_all\n  format-patch: read branch-specific To: and Cc: headers\n\n Documentation/config/branch.txt    |  10 +\n Documentation/git-format-patch.txt |  33 +-\n branch.c                           |  14 +-\n branch.h                           |   5 +\n builtin/log.c                      | 147 ++++--\n string-list.c                      |   9 +\n string-list.h                      |   7 +\n t/t4014-format-patch.sh            | 708 +++++++++++++++++------------\n t/t9902-completion.sh              |   5 +-\n 9 files changed, 590 insertions(+), 348 deletions(-)\n\n-- \n2.21.0.1049.geb646f7864\n\n"},{"id":"374917","messageId":"08685a241e36989cde0cecc17ac9ff941998142c.1557072929.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1557072929.git.liu.denton@gmail.com","subject":"[PATCH 1/7] t4014: clean up style","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-05T16:24:29Z","receivedAt":"2019-05-05T16:24:36Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In Git's tests, there is typically no space between the redirection\noperator and the filename. Remove these spaces.\n\nSince output is silenced when running without `-v` and debugging\noutput is useful with `-v`, remove redirections to /dev/null.\n\nChange here-docs from `<<\\EOF` to `<<-\\EOF` so that they can be indented\nalong with the rest of the test case.\n\nFinally, refactor to remove Git commands upstream of pipe. This way, if\nan invocation of a Git command fails, the return code won't be lost.\nKeep upstream non-Git commands since we have to assume a base level of\nsanity.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t4014-format-patch.sh | 614 +++++++++++++++++++++-------------------\n 1 file changed, 319 insertions(+), 295 deletions(-)\n\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 909c743c13..d05cd256c7 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -34,7 +34,8 @@ test_expect_success setup '\n \tgit commit -m \"Side changes #3 with \\\\n backslash-n in it.\" &&\n \n \tgit checkout master &&\n-\tgit diff-tree -p C2 | git apply --index &&\n+\tgit diff-tree -p C2 >patch &&\n+\tgit apply --index <patch &&\n \ttest_tick &&\n \tgit commit -m \"Master accepts moral equivalent of #2\"\n \n@@ -77,7 +78,7 @@ test_expect_success \"format-patch doesn't consider merge commits\" '\n \tgit checkout -b merger master &&\n \ttest_tick &&\n \tgit merge --no-ff slave &&\n-\tcnt=$(git format-patch -3 --stdout | grep \"^From \" | wc -l) &&\n+\tcnt=$(git format-patch -3 --stdout >patch && grep \"^From \" patch | wc -l) &&\n \ttest $cnt = 3\n '\n \n@@ -85,21 +86,22 @@ test_expect_success \"format-patch result applies\" '\n \n \tgit checkout -b rebuild-0 master &&\n \tgit am -3 patch0 &&\n-\tcnt=$(git rev-list master.. | wc -l) &&\n-\ttest $cnt = 2\n+\tgit rev-list master.. >list &&\n+\ttest_line_count = 2 list\n '\n \n test_expect_success \"format-patch --ignore-if-in-upstream result applies\" '\n \n \tgit checkout -b rebuild-1 master &&\n \tgit am -3 patch1 &&\n-\tcnt=$(git rev-list master.. | wc -l) &&\n-\ttest $cnt = 2\n+\tgit rev-list master.. >list &&\n+\ttest_line_count = 2 list\n '\n \n test_expect_success 'commit did not screw up the log message' '\n \n-\tgit cat-file commit side | grep \"^Side .* with .* backslash-n\"\n+\tgit cat-file commit side >actual &&\n+\tgrep \"^Side .* with .* backslash-n\" actual\n \n '\n \n@@ -112,7 +114,8 @@ test_expect_success 'format-patch did not screw up the log message' '\n \n test_expect_success 'replay did not screw up the log message' '\n \n-\tgit cat-file commit rebuild-1 | grep \"^Side .* with .* backslash-n\"\n+\tgit cat-file commit rebuild-1 >actual &&\n+\tgrep \"^Side .* with .* backslash-n\" actual\n \n '\n \n@@ -122,8 +125,8 @@ test_expect_success 'extra headers' '\n \" &&\n \tgit config --add format.headers \"Cc: S E Cipient <scipient@example.com>\n \" &&\n-\tgit format-patch --stdout master..side > patch2 &&\n-\tsed -e \"/^\\$/q\" patch2 > hdrs2 &&\n+\tgit format-patch --stdout master..side >patch2 &&\n+\tsed -e \"/^\\$/q\" patch2 >hdrs2 &&\n \tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs2 &&\n \tgrep \"^Cc: S E Cipient <scipient@example.com>\\$\" hdrs2\n \n@@ -134,7 +137,7 @@ test_expect_success 'extra headers without newlines' '\n \tgit config --replace-all format.headers \"To: R E Cipient <rcipient@example.com>\" &&\n \tgit config --add format.headers \"Cc: S E Cipient <scipient@example.com>\" &&\n \tgit format-patch --stdout master..side >patch3 &&\n-\tsed -e \"/^\\$/q\" patch3 > hdrs3 &&\n+\tsed -e \"/^\\$/q\" patch3 >hdrs3 &&\n \tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs3 &&\n \tgrep \"^Cc: S E Cipient <scipient@example.com>\\$\" hdrs3\n \n@@ -144,8 +147,8 @@ test_expect_success 'extra headers with multiple To:s' '\n \n \tgit config --replace-all format.headers \"To: R E Cipient <rcipient@example.com>\" &&\n \tgit config --add format.headers \"To: S E Cipient <scipient@example.com>\" &&\n-\tgit format-patch --stdout master..side > patch4 &&\n-\tsed -e \"/^\\$/q\" patch4 > hdrs4 &&\n+\tgit format-patch --stdout master..side >patch4 &&\n+\tsed -e \"/^\\$/q\" patch4 >hdrs4 &&\n \tgrep \"^To: R E Cipient <rcipient@example.com>,\\$\" hdrs4 &&\n \tgrep \"^ *S E Cipient <scipient@example.com>\\$\" hdrs4\n '\n@@ -153,72 +156,82 @@ test_expect_success 'extra headers with multiple To:s' '\n test_expect_success 'additional command line cc (ascii)' '\n \n \tgit config --replace-all format.headers \"Cc: R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --cc=\"S E Cipient <scipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch5 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" patch5 &&\n-\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" patch5\n+\tgit format-patch --cc=\"S E Cipient <scipient@example.com>\" --stdout master..side >patch5 &&\n+\tsed -e \"/^\\$/q\" patch5 >hdrs5 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" hdrs5 &&\n+\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" hdrs5\n '\n \n test_expect_failure 'additional command line cc (rfc822)' '\n \n \tgit config --replace-all format.headers \"Cc: R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --cc=\"S. E. Cipient <scipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch5 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" patch5 &&\n-\tgrep \"^ *\\\"S. E. Cipient\\\" <scipient@example.com>\\$\" patch5\n+\tgit format-patch --cc=\"S. E. Cipient <scipient@example.com>\" --stdout master..side >patch5 &&\n+\tsed -e \"/^\\$/q\" patch5 >hdrs5 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" hdrs5 &&\n+\tgrep \"^ *\\\"S. E. Cipient\\\" <scipient@example.com>\\$\" hdrs5\n '\n \n test_expect_success 'command line headers' '\n \n \tgit config --unset-all format.headers &&\n-\tgit format-patch --add-header=\"Cc: R E Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch6 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>\\$\" patch6\n+\tgit format-patch --add-header=\"Cc: R E Cipient <rcipient@example.com>\" --stdout master..side >patch6 &&\n+\tsed -e \"/^\\$/q\" patch6 >hdrs6 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>\\$\" hdrs6\n '\n \n test_expect_success 'configuration headers and command line headers' '\n \n \tgit config --replace-all format.headers \"Cc: R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --add-header=\"Cc: S E Cipient <scipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch7 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" patch7 &&\n-\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" patch7\n+\tgit format-patch --add-header=\"Cc: S E Cipient <scipient@example.com>\" --stdout master..side >patch7 &&\n+\tsed -e \"/^\\$/q\" patch7 >hdrs7 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" hdrs7 &&\n+\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" hdrs7\n '\n \n test_expect_success 'command line To: header (ascii)' '\n \n \tgit config --unset-all format.headers &&\n-\tgit format-patch --to=\"R E Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch8 &&\n-\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" patch8\n+\tgit format-patch --to=\"R E Cipient <rcipient@example.com>\" --stdout master..side >patch8 &&\n+\tsed -e \"/^\\$/q\" patch8 >hdrs8 &&\n+\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs8\n '\n \n test_expect_failure 'command line To: header (rfc822)' '\n \n-\tgit format-patch --to=\"R. E. Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch8 &&\n-\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" patch8\n+\tgit format-patch --to=\"R. E. Cipient <rcipient@example.com>\" --stdout master..side >patch8 &&\n+\tsed -e \"/^\\$/q\" patch8 >hdrs8 &&\n+\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" hdrs8\n '\n \n test_expect_failure 'command line To: header (rfc2047)' '\n \n-\tgit format-patch --to=\"R Ä Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch8 &&\n-\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" patch8\n+\tgit format-patch --to=\"R Ä Cipient <rcipient@example.com>\" --stdout master..side >patch8 &&\n+\tsed -e \"/^\\$/q\" patch8 >hdrs8 &&\n+\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs8\n '\n \n test_expect_success 'configuration To: header (ascii)' '\n \n \tgit config format.to \"R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --stdout master..side | sed -e \"/^\\$/q\" >patch9 &&\n-\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" patch9\n+\tgit format-patch --stdout master..side >patch9 &&\n+\tsed -e \"/^\\$/q\" patch9 >hdrs9 &&\n+\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs9\n '\n \n test_expect_failure 'configuration To: header (rfc822)' '\n \n \tgit config format.to \"R. E. Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --stdout master..side | sed -e \"/^\\$/q\" >patch9 &&\n-\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" patch9\n+\tgit format-patch --stdout master..side >patch9 &&\n+\tsed -e \"/^\\$/q\" patch9 >hdrs9 &&\n+\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" hdrs9\n '\n \n test_expect_failure 'configuration To: header (rfc2047)' '\n \n \tgit config format.to \"R Ä Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --stdout master..side | sed -e \"/^\\$/q\" >patch9 &&\n-\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" patch9\n+\tgit format-patch --stdout master..side >patch9 &&\n+\tsed -e \"/^\\$/q\" patch9 >hdrs9 &&\n+\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs9\n '\n \n # check_patch <patch>: Verify that <patch> looks like a half-sane\n@@ -231,52 +244,52 @@ check_patch () {\n \n test_expect_success 'format.from=false' '\n \n-\tgit -c format.from=false format-patch --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n+\tgit -c format.from=false format-patch --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n \tcheck_patch patch &&\n-\t! grep \"^From: C O Mitter <committer@example.com>\\$\" patch\n+\t! grep \"^From: C O Mitter <committer@example.com>\\$\" hdrs\n '\n \n test_expect_success 'format.from=true' '\n \n-\tgit -c format.from=true format-patch --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\tgrep \"^From: C O Mitter <committer@example.com>\\$\" patch\n+\tgit -c format.from=true format-patch --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\tgrep \"^From: C O Mitter <committer@example.com>\\$\" hdrs\n '\n \n test_expect_success 'format.from with address' '\n \n-\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\tgrep \"^From: F R Om <from@example.com>\\$\" patch\n+\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\tgrep \"^From: F R Om <from@example.com>\\$\" hdrs\n '\n \n test_expect_success '--no-from overrides format.from' '\n \n-\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --no-from --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\t! grep \"^From: F R Om <from@example.com>\\$\" patch\n+\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --no-from --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\t! grep \"^From: F R Om <from@example.com>\\$\" hdrs\n '\n \n test_expect_success '--from overrides format.from' '\n \n-\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --from --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\t! grep \"^From: F R Om <from@example.com>\\$\" patch\n+\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --from --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\t! grep \"^From: F R Om <from@example.com>\\$\" hdrs\n '\n \n test_expect_success '--no-to overrides config.to' '\n \n \tgit config --replace-all format.to \\\n \t\t\"R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-to --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch10 &&\n-\tcheck_patch patch10 &&\n-\t! grep \"^To: R E Cipient <rcipient@example.com>\\$\" patch10\n+\tgit format-patch --no-to --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tcheck_patch hdrs10 &&\n+\t! grep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs10\n '\n \n test_expect_success '--no-to and --to replaces config.to' '\n@@ -284,31 +297,31 @@ test_expect_success '--no-to and --to replaces config.to' '\n \tgit config --replace-all format.to \\\n \t\t\"Someone <someone@out.there>\" &&\n \tgit format-patch --no-to --to=\"Someone Else <else@out.there>\" \\\n-\t\t--stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch11 &&\n-\tcheck_patch patch11 &&\n-\t! grep \"^To: Someone <someone@out.there>\\$\" patch11 &&\n-\tgrep \"^To: Someone Else <else@out.there>\\$\" patch11\n+\t\t--stdout master..side >patch11 &&\n+\tsed -e \"/^\\$/q\" patch11 >hdrs11 &&\n+\tcheck_patch hdrs11 &&\n+\t! grep \"^To: Someone <someone@out.there>\\$\" hdrs11 &&\n+\tgrep \"^To: Someone Else <else@out.there>\\$\" hdrs11\n '\n \n test_expect_success '--no-cc overrides config.cc' '\n \n \tgit config --replace-all format.cc \\\n \t\t\"C E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-cc --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch12 &&\n-\tcheck_patch patch12 &&\n-\t! grep \"^Cc: C E Cipient <rcipient@example.com>\\$\" patch12\n+\tgit format-patch --no-cc --stdout master..side >patch12 &&\n+\tsed -e \"/^\\$/q\" patch12 >hdrs12 &&\n+\tcheck_patch hdrs12 &&\n+\t! grep \"^Cc: C E Cipient <rcipient@example.com>\\$\" hdrs12\n '\n \n test_expect_success '--no-add-header overrides config.headers' '\n \n \tgit config --replace-all format.headers \\\n \t\t\"Header1: B E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-add-header --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch13 &&\n-\tcheck_patch patch13 &&\n-\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" patch13\n+\tgit format-patch --no-add-header --stdout master..side >patch13 &&\n+\tsed -e \"/^\\$/q\" patch13 >hdrs13 &&\n+\tcheck_patch hdrs13 &&\n+\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" hdrs13\n '\n \n test_expect_success 'multiple files' '\n@@ -338,7 +351,7 @@ test_expect_success 'reroll count (-v)' '\n check_threading () {\n \texpect=\"$1\" &&\n \tshift &&\n-\t(git format-patch --stdout \"$@\"; echo $? > status.out) |\n+\t(git format-patch --stdout \"$@\"; echo $? >status.out) |\n \t# Prints everything between the Message-ID and In-Reply-To,\n \t# and replaces all Message-ID-lookalikes by a sequence number\n \tperl -ne '\n@@ -353,12 +366,12 @@ check_threading () {\n \t\t\tprint;\n \t\t}\n \t\tprint \"---\\n\" if /^From /i;\n-\t' > actual &&\n+\t' >actual &&\n \ttest 0 = \"$(cat status.out)\" &&\n \ttest_cmp \"$expect\" actual\n }\n \n-cat >> expect.no-threading <<EOF\n+cat >>expect.no-threading <<EOF\n ---\n ---\n ---\n@@ -369,7 +382,7 @@ test_expect_success 'no threading' '\n \tcheck_threading expect.no-threading master\n '\n \n-cat > expect.thread <<EOF\n+cat >expect.thread <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -386,7 +399,7 @@ test_expect_success 'thread' '\n \tcheck_threading expect.thread --thread master\n '\n \n-cat > expect.in-reply-to <<EOF\n+cat >expect.in-reply-to <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -406,7 +419,7 @@ test_expect_success 'thread in-reply-to' '\n \t\t--thread master\n '\n \n-cat > expect.cover-letter <<EOF\n+cat >expect.cover-letter <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -427,7 +440,7 @@ test_expect_success 'thread cover-letter' '\n \tcheck_threading expect.cover-letter --cover-letter --thread master\n '\n \n-cat > expect.cl-irt <<EOF\n+cat >expect.cl-irt <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -459,7 +472,7 @@ test_expect_success 'thread explicit shallow' '\n \t\t--in-reply-to=\"<test.message>\" --thread=shallow master\n '\n \n-cat > expect.deep <<EOF\n+cat >expect.deep <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -477,7 +490,7 @@ test_expect_success 'thread deep' '\n \tcheck_threading expect.deep --thread=deep master\n '\n \n-cat > expect.deep-irt <<EOF\n+cat >expect.deep-irt <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -500,7 +513,7 @@ test_expect_success 'thread deep in-reply-to' '\n \t\t--in-reply-to=\"<test.message>\" master\n '\n \n-cat > expect.deep-cl <<EOF\n+cat >expect.deep-cl <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -524,7 +537,7 @@ test_expect_success 'thread deep cover-letter' '\n \tcheck_threading expect.deep-cl --cover-letter --thread=deep master\n '\n \n-cat > expect.deep-cl-irt <<EOF\n+cat >expect.deep-cl-irt <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -600,7 +613,7 @@ test_expect_success 'cover-letter inherits diff options' '\n \n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n   This is an excessively long subject line for a message due to the\n     habit some projects have of not having a short, one-line subject at\n     the start of the commit message, but rather sticking a whole\n@@ -613,12 +626,12 @@ EOF\n test_expect_success 'shortlog of cover-letter wraps overly-long onelines' '\n \n \tgit format-patch --cover-letter -2 &&\n-\tsed -e \"1,/A U Thor/d\" -e \"/^\\$/q\" < 0000-cover-letter.patch > output &&\n+\tsed -e \"1,/A U Thor/d\" -e \"/^\\$/q\" <0000-cover-letter.patch >output &&\n \ttest_cmp expect output\n \n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n index $before..$after 100644\n --- a/file\n +++ b/file\n@@ -640,7 +653,7 @@ test_expect_success 'format-patch respects -U' '\n \n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n \n diff --git a/file b/file\n index $before..$after 100644\n@@ -656,7 +669,7 @@ EOF\n test_expect_success 'format-patch -p suppresses stat' '\n \n \tgit format-patch -p -2 &&\n-\tsed -e \"1,/^\\$/d\" -e \"/^+5/q\" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&\n+\tsed -e \"1,/^\\$/d\" -e \"/^+5/q\" <0001-This-is-an-excessively-long-subject-line-for-a-messa.patch >output &&\n \ttest_cmp expect output\n \n '\n@@ -711,7 +724,7 @@ test_expect_success 'format-patch from a subdirectory (3)' '\n '\n \n test_expect_success 'format-patch --in-reply-to' '\n-\tgit format-patch -1 --stdout --in-reply-to \"baz@foo.bar\" > patch8 &&\n+\tgit format-patch -1 --stdout --in-reply-to \"baz@foo.bar\" >patch8 &&\n \tgrep \"^In-Reply-To: <baz@foo.bar>\" patch8 &&\n \tgrep \"^References: <baz@foo.bar>\" patch8\n '\n@@ -732,20 +745,20 @@ test_expect_success 'format-patch --notes --signoff' '\n \tsed \"1,/^---$/d\" out | grep \"test message\"\n '\n \n-echo \"fatal: --name-only does not make sense\" > expect.name-only\n-echo \"fatal: --name-status does not make sense\" > expect.name-status\n-echo \"fatal: --check does not make sense\" > expect.check\n+echo \"fatal: --name-only does not make sense\" >expect.name-only\n+echo \"fatal: --name-status does not make sense\" >expect.name-status\n+echo \"fatal: --check does not make sense\" >expect.check\n \n test_expect_success 'options no longer allowed for format-patch' '\n-\ttest_must_fail git format-patch --name-only 2> output &&\n+\ttest_must_fail git format-patch --name-only 2>output &&\n \ttest_i18ncmp expect.name-only output &&\n-\ttest_must_fail git format-patch --name-status 2> output &&\n+\ttest_must_fail git format-patch --name-status 2>output &&\n \ttest_i18ncmp expect.name-status output &&\n-\ttest_must_fail git format-patch --check 2> output &&\n+\ttest_must_fail git format-patch --check 2>output &&\n \ttest_i18ncmp expect.check output'\n \n test_expect_success 'format-patch --numstat should produce a patch' '\n-\tgit format-patch --numstat --stdout master..side > output &&\n+\tgit format-patch --numstat --stdout master..side >output &&\n \ttest 5 = $(grep \"^diff --git a/\" output | wc -l)'\n \n test_expect_success 'format-patch -- <path>' '\n@@ -757,20 +770,22 @@ test_expect_success 'format-patch --ignore-if-in-upstream HEAD' '\n \tgit format-patch --ignore-if-in-upstream HEAD\n '\n \n-git_version=\"$(git --version | sed \"s/.* //\")\"\n+git_version=\"$(git --version >version && sed \"s/.* //\" version)\"\n \n signature() {\n \tprintf \"%s\\n%s\\n\\n\" \"-- \" \"${1:-$git_version}\"\n }\n \n test_expect_success 'format-patch default signature' '\n-\tgit format-patch --stdout -1 | tail -n 3 >output &&\n+\tgit format-patch --stdout -1 >patch &&\n+\ttail -n 3 patch >output &&\n \tsignature >expect &&\n \ttest_cmp expect output\n '\n \n test_expect_success 'format-patch --signature' '\n-\tgit format-patch --stdout --signature=\"my sig\" -1 | tail -n 3 >output &&\n+\tgit format-patch --stdout --signature=\"my sig\" -1 >patch &&\n+\ttail -n 3 patch >output &&\n \tsignature \"my sig\" >expect &&\n \ttest_cmp expect output\n '\n@@ -1161,282 +1176,282 @@ append_signoff()\n \n test_expect_success 'signoff: commit with no body' '\n \tappend_signoff </dev/null >actual &&\n-\tcat <<\\EOF | sed \"s/EOL$//\" >expected &&\n-4:Subject: [PATCH] EOL\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat <<-\\EOF | sed \"s/EOL$//\" >expected &&\n+\t4:Subject: [PATCH] EOL\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: commit with only subject' '\n \techo subject | append_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: commit with only subject that does not end with NL' '\n \tprintf subject | append_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: no existing signoffs' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tbody\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: no existing signoffs and no trailing NL' '\n \tprintf \"subject\\n\\nbody\" | append_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: some random signoff' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: my@house\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: my@house\n-12:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tSigned-off-by: my@house\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: my@house\n+\t12:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: misc conforming footer elements' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: my@house\n-(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)\n-Tested-by: Some One <someone@example.com>\n-Bug: 1234\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: my@house\n-15:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tSigned-off-by: my@house\n+\t(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)\n+\tTested-by: Some One <someone@example.com>\n+\tBug: 1234\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: my@house\n+\t15:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: some random signoff-alike' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n-Fooled-by-me: my@house\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-11:\n-12:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tbody\n+\tFooled-by-me: my@house\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t11:\n+\t12:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: not really a signoff' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-I want to mention about Signed-off-by: here.\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:I want to mention about Signed-off-by: here.\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tI want to mention about Signed-off-by: here.\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:I want to mention about Signed-off-by: here.\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: not really a signoff (2)' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-My unfortunate\n-Signed-off-by: example happens to be wrapped here.\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:Signed-off-by: example happens to be wrapped here.\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tMy unfortunate\n+\tSigned-off-by: example happens to be wrapped here.\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:Signed-off-by: example happens to be wrapped here.\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: valid S-o-b paragraph in the middle' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-Signed-off-by: my@house\n-Signed-off-by: your@house\n+\tSigned-off-by: my@house\n+\tSigned-off-by: your@house\n \n-A lot of houses.\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: my@house\n-10:Signed-off-by: your@house\n-11:\n-13:\n-14:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tA lot of houses.\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: my@house\n+\t10:Signed-off-by: your@house\n+\t11:\n+\t13:\n+\t14:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: the same signoff at the end' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: the same signoff at the end, no trailing NL' '\n \tprintf \"subject\\n\\nSigned-off-by: C O Mitter <committer@example.com>\" |\n \t\tappend_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: the same signoff NOT at the end' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: C O Mitter <committer@example.com>\n-Signed-off-by: my@house\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-12:Signed-off-by: my@house\n-EOF\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tSigned-off-by: my@house\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\t12:Signed-off-by: my@house\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: tolerate garbage in conforming footer' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Tested-by: my@house\n-Some Trash\n-Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-13:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tTested-by: my@house\n+\tSome Trash\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t13:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: respect trailer config' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-Myfooter: x\n-Some Trash\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-11:\n-12:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tMyfooter: x\n+\tSome Trash\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t11:\n+\t12:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual &&\n \n \ttest_config trailer.Myfooter.ifexists add &&\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-Myfooter: x\n-Some Trash\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tMyfooter: x\n+\tSome Trash\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: footer begins with non-signoff without @ sign' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Reviewed-id: Noone\n-Tested-by: my@house\n-Change-id: Ideadbeef\n-Signed-off-by: C O Mitter <committer@example.com>\n-Bug: 1234\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-14:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tReviewed-id: Noone\n+\tTested-by: my@house\n+\tChange-id: Ideadbeef\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tBug: 1234\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t14:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n@@ -1452,42 +1467,42 @@ test_expect_success 'cover letter using branch description (1)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter master >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (2)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter rebuild-1~2..rebuild-1 >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (3)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter ^master rebuild-1 >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (4)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter master.. >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (5)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter -2 HEAD >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (6)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter -2 >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter with nothing' '\n@@ -1541,7 +1556,8 @@ test_expect_success 'format-patch format.outputDirectory option' '\n \ttest_config format.outputDirectory patches &&\n \trm -fr patches &&\n \tgit format-patch master..side &&\n-\ttest $(git rev-list master..side | wc -l) -eq $(ls patches | wc -l)\n+\tgit rev-list master..side >list &&\n+\ttest_line_count = $(ls patches | wc -l) list\n '\n \n test_expect_success 'format-patch -o overrides format.outputDirectory' '\n@@ -1554,13 +1570,21 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '\n \n test_expect_success 'format-patch --base' '\n \tgit checkout side &&\n-\tgit format-patch --stdout --base=HEAD~3 -1 | tail -n 7 >actual1 &&\n-\tgit format-patch --stdout --base=HEAD~3 HEAD~.. | tail -n 7 >actual2 &&\n+\tgit format-patch --stdout --base=HEAD~3 -1 >patch &&\n+\ttail -n 7 patch >actual1 &&\n+\tgit format-patch --stdout --base=HEAD~3 HEAD~.. >patch &&\n+\ttail -n 7 patch >actual2 &&\n \techo >expected &&\n \techo \"base-commit: $(git rev-parse HEAD~3)\" >>expected &&\n-\techo \"prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n-\techo \"prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n-\tsignature >> expected &&\n+\tgit show --patch HEAD~2 >patch &&\n+\tgit patch-id --stable <patch >patch.id.raw &&\n+\tawk \"{print \\$1}\" <patch.id.raw >patch.id &&\n+\techo \"prerequisite-patch-id: $(cat patch.id)\" >>expected &&\n+\tgit show --patch HEAD~1 >patch &&\n+\tgit patch-id --stable <patch >patch.id.raw &&\n+\tawk \"{print \\$1}\" <patch.id.raw >patch.id &&\n+\techo \"prerequisite-patch-id: $(cat patch.id)\" >>expected &&\n+\tsignature >>expected &&\n \ttest_cmp expected actual1 &&\n \ttest_cmp expected actual2\n '\n-- \n2.21.0.1049.geb646f7864\n\n"},{"id":"374918","messageId":"f0d972bf8fb6575550fef2fb9a94702c0ad69dea.1557072929.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1557072929.git.liu.denton@gmail.com","subject":"[PATCH 2/7] Doc: add more detail for git-format-patch","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-05T16:24:33Z","receivedAt":"2019-05-05T16:24:39Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In git-format-patch.txt, we were missing some key user information.\nFirst of all, using the `--to` and `--cc` options don't override\n`format.to` and `format.cc` variables, respectively. They add on to each\nother. Document this.\n\nIn addition, document the special value of `--base=auto`.\n\nFinally, while we're at it, surround option arguments with <>.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/git-format-patch.txt | 17 +++++++++++------\n 1 file changed, 11 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 1af85d404f..7b71d4e2ed 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -17,9 +17,9 @@ SYNOPSIS\n \t\t   [--signature-file=<file>]\n \t\t   [-n | --numbered | -N | --no-numbered]\n \t\t   [--start-number <n>] [--numbered-files]\n-\t\t   [--in-reply-to=Message-Id] [--suffix=.<sfx>]\n+\t\t   [--in-reply-to=<Message-Id>] [--suffix=.<sfx>]\n \t\t   [--ignore-if-in-upstream]\n-\t\t   [--rfc] [--subject-prefix=Subject-Prefix]\n+\t\t   [--rfc] [--subject-prefix=<Subject-Prefix>]\n \t\t   [(--reroll-count|-v) <n>]\n \t\t   [--to=<email>] [--cc=<email>]\n \t\t   [--[no-]cover-letter] [--quiet] [--notes[=<ref>]]\n@@ -158,7 +158,7 @@ Beware that the default for 'git send-email' is to thread emails\n itself.  If you want `git format-patch` to take care of threading, you\n will want to ensure that threading is disabled for `git send-email`.\n \n---in-reply-to=Message-Id::\n+--in-reply-to=<Message-Id>::\n \tMake the first mail (or all the mails with `--no-thread`) appear as a\n \treply to the given Message-Id, which avoids breaking threads to\n \tprovide a new patch series.\n@@ -192,13 +192,17 @@ will want to ensure that threading is disabled for `git send-email`.\n \n --to=<email>::\n \tAdd a `To:` header to the email headers. This is in addition\n-\tto any configured headers, and may be used multiple times.\n+\tto any configured headers, and may be used multiple times. The\n+\temails given will be used along with any emails given by\n+\t`format.to` configurations.\n \tThe negated form `--no-to` discards all `To:` headers added so\n \tfar (from config or command line).\n \n --cc=<email>::\n \tAdd a `Cc:` header to the email headers. This is in addition\n-\tto any configured headers, and may be used multiple times.\n+\tto any configured headers, and may be used multiple times. The\n+\temails given will be used along with any emails given by\n+\t`format.cc` configurations.\n \tThe negated form `--no-cc` discards all `Cc:` headers added so\n \tfar (from config or command line).\n \n@@ -309,7 +313,8 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.\n --base=<commit>::\n \tRecord the base tree information to identify the state the\n \tpatch series applies to.  See the BASE TREE INFORMATION section\n-\tbelow for details.\n+\tbelow for details. If <commit> is equal to \"auto\", a base commit\n+\tis automatically chosen.\n \n --root::\n \tTreat the revision argument as a <revision range>, even if it\n-- \n2.21.0.1049.geb646f7864\n\n"},{"id":"374919","messageId":"5318dab19abbe2593bd24c6aaff8e6254a39a4e6.1557072929.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1557072929.git.liu.denton@gmail.com","subject":"[PATCH 3/7] branch.c: extract read_branch_config function","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-05T16:24:36Z","receivedAt":"2019-05-05T16:24:42Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the future, we'll need to use `read_branch_config` as a generic base\nfor other branch-config reading functions. Extract it from\n`read_branch_desc` so that it can be reused later.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n branch.c | 9 +++++++--\n 1 file changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/branch.c b/branch.c\nindex 28b81a7e02..4b49976924 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -162,11 +162,11 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,\n \tfree(tracking.src);\n }\n \n-int read_branch_desc(struct strbuf *buf, const char *branch_name)\n+static int read_branch_config(struct strbuf *buf, const char *branch_name, const char *key)\n {\n \tchar *v = NULL;\n \tstruct strbuf name = STRBUF_INIT;\n-\tstrbuf_addf(&name, \"branch.%s.description\", branch_name);\n+\tstrbuf_addf(&name, \"branch.%s.%s\", branch_name, key);\n \tif (git_config_get_string(name.buf, &v)) {\n \t\tstrbuf_release(&name);\n \t\treturn -1;\n@@ -177,6 +177,11 @@ int read_branch_desc(struct strbuf *buf, const char *branch_name)\n \treturn 0;\n }\n \n+int read_branch_desc(struct strbuf *buf, const char *branch_name)\n+{\n+\treturn read_branch_config(buf, branch_name, \"description\");\n+}\n+\n /*\n  * Check if 'name' can be a valid name for a branch; die otherwise.\n  * Return 1 if the named branch already exists; return 0 otherwise.\n-- \n2.21.0.1049.geb646f7864\n\n"},{"id":"374920","messageId":"0f62e4dcb977f3a5d407ff6ae4f11f464558b051.1557072929.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1557072929.git.liu.denton@gmail.com","subject":"[PATCH 4/7] format-patch: make cover letter subject configurable","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-05T16:24:40Z","receivedAt":"2019-05-05T16:24:47Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"We used to populate the subject of the cover letter generated by\ngit-format-patch with \"*** SUBJECT HERE ***\". However, if a user submits\nmultiple patchsets, they may want to keep a consistent subject between\nrerolls.\n\nIf git-format-patch is run on a branch that has\n`branch.<name>.coverSubject` defined, make the cover letter's subject be\nthat value instead of the generic \"*** SUBJECT HERE ***\".\n\nIn addition, add the `--cover-subject` option to override this setting.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/config/branch.txt    |  4 ++++\n Documentation/git-format-patch.txt | 12 ++++++++++++\n branch.c                           |  5 +++++\n branch.h                           |  5 +++++\n builtin/log.c                      | 26 +++++++++++++++++++-------\n t/t4014-format-patch.sh            | 20 ++++++++++++++++++++\n t/t9902-completion.sh              |  5 ++++-\n 7 files changed, 69 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\nindex 019d60ede2..2bff738982 100644\n--- a/Documentation/config/branch.txt\n+++ b/Documentation/config/branch.txt\n@@ -100,3 +100,7 @@ branch.<name>.description::\n \t`git branch --edit-description`. Branch description is\n \tautomatically added in the format-patch cover letter or\n \trequest-pull summary.\n+\n+branch.<name>.coverSubject::\n+\tWhen format-patch generates a cover letter, use the specified\n+\tsubject for the cover letter instead of the generic template.\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 7b71d4e2ed..af7883acbe 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -19,6 +19,7 @@ SYNOPSIS\n \t\t   [--start-number <n>] [--numbered-files]\n \t\t   [--in-reply-to=<Message-Id>] [--suffix=.<sfx>]\n \t\t   [--ignore-if-in-upstream]\n+\t\t   [--cover-subject=<subject>]\n \t\t   [--rfc] [--subject-prefix=<Subject-Prefix>]\n \t\t   [(--reroll-count|-v) <n>]\n \t\t   [--to=<email>] [--cc=<email>]\n@@ -170,6 +171,10 @@ will want to ensure that threading is disabled for `git send-email`.\n \tpatches being generated, and any patch that matches is\n \tignored.\n \n+--cover-subject=<subject>::\n+\tInstead of using the default \"*** SUBJECT HERE ***\" subject for\n+\tthe cover letter, use the given <subject>.\n+\n --subject-prefix=<Subject-Prefix>::\n \tInstead of the standard '[PATCH]' prefix in the subject\n \tline, instead use '[<Subject-Prefix>]'. This\n@@ -346,6 +351,13 @@ attachments, and sign off patches with configuration variables.\n \tcoverletter = auto\n ------------\n \n+In addition, for a specific branch, you can specify a custom cover\n+letter subject.\n+\n+------------\n+[branch \"branch-name\"]\n+\tcoverSubject = \"subject for branch-name only\"\n+------------\n \n DISCUSSION\n ----------\ndiff --git a/branch.c b/branch.c\nindex 4b49976924..40d30b8fa7 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -182,6 +182,11 @@ int read_branch_desc(struct strbuf *buf, const char *branch_name)\n \treturn read_branch_config(buf, branch_name, \"description\");\n }\n \n+int read_branch_subject(struct strbuf *buf, const char *branch_name)\n+{\n+\treturn read_branch_config(buf, branch_name, \"coversubject\");\n+}\n+\n /*\n  * Check if 'name' can be a valid name for a branch; die otherwise.\n  * Return 1 if the named branch already exists; return 0 otherwise.\ndiff --git a/branch.h b/branch.h\nindex 29c1afa4d0..6a8936bbc8 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -79,6 +79,11 @@ extern int install_branch_config(int flag, const char *local, const char *origin\n  */\n extern int read_branch_desc(struct strbuf *, const char *branch_name);\n \n+/*\n+ * Read branch subject\n+ */\n+extern int read_branch_subject(struct strbuf *, const char *branch_name);\n+\n /*\n  * Check if a branch is checked out in the main worktree or any linked\n  * worktree and die (with a message describing its checkout location) if\ndiff --git a/builtin/log.c b/builtin/log.c\nindex ab859f5904..6f19326aea 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1034,13 +1034,14 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \t\t\t      struct commit *origin,\n \t\t\t      int nr, struct commit **list,\n \t\t\t      const char *branch_name,\n+\t\t\t      const char *subject,\n \t\t\t      int quiet)\n {\n \tconst char *committer;\n-\tconst char *body = \"*** SUBJECT HERE ***\\n\\n*** BLURB HERE ***\\n\";\n-\tconst char *msg;\n+\tconst char *body = \"*** BLURB HERE ***\";\n \tstruct shortlog log;\n \tstruct strbuf sb = STRBUF_INIT;\n+\tstruct strbuf subject_sb = STRBUF_INIT;\n \tint i;\n \tconst char *encoding = \"UTF-8\";\n \tint need_8bit_cte = 0;\n@@ -1068,17 +1069,24 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tif (!branch_name)\n \t\tbranch_name = find_branch_name(rev);\n \n-\tmsg = body;\n+\tif (!subject) {\n+\t\tif (branch_name && *branch_name && !read_branch_subject(&subject_sb, branch_name))\n+\t\t\tsubject = subject_sb.buf;\n+\t\telse\n+\t\t\tsubject = \"*** SUBJECT HERE ***\";\n+\t}\n+\n \tpp.fmt = CMIT_FMT_EMAIL;\n \tpp.date_mode.type = DATE_RFC2822;\n \tpp.rev = rev;\n \tpp.print_email_subject = 1;\n \tpp_user_info(&pp, NULL, &sb, committer, encoding);\n-\tpp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);\n-\tpp_remainder(&pp, &msg, &sb, 0);\n+\tpp_title_line(&pp, &subject, &sb, encoding, need_8bit_cte);\n+\tpp_remainder(&pp, &body, &sb, 0);\n \tadd_branch_description(&sb, branch_name);\n \tfprintf(rev->diffopt.file, \"%s\\n\", sb.buf);\n \n+\tstrbuf_release(&subject_sb);\n \tstrbuf_release(&sb);\n \n \tshortlog_init(&log);\n@@ -1512,6 +1520,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tint no_binary_diff = 0;\n \tint zero_commit = 0;\n \tstruct commit *origin = NULL;\n+\tconst char *cover_subject = NULL;\n \tconst char *in_reply_to = NULL;\n \tstruct patch_ids ids;\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -1554,6 +1563,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t{ OPTION_CALLBACK, 0, \"rfc\", &rev, NULL,\n \t\t\t    N_(\"Use [RFC PATCH] instead of [PATCH]\"),\n \t\t\t    PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback },\n+\t\tOPT_STRING(0, \"cover-subject\", &cover_subject, N_(\"subject\"),\n+\t\t\t    N_(\"the subject for the cover letter\")),\n \t\t{ OPTION_CALLBACK, 0, \"subject-prefix\", &rev, N_(\"prefix\"),\n \t\t\t    N_(\"Use [<prefix>] instead of [PATCH]\"),\n \t\t\t    PARSE_OPT_NONEG, subject_prefix_callback },\n@@ -1617,8 +1628,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \textra_to.strdup_strings = 1;\n \textra_cc.strdup_strings = 1;\n \tinit_log_defaults();\n-\tgit_config(git_format_config, NULL);\n \trepo_init_revisions(the_repository, &rev, prefix);\n+\n+\tgit_config(git_format_config, NULL);\n \trev.commit_format = CMIT_FMT_EMAIL;\n \trev.expand_tabs_in_log_default = 0;\n \trev.verbose_header = 1;\n@@ -1893,7 +1905,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tif (thread)\n \t\t\tgen_message_id(&rev, \"cover\");\n \t\tmake_cover_letter(&rev, use_stdout,\n-\t\t\t\t  origin, nr, list, branch_name, quiet);\n+\t\t\t\t  origin, nr, list, branch_name, cover_subject, quiet);\n \t\tprint_bases(&bases, rev.diffopt.file);\n \t\tprint_signature(rev.diffopt.file);\n \t\ttotal++;\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex d05cd256c7..1cf4dfbce2 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -1463,6 +1463,26 @@ test_expect_success 'format patch ignores color.ui' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'cover letter with config subject' '\n+\ttest_config branch.rebuild-1.coverSubject \"config subject\" &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter master >actual &&\n+\tgrep \"Subject: \\[PATCH 0/2\\] config subject\" actual\n+'\n+\n+test_expect_success 'cover letter with command-line subject' '\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter --cover-subject \"command-line subject\" master >actual &&\n+\tgrep \"Subject: \\[PATCH 0/2\\] command-line subject\" actual\n+'\n+\n+test_expect_success 'cover letter with command-line subject overrides config' '\n+\ttest_config branch.rebuild-1.coverSubject \"config subject\" &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter --cover-subject \"command-line subject\" master >actual &&\n+\tgrep \"Subject: \\[PATCH 0/2\\] command-line subject\" actual\n+'\n+\n test_expect_success 'cover letter using branch description (1)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex f5e21bf970..0da92179da 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -1542,7 +1542,10 @@ test_expect_success 'complete tree filename with metacharacters' '\n '\n \n test_expect_success PERL 'send-email' '\n-\ttest_completion \"git send-email --cov\" \"--cover-letter \" &&\n+\ttest_completion \"git send-email --cov\" <<-\\EOF &&\n+\t--cover-letter Z\n+\t--cover-subject=Z\n+\tEOF\n \ttest_completion \"git send-email ma\" \"master \"\n '\n \n-- \n2.21.0.1049.geb646f7864\n\n"},{"id":"374921","messageId":"5ee43bc0b9c349042b60c918b2dda8ed6a7b0a6e.1557072929.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1557072929.git.liu.denton@gmail.com","subject":"[PATCH 5/7] format-patch: move extra_headers logic later","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-05T16:24:44Z","receivedAt":"2019-05-05T16:24:50Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future patch, we need to perform the addition of To: and Cc:\nto extra_headers after the branch_name logic. Simply transpose this\nlogic later in the function so that this happens. (This patch is best\nviewed with `git diff --color-moved`.)\n\nNote that this logic only depends on the `git_config` and\n`repo_init_revisions` and is depended on by the patch creation logic\nwhich is directly below it so this move is effectively a no-op as\nno dependencies being reordered.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/log.c | 58 +++++++++++++++++++++++++--------------------------\n 1 file changed, 29 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 6f19326aea..685e319078 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1665,35 +1665,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\trev.subject_prefix = strbuf_detach(&sprefix, NULL);\n \t}\n \n-\tfor (i = 0; i < extra_hdr.nr; i++) {\n-\t\tstrbuf_addstr(&buf, extra_hdr.items[i].string);\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t}\n-\n-\tif (extra_to.nr)\n-\t\tstrbuf_addstr(&buf, \"To: \");\n-\tfor (i = 0; i < extra_to.nr; i++) {\n-\t\tif (i)\n-\t\t\tstrbuf_addstr(&buf, \"    \");\n-\t\tstrbuf_addstr(&buf, extra_to.items[i].string);\n-\t\tif (i + 1 < extra_to.nr)\n-\t\t\tstrbuf_addch(&buf, ',');\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t}\n-\n-\tif (extra_cc.nr)\n-\t\tstrbuf_addstr(&buf, \"Cc: \");\n-\tfor (i = 0; i < extra_cc.nr; i++) {\n-\t\tif (i)\n-\t\t\tstrbuf_addstr(&buf, \"    \");\n-\t\tstrbuf_addstr(&buf, extra_cc.items[i].string);\n-\t\tif (i + 1 < extra_cc.nr)\n-\t\t\tstrbuf_addch(&buf, ',');\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t}\n-\n-\trev.extra_headers = strbuf_detach(&buf, NULL);\n-\n \tif (from) {\n \t\tif (split_ident_line(&rev.from_ident, from, strlen(from)))\n \t\t\tdie(_(\"invalid ident line: %s\"), from);\n@@ -1796,6 +1767,35 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \t}\n \n+\tfor (i = 0; i < extra_hdr.nr; i++) {\n+\t\tstrbuf_addstr(&buf, extra_hdr.items[i].string);\n+\t\tstrbuf_addch(&buf, '\\n');\n+\t}\n+\n+\tif (extra_to.nr)\n+\t\tstrbuf_addstr(&buf, \"To: \");\n+\tfor (i = 0; i < extra_to.nr; i++) {\n+\t\tif (i)\n+\t\t\tstrbuf_addstr(&buf, \"    \");\n+\t\tstrbuf_addstr(&buf, extra_to.items[i].string);\n+\t\tif (i + 1 < extra_to.nr)\n+\t\t\tstrbuf_addch(&buf, ',');\n+\t\tstrbuf_addch(&buf, '\\n');\n+\t}\n+\n+\tif (extra_cc.nr)\n+\t\tstrbuf_addstr(&buf, \"Cc: \");\n+\tfor (i = 0; i < extra_cc.nr; i++) {\n+\t\tif (i)\n+\t\t\tstrbuf_addstr(&buf, \"    \");\n+\t\tstrbuf_addstr(&buf, extra_cc.items[i].string);\n+\t\tif (i + 1 < extra_cc.nr)\n+\t\t\tstrbuf_addch(&buf, ',');\n+\t\tstrbuf_addch(&buf, '\\n');\n+\t}\n+\n+\trev.extra_headers = strbuf_detach(&buf, NULL);\n+\n \t/*\n \t * We cannot move this anywhere earlier because we do want to\n \t * know if --root was given explicitly from the command line.\n-- \n2.21.0.1049.geb646f7864\n\n"},{"id":"374922","messageId":"5f487f65e88569f38650efe9055b8f6391621c7a.1557072929.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1557072929.git.liu.denton@gmail.com","subject":"[PATCH 6/7] string-list: create string_list_append_all","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-05T16:24:47Z","receivedAt":"2019-05-05T16:24:52Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future patch, we'll need to take one string_list and append it to\nthe end of another. Create the `string_list_append_all` function which\ndoes this.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n string-list.c | 9 +++++++++\n string-list.h | 7 +++++++\n 2 files changed, 16 insertions(+)\n\ndiff --git a/string-list.c b/string-list.c\nindex a917955fbd..e63d58fbd2 100644\n--- a/string-list.c\n+++ b/string-list.c\n@@ -215,6 +215,15 @@ struct string_list_item *string_list_append(struct string_list *list,\n \t\t\tlist->strdup_strings ? xstrdup(string) : (char *)string);\n }\n \n+void string_list_append_all(struct string_list *list,\n+\t\t\t    const struct string_list *append_list)\n+{\n+\tstruct string_list_item *item;\n+\tALLOC_GROW(list->items, list->nr + append_list->nr, list->alloc);\n+\tfor_each_string_list_item(item, append_list)\n+\t\tstring_list_append(list, item->string);\n+}\n+\n /*\n  * Encapsulate the compare function pointer because ISO C99 forbids\n  * casting from void * to a function pointer and vice versa.\ndiff --git a/string-list.h b/string-list.h\nindex 18c718c12c..32e0c4b47f 100644\n--- a/string-list.h\n+++ b/string-list.h\n@@ -208,6 +208,13 @@ struct string_list_item *string_list_append(struct string_list *list, const char\n  */\n struct string_list_item *string_list_append_nodup(struct string_list *list, char *string);\n \n+/**\n+ * Add all strings in append_list to list.  If list->strdup_string is\n+ * set, then each string is copied; otherwise the new string_list_entry\n+ * refers to the entry in the append_list.\n+ */\n+void string_list_append_all(struct string_list *list, const struct string_list *append_list);\n+\n /**\n  * Sort the list's entries by string value in `strcmp()` order.\n  */\n-- \n2.21.0.1049.geb646f7864\n\n"},{"id":"374923","messageId":"7e625b688883c0528348f77d181bc28195d8ab92.1557072929.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1557072929.git.liu.denton@gmail.com","subject":"[PATCH 7/7] format-patch: read branch-specific To: and Cc: headers","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-05T16:24:49Z","receivedAt":"2019-05-05T16:24:55Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"If a user wishes to keep track of whom to Cc: on individual patchsets,\nthey must manually keep track of each recipient and fill it in with the\n`--cc` option on git-format-patch each time. However, on the Git mailing\nlist, Cc:'s are typically never dropped. As a result, it would be nice\nto have a method to keep track of recipients on a per-branch basis.\n\nCurrently, git-format-patch gets its To: headers from the `--to` options\nand the `format.to` config variable. The Cc: header is derived\nsimilarly.\n\nIn addition to the above, read To: and Cc: headers from\n`branch.<name>.to` and `branch.<name>.cc` so that users can have\nbranch-specific configuration options.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/config/branch.txt    |   6 ++\n Documentation/git-format-patch.txt |  10 +--\n builtin/log.c                      |  63 +++++++++++++++--\n t/t4014-format-patch.sh            | 108 +++++++++++++++++++++++------\n 4 files changed, 159 insertions(+), 28 deletions(-)\n\ndiff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\nindex 2bff738982..22b9bf3d0d 100644\n--- a/Documentation/config/branch.txt\n+++ b/Documentation/config/branch.txt\n@@ -104,3 +104,9 @@ branch.<name>.description::\n branch.<name>.coverSubject::\n \tWhen format-patch generates a cover letter, use the specified\n \tsubject for the cover letter instead of the generic template.\n+\n+branch.<name>.to::\n+branch.<name>.cc::\n+\tAdditional recipients to include in a patch to be submitted\n+\tby mail.  See the --to and --cc options in\n+\tlinkgit:git-format-patch[1].\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex af7883acbe..1c972f683a 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -199,7 +199,7 @@ will want to ensure that threading is disabled for `git send-email`.\n \tAdd a `To:` header to the email headers. This is in addition\n \tto any configured headers, and may be used multiple times. The\n \temails given will be used along with any emails given by\n-\t`format.to` configurations.\n+\t`format.to` and `branch.<name>.to` configurations.\n \tThe negated form `--no-to` discards all `To:` headers added so\n \tfar (from config or command line).\n \n@@ -207,7 +207,7 @@ will want to ensure that threading is disabled for `git send-email`.\n \tAdd a `Cc:` header to the email headers. This is in addition\n \tto any configured headers, and may be used multiple times. The\n \temails given will be used along with any emails given by\n-\t`format.cc` configurations.\n+\t`format.cc` and `branch.<name>.cc` configurations.\n \tThe negated form `--no-cc` discards all `Cc:` headers added so\n \tfar (from config or command line).\n \n@@ -335,7 +335,7 @@ CONFIGURATION\n -------------\n You can specify extra mail header lines to be added to each message,\n defaults for the subject prefix and file suffix, number patches when\n-outputting more than one patch, add \"To\" or \"Cc:\" headers, configure\n+outputting more than one patch, add \"To:\" or \"Cc:\" headers, configure\n attachments, and sign off patches with configuration variables.\n \n ------------\n@@ -352,11 +352,13 @@ attachments, and sign off patches with configuration variables.\n ------------\n \n In addition, for a specific branch, you can specify a custom cover\n-letter subject.\n+letter subject, and add additional \"To:\" or \"Cc:\" headers.\n \n ------------\n [branch \"branch-name\"]\n \tcoverSubject = \"subject for branch-name only\"\n+\tto = <email>\n+\tcc = <email>\n ------------\n \n DISCUSSION\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 685e319078..6825d95c5f 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -738,6 +738,8 @@ static char *default_attach = NULL;\n static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;\n static struct string_list extra_to = STRING_LIST_INIT_NODUP;\n static struct string_list extra_cc = STRING_LIST_INIT_NODUP;\n+int to_cleared;\n+int cc_cleared;\n \n static void add_header(const char *value)\n {\n@@ -1030,6 +1032,55 @@ static void show_diffstat(struct rev_info *rev,\n \tfprintf(rev->diffopt.file, \"\\n\");\n }\n \n+static void add_branch_headers(struct rev_info *rev, const char *branch_name)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst struct string_list *values;\n+\n+\tif (!branch_name)\n+\t\tbranch_name = find_branch_name(rev);\n+\n+\tif (!branch_name || !*branch_name)\n+\t\treturn;\n+\n+\t/*\n+\t * HACK: We only use branch-specific recipients iff the list has not\n+\t * been cleared by an earlier --no-{to,cc} option on the command-line.\n+\t *\n+\t * When we get format.{to,cc} options, they can be cleared by\n+\t * --no-{to,cc} options since the `git_config` call comes before the\n+\t *  `parse_options` call.\n+\t *\n+\t *  However, in the case of branch.<name>.{to,cc}, this function needs\n+\t *  to be called after `setup_revisions`, which must be called after\n+\t *  `parse_options`. However, in order for the --no-{to,cc} logic to\n+\t *  clear the extra_{to,cc} string_list, this function should actually\n+\t *  be called _before_ `parse_options`. As a result, we have a circular\n+\t *  dependency.\n+\t *\n+\t *  The {to,cc}_cleared flag lets us workaround this by just no\n+\t *  including branch-specific recipients iff --no-{to,cc} has been\n+\t *  specified on the command-line.\n+\t */\n+\n+\tif (!to_cleared) {\n+\t\tstrbuf_addf(&buf, \"branch.%s.to\", branch_name);\n+\t\tvalues = git_config_get_value_multi(buf.buf);\n+\t\tif (values)\n+\t\t\tstring_list_append_all(&extra_to, values);\n+\t}\n+\n+\tif (!cc_cleared) {\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addf(&buf, \"branch.%s.cc\", branch_name);\n+\t\tvalues = git_config_get_value_multi(buf.buf);\n+\t\tif (values)\n+\t\t\tstring_list_append_all(&extra_cc, values);\n+\t}\n+\n+\tstrbuf_release(&buf);\n+}\n+\n static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \t\t\t      struct commit *origin,\n \t\t\t      int nr, struct commit **list,\n@@ -1289,18 +1340,20 @@ static int header_callback(const struct option *opt, const char *arg, int unset)\n \n static int to_callback(const struct option *opt, const char *arg, int unset)\n {\n-\tif (unset)\n+\tif (unset) {\n+\t\tto_cleared = 1;\n \t\tstring_list_clear(&extra_to, 0);\n-\telse\n+\t} else\n \t\tstring_list_append(&extra_to, arg);\n \treturn 0;\n }\n \n static int cc_callback(const struct option *opt, const char *arg, int unset)\n {\n-\tif (unset)\n+\tif (unset) {\n+\t\tcc_cleared = 1;\n \t\tstring_list_clear(&extra_cc, 0);\n-\telse\n+\t} else\n \t\tstring_list_append(&extra_cc, arg);\n \treturn 0;\n }\n@@ -1767,6 +1820,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \t}\n \n+\tadd_branch_headers(&rev, branch_name);\n+\n \tfor (i = 0; i < extra_hdr.nr; i++) {\n \t\tstrbuf_addstr(&buf, extra_hdr.items[i].string);\n \t\tstrbuf_addch(&buf, '\\n');\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 1cf4dfbce2..6578e6b433 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -234,6 +234,65 @@ test_expect_failure 'configuration To: header (rfc2047)' '\n \tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs9\n '\n \n+test_expect_success 'branch-specific configuration To: header (ascii)' '\n+\n+\ttest_unconfig format.to &&\n+\tgit config branch.side.to \"R E Cipient <rcipient@example.com>\" &&\n+\tgit format-patch --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs10\n+'\n+\n+test_expect_failure 'branch-specific configuration To: header (rfc822)' '\n+\n+\tgit config branch.side.to \"R. E. Cipient <rcipient@example.com>\" &&\n+\tgit format-patch --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" hdrs10\n+'\n+\n+test_expect_failure 'branch-specific configuration To: header (rfc2047)' '\n+\n+\tgit config branch.side.to \"R Ä Cipient <rcipient@example.com>\" &&\n+\tgit format-patch --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs10\n+'\n+\n+test_expect_success 'all recipients included from all sources' '\n+\n+\tgit config format.to \"Format To1 <formatto1@example.com>\" &&\n+\tgit config --add format.to \"Format To2 <formatto2@example.com>\" &&\n+\tgit config format.cc \"Format Cc1 <formatcc1@example.com>\" &&\n+\tgit config --add format.cc \"Format Cc2 <formatcc2@example.com>\" &&\n+\tgit config branch.side.to \"Branch To1 <branchto1@example.com>\" &&\n+\tgit config --add branch.side.to \"Branch To2 <branchto2@example.com>\" &&\n+\tgit config branch.side.cc \"Branch Cc1 <branchcc1@example.com>\" &&\n+\tgit config --add branch.side.cc \"Branch Cc2 <branchcc2@example.com>\" &&\n+\tcat <<-\\EOF >expect &&\n+\tTo: Format To1 <formatto1@example.com>,\n+\t    Format To2 <formatto2@example.com>,\n+\t    Command-line To1 <commandlineto1@example.com>,\n+\t    Command-line To2 <commandlineto2@example.com>,\n+\t    Branch To1 <branchto1@example.com>,\n+\t    Branch To2 <branchto2@example.com>\n+\tCc: Format Cc1 <formatcc1@example.com>,\n+\t    Format Cc2 <formatcc2@example.com>,\n+\t    Command-line Cc1 <commandlinecc1@example.com>,\n+\t    Command-line Cc2 <commandlinecc2@example.com>,\n+\t    Branch Cc1 <branchcc1@example.com>,\n+\t    Branch Cc2 <branchcc2@example.com>\n+\n+\tEOF\n+\tgit format-patch --stdout \\\n+\t\t--to=\"Command-line To1 <commandlineto1@example.com>\" \\\n+\t\t--to=\"Command-line To2 <commandlineto2@example.com>\" \\\n+\t\t--cc=\"Command-line Cc1 <commandlinecc1@example.com>\" \\\n+\t\t--cc=\"Command-line Cc2 <commandlinecc2@example.com>\" \\\n+\t\tmaster..side | sed -ne \"/^To:/,/^$/p;/^$/q\" >patch10 &&\n+\ttest_cmp expect patch10\n+'\n+\n # check_patch <patch>: Verify that <patch> looks like a half-sane\n # patch email to avoid a false positive with !grep\n check_patch () {\n@@ -286,42 +345,51 @@ test_expect_success '--no-to overrides config.to' '\n \n \tgit config --replace-all format.to \\\n \t\t\"R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-to --stdout master..side >patch10 &&\n-\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n-\tcheck_patch hdrs10 &&\n-\t! grep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs10\n+\tgit config --replace-all branch.side.to \\\n+\t\t\"B R Anch <branch@example.com>\" &&\n+\tgit format-patch --no-to --stdout master..side >patch11 &&\n+\tsed -e \"/^\\$/q\" patch11 >hdrs11 &&\n+\tcheck_patch hdrs11 &&\n+\t! grep \"R E Cipient <rcipient@example.com>\" hdrs11 &&\n+\t! grep \"B R Anch <branch@example.com>\" hdrs11\n '\n \n test_expect_success '--no-to and --to replaces config.to' '\n \n \tgit config --replace-all format.to \\\n \t\t\"Someone <someone@out.there>\" &&\n+\tgit config --replace-all branch.side.to \\\n+\t\t\"B R Anch2 <branch2@example.com>\" &&\n \tgit format-patch --no-to --to=\"Someone Else <else@out.there>\" \\\n-\t\t--stdout master..side >patch11 &&\n-\tsed -e \"/^\\$/q\" patch11 >hdrs11 &&\n-\tcheck_patch hdrs11 &&\n-\t! grep \"^To: Someone <someone@out.there>\\$\" hdrs11 &&\n-\tgrep \"^To: Someone Else <else@out.there>\\$\" hdrs11\n+\t\t--stdout master..side >patch12 &&\n+\tsed -e \"/^\\$/q\" patch12 >hdrs12 &&\n+\tcheck_patch hdrs12 &&\n+\t! grep \"Someone <someone@out.there>\" hdrs12 &&\n+\t! grep \"B R Anch2 <branch2@example.com>\" hdrs12 &&\n+\tgrep \"^To: Someone Else <else@out.there>\\$\" hdrs12\n '\n \n test_expect_success '--no-cc overrides config.cc' '\n \n \tgit config --replace-all format.cc \\\n \t\t\"C E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-cc --stdout master..side >patch12 &&\n-\tsed -e \"/^\\$/q\" patch12 >hdrs12 &&\n-\tcheck_patch hdrs12 &&\n-\t! grep \"^Cc: C E Cipient <rcipient@example.com>\\$\" hdrs12\n+\tgit config --replace-all branch.side.cc \\\n+\t\t\"B R Anch3 <branch3@example.com>\" &&\n+\tgit format-patch --no-cc --stdout master..side >patch13 &&\n+\tsed -e \"/^\\$/q\" patch13 >hdrs13 &&\n+\tcheck_patch hdrs13 &&\n+\t! grep \"C E Cipient <rcipient@example.com>\" hdrs13 &&\n+\t! grep \"B R Anch3 <branch3@example.com>\" hdrs13\n '\n \n test_expect_success '--no-add-header overrides config.headers' '\n \n \tgit config --replace-all format.headers \\\n \t\t\"Header1: B E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-add-header --stdout master..side >patch13 &&\n-\tsed -e \"/^\\$/q\" patch13 >hdrs13 &&\n-\tcheck_patch hdrs13 &&\n-\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" hdrs13\n+\tgit format-patch --no-add-header --stdout master..side >patch14 &&\n+\tsed -e \"/^\\$/q\" patch14 >hdrs14 &&\n+\tcheck_patch hdrs14 &&\n+\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" hdrs14\n '\n \n test_expect_success 'multiple files' '\n@@ -957,7 +1025,7 @@ test_expect_success 'format-patch wraps extremely long subject (ascii)' '\n \tgit add file &&\n \tgit commit -m \"$M512\" &&\n \tgit format-patch --stdout -1 >patch &&\n-\tsed -n \"/^Subject/p; /^ /p; /^$/q\" <patch >subject &&\n+\tsed -n \"/^Subject/p; /^ /p; /^Header1:/q\" <patch >subject &&\n \ttest_cmp expect subject\n '\n \n@@ -996,7 +1064,7 @@ test_expect_success 'format-patch wraps extremely long subject (rfc2047)' '\n \tgit add file &&\n \tgit commit -m \"$M512\" &&\n \tgit format-patch --stdout -1 >patch &&\n-\tsed -n \"/^Subject/p; /^ /p; /^$/q\" <patch >subject &&\n+\tsed -n \"/^Subject/p; /^ /p; /^Header1:/q\" <patch >subject &&\n \ttest_cmp expect subject\n '\n \n@@ -1005,7 +1073,7 @@ check_author() {\n \tgit add file &&\n \tGIT_AUTHOR_NAME=$1 git commit -m author-check &&\n \tgit format-patch --stdout -1 >patch &&\n-\tsed -n \"/^From: /p; /^ /p; /^$/q\" <patch >actual &&\n+\tsed -n \"/^From: /p; /^ /p; /^Date:/q\" <patch >actual &&\n \ttest_cmp expect actual\n }\n \n-- \n2.21.0.1049.geb646f7864\n\n"},{"id":"375000","messageId":"xmqq4l66myn3.fsf@gitster-ct.c.googlers.com","threadId":"51038","inReplyTo":"cover.1557072929.git.liu.denton@gmail.com","subject":"Re: [PATCH 0/7] teach branch-specific options for format-patch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-05-07T08:56:00Z","receivedAt":"2019-05-07T08:56:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> Currently, format-patch only accepts branch.<name>.description as a\n> branch-specific configuration variable. However, there are many other\n> options which would be useful to have on a branch-by-branch basis,\n> namely cover letter subject and To: and Cc: headers.\n>\n> Teach format-patch to recognise these branch-specific configuration\n> options.\n>\n> Note that this patchset[1] was created using these new configuration\n> options:\n>\n> \t[branch \"submitted/fix-revisions-txt\"]\n> \t\tcoverSubject = \"cleanup revisions.txt\"\n> \t\tcc = \"Andreas Heiduk <asheiduk@gmail.com>\"\n> \t\tcc = \"Duy Nguyen <pclouds@gmail.com>\"\n> \t\tcc = \"Junio C Hamano <gitster@pobox.com>\"\n\nDo we have format.<something> configuration for these things?\n\nWhat I am trying to get at is if these are better structured similar\nto http options where http.<something> supplies the overall default\nfor <something>, while http.<destination>.<something> gives a more\ndestination site specific override of that default.  I.e. format.cc\nis used as fallback, while format.<branch>.cc is used to override.\n\nIn any case, it smells to me that branch.<branch>.cc does not hint\nstrongly enough that they are meant to affect format-patch.\n\n\n"},{"id":"375032","messageId":"20190507141942.GA359@archbookpro.localdomain","threadId":"51038","inReplyTo":"xmqq4l66myn3.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 0/7] teach branch-specific options for format-patch","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-07T14:19:42Z","receivedAt":"2019-05-07T14:19:47Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Junio,\n\nOn Tue, May 07, 2019 at 05:56:00PM +0900, Junio C Hamano wrote:\n> Denton Liu <liu.denton@gmail.com> writes:\n>\n> > Currently, format-patch only accepts branch.<name>.description as a\n> > branch-specific configuration variable. However, there are many other\n> > options which would be useful to have on a branch-by-branch basis,\n> > namely cover letter subject and To: and Cc: headers.\n> >\n> > Teach format-patch to recognise these branch-specific configuration\n> > options.\n> >\n> > Note that this patchset[1] was created using these new configuration\n> > options:\n> >\n> > \t[branch \"submitted/fix-revisions-txt\"]\n> > \t\tcoverSubject = \"cleanup revisions.txt\"\n> > \t\tcc = \"Andreas Heiduk <asheiduk@gmail.com>\"\n> > \t\tcc = \"Duy Nguyen <pclouds@gmail.com>\"\n> > \t\tcc = \"Junio C Hamano <gitster@pobox.com>\"\n>\n> Do we have format.<something> configuration for these things?\n\nCurrently, we have format.{to,cc} but not format.coverSubject. The\nreason why is that for the cover-subject, I didn't think that it would\nmake a lot of sense to have a general configuration for this since it\nvaries between branches, just like how branch.<name>.description does\nnot have a matching format.description.\n\n>\n> What I am trying to get at is if these are better structured similar\n> to http options where http.<something> supplies the overall default\n> for <something>, while http.<destination>.<something> gives a more\n> destination site specific override of that default.  I.e. format.cc\n> is used as fallback, while format.<branch>.cc is used to override.\n\nThe reason why I chose to use branch.<name>.* is because format-patch\ncurrently reads from branch.<name>.description and I wanted to build on\ntop of that. In addition, I didn't want to scatter branch-specific\nconfigs in two different place (i.e. have a branch.<branchName>.description\nalongside a format.<branchName>.coverSubject).\n\n>\n> In any case, it smells to me that branch.<branch>.cc does not hint\n> strongly enough that they are meant to affect format-patch.\n>\n>\n\nWould you suggest moving to a format.<branchname>.* approach or would it\nmake sense to rename the configs to something like\nbranch.<name>.{emailCoverSubject,emailTo,emailCc}?\n\nThanks,\n\nDenton\n"},{"id":"375037","messageId":"xmqq36lql2yg.fsf@gitster-ct.c.googlers.com","threadId":"51038","inReplyTo":"20190507141942.GA359@archbookpro.localdomain","subject":"Re: [PATCH 0/7] teach branch-specific options for format-patch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-05-07T15:05:43Z","receivedAt":"2019-05-07T15:05:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> The reason why I chose to use branch.<name>.* is because format-patch\n> currently reads from branch.<name>.description and I wanted to build on\n> top of that. In addition, I didn't want to scatter branch-specific\n> configs in two different place (i.e. have a branch.<branchName>.description\n> alongside a format.<branchName>.coverSubject).\n\nThe \"branch.<name>.description\" variable lives at a lot higher level\nof abstraction than \"use this for the default value of option X of\nthe command Y\".  It gives a place for the user to state what the\nbranch is about.  It makes 100% sense not to have it under format.*\nhierarchy, because it is designed to be agnostic to what individual\ncommand uses it for.  It is there to talk about what the branch is\nfor, and what the variable says about the branch to its users,\ni.e. various tools, does not change depending on who is listening to\nit.\n\nThe format-patch command may use it as a hint for writing the cover\nletter message.  Repository browsers may use it while listing the\nbranches when more descriptive text than the branch name is desired.\nRequest-pull tool may use it when preparing the branch summary.\n\nThe point is that \"description\" is about \"what that *branch* is\",\nnot \"what that branch means for the format-patch user\".\n\nYou cannot compare that with something like \"when format-patch\nprepares an e-mail, add CC: header to this address\", which is very\nspecific to the single command, i.e. \"how command X uses it\".  I\nthink that is the gist of the new variables you are adding.\n\n>> In any case, it smells to me that branch.<branch>.cc does not hint\n>> strongly enough that they are meant to affect format-patch.\n>\n> Would you suggest moving to a format.<branchname>.* approach or would it\n> make sense to rename the configs to something like\n> branch.<name>.{emailCoverSubject,emailTo,emailCc}?\n\nSo if I have to pick between the two, I would probably vote for the\nformer from the philosophical ground, but operationally, I suspect\nthat the latter would be much simpler to use.  You could even have\n\"git branch -d <name>\" to get rid of them at the same time.\n\nBut as I may have hinted in the message you are responding to, I am\nnot quite convinced we want these configuration variables in the\nfirst place.  Why should both description and coverSubject need to\nexist?  Perhaps we should add a heuristic like \"If the branch\ndescription looks like a single line, optionally followed by 'a\nblank line and more paragraphs', use the first line as the subject\nof the cover letter (and the remainder as the body of the cover\nletter) or something?\n\n"},{"id":"375040","messageId":"20190507152118.GA12057@archbookpro.localdomain","threadId":"51038","inReplyTo":"xmqq36lql2yg.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 0/7] teach branch-specific options for format-patch","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-07T15:21:18Z","receivedAt":"2019-05-07T15:21:23Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"On Wed, May 08, 2019 at 12:05:43AM +0900, Junio C Hamano wrote:\n> Denton Liu <liu.denton@gmail.com> writes:\n\n[snip]\n\n> >\n> > Would you suggest moving to a format.<branchname>.* approach or would it\n> > make sense to rename the configs to something like\n> > branch.<name>.{emailCoverSubject,emailTo,emailCc}?\n> \n> So if I have to pick between the two, I would probably vote for the\n> former from the philosophical ground, but operationally, I suspect\n> that the latter would be much simpler to use.  You could even have\n> \"git branch -d <name>\" to get rid of them at the same time.\n> \n> But as I may have hinted in the message you are responding to, I am\n> not quite convinced we want these configuration variables in the\n> first place.  Why should both description and coverSubject need to\n> exist?  Perhaps we should add a heuristic like \"If the branch\n> description looks like a single line, optionally followed by 'a\n> blank line and more paragraphs', use the first line as the subject\n> of the cover letter (and the remainder as the body of the cover\n> letter) or something?\n> \n\nI considered doing something like that but I opted not to because it\nwouldn't be a backwards compatible change and I didn't want to surprise\nany future users with a change like this.\n\nFor branch.<branchname>.{to,cc}, I wanted these config options because\nthe current method for format-patch to handle Cc-lists is just manually\nkeeping track of the people who have responded and entering them into\nthe --cc option of format-patch.\n"},{"id":"375042","messageId":"87tve6gtcp.fsf@evledraar.gmail.com","threadId":"51038","inReplyTo":"20190507152118.GA12057@archbookpro.localdomain","subject":"Re: [PATCH 0/7] teach branch-specific options for format-patch","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2019-05-07T15:46:46Z","receivedAt":"2019-05-07T15:46:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, May 07 2019, Denton Liu wrote:\n\n> On Wed, May 08, 2019 at 12:05:43AM +0900, Junio C Hamano wrote:\n>> Denton Liu <liu.denton@gmail.com> writes:\n>\n> [snip]\n>\n>> >\n>> > Would you suggest moving to a format.<branchname>.* approach or would it\n>> > make sense to rename the configs to something like\n>> > branch.<name>.{emailCoverSubject,emailTo,emailCc}?\n>>\n>> So if I have to pick between the two, I would probably vote for the\n>> former from the philosophical ground, but operationally, I suspect\n>> that the latter would be much simpler to use.  You could even have\n>> \"git branch -d <name>\" to get rid of them at the same time.\n>>\n>> But as I may have hinted in the message you are responding to, I am\n>> not quite convinced we want these configuration variables in the\n>> first place.  Why should both description and coverSubject need to\n>> exist?  Perhaps we should add a heuristic like \"If the branch\n>> description looks like a single line, optionally followed by 'a\n>> blank line and more paragraphs', use the first line as the subject\n>> of the cover letter (and the remainder as the body of the cover\n>> letter) or something?\n>>\n>\n> I considered doing something like that but I opted not to because it\n> wouldn't be a backwards compatible change and I didn't want to surprise\n> any future users with a change like this.\n>\n> For branch.<branchname>.{to,cc}, I wanted these config options because\n> the current method for format-patch to handle Cc-lists is just manually\n> keeping track of the people who have responded and entering them into\n> the --cc option of format-patch.\n\nThis may just be more insanity to implement right now, but perhaps in\naddition to \"gitdir:\" etc. in the IncludeIf config syntax we'd want to\naim for \"HEADref\" (or some saner name). I.e. allowing to include/enable\narbitrary config based on the ref name.\n\nChicken & egg problems though...\n"},{"id":"375083","messageId":"xmqqh8a5k9bu.fsf@gitster-ct.c.googlers.com","threadId":"51038","inReplyTo":"87tve6gtcp.fsf@evledraar.gmail.com","subject":"Re: [PATCH 0/7] teach branch-specific options for format-patch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-05-08T01:45:41Z","receivedAt":"2019-05-08T01:45:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> This may just be more insanity to implement right now, but perhaps in\n> addition to \"gitdir:\" etc. in the IncludeIf config syntax we'd want to\n> aim for \"HEADref\" (or some saner name). I.e. allowing to include/enable\n> arbitrary config based on the ref name.\n>\n> Chicken & egg problems though...\n\nThe saner name might be\n\n    [IncludeIf \"onBranch:my-topic\"] path = ...\n\nWould we discard the section when \"branch -d my-topic\" is done, or\nwould we keep it for the next creation of the branch with the same\nname?\n"},{"id":"375771","messageId":"cover.1558052674.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1557072929.git.liu.denton@gmail.com","subject":"[PATCH v2 0/6] teach branch-specific options for format-patch","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-17T00:27:38Z","receivedAt":"2019-05-17T00:27:43Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi all,\n\nI thought a lot about building the branch-specific include option but I\nopted against this because `format.<name>.coverSubject` is a\nbranch-exclusive config option, whereas it doesn't make sense to have a\n`format.coverSubject` option.\n\nThus, if we pursue this, we *need* to have a `format.<name>.*` section\nso let's not deal with the complexity of buliding a generic include\nsystem for now.\n\nAlso, I might consider adding a `format.<name>.outputDirectory` option\nin the future but let's see what kind of comments this patchset gets.\n\nChanges since v1:\n\n* Used format.<branch-name>.* variables instead of branch.<branch-name>.*\n\n\nDenton Liu (6):\n  t4014: clean up style\n  Doc: add more detail for git-format-patch\n  format-patch: make cover letter subject configurable\n  format-patch: move extra_headers logic later\n  string-list: create string_list_append_all\n  format-patch: read branch-specific To: and Cc: headers\n\n Documentation/config/format.txt    |  12 +-\n Documentation/git-format-patch.txt |  33 +-\n builtin/branch.c                   |  16 +-\n builtin/log.c                      | 162 +++++--\n string-list.c                      |   9 +\n string-list.h                      |   7 +\n t/t3200-branch.sh                  |   8 +-\n t/t4014-format-patch.sh            | 708 +++++++++++++++++------------\n t/t9902-completion.sh              |   5 +-\n 9 files changed, 608 insertions(+), 352 deletions(-)\n\nInterdiff against v1:\ndiff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\nindex d292210cf6..8f4b3faadd 100644\n--- a/Documentation/config/branch.txt\n+++ b/Documentation/config/branch.txt\n@@ -100,13 +100,3 @@ branch.<name>.description::\n \t`git branch --edit-description`. Branch description is\n \tautomatically added in the format-patch cover letter or\n \trequest-pull summary.\n-\n-branch.<name>.coverSubject::\n-\tWhen format-patch generates a cover letter, use the specified\n-\tsubject for the cover letter instead of the generic template.\n-\n-branch.<name>.to::\n-branch.<name>.cc::\n-\tAdditional recipients to include in a patch to be submitted\n-\tby mail.  See the --to and --cc options in\n-\tlinkgit:git-format-patch[1].\ndiff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\nindex dc77941c48..d387451573 100644\n--- a/Documentation/config/format.txt\n+++ b/Documentation/config/format.txt\n@@ -28,14 +28,22 @@ format.headers::\n \n format.to::\n format.cc::\n+format.<branch-name>.to::\n+format.<branch-name>.cc::\n \tAdditional recipients to include in a patch to be submitted\n-\tby mail.  See the --to and --cc options in\n-\tlinkgit:git-format-patch[1].\n+\tby mail.  For the <branch-name> options, the recipients are only\n+\tincluded if patches are generated for the given <branch-name>.\n+\tSee the --to and --cc options in linkgit:git-format-patch[1].\n \n format.subjectPrefix::\n \tThe default for format-patch is to output files with the '[PATCH]'\n \tsubject prefix. Use this variable to change that prefix.\n \n+format.<branch-name>.coverSubject::\n+\tWhen format-patch generates a cover letter for the given\n+\t<branch-name>, use the specified subject for the cover letter\n+\tinstead of the generic template.\n+\n format.signature::\n \tThe default for format-patch is to output a signature containing\n \tthe Git version number. Use this variable to change that default.\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 1c972f683a..4e826010f6 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -199,7 +199,7 @@ will want to ensure that threading is disabled for `git send-email`.\n \tAdd a `To:` header to the email headers. This is in addition\n \tto any configured headers, and may be used multiple times. The\n \temails given will be used along with any emails given by\n-\t`format.to` and `branch.<name>.to` configurations.\n+\t`format.to` and `format.<branch-name>.to` configurations.\n \tThe negated form `--no-to` discards all `To:` headers added so\n \tfar (from config or command line).\n \n@@ -207,7 +207,7 @@ will want to ensure that threading is disabled for `git send-email`.\n \tAdd a `Cc:` header to the email headers. This is in addition\n \tto any configured headers, and may be used multiple times. The\n \temails given will be used along with any emails given by\n-\t`format.cc` and `branch.<name>.cc` configurations.\n+\t`format.cc` and `format.<branch-name>.cc` configurations.\n \tThe negated form `--no-cc` discards all `Cc:` headers added so\n \tfar (from config or command line).\n \n@@ -355,7 +355,7 @@ In addition, for a specific branch, you can specify a custom cover\n letter subject, and add additional \"To:\" or \"Cc:\" headers.\n \n ------------\n-[branch \"branch-name\"]\n+[format \"branch-name\"]\n \tcoverSubject = \"subject for branch-name only\"\n \tto = <email>\n \tcc = <email>\ndiff --git a/branch.c b/branch.c\nindex 83cd441790..643694542a 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -163,11 +163,11 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,\n \tfree(tracking.src);\n }\n \n-static int read_branch_config(struct strbuf *buf, const char *branch_name, const char *key)\n+int read_branch_desc(struct strbuf *buf, const char *branch_name)\n {\n \tchar *v = NULL;\n \tstruct strbuf name = STRBUF_INIT;\n-\tstrbuf_addf(&name, \"branch.%s.%s\", branch_name, key);\n+\tstrbuf_addf(&name, \"branch.%s.description\", branch_name);\n \tif (git_config_get_string(name.buf, &v)) {\n \t\tstrbuf_release(&name);\n \t\treturn -1;\n@@ -178,16 +178,6 @@ static int read_branch_config(struct strbuf *buf, const char *branch_name, const\n \treturn 0;\n }\n \n-int read_branch_desc(struct strbuf *buf, const char *branch_name)\n-{\n-\treturn read_branch_config(buf, branch_name, \"description\");\n-}\n-\n-int read_branch_subject(struct strbuf *buf, const char *branch_name)\n-{\n-\treturn read_branch_config(buf, branch_name, \"coversubject\");\n-}\n-\n /*\n  * Check if 'name' can be a valid name for a branch; die otherwise.\n  * Return 1 if the named branch already exists; return 0 otherwise.\ndiff --git a/branch.h b/branch.h\nindex 363a4fae9d..6f38db14e9 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -79,11 +79,6 @@ int install_branch_config(int flag, const char *local, const char *origin, const\n  */\n int read_branch_desc(struct strbuf *, const char *branch_name);\n \n-/*\n- * Read branch subject\n- */\n-extern int read_branch_subject(struct strbuf *, const char *branch_name);\n-\n /*\n  * Check if a branch is checked out in the main worktree or any linked\n  * worktree and die (with a message describing its checkout location) if\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex d4359b33ac..367e1fc9bc 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -178,12 +178,22 @@ static int check_branch_commit(const char *branchname, const char *refname,\n \treturn 0;\n }\n \n+static const char *branch_specific_config[] = {\n+\t\"branch\",\n+\t\"format\",\n+\tNULL\n+};\n+\n static void delete_branch_config(const char *branchname)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n-\tstrbuf_addf(&buf, \"branch.%s\", branchname);\n-\tif (git_config_rename_section(buf.buf, NULL) < 0)\n-\t\twarning(_(\"Update of config-file failed\"));\n+\tint i;\n+\tfor (i = 0; branch_specific_config[i]; i++) {\n+\t\tstrbuf_addf(&buf, \"%s.%s\", branch_specific_config[i], branchname);\n+\t\tif (git_config_rename_section(buf.buf, NULL) < 0)\n+\t\t\twarning(_(\"Update of config-file failed\"));\n+\t\tstrbuf_reset(&buf);\n+\t}\n \tstrbuf_release(&buf);\n }\n \ndiff --git a/builtin/log.c b/builtin/log.c\nindex 3adc942b8c..e1d793577d 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1032,6 +1032,21 @@ static void show_diffstat(struct rev_info *rev,\n \tfprintf(rev->diffopt.file, \"\\n\");\n }\n \n+static int read_branch_subject(struct strbuf *buf, const char *branch_name)\n+{\n+\tchar *v = NULL;\n+\tstruct strbuf name = STRBUF_INIT;\n+\tstrbuf_addf(&name, \"format.%s.coverSubject\", branch_name);\n+\tif (git_config_get_string(name.buf, &v)) {\n+\t\tstrbuf_release(&name);\n+\t\treturn -1;\n+\t}\n+\tstrbuf_addstr(buf, v);\n+\tfree(v);\n+\tstrbuf_release(&name);\n+\treturn 0;\n+}\n+\n static void add_branch_headers(struct rev_info *rev, const char *branch_name)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -1064,7 +1079,7 @@ static void add_branch_headers(struct rev_info *rev, const char *branch_name)\n \t */\n \n \tif (!to_cleared) {\n-\t\tstrbuf_addf(&buf, \"branch.%s.to\", branch_name);\n+\t\tstrbuf_addf(&buf, \"format.%s.to\", branch_name);\n \t\tvalues = git_config_get_value_multi(buf.buf);\n \t\tif (values)\n \t\t\tstring_list_append_all(&extra_to, values);\n@@ -1072,7 +1087,7 @@ static void add_branch_headers(struct rev_info *rev, const char *branch_name)\n \n \tif (!cc_cleared) {\n \t\tstrbuf_reset(&buf);\n-\t\tstrbuf_addf(&buf, \"branch.%s.cc\", branch_name);\n+\t\tstrbuf_addf(&buf, \"format.%s.cc\", branch_name);\n \t\tvalues = git_config_get_value_multi(buf.buf);\n \t\tif (values)\n \t\t\tstring_list_append_all(&extra_cc, values);\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex e9ad50b66d..9ae3da888e 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -782,9 +782,15 @@ test_expect_success 'test tracking setup via --track but deeper' '\n '\n \n test_expect_success 'test deleting branch deletes branch config' '\n+\tgit config format.my7.coverSubject \"cover subject\" &&\n+\tgit config format.my7.to \"To Me <to@example.com>\" &&\n+\tgit config format.my7.cc \"Cc Me <cc@example.com>\" &&\n \tgit branch -d my7 &&\n \ttest -z \"$(git config branch.my7.remote)\" &&\n-\ttest -z \"$(git config branch.my7.merge)\"\n+\ttest -z \"$(git config branch.my7.merge)\" &&\n+\ttest -z \"$(git config format.my7.coverSubject)\"\n+\ttest -z \"$(git config format.my7.to)\" &&\n+\ttest -z \"$(git config format.my7.cc)\"\n '\n \n test_expect_success 'test deleting branch without config' '\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 5d6c85489e..9c527510c3 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -237,7 +237,7 @@ test_expect_failure 'configuration To: header (rfc2047)' '\n test_expect_success 'branch-specific configuration To: header (ascii)' '\n \n \ttest_unconfig format.to &&\n-\tgit config branch.side.to \"R E Cipient <rcipient@example.com>\" &&\n+\tgit config format.side.to \"R E Cipient <rcipient@example.com>\" &&\n \tgit format-patch --stdout master..side >patch10 &&\n \tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n \tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs10\n@@ -245,7 +245,7 @@ test_expect_success 'branch-specific configuration To: header (ascii)' '\n \n test_expect_failure 'branch-specific configuration To: header (rfc822)' '\n \n-\tgit config branch.side.to \"R. E. Cipient <rcipient@example.com>\" &&\n+\tgit config format.side.to \"R. E. Cipient <rcipient@example.com>\" &&\n \tgit format-patch --stdout master..side >patch10 &&\n \tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n \tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" hdrs10\n@@ -253,7 +253,7 @@ test_expect_failure 'branch-specific configuration To: header (rfc822)' '\n \n test_expect_failure 'branch-specific configuration To: header (rfc2047)' '\n \n-\tgit config branch.side.to \"R Ä Cipient <rcipient@example.com>\" &&\n+\tgit config format.side.to \"R Ä Cipient <rcipient@example.com>\" &&\n \tgit format-patch --stdout master..side >patch10 &&\n \tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n \tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs10\n@@ -265,10 +265,10 @@ test_expect_success 'all recipients included from all sources' '\n \tgit config --add format.to \"Format To2 <formatto2@example.com>\" &&\n \tgit config format.cc \"Format Cc1 <formatcc1@example.com>\" &&\n \tgit config --add format.cc \"Format Cc2 <formatcc2@example.com>\" &&\n-\tgit config branch.side.to \"Branch To1 <branchto1@example.com>\" &&\n-\tgit config --add branch.side.to \"Branch To2 <branchto2@example.com>\" &&\n-\tgit config branch.side.cc \"Branch Cc1 <branchcc1@example.com>\" &&\n-\tgit config --add branch.side.cc \"Branch Cc2 <branchcc2@example.com>\" &&\n+\tgit config format.side.to \"Branch To1 <branchto1@example.com>\" &&\n+\tgit config --add format.side.to \"Branch To2 <branchto2@example.com>\" &&\n+\tgit config format.side.cc \"Branch Cc1 <branchcc1@example.com>\" &&\n+\tgit config --add format.side.cc \"Branch Cc2 <branchcc2@example.com>\" &&\n \tcat <<-\\EOF >expect &&\n \tTo: Format To1 <formatto1@example.com>,\n \t    Format To2 <formatto2@example.com>,\n@@ -345,7 +345,7 @@ test_expect_success '--no-to overrides config.to' '\n \n \tgit config --replace-all format.to \\\n \t\t\"R E Cipient <rcipient@example.com>\" &&\n-\tgit config --replace-all branch.side.to \\\n+\tgit config --replace-all format.side.to \\\n \t\t\"B R Anch <branch@example.com>\" &&\n \tgit format-patch --no-to --stdout master..side >patch11 &&\n \tsed -e \"/^\\$/q\" patch11 >hdrs11 &&\n@@ -358,7 +358,7 @@ test_expect_success '--no-to and --to replaces config.to' '\n \n \tgit config --replace-all format.to \\\n \t\t\"Someone <someone@out.there>\" &&\n-\tgit config --replace-all branch.side.to \\\n+\tgit config --replace-all format.side.to \\\n \t\t\"B R Anch2 <branch2@example.com>\" &&\n \tgit format-patch --no-to --to=\"Someone Else <else@out.there>\" \\\n \t\t--stdout master..side >patch12 &&\n@@ -373,7 +373,7 @@ test_expect_success '--no-cc overrides config.cc' '\n \n \tgit config --replace-all format.cc \\\n \t\t\"C E Cipient <rcipient@example.com>\" &&\n-\tgit config --replace-all branch.side.cc \\\n+\tgit config --replace-all format.side.cc \\\n \t\t\"B R Anch3 <branch3@example.com>\" &&\n \tgit format-patch --no-cc --stdout master..side >patch13 &&\n \tsed -e \"/^\\$/q\" patch13 >hdrs13 &&\n@@ -1538,7 +1538,7 @@ test_expect_success 'format patch ignores color.ui' '\n '\n \n test_expect_success 'cover letter with config subject' '\n-\ttest_config branch.rebuild-1.coverSubject \"config subject\" &&\n+\ttest_config format.rebuild-1.coverSubject \"config subject\" &&\n \tgit checkout rebuild-1 &&\n \tgit format-patch --stdout --cover-letter master >actual &&\n \tgrep \"Subject: \\[PATCH 0/2\\] config subject\" actual\n@@ -1551,7 +1551,7 @@ test_expect_success 'cover letter with command-line subject' '\n '\n \n test_expect_success 'cover letter with command-line subject overrides config' '\n-\ttest_config branch.rebuild-1.coverSubject \"config subject\" &&\n+\ttest_config format.rebuild-1.coverSubject \"config subject\" &&\n \tgit checkout rebuild-1 &&\n \tgit format-patch --stdout --cover-letter --cover-subject \"command-line subject\" master >actual &&\n \tgrep \"Subject: \\[PATCH 0/2\\] command-line subject\" actual\n-- \n2.21.0.1049.geb646f7864\n\n"},{"id":"375772","messageId":"4ca24ea17652fbd3dd3b960cb4c3e03abf9689b5.1558052674.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558052674.git.liu.denton@gmail.com","subject":"[PATCH v2 1/6] t4014: clean up style","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-17T00:27:40Z","receivedAt":"2019-05-17T00:27:46Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In Git's tests, there is typically no space between the redirection\noperator and the filename. Remove these spaces.\n\nSince output is silenced when running without `-v` and debugging\noutput is useful with `-v`, remove redirections to /dev/null.\n\nChange here-docs from `<<\\EOF` to `<<-\\EOF` so that they can be indented\nalong with the rest of the test case.\n\nFinally, refactor to remove Git commands upstream of pipe. This way, if\nan invocation of a Git command fails, the return code won't be lost.\nKeep upstream non-Git commands since we have to assume a base level of\nsanity.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t4014-format-patch.sh | 614 +++++++++++++++++++++-------------------\n 1 file changed, 319 insertions(+), 295 deletions(-)\n\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex b6e2fdbc44..3423f974bc 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -34,7 +34,8 @@ test_expect_success setup '\n \tgit commit -m \"Side changes #3 with \\\\n backslash-n in it.\" &&\n \n \tgit checkout master &&\n-\tgit diff-tree -p C2 | git apply --index &&\n+\tgit diff-tree -p C2 >patch &&\n+\tgit apply --index <patch &&\n \ttest_tick &&\n \tgit commit -m \"Master accepts moral equivalent of #2\"\n \n@@ -77,7 +78,7 @@ test_expect_success \"format-patch doesn't consider merge commits\" '\n \tgit checkout -b merger master &&\n \ttest_tick &&\n \tgit merge --no-ff slave &&\n-\tcnt=$(git format-patch -3 --stdout | grep \"^From \" | wc -l) &&\n+\tcnt=$(git format-patch -3 --stdout >patch && grep \"^From \" patch | wc -l) &&\n \ttest $cnt = 3\n '\n \n@@ -85,21 +86,22 @@ test_expect_success \"format-patch result applies\" '\n \n \tgit checkout -b rebuild-0 master &&\n \tgit am -3 patch0 &&\n-\tcnt=$(git rev-list master.. | wc -l) &&\n-\ttest $cnt = 2\n+\tgit rev-list master.. >list &&\n+\ttest_line_count = 2 list\n '\n \n test_expect_success \"format-patch --ignore-if-in-upstream result applies\" '\n \n \tgit checkout -b rebuild-1 master &&\n \tgit am -3 patch1 &&\n-\tcnt=$(git rev-list master.. | wc -l) &&\n-\ttest $cnt = 2\n+\tgit rev-list master.. >list &&\n+\ttest_line_count = 2 list\n '\n \n test_expect_success 'commit did not screw up the log message' '\n \n-\tgit cat-file commit side | grep \"^Side .* with .* backslash-n\"\n+\tgit cat-file commit side >actual &&\n+\tgrep \"^Side .* with .* backslash-n\" actual\n \n '\n \n@@ -112,7 +114,8 @@ test_expect_success 'format-patch did not screw up the log message' '\n \n test_expect_success 'replay did not screw up the log message' '\n \n-\tgit cat-file commit rebuild-1 | grep \"^Side .* with .* backslash-n\"\n+\tgit cat-file commit rebuild-1 >actual &&\n+\tgrep \"^Side .* with .* backslash-n\" actual\n \n '\n \n@@ -122,8 +125,8 @@ test_expect_success 'extra headers' '\n \" &&\n \tgit config --add format.headers \"Cc: S E Cipient <scipient@example.com>\n \" &&\n-\tgit format-patch --stdout master..side > patch2 &&\n-\tsed -e \"/^\\$/q\" patch2 > hdrs2 &&\n+\tgit format-patch --stdout master..side >patch2 &&\n+\tsed -e \"/^\\$/q\" patch2 >hdrs2 &&\n \tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs2 &&\n \tgrep \"^Cc: S E Cipient <scipient@example.com>\\$\" hdrs2\n \n@@ -134,7 +137,7 @@ test_expect_success 'extra headers without newlines' '\n \tgit config --replace-all format.headers \"To: R E Cipient <rcipient@example.com>\" &&\n \tgit config --add format.headers \"Cc: S E Cipient <scipient@example.com>\" &&\n \tgit format-patch --stdout master..side >patch3 &&\n-\tsed -e \"/^\\$/q\" patch3 > hdrs3 &&\n+\tsed -e \"/^\\$/q\" patch3 >hdrs3 &&\n \tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs3 &&\n \tgrep \"^Cc: S E Cipient <scipient@example.com>\\$\" hdrs3\n \n@@ -144,8 +147,8 @@ test_expect_success 'extra headers with multiple To:s' '\n \n \tgit config --replace-all format.headers \"To: R E Cipient <rcipient@example.com>\" &&\n \tgit config --add format.headers \"To: S E Cipient <scipient@example.com>\" &&\n-\tgit format-patch --stdout master..side > patch4 &&\n-\tsed -e \"/^\\$/q\" patch4 > hdrs4 &&\n+\tgit format-patch --stdout master..side >patch4 &&\n+\tsed -e \"/^\\$/q\" patch4 >hdrs4 &&\n \tgrep \"^To: R E Cipient <rcipient@example.com>,\\$\" hdrs4 &&\n \tgrep \"^ *S E Cipient <scipient@example.com>\\$\" hdrs4\n '\n@@ -153,72 +156,82 @@ test_expect_success 'extra headers with multiple To:s' '\n test_expect_success 'additional command line cc (ascii)' '\n \n \tgit config --replace-all format.headers \"Cc: R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --cc=\"S E Cipient <scipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch5 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" patch5 &&\n-\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" patch5\n+\tgit format-patch --cc=\"S E Cipient <scipient@example.com>\" --stdout master..side >patch5 &&\n+\tsed -e \"/^\\$/q\" patch5 >hdrs5 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" hdrs5 &&\n+\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" hdrs5\n '\n \n test_expect_failure 'additional command line cc (rfc822)' '\n \n \tgit config --replace-all format.headers \"Cc: R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --cc=\"S. E. Cipient <scipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch5 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" patch5 &&\n-\tgrep \"^ *\\\"S. E. Cipient\\\" <scipient@example.com>\\$\" patch5\n+\tgit format-patch --cc=\"S. E. Cipient <scipient@example.com>\" --stdout master..side >patch5 &&\n+\tsed -e \"/^\\$/q\" patch5 >hdrs5 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" hdrs5 &&\n+\tgrep \"^ *\\\"S. E. Cipient\\\" <scipient@example.com>\\$\" hdrs5\n '\n \n test_expect_success 'command line headers' '\n \n \tgit config --unset-all format.headers &&\n-\tgit format-patch --add-header=\"Cc: R E Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch6 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>\\$\" patch6\n+\tgit format-patch --add-header=\"Cc: R E Cipient <rcipient@example.com>\" --stdout master..side >patch6 &&\n+\tsed -e \"/^\\$/q\" patch6 >hdrs6 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>\\$\" hdrs6\n '\n \n test_expect_success 'configuration headers and command line headers' '\n \n \tgit config --replace-all format.headers \"Cc: R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --add-header=\"Cc: S E Cipient <scipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch7 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" patch7 &&\n-\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" patch7\n+\tgit format-patch --add-header=\"Cc: S E Cipient <scipient@example.com>\" --stdout master..side >patch7 &&\n+\tsed -e \"/^\\$/q\" patch7 >hdrs7 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" hdrs7 &&\n+\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" hdrs7\n '\n \n test_expect_success 'command line To: header (ascii)' '\n \n \tgit config --unset-all format.headers &&\n-\tgit format-patch --to=\"R E Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch8 &&\n-\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" patch8\n+\tgit format-patch --to=\"R E Cipient <rcipient@example.com>\" --stdout master..side >patch8 &&\n+\tsed -e \"/^\\$/q\" patch8 >hdrs8 &&\n+\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs8\n '\n \n test_expect_failure 'command line To: header (rfc822)' '\n \n-\tgit format-patch --to=\"R. E. Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch8 &&\n-\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" patch8\n+\tgit format-patch --to=\"R. E. Cipient <rcipient@example.com>\" --stdout master..side >patch8 &&\n+\tsed -e \"/^\\$/q\" patch8 >hdrs8 &&\n+\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" hdrs8\n '\n \n test_expect_failure 'command line To: header (rfc2047)' '\n \n-\tgit format-patch --to=\"R Ä Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch8 &&\n-\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" patch8\n+\tgit format-patch --to=\"R Ä Cipient <rcipient@example.com>\" --stdout master..side >patch8 &&\n+\tsed -e \"/^\\$/q\" patch8 >hdrs8 &&\n+\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs8\n '\n \n test_expect_success 'configuration To: header (ascii)' '\n \n \tgit config format.to \"R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --stdout master..side | sed -e \"/^\\$/q\" >patch9 &&\n-\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" patch9\n+\tgit format-patch --stdout master..side >patch9 &&\n+\tsed -e \"/^\\$/q\" patch9 >hdrs9 &&\n+\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs9\n '\n \n test_expect_failure 'configuration To: header (rfc822)' '\n \n \tgit config format.to \"R. E. Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --stdout master..side | sed -e \"/^\\$/q\" >patch9 &&\n-\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" patch9\n+\tgit format-patch --stdout master..side >patch9 &&\n+\tsed -e \"/^\\$/q\" patch9 >hdrs9 &&\n+\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" hdrs9\n '\n \n test_expect_failure 'configuration To: header (rfc2047)' '\n \n \tgit config format.to \"R Ä Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --stdout master..side | sed -e \"/^\\$/q\" >patch9 &&\n-\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" patch9\n+\tgit format-patch --stdout master..side >patch9 &&\n+\tsed -e \"/^\\$/q\" patch9 >hdrs9 &&\n+\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs9\n '\n \n # check_patch <patch>: Verify that <patch> looks like a half-sane\n@@ -231,52 +244,52 @@ check_patch () {\n \n test_expect_success 'format.from=false' '\n \n-\tgit -c format.from=false format-patch --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n+\tgit -c format.from=false format-patch --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n \tcheck_patch patch &&\n-\t! grep \"^From: C O Mitter <committer@example.com>\\$\" patch\n+\t! grep \"^From: C O Mitter <committer@example.com>\\$\" hdrs\n '\n \n test_expect_success 'format.from=true' '\n \n-\tgit -c format.from=true format-patch --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\tgrep \"^From: C O Mitter <committer@example.com>\\$\" patch\n+\tgit -c format.from=true format-patch --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\tgrep \"^From: C O Mitter <committer@example.com>\\$\" hdrs\n '\n \n test_expect_success 'format.from with address' '\n \n-\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\tgrep \"^From: F R Om <from@example.com>\\$\" patch\n+\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\tgrep \"^From: F R Om <from@example.com>\\$\" hdrs\n '\n \n test_expect_success '--no-from overrides format.from' '\n \n-\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --no-from --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\t! grep \"^From: F R Om <from@example.com>\\$\" patch\n+\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --no-from --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\t! grep \"^From: F R Om <from@example.com>\\$\" hdrs\n '\n \n test_expect_success '--from overrides format.from' '\n \n-\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --from --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\t! grep \"^From: F R Om <from@example.com>\\$\" patch\n+\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --from --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\t! grep \"^From: F R Om <from@example.com>\\$\" hdrs\n '\n \n test_expect_success '--no-to overrides config.to' '\n \n \tgit config --replace-all format.to \\\n \t\t\"R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-to --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch10 &&\n-\tcheck_patch patch10 &&\n-\t! grep \"^To: R E Cipient <rcipient@example.com>\\$\" patch10\n+\tgit format-patch --no-to --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tcheck_patch hdrs10 &&\n+\t! grep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs10\n '\n \n test_expect_success '--no-to and --to replaces config.to' '\n@@ -284,31 +297,31 @@ test_expect_success '--no-to and --to replaces config.to' '\n \tgit config --replace-all format.to \\\n \t\t\"Someone <someone@out.there>\" &&\n \tgit format-patch --no-to --to=\"Someone Else <else@out.there>\" \\\n-\t\t--stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch11 &&\n-\tcheck_patch patch11 &&\n-\t! grep \"^To: Someone <someone@out.there>\\$\" patch11 &&\n-\tgrep \"^To: Someone Else <else@out.there>\\$\" patch11\n+\t\t--stdout master..side >patch11 &&\n+\tsed -e \"/^\\$/q\" patch11 >hdrs11 &&\n+\tcheck_patch hdrs11 &&\n+\t! grep \"^To: Someone <someone@out.there>\\$\" hdrs11 &&\n+\tgrep \"^To: Someone Else <else@out.there>\\$\" hdrs11\n '\n \n test_expect_success '--no-cc overrides config.cc' '\n \n \tgit config --replace-all format.cc \\\n \t\t\"C E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-cc --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch12 &&\n-\tcheck_patch patch12 &&\n-\t! grep \"^Cc: C E Cipient <rcipient@example.com>\\$\" patch12\n+\tgit format-patch --no-cc --stdout master..side >patch12 &&\n+\tsed -e \"/^\\$/q\" patch12 >hdrs12 &&\n+\tcheck_patch hdrs12 &&\n+\t! grep \"^Cc: C E Cipient <rcipient@example.com>\\$\" hdrs12\n '\n \n test_expect_success '--no-add-header overrides config.headers' '\n \n \tgit config --replace-all format.headers \\\n \t\t\"Header1: B E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-add-header --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch13 &&\n-\tcheck_patch patch13 &&\n-\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" patch13\n+\tgit format-patch --no-add-header --stdout master..side >patch13 &&\n+\tsed -e \"/^\\$/q\" patch13 >hdrs13 &&\n+\tcheck_patch hdrs13 &&\n+\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" hdrs13\n '\n \n test_expect_success 'multiple files' '\n@@ -338,7 +351,7 @@ test_expect_success 'reroll count (-v)' '\n check_threading () {\n \texpect=\"$1\" &&\n \tshift &&\n-\t(git format-patch --stdout \"$@\"; echo $? > status.out) |\n+\t(git format-patch --stdout \"$@\"; echo $? >status.out) |\n \t# Prints everything between the Message-ID and In-Reply-To,\n \t# and replaces all Message-ID-lookalikes by a sequence number\n \tperl -ne '\n@@ -353,12 +366,12 @@ check_threading () {\n \t\t\tprint;\n \t\t}\n \t\tprint \"---\\n\" if /^From /i;\n-\t' > actual &&\n+\t' >actual &&\n \ttest 0 = \"$(cat status.out)\" &&\n \ttest_cmp \"$expect\" actual\n }\n \n-cat >> expect.no-threading <<EOF\n+cat >>expect.no-threading <<EOF\n ---\n ---\n ---\n@@ -369,7 +382,7 @@ test_expect_success 'no threading' '\n \tcheck_threading expect.no-threading master\n '\n \n-cat > expect.thread <<EOF\n+cat >expect.thread <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -386,7 +399,7 @@ test_expect_success 'thread' '\n \tcheck_threading expect.thread --thread master\n '\n \n-cat > expect.in-reply-to <<EOF\n+cat >expect.in-reply-to <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -406,7 +419,7 @@ test_expect_success 'thread in-reply-to' '\n \t\t--thread master\n '\n \n-cat > expect.cover-letter <<EOF\n+cat >expect.cover-letter <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -427,7 +440,7 @@ test_expect_success 'thread cover-letter' '\n \tcheck_threading expect.cover-letter --cover-letter --thread master\n '\n \n-cat > expect.cl-irt <<EOF\n+cat >expect.cl-irt <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -459,7 +472,7 @@ test_expect_success 'thread explicit shallow' '\n \t\t--in-reply-to=\"<test.message>\" --thread=shallow master\n '\n \n-cat > expect.deep <<EOF\n+cat >expect.deep <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -477,7 +490,7 @@ test_expect_success 'thread deep' '\n \tcheck_threading expect.deep --thread=deep master\n '\n \n-cat > expect.deep-irt <<EOF\n+cat >expect.deep-irt <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -500,7 +513,7 @@ test_expect_success 'thread deep in-reply-to' '\n \t\t--in-reply-to=\"<test.message>\" master\n '\n \n-cat > expect.deep-cl <<EOF\n+cat >expect.deep-cl <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -524,7 +537,7 @@ test_expect_success 'thread deep cover-letter' '\n \tcheck_threading expect.deep-cl --cover-letter --thread=deep master\n '\n \n-cat > expect.deep-cl-irt <<EOF\n+cat >expect.deep-cl-irt <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -606,7 +619,7 @@ test_expect_success 'cover-letter inherits diff options' '\n \n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n   This is an excessively long subject line for a message due to the\n     habit some projects have of not having a short, one-line subject at\n     the start of the commit message, but rather sticking a whole\n@@ -619,12 +632,12 @@ EOF\n test_expect_success 'shortlog of cover-letter wraps overly-long onelines' '\n \n \tgit format-patch --cover-letter -2 &&\n-\tsed -e \"1,/A U Thor/d\" -e \"/^\\$/q\" < 0000-cover-letter.patch > output &&\n+\tsed -e \"1,/A U Thor/d\" -e \"/^\\$/q\" <0000-cover-letter.patch >output &&\n \ttest_cmp expect output\n \n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n index $before..$after 100644\n --- a/file\n +++ b/file\n@@ -646,7 +659,7 @@ test_expect_success 'format-patch respects -U' '\n \n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n \n diff --git a/file b/file\n index $before..$after 100644\n@@ -662,7 +675,7 @@ EOF\n test_expect_success 'format-patch -p suppresses stat' '\n \n \tgit format-patch -p -2 &&\n-\tsed -e \"1,/^\\$/d\" -e \"/^+5/q\" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&\n+\tsed -e \"1,/^\\$/d\" -e \"/^+5/q\" <0001-This-is-an-excessively-long-subject-line-for-a-messa.patch >output &&\n \ttest_cmp expect output\n \n '\n@@ -717,7 +730,7 @@ test_expect_success 'format-patch from a subdirectory (3)' '\n '\n \n test_expect_success 'format-patch --in-reply-to' '\n-\tgit format-patch -1 --stdout --in-reply-to \"baz@foo.bar\" > patch8 &&\n+\tgit format-patch -1 --stdout --in-reply-to \"baz@foo.bar\" >patch8 &&\n \tgrep \"^In-Reply-To: <baz@foo.bar>\" patch8 &&\n \tgrep \"^References: <baz@foo.bar>\" patch8\n '\n@@ -738,20 +751,20 @@ test_expect_success 'format-patch --notes --signoff' '\n \tsed \"1,/^---$/d\" out | grep \"test message\"\n '\n \n-echo \"fatal: --name-only does not make sense\" > expect.name-only\n-echo \"fatal: --name-status does not make sense\" > expect.name-status\n-echo \"fatal: --check does not make sense\" > expect.check\n+echo \"fatal: --name-only does not make sense\" >expect.name-only\n+echo \"fatal: --name-status does not make sense\" >expect.name-status\n+echo \"fatal: --check does not make sense\" >expect.check\n \n test_expect_success 'options no longer allowed for format-patch' '\n-\ttest_must_fail git format-patch --name-only 2> output &&\n+\ttest_must_fail git format-patch --name-only 2>output &&\n \ttest_i18ncmp expect.name-only output &&\n-\ttest_must_fail git format-patch --name-status 2> output &&\n+\ttest_must_fail git format-patch --name-status 2>output &&\n \ttest_i18ncmp expect.name-status output &&\n-\ttest_must_fail git format-patch --check 2> output &&\n+\ttest_must_fail git format-patch --check 2>output &&\n \ttest_i18ncmp expect.check output'\n \n test_expect_success 'format-patch --numstat should produce a patch' '\n-\tgit format-patch --numstat --stdout master..side > output &&\n+\tgit format-patch --numstat --stdout master..side >output &&\n \ttest 5 = $(grep \"^diff --git a/\" output | wc -l)'\n \n test_expect_success 'format-patch -- <path>' '\n@@ -763,20 +776,22 @@ test_expect_success 'format-patch --ignore-if-in-upstream HEAD' '\n \tgit format-patch --ignore-if-in-upstream HEAD\n '\n \n-git_version=\"$(git --version | sed \"s/.* //\")\"\n+git_version=\"$(git --version >version && sed \"s/.* //\" version)\"\n \n signature() {\n \tprintf \"%s\\n%s\\n\\n\" \"-- \" \"${1:-$git_version}\"\n }\n \n test_expect_success 'format-patch default signature' '\n-\tgit format-patch --stdout -1 | tail -n 3 >output &&\n+\tgit format-patch --stdout -1 >patch &&\n+\ttail -n 3 patch >output &&\n \tsignature >expect &&\n \ttest_cmp expect output\n '\n \n test_expect_success 'format-patch --signature' '\n-\tgit format-patch --stdout --signature=\"my sig\" -1 | tail -n 3 >output &&\n+\tgit format-patch --stdout --signature=\"my sig\" -1 >patch &&\n+\ttail -n 3 patch >output &&\n \tsignature \"my sig\" >expect &&\n \ttest_cmp expect output\n '\n@@ -1167,282 +1182,282 @@ append_signoff()\n \n test_expect_success 'signoff: commit with no body' '\n \tappend_signoff </dev/null >actual &&\n-\tcat <<\\EOF | sed \"s/EOL$//\" >expected &&\n-4:Subject: [PATCH] EOL\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat <<-\\EOF | sed \"s/EOL$//\" >expected &&\n+\t4:Subject: [PATCH] EOL\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: commit with only subject' '\n \techo subject | append_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: commit with only subject that does not end with NL' '\n \tprintf subject | append_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: no existing signoffs' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tbody\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: no existing signoffs and no trailing NL' '\n \tprintf \"subject\\n\\nbody\" | append_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: some random signoff' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: my@house\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: my@house\n-12:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tSigned-off-by: my@house\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: my@house\n+\t12:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: misc conforming footer elements' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: my@house\n-(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)\n-Tested-by: Some One <someone@example.com>\n-Bug: 1234\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: my@house\n-15:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tSigned-off-by: my@house\n+\t(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)\n+\tTested-by: Some One <someone@example.com>\n+\tBug: 1234\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: my@house\n+\t15:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: some random signoff-alike' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n-Fooled-by-me: my@house\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-11:\n-12:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tbody\n+\tFooled-by-me: my@house\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t11:\n+\t12:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: not really a signoff' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-I want to mention about Signed-off-by: here.\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:I want to mention about Signed-off-by: here.\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tI want to mention about Signed-off-by: here.\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:I want to mention about Signed-off-by: here.\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: not really a signoff (2)' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-My unfortunate\n-Signed-off-by: example happens to be wrapped here.\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:Signed-off-by: example happens to be wrapped here.\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tMy unfortunate\n+\tSigned-off-by: example happens to be wrapped here.\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:Signed-off-by: example happens to be wrapped here.\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: valid S-o-b paragraph in the middle' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-Signed-off-by: my@house\n-Signed-off-by: your@house\n+\tSigned-off-by: my@house\n+\tSigned-off-by: your@house\n \n-A lot of houses.\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: my@house\n-10:Signed-off-by: your@house\n-11:\n-13:\n-14:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tA lot of houses.\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: my@house\n+\t10:Signed-off-by: your@house\n+\t11:\n+\t13:\n+\t14:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: the same signoff at the end' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: the same signoff at the end, no trailing NL' '\n \tprintf \"subject\\n\\nSigned-off-by: C O Mitter <committer@example.com>\" |\n \t\tappend_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: the same signoff NOT at the end' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: C O Mitter <committer@example.com>\n-Signed-off-by: my@house\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-12:Signed-off-by: my@house\n-EOF\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tSigned-off-by: my@house\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\t12:Signed-off-by: my@house\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: tolerate garbage in conforming footer' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Tested-by: my@house\n-Some Trash\n-Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-13:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tTested-by: my@house\n+\tSome Trash\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t13:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: respect trailer config' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-Myfooter: x\n-Some Trash\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-11:\n-12:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tMyfooter: x\n+\tSome Trash\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t11:\n+\t12:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual &&\n \n \ttest_config trailer.Myfooter.ifexists add &&\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-Myfooter: x\n-Some Trash\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tMyfooter: x\n+\tSome Trash\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: footer begins with non-signoff without @ sign' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Reviewed-id: Noone\n-Tested-by: my@house\n-Change-id: Ideadbeef\n-Signed-off-by: C O Mitter <committer@example.com>\n-Bug: 1234\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-14:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tReviewed-id: Noone\n+\tTested-by: my@house\n+\tChange-id: Ideadbeef\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tBug: 1234\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t14:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n@@ -1458,42 +1473,42 @@ test_expect_success 'cover letter using branch description (1)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter master >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (2)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter rebuild-1~2..rebuild-1 >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (3)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter ^master rebuild-1 >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (4)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter master.. >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (5)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter -2 HEAD >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (6)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter -2 >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter with nothing' '\n@@ -1547,7 +1562,8 @@ test_expect_success 'format-patch format.outputDirectory option' '\n \ttest_config format.outputDirectory patches &&\n \trm -fr patches &&\n \tgit format-patch master..side &&\n-\ttest $(git rev-list master..side | wc -l) -eq $(ls patches | wc -l)\n+\tgit rev-list master..side >list &&\n+\ttest_line_count = $(ls patches | wc -l) list\n '\n \n test_expect_success 'format-patch -o overrides format.outputDirectory' '\n@@ -1560,13 +1576,21 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '\n \n test_expect_success 'format-patch --base' '\n \tgit checkout side &&\n-\tgit format-patch --stdout --base=HEAD~3 -1 | tail -n 7 >actual1 &&\n-\tgit format-patch --stdout --base=HEAD~3 HEAD~.. | tail -n 7 >actual2 &&\n+\tgit format-patch --stdout --base=HEAD~3 -1 >patch &&\n+\ttail -n 7 patch >actual1 &&\n+\tgit format-patch --stdout --base=HEAD~3 HEAD~.. >patch &&\n+\ttail -n 7 patch >actual2 &&\n \techo >expected &&\n \techo \"base-commit: $(git rev-parse HEAD~3)\" >>expected &&\n-\techo \"prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n-\techo \"prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n-\tsignature >> expected &&\n+\tgit show --patch HEAD~2 >patch &&\n+\tgit patch-id --stable <patch >patch.id.raw &&\n+\tawk \"{print \\$1}\" <patch.id.raw >patch.id &&\n+\techo \"prerequisite-patch-id: $(cat patch.id)\" >>expected &&\n+\tgit show --patch HEAD~1 >patch &&\n+\tgit patch-id --stable <patch >patch.id.raw &&\n+\tawk \"{print \\$1}\" <patch.id.raw >patch.id &&\n+\techo \"prerequisite-patch-id: $(cat patch.id)\" >>expected &&\n+\tsignature >>expected &&\n \ttest_cmp expected actual1 &&\n \ttest_cmp expected actual2\n '\n-- \n2.21.0.1049.geb646f7864\n\n"},{"id":"375773","messageId":"1082db2e1b6cdcaa13e32cf33ca0dafba9bc8925.1558052674.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558052674.git.liu.denton@gmail.com","subject":"[PATCH v2 2/6] Doc: add more detail for git-format-patch","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-17T00:27:43Z","receivedAt":"2019-05-17T00:27:48Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In git-format-patch.txt, we were missing some key user information.\nFirst of all, using the `--to` and `--cc` options don't override\n`format.to` and `format.cc` variables, respectively. They add on to each\nother. Document this.\n\nIn addition, document the special value of `--base=auto`.\n\nFinally, while we're at it, surround option arguments with <>.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/git-format-patch.txt | 17 +++++++++++------\n 1 file changed, 11 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 1af85d404f..7b71d4e2ed 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -17,9 +17,9 @@ SYNOPSIS\n \t\t   [--signature-file=<file>]\n \t\t   [-n | --numbered | -N | --no-numbered]\n \t\t   [--start-number <n>] [--numbered-files]\n-\t\t   [--in-reply-to=Message-Id] [--suffix=.<sfx>]\n+\t\t   [--in-reply-to=<Message-Id>] [--suffix=.<sfx>]\n \t\t   [--ignore-if-in-upstream]\n-\t\t   [--rfc] [--subject-prefix=Subject-Prefix]\n+\t\t   [--rfc] [--subject-prefix=<Subject-Prefix>]\n \t\t   [(--reroll-count|-v) <n>]\n \t\t   [--to=<email>] [--cc=<email>]\n \t\t   [--[no-]cover-letter] [--quiet] [--notes[=<ref>]]\n@@ -158,7 +158,7 @@ Beware that the default for 'git send-email' is to thread emails\n itself.  If you want `git format-patch` to take care of threading, you\n will want to ensure that threading is disabled for `git send-email`.\n \n---in-reply-to=Message-Id::\n+--in-reply-to=<Message-Id>::\n \tMake the first mail (or all the mails with `--no-thread`) appear as a\n \treply to the given Message-Id, which avoids breaking threads to\n \tprovide a new patch series.\n@@ -192,13 +192,17 @@ will want to ensure that threading is disabled for `git send-email`.\n \n --to=<email>::\n \tAdd a `To:` header to the email headers. This is in addition\n-\tto any configured headers, and may be used multiple times.\n+\tto any configured headers, and may be used multiple times. The\n+\temails given will be used along with any emails given by\n+\t`format.to` configurations.\n \tThe negated form `--no-to` discards all `To:` headers added so\n \tfar (from config or command line).\n \n --cc=<email>::\n \tAdd a `Cc:` header to the email headers. This is in addition\n-\tto any configured headers, and may be used multiple times.\n+\tto any configured headers, and may be used multiple times. The\n+\temails given will be used along with any emails given by\n+\t`format.cc` configurations.\n \tThe negated form `--no-cc` discards all `Cc:` headers added so\n \tfar (from config or command line).\n \n@@ -309,7 +313,8 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.\n --base=<commit>::\n \tRecord the base tree information to identify the state the\n \tpatch series applies to.  See the BASE TREE INFORMATION section\n-\tbelow for details.\n+\tbelow for details. If <commit> is equal to \"auto\", a base commit\n+\tis automatically chosen.\n \n --root::\n \tTreat the revision argument as a <revision range>, even if it\n-- \n2.21.0.1049.geb646f7864\n\n"},{"id":"375774","messageId":"cc7e6909f85575d107e022997fdc8ae99baaee8f.1558052674.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558052674.git.liu.denton@gmail.com","subject":"[PATCH v2 3/6] format-patch: make cover letter subject configurable","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-17T00:27:45Z","receivedAt":"2019-05-17T00:27:50Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"We used to populate the subject of the cover letter generated by\ngit-format-patch with \"*** SUBJECT HERE ***\". However, if a user submits\nmultiple patchsets, they may want to keep a consistent subject between\nrerolls.\n\nIf git-format-patch is run on a branch that has\n`format.<branch-name>.coverSubject` defined, make the cover letter's\nsubject be that value instead of the generic \"*** SUBJECT HERE ***\".\n\nIn addition, add the `--cover-subject` option to override this setting.\n\nFinally, make `git branch -d` also remove any `format.<branch-name>.*`\nconfigs, just like it currently removes all `branch.<branch-name>.*`\nconfigs.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/config/format.txt    |  5 ++++\n Documentation/git-format-patch.txt | 12 +++++++++\n builtin/branch.c                   | 16 +++++++++---\n builtin/log.c                      | 41 +++++++++++++++++++++++++-----\n t/t3200-branch.sh                  |  4 ++-\n t/t4014-format-patch.sh            | 20 +++++++++++++++\n t/t9902-completion.sh              |  5 +++-\n 7 files changed, 91 insertions(+), 12 deletions(-)\n\ndiff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\nindex dc77941c48..7d2b3b7331 100644\n--- a/Documentation/config/format.txt\n+++ b/Documentation/config/format.txt\n@@ -36,6 +36,11 @@ format.subjectPrefix::\n \tThe default for format-patch is to output files with the '[PATCH]'\n \tsubject prefix. Use this variable to change that prefix.\n \n+format.<branch-name>.coverSubject::\n+\tWhen format-patch generates a cover letter for the given\n+\t<branch-name>, use the specified subject for the cover letter\n+\tinstead of the generic template.\n+\n format.signature::\n \tThe default for format-patch is to output a signature containing\n \tthe Git version number. Use this variable to change that default.\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 7b71d4e2ed..a43f317877 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -19,6 +19,7 @@ SYNOPSIS\n \t\t   [--start-number <n>] [--numbered-files]\n \t\t   [--in-reply-to=<Message-Id>] [--suffix=.<sfx>]\n \t\t   [--ignore-if-in-upstream]\n+\t\t   [--cover-subject=<subject>]\n \t\t   [--rfc] [--subject-prefix=<Subject-Prefix>]\n \t\t   [(--reroll-count|-v) <n>]\n \t\t   [--to=<email>] [--cc=<email>]\n@@ -170,6 +171,10 @@ will want to ensure that threading is disabled for `git send-email`.\n \tpatches being generated, and any patch that matches is\n \tignored.\n \n+--cover-subject=<subject>::\n+\tInstead of using the default \"*** SUBJECT HERE ***\" subject for\n+\tthe cover letter, use the given <subject>.\n+\n --subject-prefix=<Subject-Prefix>::\n \tInstead of the standard '[PATCH]' prefix in the subject\n \tline, instead use '[<Subject-Prefix>]'. This\n@@ -346,6 +351,13 @@ attachments, and sign off patches with configuration variables.\n \tcoverletter = auto\n ------------\n \n+In addition, for a specific branch, you can specify a custom cover\n+letter subject.\n+\n+------------\n+[format \"branch-name\"]\n+\tcoverSubject = \"subject for branch-name only\"\n+------------\n \n DISCUSSION\n ----------\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex d4359b33ac..367e1fc9bc 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -178,12 +178,22 @@ static int check_branch_commit(const char *branchname, const char *refname,\n \treturn 0;\n }\n \n+static const char *branch_specific_config[] = {\n+\t\"branch\",\n+\t\"format\",\n+\tNULL\n+};\n+\n static void delete_branch_config(const char *branchname)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n-\tstrbuf_addf(&buf, \"branch.%s\", branchname);\n-\tif (git_config_rename_section(buf.buf, NULL) < 0)\n-\t\twarning(_(\"Update of config-file failed\"));\n+\tint i;\n+\tfor (i = 0; branch_specific_config[i]; i++) {\n+\t\tstrbuf_addf(&buf, \"%s.%s\", branch_specific_config[i], branchname);\n+\t\tif (git_config_rename_section(buf.buf, NULL) < 0)\n+\t\t\twarning(_(\"Update of config-file failed\"));\n+\t\tstrbuf_reset(&buf);\n+\t}\n \tstrbuf_release(&buf);\n }\n \ndiff --git a/builtin/log.c b/builtin/log.c\nindex e43ee12fb1..2bbe712e7c 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1030,17 +1030,33 @@ static void show_diffstat(struct rev_info *rev,\n \tfprintf(rev->diffopt.file, \"\\n\");\n }\n \n+static int read_branch_subject(struct strbuf *buf, const char *branch_name)\n+{\n+\tchar *v = NULL;\n+\tstruct strbuf name = STRBUF_INIT;\n+\tstrbuf_addf(&name, \"format.%s.coverSubject\", branch_name);\n+\tif (git_config_get_string(name.buf, &v)) {\n+\t\tstrbuf_release(&name);\n+\t\treturn -1;\n+\t}\n+\tstrbuf_addstr(buf, v);\n+\tfree(v);\n+\tstrbuf_release(&name);\n+\treturn 0;\n+}\n+\n static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \t\t\t      struct commit *origin,\n \t\t\t      int nr, struct commit **list,\n \t\t\t      const char *branch_name,\n+\t\t\t      const char *subject,\n \t\t\t      int quiet)\n {\n \tconst char *committer;\n-\tconst char *body = \"*** SUBJECT HERE ***\\n\\n*** BLURB HERE ***\\n\";\n-\tconst char *msg;\n+\tconst char *body = \"*** BLURB HERE ***\";\n \tstruct shortlog log;\n \tstruct strbuf sb = STRBUF_INIT;\n+\tstruct strbuf subject_sb = STRBUF_INIT;\n \tint i;\n \tconst char *encoding = \"UTF-8\";\n \tint need_8bit_cte = 0;\n@@ -1068,17 +1084,24 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tif (!branch_name)\n \t\tbranch_name = find_branch_name(rev);\n \n-\tmsg = body;\n+\tif (!subject) {\n+\t\tif (branch_name && *branch_name && !read_branch_subject(&subject_sb, branch_name))\n+\t\t\tsubject = subject_sb.buf;\n+\t\telse\n+\t\t\tsubject = \"*** SUBJECT HERE ***\";\n+\t}\n+\n \tpp.fmt = CMIT_FMT_EMAIL;\n \tpp.date_mode.type = DATE_RFC2822;\n \tpp.rev = rev;\n \tpp.print_email_subject = 1;\n \tpp_user_info(&pp, NULL, &sb, committer, encoding);\n-\tpp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);\n-\tpp_remainder(&pp, &msg, &sb, 0);\n+\tpp_title_line(&pp, &subject, &sb, encoding, need_8bit_cte);\n+\tpp_remainder(&pp, &body, &sb, 0);\n \tadd_branch_description(&sb, branch_name);\n \tfprintf(rev->diffopt.file, \"%s\\n\", sb.buf);\n \n+\tstrbuf_release(&subject_sb);\n \tstrbuf_release(&sb);\n \n \tshortlog_init(&log);\n@@ -1512,6 +1535,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tint no_binary_diff = 0;\n \tint zero_commit = 0;\n \tstruct commit *origin = NULL;\n+\tconst char *cover_subject = NULL;\n \tconst char *in_reply_to = NULL;\n \tstruct patch_ids ids;\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -1554,6 +1578,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t{ OPTION_CALLBACK, 0, \"rfc\", &rev, NULL,\n \t\t\t    N_(\"Use [RFC PATCH] instead of [PATCH]\"),\n \t\t\t    PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback },\n+\t\tOPT_STRING(0, \"cover-subject\", &cover_subject, N_(\"subject\"),\n+\t\t\t    N_(\"the subject for the cover letter\")),\n \t\t{ OPTION_CALLBACK, 0, \"subject-prefix\", &rev, N_(\"prefix\"),\n \t\t\t    N_(\"Use [<prefix>] instead of [PATCH]\"),\n \t\t\t    PARSE_OPT_NONEG, subject_prefix_callback },\n@@ -1617,8 +1643,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \textra_to.strdup_strings = 1;\n \textra_cc.strdup_strings = 1;\n \tinit_log_defaults();\n-\tgit_config(git_format_config, NULL);\n \trepo_init_revisions(the_repository, &rev, prefix);\n+\n+\tgit_config(git_format_config, NULL);\n \trev.commit_format = CMIT_FMT_EMAIL;\n \trev.expand_tabs_in_log_default = 0;\n \trev.verbose_header = 1;\n@@ -1893,7 +1920,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tif (thread)\n \t\t\tgen_message_id(&rev, \"cover\");\n \t\tmake_cover_letter(&rev, use_stdout,\n-\t\t\t\t  origin, nr, list, branch_name, quiet);\n+\t\t\t\t  origin, nr, list, branch_name, cover_subject, quiet);\n \t\tprint_bases(&bases, rev.diffopt.file);\n \t\tprint_signature(rev.diffopt.file);\n \t\ttotal++;\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex e9ad50b66d..dad23f7d4c 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -782,9 +782,11 @@ test_expect_success 'test tracking setup via --track but deeper' '\n '\n \n test_expect_success 'test deleting branch deletes branch config' '\n+\tgit config format.my7.coverSubject \"cover subject\" &&\n \tgit branch -d my7 &&\n \ttest -z \"$(git config branch.my7.remote)\" &&\n-\ttest -z \"$(git config branch.my7.merge)\"\n+\ttest -z \"$(git config branch.my7.merge)\" &&\n+\ttest -z \"$(git config format.my7.coverSubject)\"\n '\n \n test_expect_success 'test deleting branch without config' '\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 3423f974bc..9d11992e10 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -1469,6 +1469,26 @@ test_expect_success 'format patch ignores color.ui' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'cover letter with config subject' '\n+\ttest_config format.rebuild-1.coverSubject \"config subject\" &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter master >actual &&\n+\tgrep \"Subject: \\[PATCH 0/2\\] config subject\" actual\n+'\n+\n+test_expect_success 'cover letter with command-line subject' '\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter --cover-subject \"command-line subject\" master >actual &&\n+\tgrep \"Subject: \\[PATCH 0/2\\] command-line subject\" actual\n+'\n+\n+test_expect_success 'cover letter with command-line subject overrides config' '\n+\ttest_config format.rebuild-1.coverSubject \"config subject\" &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter --cover-subject \"command-line subject\" master >actual &&\n+\tgrep \"Subject: \\[PATCH 0/2\\] command-line subject\" actual\n+'\n+\n test_expect_success 'cover letter using branch description (1)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex 43cf313a1c..a96f1662c7 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -1548,7 +1548,10 @@ test_expect_success 'complete tree filename with metacharacters' '\n '\n \n test_expect_success PERL 'send-email' '\n-\ttest_completion \"git send-email --cov\" \"--cover-letter \" &&\n+\ttest_completion \"git send-email --cov\" <<-\\EOF &&\n+\t--cover-letter Z\n+\t--cover-subject=Z\n+\tEOF\n \ttest_completion \"git send-email ma\" \"master \"\n '\n \n-- \n2.21.0.1049.geb646f7864\n\n"},{"id":"375775","messageId":"57d067b3bbd2e7169c573e565d3420776b6b5152.1558052674.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558052674.git.liu.denton@gmail.com","subject":"[PATCH v2 4/6] format-patch: move extra_headers logic later","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-17T00:27:48Z","receivedAt":"2019-05-17T00:27:52Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future patch, we need to perform the addition of To: and Cc:\nto extra_headers after the branch_name logic. Simply transpose this\nlogic later in the function so that this happens. (This patch is best\nviewed with `git diff --color-moved`.)\n\nNote that this logic only depends on the `git_config` and\n`repo_init_revisions` and is depended on by the patch creation logic\nwhich is directly below it so this move is effectively a no-op as\nno dependencies being reordered.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/log.c | 58 +++++++++++++++++++++++++--------------------------\n 1 file changed, 29 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 2bbe712e7c..f66a65fe84 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1680,35 +1680,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\trev.subject_prefix = strbuf_detach(&sprefix, NULL);\n \t}\n \n-\tfor (i = 0; i < extra_hdr.nr; i++) {\n-\t\tstrbuf_addstr(&buf, extra_hdr.items[i].string);\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t}\n-\n-\tif (extra_to.nr)\n-\t\tstrbuf_addstr(&buf, \"To: \");\n-\tfor (i = 0; i < extra_to.nr; i++) {\n-\t\tif (i)\n-\t\t\tstrbuf_addstr(&buf, \"    \");\n-\t\tstrbuf_addstr(&buf, extra_to.items[i].string);\n-\t\tif (i + 1 < extra_to.nr)\n-\t\t\tstrbuf_addch(&buf, ',');\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t}\n-\n-\tif (extra_cc.nr)\n-\t\tstrbuf_addstr(&buf, \"Cc: \");\n-\tfor (i = 0; i < extra_cc.nr; i++) {\n-\t\tif (i)\n-\t\t\tstrbuf_addstr(&buf, \"    \");\n-\t\tstrbuf_addstr(&buf, extra_cc.items[i].string);\n-\t\tif (i + 1 < extra_cc.nr)\n-\t\t\tstrbuf_addch(&buf, ',');\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t}\n-\n-\trev.extra_headers = strbuf_detach(&buf, NULL);\n-\n \tif (from) {\n \t\tif (split_ident_line(&rev.from_ident, from, strlen(from)))\n \t\t\tdie(_(\"invalid ident line: %s\"), from);\n@@ -1811,6 +1782,35 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \t}\n \n+\tfor (i = 0; i < extra_hdr.nr; i++) {\n+\t\tstrbuf_addstr(&buf, extra_hdr.items[i].string);\n+\t\tstrbuf_addch(&buf, '\\n');\n+\t}\n+\n+\tif (extra_to.nr)\n+\t\tstrbuf_addstr(&buf, \"To: \");\n+\tfor (i = 0; i < extra_to.nr; i++) {\n+\t\tif (i)\n+\t\t\tstrbuf_addstr(&buf, \"    \");\n+\t\tstrbuf_addstr(&buf, extra_to.items[i].string);\n+\t\tif (i + 1 < extra_to.nr)\n+\t\t\tstrbuf_addch(&buf, ',');\n+\t\tstrbuf_addch(&buf, '\\n');\n+\t}\n+\n+\tif (extra_cc.nr)\n+\t\tstrbuf_addstr(&buf, \"Cc: \");\n+\tfor (i = 0; i < extra_cc.nr; i++) {\n+\t\tif (i)\n+\t\t\tstrbuf_addstr(&buf, \"    \");\n+\t\tstrbuf_addstr(&buf, extra_cc.items[i].string);\n+\t\tif (i + 1 < extra_cc.nr)\n+\t\t\tstrbuf_addch(&buf, ',');\n+\t\tstrbuf_addch(&buf, '\\n');\n+\t}\n+\n+\trev.extra_headers = strbuf_detach(&buf, NULL);\n+\n \t/*\n \t * We cannot move this anywhere earlier because we do want to\n \t * know if --root was given explicitly from the command line.\n-- \n2.21.0.1049.geb646f7864\n\n"},{"id":"375776","messageId":"cafb8c0650af0746319061a296dc09cf773866a4.1558052674.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558052674.git.liu.denton@gmail.com","subject":"[PATCH v2 5/6] string-list: create string_list_append_all","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-17T00:27:50Z","receivedAt":"2019-05-17T00:27:54Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future patch, we'll need to take one string_list and append it to\nthe end of another. Create the `string_list_append_all` function which\ndoes this.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n string-list.c | 9 +++++++++\n string-list.h | 7 +++++++\n 2 files changed, 16 insertions(+)\n\ndiff --git a/string-list.c b/string-list.c\nindex a917955fbd..e63d58fbd2 100644\n--- a/string-list.c\n+++ b/string-list.c\n@@ -215,6 +215,15 @@ struct string_list_item *string_list_append(struct string_list *list,\n \t\t\tlist->strdup_strings ? xstrdup(string) : (char *)string);\n }\n \n+void string_list_append_all(struct string_list *list,\n+\t\t\t    const struct string_list *append_list)\n+{\n+\tstruct string_list_item *item;\n+\tALLOC_GROW(list->items, list->nr + append_list->nr, list->alloc);\n+\tfor_each_string_list_item(item, append_list)\n+\t\tstring_list_append(list, item->string);\n+}\n+\n /*\n  * Encapsulate the compare function pointer because ISO C99 forbids\n  * casting from void * to a function pointer and vice versa.\ndiff --git a/string-list.h b/string-list.h\nindex f964399949..8227e00e6a 100644\n--- a/string-list.h\n+++ b/string-list.h\n@@ -208,6 +208,13 @@ struct string_list_item *string_list_append(struct string_list *list, const char\n  */\n struct string_list_item *string_list_append_nodup(struct string_list *list, char *string);\n \n+/**\n+ * Add all strings in append_list to list.  If list->strdup_string is\n+ * set, then each string is copied; otherwise the new string_list_entry\n+ * refers to the entry in the append_list.\n+ */\n+void string_list_append_all(struct string_list *list, const struct string_list *append_list);\n+\n /**\n  * Sort the list's entries by string value in `strcmp()` order.\n  */\n-- \n2.21.0.1049.geb646f7864\n\n"},{"id":"375777","messageId":"c36fbb6c728cfced22b68077277aad784074e53e.1558052674.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558052674.git.liu.denton@gmail.com","subject":"[PATCH v2 6/6] format-patch: read branch-specific To: and Cc: headers","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-17T00:27:52Z","receivedAt":"2019-05-17T00:27:58Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"If a user wishes to keep track of whom to Cc: on individual patchsets,\nthey must manually keep track of each recipient and fill it in with the\n`--cc` option on git-format-patch each time. However, on the Git mailing\nlist, Cc:'s are typically never dropped. As a result, it would be nice\nto have a method to keep track of recipients on a per-branch basis.\n\nCurrently, git-format-patch gets its To: headers from the `--to` options\nand the `format.to` config variable. The Cc: header is derived\nsimilarly.\n\nIn addition to the above, read To: and Cc: headers from\n`format.<branch-name>.to` and `format.<branch-name>.cc` so that users\ncan have branch-specific configuration options.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/config/format.txt    |   7 +-\n Documentation/git-format-patch.txt |  10 +--\n builtin/log.c                      |  63 +++++++++++++++--\n t/t3200-branch.sh                  |   4 ++\n t/t4014-format-patch.sh            | 108 +++++++++++++++++++++++------\n 5 files changed, 162 insertions(+), 30 deletions(-)\n\ndiff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\nindex 7d2b3b7331..d387451573 100644\n--- a/Documentation/config/format.txt\n+++ b/Documentation/config/format.txt\n@@ -28,9 +28,12 @@ format.headers::\n \n format.to::\n format.cc::\n+format.<branch-name>.to::\n+format.<branch-name>.cc::\n \tAdditional recipients to include in a patch to be submitted\n-\tby mail.  See the --to and --cc options in\n-\tlinkgit:git-format-patch[1].\n+\tby mail.  For the <branch-name> options, the recipients are only\n+\tincluded if patches are generated for the given <branch-name>.\n+\tSee the --to and --cc options in linkgit:git-format-patch[1].\n \n format.subjectPrefix::\n \tThe default for format-patch is to output files with the '[PATCH]'\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex a43f317877..4e826010f6 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -199,7 +199,7 @@ will want to ensure that threading is disabled for `git send-email`.\n \tAdd a `To:` header to the email headers. This is in addition\n \tto any configured headers, and may be used multiple times. The\n \temails given will be used along with any emails given by\n-\t`format.to` configurations.\n+\t`format.to` and `format.<branch-name>.to` configurations.\n \tThe negated form `--no-to` discards all `To:` headers added so\n \tfar (from config or command line).\n \n@@ -207,7 +207,7 @@ will want to ensure that threading is disabled for `git send-email`.\n \tAdd a `Cc:` header to the email headers. This is in addition\n \tto any configured headers, and may be used multiple times. The\n \temails given will be used along with any emails given by\n-\t`format.cc` configurations.\n+\t`format.cc` and `format.<branch-name>.cc` configurations.\n \tThe negated form `--no-cc` discards all `Cc:` headers added so\n \tfar (from config or command line).\n \n@@ -335,7 +335,7 @@ CONFIGURATION\n -------------\n You can specify extra mail header lines to be added to each message,\n defaults for the subject prefix and file suffix, number patches when\n-outputting more than one patch, add \"To\" or \"Cc:\" headers, configure\n+outputting more than one patch, add \"To:\" or \"Cc:\" headers, configure\n attachments, and sign off patches with configuration variables.\n \n ------------\n@@ -352,11 +352,13 @@ attachments, and sign off patches with configuration variables.\n ------------\n \n In addition, for a specific branch, you can specify a custom cover\n-letter subject.\n+letter subject, and add additional \"To:\" or \"Cc:\" headers.\n \n ------------\n [format \"branch-name\"]\n \tcoverSubject = \"subject for branch-name only\"\n+\tto = <email>\n+\tcc = <email>\n ------------\n \n DISCUSSION\ndiff --git a/builtin/log.c b/builtin/log.c\nindex f66a65fe84..e1d793577d 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -738,6 +738,8 @@ static char *default_attach = NULL;\n static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;\n static struct string_list extra_to = STRING_LIST_INIT_NODUP;\n static struct string_list extra_cc = STRING_LIST_INIT_NODUP;\n+int to_cleared;\n+int cc_cleared;\n \n static void add_header(const char *value)\n {\n@@ -1045,6 +1047,55 @@ static int read_branch_subject(struct strbuf *buf, const char *branch_name)\n \treturn 0;\n }\n \n+static void add_branch_headers(struct rev_info *rev, const char *branch_name)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst struct string_list *values;\n+\n+\tif (!branch_name)\n+\t\tbranch_name = find_branch_name(rev);\n+\n+\tif (!branch_name || !*branch_name)\n+\t\treturn;\n+\n+\t/*\n+\t * HACK: We only use branch-specific recipients iff the list has not\n+\t * been cleared by an earlier --no-{to,cc} option on the command-line.\n+\t *\n+\t * When we get format.{to,cc} options, they can be cleared by\n+\t * --no-{to,cc} options since the `git_config` call comes before the\n+\t *  `parse_options` call.\n+\t *\n+\t *  However, in the case of branch.<name>.{to,cc}, this function needs\n+\t *  to be called after `setup_revisions`, which must be called after\n+\t *  `parse_options`. However, in order for the --no-{to,cc} logic to\n+\t *  clear the extra_{to,cc} string_list, this function should actually\n+\t *  be called _before_ `parse_options`. As a result, we have a circular\n+\t *  dependency.\n+\t *\n+\t *  The {to,cc}_cleared flag lets us workaround this by just no\n+\t *  including branch-specific recipients iff --no-{to,cc} has been\n+\t *  specified on the command-line.\n+\t */\n+\n+\tif (!to_cleared) {\n+\t\tstrbuf_addf(&buf, \"format.%s.to\", branch_name);\n+\t\tvalues = git_config_get_value_multi(buf.buf);\n+\t\tif (values)\n+\t\t\tstring_list_append_all(&extra_to, values);\n+\t}\n+\n+\tif (!cc_cleared) {\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addf(&buf, \"format.%s.cc\", branch_name);\n+\t\tvalues = git_config_get_value_multi(buf.buf);\n+\t\tif (values)\n+\t\t\tstring_list_append_all(&extra_cc, values);\n+\t}\n+\n+\tstrbuf_release(&buf);\n+}\n+\n static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \t\t\t      struct commit *origin,\n \t\t\t      int nr, struct commit **list,\n@@ -1304,18 +1355,20 @@ static int header_callback(const struct option *opt, const char *arg, int unset)\n \n static int to_callback(const struct option *opt, const char *arg, int unset)\n {\n-\tif (unset)\n+\tif (unset) {\n+\t\tto_cleared = 1;\n \t\tstring_list_clear(&extra_to, 0);\n-\telse\n+\t} else\n \t\tstring_list_append(&extra_to, arg);\n \treturn 0;\n }\n \n static int cc_callback(const struct option *opt, const char *arg, int unset)\n {\n-\tif (unset)\n+\tif (unset) {\n+\t\tcc_cleared = 1;\n \t\tstring_list_clear(&extra_cc, 0);\n-\telse\n+\t} else\n \t\tstring_list_append(&extra_cc, arg);\n \treturn 0;\n }\n@@ -1782,6 +1835,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \t}\n \n+\tadd_branch_headers(&rev, branch_name);\n+\n \tfor (i = 0; i < extra_hdr.nr; i++) {\n \t\tstrbuf_addstr(&buf, extra_hdr.items[i].string);\n \t\tstrbuf_addch(&buf, '\\n');\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex dad23f7d4c..9ae3da888e 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -783,10 +783,14 @@ test_expect_success 'test tracking setup via --track but deeper' '\n \n test_expect_success 'test deleting branch deletes branch config' '\n \tgit config format.my7.coverSubject \"cover subject\" &&\n+\tgit config format.my7.to \"To Me <to@example.com>\" &&\n+\tgit config format.my7.cc \"Cc Me <cc@example.com>\" &&\n \tgit branch -d my7 &&\n \ttest -z \"$(git config branch.my7.remote)\" &&\n \ttest -z \"$(git config branch.my7.merge)\" &&\n \ttest -z \"$(git config format.my7.coverSubject)\"\n+\ttest -z \"$(git config format.my7.to)\" &&\n+\ttest -z \"$(git config format.my7.cc)\"\n '\n \n test_expect_success 'test deleting branch without config' '\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 9d11992e10..9c527510c3 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -234,6 +234,65 @@ test_expect_failure 'configuration To: header (rfc2047)' '\n \tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs9\n '\n \n+test_expect_success 'branch-specific configuration To: header (ascii)' '\n+\n+\ttest_unconfig format.to &&\n+\tgit config format.side.to \"R E Cipient <rcipient@example.com>\" &&\n+\tgit format-patch --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs10\n+'\n+\n+test_expect_failure 'branch-specific configuration To: header (rfc822)' '\n+\n+\tgit config format.side.to \"R. E. Cipient <rcipient@example.com>\" &&\n+\tgit format-patch --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" hdrs10\n+'\n+\n+test_expect_failure 'branch-specific configuration To: header (rfc2047)' '\n+\n+\tgit config format.side.to \"R Ä Cipient <rcipient@example.com>\" &&\n+\tgit format-patch --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs10\n+'\n+\n+test_expect_success 'all recipients included from all sources' '\n+\n+\tgit config format.to \"Format To1 <formatto1@example.com>\" &&\n+\tgit config --add format.to \"Format To2 <formatto2@example.com>\" &&\n+\tgit config format.cc \"Format Cc1 <formatcc1@example.com>\" &&\n+\tgit config --add format.cc \"Format Cc2 <formatcc2@example.com>\" &&\n+\tgit config format.side.to \"Branch To1 <branchto1@example.com>\" &&\n+\tgit config --add format.side.to \"Branch To2 <branchto2@example.com>\" &&\n+\tgit config format.side.cc \"Branch Cc1 <branchcc1@example.com>\" &&\n+\tgit config --add format.side.cc \"Branch Cc2 <branchcc2@example.com>\" &&\n+\tcat <<-\\EOF >expect &&\n+\tTo: Format To1 <formatto1@example.com>,\n+\t    Format To2 <formatto2@example.com>,\n+\t    Command-line To1 <commandlineto1@example.com>,\n+\t    Command-line To2 <commandlineto2@example.com>,\n+\t    Branch To1 <branchto1@example.com>,\n+\t    Branch To2 <branchto2@example.com>\n+\tCc: Format Cc1 <formatcc1@example.com>,\n+\t    Format Cc2 <formatcc2@example.com>,\n+\t    Command-line Cc1 <commandlinecc1@example.com>,\n+\t    Command-line Cc2 <commandlinecc2@example.com>,\n+\t    Branch Cc1 <branchcc1@example.com>,\n+\t    Branch Cc2 <branchcc2@example.com>\n+\n+\tEOF\n+\tgit format-patch --stdout \\\n+\t\t--to=\"Command-line To1 <commandlineto1@example.com>\" \\\n+\t\t--to=\"Command-line To2 <commandlineto2@example.com>\" \\\n+\t\t--cc=\"Command-line Cc1 <commandlinecc1@example.com>\" \\\n+\t\t--cc=\"Command-line Cc2 <commandlinecc2@example.com>\" \\\n+\t\tmaster..side | sed -ne \"/^To:/,/^$/p;/^$/q\" >patch10 &&\n+\ttest_cmp expect patch10\n+'\n+\n # check_patch <patch>: Verify that <patch> looks like a half-sane\n # patch email to avoid a false positive with !grep\n check_patch () {\n@@ -286,42 +345,51 @@ test_expect_success '--no-to overrides config.to' '\n \n \tgit config --replace-all format.to \\\n \t\t\"R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-to --stdout master..side >patch10 &&\n-\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n-\tcheck_patch hdrs10 &&\n-\t! grep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs10\n+\tgit config --replace-all format.side.to \\\n+\t\t\"B R Anch <branch@example.com>\" &&\n+\tgit format-patch --no-to --stdout master..side >patch11 &&\n+\tsed -e \"/^\\$/q\" patch11 >hdrs11 &&\n+\tcheck_patch hdrs11 &&\n+\t! grep \"R E Cipient <rcipient@example.com>\" hdrs11 &&\n+\t! grep \"B R Anch <branch@example.com>\" hdrs11\n '\n \n test_expect_success '--no-to and --to replaces config.to' '\n \n \tgit config --replace-all format.to \\\n \t\t\"Someone <someone@out.there>\" &&\n+\tgit config --replace-all format.side.to \\\n+\t\t\"B R Anch2 <branch2@example.com>\" &&\n \tgit format-patch --no-to --to=\"Someone Else <else@out.there>\" \\\n-\t\t--stdout master..side >patch11 &&\n-\tsed -e \"/^\\$/q\" patch11 >hdrs11 &&\n-\tcheck_patch hdrs11 &&\n-\t! grep \"^To: Someone <someone@out.there>\\$\" hdrs11 &&\n-\tgrep \"^To: Someone Else <else@out.there>\\$\" hdrs11\n+\t\t--stdout master..side >patch12 &&\n+\tsed -e \"/^\\$/q\" patch12 >hdrs12 &&\n+\tcheck_patch hdrs12 &&\n+\t! grep \"Someone <someone@out.there>\" hdrs12 &&\n+\t! grep \"B R Anch2 <branch2@example.com>\" hdrs12 &&\n+\tgrep \"^To: Someone Else <else@out.there>\\$\" hdrs12\n '\n \n test_expect_success '--no-cc overrides config.cc' '\n \n \tgit config --replace-all format.cc \\\n \t\t\"C E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-cc --stdout master..side >patch12 &&\n-\tsed -e \"/^\\$/q\" patch12 >hdrs12 &&\n-\tcheck_patch hdrs12 &&\n-\t! grep \"^Cc: C E Cipient <rcipient@example.com>\\$\" hdrs12\n+\tgit config --replace-all format.side.cc \\\n+\t\t\"B R Anch3 <branch3@example.com>\" &&\n+\tgit format-patch --no-cc --stdout master..side >patch13 &&\n+\tsed -e \"/^\\$/q\" patch13 >hdrs13 &&\n+\tcheck_patch hdrs13 &&\n+\t! grep \"C E Cipient <rcipient@example.com>\" hdrs13 &&\n+\t! grep \"B R Anch3 <branch3@example.com>\" hdrs13\n '\n \n test_expect_success '--no-add-header overrides config.headers' '\n \n \tgit config --replace-all format.headers \\\n \t\t\"Header1: B E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-add-header --stdout master..side >patch13 &&\n-\tsed -e \"/^\\$/q\" patch13 >hdrs13 &&\n-\tcheck_patch hdrs13 &&\n-\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" hdrs13\n+\tgit format-patch --no-add-header --stdout master..side >patch14 &&\n+\tsed -e \"/^\\$/q\" patch14 >hdrs14 &&\n+\tcheck_patch hdrs14 &&\n+\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" hdrs14\n '\n \n test_expect_success 'multiple files' '\n@@ -963,7 +1031,7 @@ test_expect_success 'format-patch wraps extremely long subject (ascii)' '\n \tgit add file &&\n \tgit commit -m \"$M512\" &&\n \tgit format-patch --stdout -1 >patch &&\n-\tsed -n \"/^Subject/p; /^ /p; /^$/q\" <patch >subject &&\n+\tsed -n \"/^Subject/p; /^ /p; /^Header1:/q\" <patch >subject &&\n \ttest_cmp expect subject\n '\n \n@@ -1002,7 +1070,7 @@ test_expect_success 'format-patch wraps extremely long subject (rfc2047)' '\n \tgit add file &&\n \tgit commit -m \"$M512\" &&\n \tgit format-patch --stdout -1 >patch &&\n-\tsed -n \"/^Subject/p; /^ /p; /^$/q\" <patch >subject &&\n+\tsed -n \"/^Subject/p; /^ /p; /^Header1:/q\" <patch >subject &&\n \ttest_cmp expect subject\n '\n \n@@ -1011,7 +1079,7 @@ check_author() {\n \tgit add file &&\n \tGIT_AUTHOR_NAME=$1 git commit -m author-check &&\n \tgit format-patch --stdout -1 >patch &&\n-\tsed -n \"/^From: /p; /^ /p; /^$/q\" <patch >actual &&\n+\tsed -n \"/^From: /p; /^ /p; /^Date:/q\" <patch >actual &&\n \ttest_cmp expect actual\n }\n \n-- \n2.21.0.1049.geb646f7864\n\n"},{"id":"375784","messageId":"xmqqk1ep3ejv.fsf@gitster-ct.c.googlers.com","threadId":"51038","inReplyTo":"cover.1558052674.git.liu.denton@gmail.com","subject":"Re: [PATCH v2 0/6] teach branch-specific options for format-patch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-05-17T04:12:04Z","receivedAt":"2019-05-17T04:12:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\n> index dc77941c48..d387451573 100644\n> --- a/Documentation/config/format.txt\n> +++ b/Documentation/config/format.txt\n> @@ -28,14 +28,22 @@ format.headers::\n>  \n>  format.to::\n>  format.cc::\n> +format.<branch-name>.to::\n> +format.<branch-name>.cc::\n>  \tAdditional recipients to include in a patch to be submitted\n> -\tby mail.  See the --to and --cc options in\n> -\tlinkgit:git-format-patch[1].\n> +\tby mail.  For the <branch-name> options, the recipients are only\n> +\tincluded if patches are generated for the given <branch-name>.\n> +\tSee the --to and --cc options in linkgit:git-format-patch[1].\n\nAn obvious question that somebody else may raise is:\n\n    What makes the branch name that special?  What guarantees that\n    it would stay to be the *only* thing that affects the choice of\n    these variables?\n\nAn obvious answer to that is \"nothing---we are painting ourselves in\na corner we cannot easily get out of with this design\".\n\nIf we want to drive format-patch differently depending on the\ncombination of the worktree location *and* the branch the patches\nare generated from, we can do something like:\n\n\t[includeif \"gitdir:/path/to/worktree/1\"] path = one.inc\n\t[includeif \"gitdir:/path/to/worktree/2\"] path = two.inc\n\nand then have one.inc/two.inc have customized definition of these\nformat.<branch>.{to,cc,...} variables.\n\nBut at that point, Ævar's \"wouldn't this fit better with includeif\"\nsuggestion becomes more and more appropriate.  Once we invent the\nway to combine the conditions for includeIf, it would benefit not\njust this set of variables but all others that will follow in the\nfuture.\n\nHaving said that, as long as we are fine with the plan to deprecate\nand remove these three-level variables this patch introdues in the\nfuture, I think it is OK to have them as a temporary stop-gap\nmeasure.\n\n> +format.<branch-name>.coverSubject::\n> +\tWhen format-patch generates a cover letter for the given\n> +\t<branch-name>, use the specified subject for the cover letter\n> +\tinstead of the generic template.\n\nI still think it is a mistake that this has to be given separately\nand possibly redundantly from the branch description.\n\n> +static const char *branch_specific_config[] = {\n> +\t\"branch\",\n> +\t\"format\",\n> +\tNULL\n> +};\n\nYuck.  This will break a workflow where a fixed branch with a known\nconfiguration is deleted and recreated over and over again\n(e.g. think of \"for-linus\" branches used for request-pull in each\nmerge window).\n\n>  static void delete_branch_config(const char *branchname)\n>  {\n>  \tstruct strbuf buf = STRBUF_INIT;\n> -\tstrbuf_addf(&buf, \"branch.%s\", branchname);\n> -\tif (git_config_rename_section(buf.buf, NULL) < 0)\n> -\t\twarning(_(\"Update of config-file failed\"));\n> +\tint i;\n> +\tfor (i = 0; branch_specific_config[i]; i++) {\n> +\t\tstrbuf_addf(&buf, \"%s.%s\", branch_specific_config[i], branchname);\n> +\t\tif (git_config_rename_section(buf.buf, NULL) < 0)\n> +\t\t\twarning(_(\"Update of config-file failed\"));\n> +\t\tstrbuf_reset(&buf);\n> +\t}\n\nThis will hardcode the unwarranted limitation that the second level\nof the format.*.var hierarchy MUST be branch names and nothing else,\nwon't it?  \n\n"},{"id":"375791","messageId":"20190517072515.GA22326@archbookpro.localdomain","threadId":"51038","inReplyTo":"xmqqk1ep3ejv.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 0/6] teach branch-specific options for format-patch","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-17T07:25:15Z","receivedAt":"2019-05-17T07:25:20Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Junio,\n\nOn Fri, May 17, 2019 at 01:12:04PM +0900, Junio C Hamano wrote:\n> Denton Liu <liu.denton@gmail.com> writes:\n> \n> > diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\n> > index dc77941c48..d387451573 100644\n> > --- a/Documentation/config/format.txt\n> > +++ b/Documentation/config/format.txt\n> > @@ -28,14 +28,22 @@ format.headers::\n> >  \n> >  format.to::\n> >  format.cc::\n> > +format.<branch-name>.to::\n> > +format.<branch-name>.cc::\n> >  \tAdditional recipients to include in a patch to be submitted\n> > -\tby mail.  See the --to and --cc options in\n> > -\tlinkgit:git-format-patch[1].\n> > +\tby mail.  For the <branch-name> options, the recipients are only\n> > +\tincluded if patches are generated for the given <branch-name>.\n> > +\tSee the --to and --cc options in linkgit:git-format-patch[1].\n> \n> An obvious question that somebody else may raise is:\n> \n>     What makes the branch name that special?  What guarantees that\n>     it would stay to be the *only* thing that affects the choice of\n>     these variables?\n> \n> An obvious answer to that is \"nothing---we are painting ourselves in\n> a corner we cannot easily get out of with this design\".\n> \n> If we want to drive format-patch differently depending on the\n> combination of the worktree location *and* the branch the patches\n> are generated from, we can do something like:\n> \n> \t[includeif \"gitdir:/path/to/worktree/1\"] path = one.inc\n> \t[includeif \"gitdir:/path/to/worktree/2\"] path = two.inc\n> \n> and then have one.inc/two.inc have customized definition of these\n> format.<branch>.{to,cc,...} variables.\n> \n> But at that point, Ævar's \"wouldn't this fit better with includeif\"\n> suggestion becomes more and more appropriate.  Once we invent the\n> way to combine the conditions for includeIf, it would benefit not\n> just this set of variables but all others that will follow in the\n> future.\n\nHmm, I'm starting to like Ævar's idea more the more I think about it.\n\n> \n> Having said that, as long as we are fine with the plan to deprecate\n> and remove these three-level variables this patch introdues in the\n> future, I think it is OK to have them as a temporary stop-gap\n> measure.\n> \n> > +format.<branch-name>.coverSubject::\n> > +\tWhen format-patch generates a cover letter for the given\n> > +\t<branch-name>, use the specified subject for the cover letter\n> > +\tinstead of the generic template.\n> \n> I still think it is a mistake that this has to be given separately\n> and possibly redundantly from the branch description.\n\nI forgot about incorporating this. Since we don't need a branch-specific\ncoverSubject anymore, we can push everything into a includeif since now\nformat.<name>.coverSubject doesn't really need to exist.\n\nI'm going to repurpose --cover-subject format.coverSubject to be a\nboolean option which'll mean \"process the description and if you can\nextract a subject out of it, put it on the cover letter\". This way, we\ncan maintain backwards compatability in case users have some specific\nuse-case.\n\nUnless you'd like this processing to be the default behaviour? I'm\nimpartial either way.\n\n> \n> > +static const char *branch_specific_config[] = {\n> > +\t\"branch\",\n> > +\t\"format\",\n> > +\tNULL\n> > +};\n> \n> Yuck.  This will break a workflow where a fixed branch with a known\n> configuration is deleted and recreated over and over again\n> (e.g. think of \"for-linus\" branches used for request-pull in each\n> merge window).\n\nI suppose when we implement `onBranch`, you'd prefer `git branch -d` to\nalso not discard those sections.\n\n> \n> >  static void delete_branch_config(const char *branchname)\n> >  {\n> >  \tstruct strbuf buf = STRBUF_INIT;\n> > -\tstrbuf_addf(&buf, \"branch.%s\", branchname);\n> > -\tif (git_config_rename_section(buf.buf, NULL) < 0)\n> > -\t\twarning(_(\"Update of config-file failed\"));\n> > +\tint i;\n> > +\tfor (i = 0; branch_specific_config[i]; i++) {\n> > +\t\tstrbuf_addf(&buf, \"%s.%s\", branch_specific_config[i], branchname);\n> > +\t\tif (git_config_rename_section(buf.buf, NULL) < 0)\n> > +\t\t\twarning(_(\"Update of config-file failed\"));\n> > +\t\tstrbuf_reset(&buf);\n> > +\t}\n> \n> This will hardcode the unwarranted limitation that the second level\n> of the format.*.var hierarchy MUST be branch names and nothing else,\n> won't it?  \n> \n\nI was expecting it to only be branch names but now let's take a\ndifferent approach.\n\nConsider patches 3-6 dropped. I'd like to queue 1-2, though, since\nthey're just cleanup patches.\n\nAlso, expect a onBranch patchset some time in the future (not the near\nfuture, school is busy).\n\nThanks for your feedback, Junio.\n"},{"id":"375804","messageId":"20190517165405.GA2541@archbookpro.localdomain","threadId":"51038","inReplyTo":"20190517072515.GA22326@archbookpro.localdomain","subject":"Re: [PATCH v2 0/6] teach branch-specific options for format-patch","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-17T16:54:05Z","receivedAt":"2019-05-17T16:54:09Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Junio,\n\nI just realised that my use-cases wouldn't be fully covered with the\nonBranch configuration option.\n\nOn Fri, May 17, 2019 at 03:25:15AM -0400, Denton Liu wrote:\n> Hi Junio,\n> \n> On Fri, May 17, 2019 at 01:12:04PM +0900, Junio C Hamano wrote:\n> > Denton Liu <liu.denton@gmail.com> writes:\n> > \n> > > diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\n> > > index dc77941c48..d387451573 100644\n> > > --- a/Documentation/config/format.txt\n> > > +++ b/Documentation/config/format.txt\n> > > @@ -28,14 +28,22 @@ format.headers::\n> > >  \n> > >  format.to::\n> > >  format.cc::\n> > > +format.<branch-name>.to::\n> > > +format.<branch-name>.cc::\n> > >  \tAdditional recipients to include in a patch to be submitted\n> > > -\tby mail.  See the --to and --cc options in\n> > > -\tlinkgit:git-format-patch[1].\n> > > +\tby mail.  For the <branch-name> options, the recipients are only\n> > > +\tincluded if patches are generated for the given <branch-name>.\n> > > +\tSee the --to and --cc options in linkgit:git-format-patch[1].\n> > \n> > An obvious question that somebody else may raise is:\n> > \n> >     What makes the branch name that special?  What guarantees that\n> >     it would stay to be the *only* thing that affects the choice of\n> >     these variables?\n> > \n> > An obvious answer to that is \"nothing---we are painting ourselves in\n> > a corner we cannot easily get out of with this design\".\n> > \n> > If we want to drive format-patch differently depending on the\n> > combination of the worktree location *and* the branch the patches\n> > are generated from, we can do something like:\n> > \n> > \t[includeif \"gitdir:/path/to/worktree/1\"] path = one.inc\n> > \t[includeif \"gitdir:/path/to/worktree/2\"] path = two.inc\n> > \n> > and then have one.inc/two.inc have customized definition of these\n> > format.<branch>.{to,cc,...} variables.\n> > \n> > But at that point, Ævar's \"wouldn't this fit better with includeif\"\n> > suggestion becomes more and more appropriate.  Once we invent the\n> > way to combine the conditions for includeIf, it would benefit not\n> > just this set of variables but all others that will follow in the\n> > future.\n> \n> Hmm, I'm starting to like Ævar's idea more the more I think about it.\n> \n\nThere is one limitation with onBranch. Suppose someone runs\n\n\t$ git checkout other\n\t$ git format-patch master..feature\n\nThen, with onBranch, they'd use get the To and Cc of `other`. But with\n`format.feature.*`, format-patch correctly handles this and will use\n`feature`'s To and Cc.\n\n> > \n> > Having said that, as long as we are fine with the plan to deprecate\n> > and remove these three-level variables this patch introdues in the\n> > future, I think it is OK to have them as a temporary stop-gap\n> > measure.\n\nWith this new discovery, I'm not sure it'd be possible to deprecate it\nwithout losing a use-case.\n\n> > \n> > > +format.<branch-name>.coverSubject::\n> > > +\tWhen format-patch generates a cover letter for the given\n> > > +\t<branch-name>, use the specified subject for the cover letter\n> > > +\tinstead of the generic template.\n> > \n> > I still think it is a mistake that this has to be given separately\n> > and possibly redundantly from the branch description.\n> \n> I forgot about incorporating this. Since we don't need a branch-specific\n> coverSubject anymore, we can push everything into a includeif since now\n> format.<name>.coverSubject doesn't really need to exist.\n> \n> I'm going to repurpose --cover-subject format.coverSubject to be a\n> boolean option which'll mean \"process the description and if you can\n> extract a subject out of it, put it on the cover letter\". This way, we\n> can maintain backwards compatability in case users have some specific\n> use-case.\n> \n> Unless you'd like this processing to be the default behaviour? I'm\n> impartial either way.\n> \n> > \n> > > +static const char *branch_specific_config[] = {\n> > > +\t\"branch\",\n> > > +\t\"format\",\n> > > +\tNULL\n> > > +};\n> > \n> > Yuck.  This will break a workflow where a fixed branch with a known\n> > configuration is deleted and recreated over and over again\n> > (e.g. think of \"for-linus\" branches used for request-pull in each\n> > merge window).\n> \n> I suppose when we implement `onBranch`, you'd prefer `git branch -d` to\n> also not discard those sections.\n> \n> > \n> > >  static void delete_branch_config(const char *branchname)\n> > >  {\n> > >  \tstruct strbuf buf = STRBUF_INIT;\n> > > -\tstrbuf_addf(&buf, \"branch.%s\", branchname);\n> > > -\tif (git_config_rename_section(buf.buf, NULL) < 0)\n> > > -\t\twarning(_(\"Update of config-file failed\"));\n> > > +\tint i;\n> > > +\tfor (i = 0; branch_specific_config[i]; i++) {\n> > > +\t\tstrbuf_addf(&buf, \"%s.%s\", branch_specific_config[i], branchname);\n> > > +\t\tif (git_config_rename_section(buf.buf, NULL) < 0)\n> > > +\t\t\twarning(_(\"Update of config-file failed\"));\n> > > +\t\tstrbuf_reset(&buf);\n> > > +\t}\n> > \n> > This will hardcode the unwarranted limitation that the second level\n> > of the format.*.var hierarchy MUST be branch names and nothing else,\n> > won't it?  \n> > \n> \n> I was expecting it to only be branch names but now let's take a\n> different approach.\n> \n> Consider patches 3-6 dropped. I'd like to queue 1-2, though, since\n> they're just cleanup patches.\n\nIn light of this, I don't plan on dropping 3-6 anymore. I'm going to\nreroll the new behaviour of coverSubject.\n\n> \n> Also, expect a onBranch patchset some time in the future (not the near\n> future, school is busy).\n> \n> Thanks for your feedback, Junio.\n"},{"id":"376037","messageId":"cover.1558492582.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558052674.git.liu.denton@gmail.com","subject":"[PATCH v3 0/8] teach branch-specific options for format-patch","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-22T02:44:23Z","receivedAt":"2019-05-22T02:44:28Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Junio,\n\nI've gotten rid of all the `format.*.coverSubject` stuff and replaced it\nwith a generic `format.inferCoverSubject` that will read the subject\nfrom the branch description. I've also made `git branch -d` stop\ndeleting the `format.<branch>.*` variables.\n\nA new addition in this patchset is that format-patch is now taught to\nunderstand the `format.<branch-name>.outputDirectory` configuration.\n\nChanges since v2:\n\n* Replaced `format.<branch-name>.coverSubject` and `--cover-subject`\n  with `format.inferCoverSubject` and `--infer-cover-subject` which\n  reads the subject from the branch description\n* Do not let `git branch -d` delete `format.<branch-name>.*` configs\n* More documentation cleanup\n* Taught format-patch to read `format.<branch-name>.outputDirectory` as\n  well\n\nChanges since v1:\n\n* Used format.<branch-name>.* variables instead of branch.<branch-name>.*\n\n\nDenton Liu (8):\n  t4014: clean up style\n  Doc: add more detail for git-format-patch\n  format-patch: infer cover letter from branch description\n  format-patch: move extra_headers logic later\n  string-list: create string_list_append_all\n  format-patch: read branch-specific To: and Cc: headers\n  format-patch: move output_directory logic later\n  format-patch: read branch-specific output directory\n\n Documentation/config/format.txt    |  17 +-\n Documentation/git-format-patch.txt |  44 +-\n builtin/log.c                      | 237 ++++++---\n string-list.c                      |   9 +\n string-list.h                      |   7 +\n t/t4014-format-patch.sh            | 739 +++++++++++++++++------------\n 6 files changed, 667 insertions(+), 386 deletions(-)\n\nRange-diff against v2:\n1:  92d7be10aa = 1:  82c0dc9cc8 t4014: clean up style\n2:  930a021b7f ! 2:  a8cc599fd2 Doc: add more detail for git-format-patch\n    @@ -9,7 +9,10 @@\n     \n         In addition, document the special value of `--base=auto`.\n     \n    -    Finally, while we're at it, surround option arguments with <>.\n    +    Next, while we're at it, surround option arguments with <>.\n    +\n    +    Finally, document the `format.outputDirectory` config and change\n    +    `format.coverletter` to use camelcase.\n     \n         Signed-off-by: Denton Liu <liu.denton@gmail.com>\n     \n    @@ -67,3 +70,25 @@\n      \n      --root::\n      \tTreat the revision argument as a <revision range>, even if it\n    +@@\n    + -------------\n    + You can specify extra mail header lines to be added to each message,\n    + defaults for the subject prefix and file suffix, number patches when\n    +-outputting more than one patch, add \"To\" or \"Cc:\" headers, configure\n    +-attachments, and sign off patches with configuration variables.\n    ++outputting more than one patch, add \"To:\" or \"Cc:\" headers, configure\n    ++attachments, change the patch output directory, and sign off patches\n    ++with configuration variables.\n    + \n    + ------------\n    + [format]\n    +@@\n    + \tcc = <email>\n    + \tattach [ = mime-boundary-string ]\n    + \tsignOff = true\n    +-\tcoverletter = auto\n    ++\toutputDirectory = <directory>\n    ++\tcoverLetter = auto\n    + ------------\n    + \n    + \n3:  075e2c0721 < -:  ---------- format-patch: make cover letter subject configurable\n-:  ---------- > 3:  e3b8c96b1c format-patch: infer cover letter from branch description\n4:  323179377e ! 4:  52ee126825 format-patch: move extra_headers logic later\n    @@ -7,10 +7,10 @@\n         logic later in the function so that this happens. (This patch is best\n         viewed with `git diff --color-moved`.)\n     \n    -    Note that this logic only depends on the `git_config` and\n    +    Note that this logic only depends on `git_config` and\n         `repo_init_revisions` and is depended on by the patch creation logic\n    -    which is directly below it so this move is effectively a no-op as\n    -    no dependencies being reordered.\n    +    which is directly below it so this move is effectively a no-op as no\n    +    dependencies being reordered.\n     \n         Signed-off-by: Denton Liu <liu.denton@gmail.com>\n     \n5:  7a8fa4f5f6 = 5:  27c60c715c string-list: create string_list_append_all\n6:  9d7338f067 ! 6:  f2a1546b2d format-patch: read branch-specific To: and Cc: headers\n    @@ -59,29 +59,20 @@\n      \tfar (from config or command line).\n      \n     @@\n    - -------------\n    - You can specify extra mail header lines to be added to each message,\n    - defaults for the subject prefix and file suffix, number patches when\n    --outputting more than one patch, add \"To\" or \"Cc:\" headers, configure\n    -+outputting more than one patch, add \"To:\" or \"Cc:\" headers, configure\n    - attachments, and sign off patches with configuration variables.\n    - \n    - ------------\n    -@@\n    + \tinferCoverSubject = true\n      ------------\n      \n    - In addition, for a specific branch, you can specify a custom cover\n    --letter subject.\n    ++In addition, for a specific branch, you can specify a custom cover\n     +letter subject, and add additional \"To:\" or \"Cc:\" headers.\n    - \n    - ------------\n    - [format \"branch-name\"]\n    - \tcoverSubject = \"subject for branch-name only\"\n    ++\n    ++------------\n    ++[format \"branch-name\"]\n     +\tto = <email>\n     +\tcc = <email>\n    - ------------\n    ++------------\n      \n      DISCUSSION\n    + ----------\n     \n      diff --git a/builtin/log.c b/builtin/log.c\n      --- a/builtin/log.c\n    @@ -96,7 +87,7 @@\n      static void add_header(const char *value)\n      {\n     @@\n    - \treturn 0;\n    + \tfprintf(rev->diffopt.file, \"\\n\");\n      }\n      \n     +static void add_branch_headers(struct rev_info *rev, const char *branch_name)\n    @@ -186,25 +177,6 @@\n      \t\tstrbuf_addstr(&buf, extra_hdr.items[i].string);\n      \t\tstrbuf_addch(&buf, '\\n');\n     \n    - diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\n    - --- a/t/t3200-branch.sh\n    - +++ b/t/t3200-branch.sh\n    -@@\n    - \n    - test_expect_success 'test deleting branch deletes branch config' '\n    - \tgit config format.my7.coverSubject \"cover subject\" &&\n    -+\tgit config format.my7.to \"To Me <to@example.com>\" &&\n    -+\tgit config format.my7.cc \"Cc Me <cc@example.com>\" &&\n    - \tgit branch -d my7 &&\n    - \ttest -z \"$(git config branch.my7.remote)\" &&\n    - \ttest -z \"$(git config branch.my7.merge)\" &&\n    - \ttest -z \"$(git config format.my7.coverSubject)\"\n    -+\ttest -z \"$(git config format.my7.to)\" &&\n    -+\ttest -z \"$(git config format.my7.cc)\"\n    - '\n    - \n    - test_expect_success 'test deleting branch without config' '\n    -\n      diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\n      --- a/t/t4014-format-patch.sh\n      +++ b/t/t4014-format-patch.sh\n-:  ---------- > 7:  70e28c5472 format-patch: move output_directory logic later\n-:  ---------- > 8:  b23c3c16f7 format-patch: read branch-specific output directory\n-- \n2.22.0.rc1.169.g49223abbf8\n\n"},{"id":"376038","messageId":"82c0dc9cc8507340d5eee582146dc917a73462fe.1558492582.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558492582.git.liu.denton@gmail.com","subject":"[PATCH v3 1/8] t4014: clean up style","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-22T02:44:26Z","receivedAt":"2019-05-22T02:44:33Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In Git's tests, there is typically no space between the redirection\noperator and the filename. Remove these spaces.\n\nSince output is silenced when running without `-v` and debugging\noutput is useful with `-v`, remove redirections to /dev/null.\n\nChange here-docs from `<<\\EOF` to `<<-\\EOF` so that they can be indented\nalong with the rest of the test case.\n\nFinally, refactor to remove Git commands upstream of pipe. This way, if\nan invocation of a Git command fails, the return code won't be lost.\nKeep upstream non-Git commands since we have to assume a base level of\nsanity.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t4014-format-patch.sh | 614 +++++++++++++++++++++-------------------\n 1 file changed, 319 insertions(+), 295 deletions(-)\n\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex b6e2fdbc44..3423f974bc 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -34,7 +34,8 @@ test_expect_success setup '\n \tgit commit -m \"Side changes #3 with \\\\n backslash-n in it.\" &&\n \n \tgit checkout master &&\n-\tgit diff-tree -p C2 | git apply --index &&\n+\tgit diff-tree -p C2 >patch &&\n+\tgit apply --index <patch &&\n \ttest_tick &&\n \tgit commit -m \"Master accepts moral equivalent of #2\"\n \n@@ -77,7 +78,7 @@ test_expect_success \"format-patch doesn't consider merge commits\" '\n \tgit checkout -b merger master &&\n \ttest_tick &&\n \tgit merge --no-ff slave &&\n-\tcnt=$(git format-patch -3 --stdout | grep \"^From \" | wc -l) &&\n+\tcnt=$(git format-patch -3 --stdout >patch && grep \"^From \" patch | wc -l) &&\n \ttest $cnt = 3\n '\n \n@@ -85,21 +86,22 @@ test_expect_success \"format-patch result applies\" '\n \n \tgit checkout -b rebuild-0 master &&\n \tgit am -3 patch0 &&\n-\tcnt=$(git rev-list master.. | wc -l) &&\n-\ttest $cnt = 2\n+\tgit rev-list master.. >list &&\n+\ttest_line_count = 2 list\n '\n \n test_expect_success \"format-patch --ignore-if-in-upstream result applies\" '\n \n \tgit checkout -b rebuild-1 master &&\n \tgit am -3 patch1 &&\n-\tcnt=$(git rev-list master.. | wc -l) &&\n-\ttest $cnt = 2\n+\tgit rev-list master.. >list &&\n+\ttest_line_count = 2 list\n '\n \n test_expect_success 'commit did not screw up the log message' '\n \n-\tgit cat-file commit side | grep \"^Side .* with .* backslash-n\"\n+\tgit cat-file commit side >actual &&\n+\tgrep \"^Side .* with .* backslash-n\" actual\n \n '\n \n@@ -112,7 +114,8 @@ test_expect_success 'format-patch did not screw up the log message' '\n \n test_expect_success 'replay did not screw up the log message' '\n \n-\tgit cat-file commit rebuild-1 | grep \"^Side .* with .* backslash-n\"\n+\tgit cat-file commit rebuild-1 >actual &&\n+\tgrep \"^Side .* with .* backslash-n\" actual\n \n '\n \n@@ -122,8 +125,8 @@ test_expect_success 'extra headers' '\n \" &&\n \tgit config --add format.headers \"Cc: S E Cipient <scipient@example.com>\n \" &&\n-\tgit format-patch --stdout master..side > patch2 &&\n-\tsed -e \"/^\\$/q\" patch2 > hdrs2 &&\n+\tgit format-patch --stdout master..side >patch2 &&\n+\tsed -e \"/^\\$/q\" patch2 >hdrs2 &&\n \tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs2 &&\n \tgrep \"^Cc: S E Cipient <scipient@example.com>\\$\" hdrs2\n \n@@ -134,7 +137,7 @@ test_expect_success 'extra headers without newlines' '\n \tgit config --replace-all format.headers \"To: R E Cipient <rcipient@example.com>\" &&\n \tgit config --add format.headers \"Cc: S E Cipient <scipient@example.com>\" &&\n \tgit format-patch --stdout master..side >patch3 &&\n-\tsed -e \"/^\\$/q\" patch3 > hdrs3 &&\n+\tsed -e \"/^\\$/q\" patch3 >hdrs3 &&\n \tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs3 &&\n \tgrep \"^Cc: S E Cipient <scipient@example.com>\\$\" hdrs3\n \n@@ -144,8 +147,8 @@ test_expect_success 'extra headers with multiple To:s' '\n \n \tgit config --replace-all format.headers \"To: R E Cipient <rcipient@example.com>\" &&\n \tgit config --add format.headers \"To: S E Cipient <scipient@example.com>\" &&\n-\tgit format-patch --stdout master..side > patch4 &&\n-\tsed -e \"/^\\$/q\" patch4 > hdrs4 &&\n+\tgit format-patch --stdout master..side >patch4 &&\n+\tsed -e \"/^\\$/q\" patch4 >hdrs4 &&\n \tgrep \"^To: R E Cipient <rcipient@example.com>,\\$\" hdrs4 &&\n \tgrep \"^ *S E Cipient <scipient@example.com>\\$\" hdrs4\n '\n@@ -153,72 +156,82 @@ test_expect_success 'extra headers with multiple To:s' '\n test_expect_success 'additional command line cc (ascii)' '\n \n \tgit config --replace-all format.headers \"Cc: R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --cc=\"S E Cipient <scipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch5 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" patch5 &&\n-\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" patch5\n+\tgit format-patch --cc=\"S E Cipient <scipient@example.com>\" --stdout master..side >patch5 &&\n+\tsed -e \"/^\\$/q\" patch5 >hdrs5 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" hdrs5 &&\n+\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" hdrs5\n '\n \n test_expect_failure 'additional command line cc (rfc822)' '\n \n \tgit config --replace-all format.headers \"Cc: R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --cc=\"S. E. Cipient <scipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch5 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" patch5 &&\n-\tgrep \"^ *\\\"S. E. Cipient\\\" <scipient@example.com>\\$\" patch5\n+\tgit format-patch --cc=\"S. E. Cipient <scipient@example.com>\" --stdout master..side >patch5 &&\n+\tsed -e \"/^\\$/q\" patch5 >hdrs5 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" hdrs5 &&\n+\tgrep \"^ *\\\"S. E. Cipient\\\" <scipient@example.com>\\$\" hdrs5\n '\n \n test_expect_success 'command line headers' '\n \n \tgit config --unset-all format.headers &&\n-\tgit format-patch --add-header=\"Cc: R E Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch6 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>\\$\" patch6\n+\tgit format-patch --add-header=\"Cc: R E Cipient <rcipient@example.com>\" --stdout master..side >patch6 &&\n+\tsed -e \"/^\\$/q\" patch6 >hdrs6 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>\\$\" hdrs6\n '\n \n test_expect_success 'configuration headers and command line headers' '\n \n \tgit config --replace-all format.headers \"Cc: R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --add-header=\"Cc: S E Cipient <scipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch7 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" patch7 &&\n-\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" patch7\n+\tgit format-patch --add-header=\"Cc: S E Cipient <scipient@example.com>\" --stdout master..side >patch7 &&\n+\tsed -e \"/^\\$/q\" patch7 >hdrs7 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" hdrs7 &&\n+\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" hdrs7\n '\n \n test_expect_success 'command line To: header (ascii)' '\n \n \tgit config --unset-all format.headers &&\n-\tgit format-patch --to=\"R E Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch8 &&\n-\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" patch8\n+\tgit format-patch --to=\"R E Cipient <rcipient@example.com>\" --stdout master..side >patch8 &&\n+\tsed -e \"/^\\$/q\" patch8 >hdrs8 &&\n+\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs8\n '\n \n test_expect_failure 'command line To: header (rfc822)' '\n \n-\tgit format-patch --to=\"R. E. Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch8 &&\n-\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" patch8\n+\tgit format-patch --to=\"R. E. Cipient <rcipient@example.com>\" --stdout master..side >patch8 &&\n+\tsed -e \"/^\\$/q\" patch8 >hdrs8 &&\n+\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" hdrs8\n '\n \n test_expect_failure 'command line To: header (rfc2047)' '\n \n-\tgit format-patch --to=\"R Ä Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch8 &&\n-\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" patch8\n+\tgit format-patch --to=\"R Ä Cipient <rcipient@example.com>\" --stdout master..side >patch8 &&\n+\tsed -e \"/^\\$/q\" patch8 >hdrs8 &&\n+\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs8\n '\n \n test_expect_success 'configuration To: header (ascii)' '\n \n \tgit config format.to \"R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --stdout master..side | sed -e \"/^\\$/q\" >patch9 &&\n-\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" patch9\n+\tgit format-patch --stdout master..side >patch9 &&\n+\tsed -e \"/^\\$/q\" patch9 >hdrs9 &&\n+\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs9\n '\n \n test_expect_failure 'configuration To: header (rfc822)' '\n \n \tgit config format.to \"R. E. Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --stdout master..side | sed -e \"/^\\$/q\" >patch9 &&\n-\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" patch9\n+\tgit format-patch --stdout master..side >patch9 &&\n+\tsed -e \"/^\\$/q\" patch9 >hdrs9 &&\n+\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" hdrs9\n '\n \n test_expect_failure 'configuration To: header (rfc2047)' '\n \n \tgit config format.to \"R Ä Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --stdout master..side | sed -e \"/^\\$/q\" >patch9 &&\n-\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" patch9\n+\tgit format-patch --stdout master..side >patch9 &&\n+\tsed -e \"/^\\$/q\" patch9 >hdrs9 &&\n+\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs9\n '\n \n # check_patch <patch>: Verify that <patch> looks like a half-sane\n@@ -231,52 +244,52 @@ check_patch () {\n \n test_expect_success 'format.from=false' '\n \n-\tgit -c format.from=false format-patch --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n+\tgit -c format.from=false format-patch --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n \tcheck_patch patch &&\n-\t! grep \"^From: C O Mitter <committer@example.com>\\$\" patch\n+\t! grep \"^From: C O Mitter <committer@example.com>\\$\" hdrs\n '\n \n test_expect_success 'format.from=true' '\n \n-\tgit -c format.from=true format-patch --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\tgrep \"^From: C O Mitter <committer@example.com>\\$\" patch\n+\tgit -c format.from=true format-patch --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\tgrep \"^From: C O Mitter <committer@example.com>\\$\" hdrs\n '\n \n test_expect_success 'format.from with address' '\n \n-\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\tgrep \"^From: F R Om <from@example.com>\\$\" patch\n+\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\tgrep \"^From: F R Om <from@example.com>\\$\" hdrs\n '\n \n test_expect_success '--no-from overrides format.from' '\n \n-\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --no-from --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\t! grep \"^From: F R Om <from@example.com>\\$\" patch\n+\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --no-from --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\t! grep \"^From: F R Om <from@example.com>\\$\" hdrs\n '\n \n test_expect_success '--from overrides format.from' '\n \n-\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --from --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\t! grep \"^From: F R Om <from@example.com>\\$\" patch\n+\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --from --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\t! grep \"^From: F R Om <from@example.com>\\$\" hdrs\n '\n \n test_expect_success '--no-to overrides config.to' '\n \n \tgit config --replace-all format.to \\\n \t\t\"R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-to --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch10 &&\n-\tcheck_patch patch10 &&\n-\t! grep \"^To: R E Cipient <rcipient@example.com>\\$\" patch10\n+\tgit format-patch --no-to --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tcheck_patch hdrs10 &&\n+\t! grep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs10\n '\n \n test_expect_success '--no-to and --to replaces config.to' '\n@@ -284,31 +297,31 @@ test_expect_success '--no-to and --to replaces config.to' '\n \tgit config --replace-all format.to \\\n \t\t\"Someone <someone@out.there>\" &&\n \tgit format-patch --no-to --to=\"Someone Else <else@out.there>\" \\\n-\t\t--stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch11 &&\n-\tcheck_patch patch11 &&\n-\t! grep \"^To: Someone <someone@out.there>\\$\" patch11 &&\n-\tgrep \"^To: Someone Else <else@out.there>\\$\" patch11\n+\t\t--stdout master..side >patch11 &&\n+\tsed -e \"/^\\$/q\" patch11 >hdrs11 &&\n+\tcheck_patch hdrs11 &&\n+\t! grep \"^To: Someone <someone@out.there>\\$\" hdrs11 &&\n+\tgrep \"^To: Someone Else <else@out.there>\\$\" hdrs11\n '\n \n test_expect_success '--no-cc overrides config.cc' '\n \n \tgit config --replace-all format.cc \\\n \t\t\"C E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-cc --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch12 &&\n-\tcheck_patch patch12 &&\n-\t! grep \"^Cc: C E Cipient <rcipient@example.com>\\$\" patch12\n+\tgit format-patch --no-cc --stdout master..side >patch12 &&\n+\tsed -e \"/^\\$/q\" patch12 >hdrs12 &&\n+\tcheck_patch hdrs12 &&\n+\t! grep \"^Cc: C E Cipient <rcipient@example.com>\\$\" hdrs12\n '\n \n test_expect_success '--no-add-header overrides config.headers' '\n \n \tgit config --replace-all format.headers \\\n \t\t\"Header1: B E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-add-header --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch13 &&\n-\tcheck_patch patch13 &&\n-\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" patch13\n+\tgit format-patch --no-add-header --stdout master..side >patch13 &&\n+\tsed -e \"/^\\$/q\" patch13 >hdrs13 &&\n+\tcheck_patch hdrs13 &&\n+\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" hdrs13\n '\n \n test_expect_success 'multiple files' '\n@@ -338,7 +351,7 @@ test_expect_success 'reroll count (-v)' '\n check_threading () {\n \texpect=\"$1\" &&\n \tshift &&\n-\t(git format-patch --stdout \"$@\"; echo $? > status.out) |\n+\t(git format-patch --stdout \"$@\"; echo $? >status.out) |\n \t# Prints everything between the Message-ID and In-Reply-To,\n \t# and replaces all Message-ID-lookalikes by a sequence number\n \tperl -ne '\n@@ -353,12 +366,12 @@ check_threading () {\n \t\t\tprint;\n \t\t}\n \t\tprint \"---\\n\" if /^From /i;\n-\t' > actual &&\n+\t' >actual &&\n \ttest 0 = \"$(cat status.out)\" &&\n \ttest_cmp \"$expect\" actual\n }\n \n-cat >> expect.no-threading <<EOF\n+cat >>expect.no-threading <<EOF\n ---\n ---\n ---\n@@ -369,7 +382,7 @@ test_expect_success 'no threading' '\n \tcheck_threading expect.no-threading master\n '\n \n-cat > expect.thread <<EOF\n+cat >expect.thread <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -386,7 +399,7 @@ test_expect_success 'thread' '\n \tcheck_threading expect.thread --thread master\n '\n \n-cat > expect.in-reply-to <<EOF\n+cat >expect.in-reply-to <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -406,7 +419,7 @@ test_expect_success 'thread in-reply-to' '\n \t\t--thread master\n '\n \n-cat > expect.cover-letter <<EOF\n+cat >expect.cover-letter <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -427,7 +440,7 @@ test_expect_success 'thread cover-letter' '\n \tcheck_threading expect.cover-letter --cover-letter --thread master\n '\n \n-cat > expect.cl-irt <<EOF\n+cat >expect.cl-irt <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -459,7 +472,7 @@ test_expect_success 'thread explicit shallow' '\n \t\t--in-reply-to=\"<test.message>\" --thread=shallow master\n '\n \n-cat > expect.deep <<EOF\n+cat >expect.deep <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -477,7 +490,7 @@ test_expect_success 'thread deep' '\n \tcheck_threading expect.deep --thread=deep master\n '\n \n-cat > expect.deep-irt <<EOF\n+cat >expect.deep-irt <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -500,7 +513,7 @@ test_expect_success 'thread deep in-reply-to' '\n \t\t--in-reply-to=\"<test.message>\" master\n '\n \n-cat > expect.deep-cl <<EOF\n+cat >expect.deep-cl <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -524,7 +537,7 @@ test_expect_success 'thread deep cover-letter' '\n \tcheck_threading expect.deep-cl --cover-letter --thread=deep master\n '\n \n-cat > expect.deep-cl-irt <<EOF\n+cat >expect.deep-cl-irt <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -606,7 +619,7 @@ test_expect_success 'cover-letter inherits diff options' '\n \n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n   This is an excessively long subject line for a message due to the\n     habit some projects have of not having a short, one-line subject at\n     the start of the commit message, but rather sticking a whole\n@@ -619,12 +632,12 @@ EOF\n test_expect_success 'shortlog of cover-letter wraps overly-long onelines' '\n \n \tgit format-patch --cover-letter -2 &&\n-\tsed -e \"1,/A U Thor/d\" -e \"/^\\$/q\" < 0000-cover-letter.patch > output &&\n+\tsed -e \"1,/A U Thor/d\" -e \"/^\\$/q\" <0000-cover-letter.patch >output &&\n \ttest_cmp expect output\n \n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n index $before..$after 100644\n --- a/file\n +++ b/file\n@@ -646,7 +659,7 @@ test_expect_success 'format-patch respects -U' '\n \n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n \n diff --git a/file b/file\n index $before..$after 100644\n@@ -662,7 +675,7 @@ EOF\n test_expect_success 'format-patch -p suppresses stat' '\n \n \tgit format-patch -p -2 &&\n-\tsed -e \"1,/^\\$/d\" -e \"/^+5/q\" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&\n+\tsed -e \"1,/^\\$/d\" -e \"/^+5/q\" <0001-This-is-an-excessively-long-subject-line-for-a-messa.patch >output &&\n \ttest_cmp expect output\n \n '\n@@ -717,7 +730,7 @@ test_expect_success 'format-patch from a subdirectory (3)' '\n '\n \n test_expect_success 'format-patch --in-reply-to' '\n-\tgit format-patch -1 --stdout --in-reply-to \"baz@foo.bar\" > patch8 &&\n+\tgit format-patch -1 --stdout --in-reply-to \"baz@foo.bar\" >patch8 &&\n \tgrep \"^In-Reply-To: <baz@foo.bar>\" patch8 &&\n \tgrep \"^References: <baz@foo.bar>\" patch8\n '\n@@ -738,20 +751,20 @@ test_expect_success 'format-patch --notes --signoff' '\n \tsed \"1,/^---$/d\" out | grep \"test message\"\n '\n \n-echo \"fatal: --name-only does not make sense\" > expect.name-only\n-echo \"fatal: --name-status does not make sense\" > expect.name-status\n-echo \"fatal: --check does not make sense\" > expect.check\n+echo \"fatal: --name-only does not make sense\" >expect.name-only\n+echo \"fatal: --name-status does not make sense\" >expect.name-status\n+echo \"fatal: --check does not make sense\" >expect.check\n \n test_expect_success 'options no longer allowed for format-patch' '\n-\ttest_must_fail git format-patch --name-only 2> output &&\n+\ttest_must_fail git format-patch --name-only 2>output &&\n \ttest_i18ncmp expect.name-only output &&\n-\ttest_must_fail git format-patch --name-status 2> output &&\n+\ttest_must_fail git format-patch --name-status 2>output &&\n \ttest_i18ncmp expect.name-status output &&\n-\ttest_must_fail git format-patch --check 2> output &&\n+\ttest_must_fail git format-patch --check 2>output &&\n \ttest_i18ncmp expect.check output'\n \n test_expect_success 'format-patch --numstat should produce a patch' '\n-\tgit format-patch --numstat --stdout master..side > output &&\n+\tgit format-patch --numstat --stdout master..side >output &&\n \ttest 5 = $(grep \"^diff --git a/\" output | wc -l)'\n \n test_expect_success 'format-patch -- <path>' '\n@@ -763,20 +776,22 @@ test_expect_success 'format-patch --ignore-if-in-upstream HEAD' '\n \tgit format-patch --ignore-if-in-upstream HEAD\n '\n \n-git_version=\"$(git --version | sed \"s/.* //\")\"\n+git_version=\"$(git --version >version && sed \"s/.* //\" version)\"\n \n signature() {\n \tprintf \"%s\\n%s\\n\\n\" \"-- \" \"${1:-$git_version}\"\n }\n \n test_expect_success 'format-patch default signature' '\n-\tgit format-patch --stdout -1 | tail -n 3 >output &&\n+\tgit format-patch --stdout -1 >patch &&\n+\ttail -n 3 patch >output &&\n \tsignature >expect &&\n \ttest_cmp expect output\n '\n \n test_expect_success 'format-patch --signature' '\n-\tgit format-patch --stdout --signature=\"my sig\" -1 | tail -n 3 >output &&\n+\tgit format-patch --stdout --signature=\"my sig\" -1 >patch &&\n+\ttail -n 3 patch >output &&\n \tsignature \"my sig\" >expect &&\n \ttest_cmp expect output\n '\n@@ -1167,282 +1182,282 @@ append_signoff()\n \n test_expect_success 'signoff: commit with no body' '\n \tappend_signoff </dev/null >actual &&\n-\tcat <<\\EOF | sed \"s/EOL$//\" >expected &&\n-4:Subject: [PATCH] EOL\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat <<-\\EOF | sed \"s/EOL$//\" >expected &&\n+\t4:Subject: [PATCH] EOL\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: commit with only subject' '\n \techo subject | append_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: commit with only subject that does not end with NL' '\n \tprintf subject | append_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: no existing signoffs' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tbody\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: no existing signoffs and no trailing NL' '\n \tprintf \"subject\\n\\nbody\" | append_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: some random signoff' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: my@house\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: my@house\n-12:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tSigned-off-by: my@house\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: my@house\n+\t12:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: misc conforming footer elements' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: my@house\n-(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)\n-Tested-by: Some One <someone@example.com>\n-Bug: 1234\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: my@house\n-15:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tSigned-off-by: my@house\n+\t(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)\n+\tTested-by: Some One <someone@example.com>\n+\tBug: 1234\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: my@house\n+\t15:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: some random signoff-alike' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n-Fooled-by-me: my@house\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-11:\n-12:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tbody\n+\tFooled-by-me: my@house\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t11:\n+\t12:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: not really a signoff' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-I want to mention about Signed-off-by: here.\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:I want to mention about Signed-off-by: here.\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tI want to mention about Signed-off-by: here.\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:I want to mention about Signed-off-by: here.\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: not really a signoff (2)' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-My unfortunate\n-Signed-off-by: example happens to be wrapped here.\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:Signed-off-by: example happens to be wrapped here.\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tMy unfortunate\n+\tSigned-off-by: example happens to be wrapped here.\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:Signed-off-by: example happens to be wrapped here.\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: valid S-o-b paragraph in the middle' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-Signed-off-by: my@house\n-Signed-off-by: your@house\n+\tSigned-off-by: my@house\n+\tSigned-off-by: your@house\n \n-A lot of houses.\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: my@house\n-10:Signed-off-by: your@house\n-11:\n-13:\n-14:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tA lot of houses.\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: my@house\n+\t10:Signed-off-by: your@house\n+\t11:\n+\t13:\n+\t14:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: the same signoff at the end' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: the same signoff at the end, no trailing NL' '\n \tprintf \"subject\\n\\nSigned-off-by: C O Mitter <committer@example.com>\" |\n \t\tappend_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: the same signoff NOT at the end' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: C O Mitter <committer@example.com>\n-Signed-off-by: my@house\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-12:Signed-off-by: my@house\n-EOF\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tSigned-off-by: my@house\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\t12:Signed-off-by: my@house\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: tolerate garbage in conforming footer' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Tested-by: my@house\n-Some Trash\n-Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-13:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tTested-by: my@house\n+\tSome Trash\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t13:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: respect trailer config' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-Myfooter: x\n-Some Trash\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-11:\n-12:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tMyfooter: x\n+\tSome Trash\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t11:\n+\t12:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual &&\n \n \ttest_config trailer.Myfooter.ifexists add &&\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-Myfooter: x\n-Some Trash\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tMyfooter: x\n+\tSome Trash\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: footer begins with non-signoff without @ sign' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Reviewed-id: Noone\n-Tested-by: my@house\n-Change-id: Ideadbeef\n-Signed-off-by: C O Mitter <committer@example.com>\n-Bug: 1234\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-14:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tReviewed-id: Noone\n+\tTested-by: my@house\n+\tChange-id: Ideadbeef\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tBug: 1234\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t14:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n@@ -1458,42 +1473,42 @@ test_expect_success 'cover letter using branch description (1)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter master >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (2)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter rebuild-1~2..rebuild-1 >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (3)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter ^master rebuild-1 >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (4)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter master.. >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (5)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter -2 HEAD >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (6)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter -2 >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter with nothing' '\n@@ -1547,7 +1562,8 @@ test_expect_success 'format-patch format.outputDirectory option' '\n \ttest_config format.outputDirectory patches &&\n \trm -fr patches &&\n \tgit format-patch master..side &&\n-\ttest $(git rev-list master..side | wc -l) -eq $(ls patches | wc -l)\n+\tgit rev-list master..side >list &&\n+\ttest_line_count = $(ls patches | wc -l) list\n '\n \n test_expect_success 'format-patch -o overrides format.outputDirectory' '\n@@ -1560,13 +1576,21 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '\n \n test_expect_success 'format-patch --base' '\n \tgit checkout side &&\n-\tgit format-patch --stdout --base=HEAD~3 -1 | tail -n 7 >actual1 &&\n-\tgit format-patch --stdout --base=HEAD~3 HEAD~.. | tail -n 7 >actual2 &&\n+\tgit format-patch --stdout --base=HEAD~3 -1 >patch &&\n+\ttail -n 7 patch >actual1 &&\n+\tgit format-patch --stdout --base=HEAD~3 HEAD~.. >patch &&\n+\ttail -n 7 patch >actual2 &&\n \techo >expected &&\n \techo \"base-commit: $(git rev-parse HEAD~3)\" >>expected &&\n-\techo \"prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n-\techo \"prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n-\tsignature >> expected &&\n+\tgit show --patch HEAD~2 >patch &&\n+\tgit patch-id --stable <patch >patch.id.raw &&\n+\tawk \"{print \\$1}\" <patch.id.raw >patch.id &&\n+\techo \"prerequisite-patch-id: $(cat patch.id)\" >>expected &&\n+\tgit show --patch HEAD~1 >patch &&\n+\tgit patch-id --stable <patch >patch.id.raw &&\n+\tawk \"{print \\$1}\" <patch.id.raw >patch.id &&\n+\techo \"prerequisite-patch-id: $(cat patch.id)\" >>expected &&\n+\tsignature >>expected &&\n \ttest_cmp expected actual1 &&\n \ttest_cmp expected actual2\n '\n-- \n2.22.0.rc1.169.g49223abbf8\n\n"},{"id":"376039","messageId":"a8cc599fd2c6714c92e21d4dee9763c353e5f550.1558492582.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558492582.git.liu.denton@gmail.com","subject":"[PATCH v3 2/8] Doc: add more detail for git-format-patch","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-22T02:44:28Z","receivedAt":"2019-05-22T02:44:33Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In git-format-patch.txt, we were missing some key user information.\nFirst of all, using the `--to` and `--cc` options don't override\n`format.to` and `format.cc` variables, respectively. They add on to each\nother. Document this.\n\nIn addition, document the special value of `--base=auto`.\n\nNext, while we're at it, surround option arguments with <>.\n\nFinally, document the `format.outputDirectory` config and change\n`format.coverletter` to use camelcase.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/git-format-patch.txt | 25 ++++++++++++++++---------\n 1 file changed, 16 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 1af85d404f..4e180bbfbb 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -17,9 +17,9 @@ SYNOPSIS\n \t\t   [--signature-file=<file>]\n \t\t   [-n | --numbered | -N | --no-numbered]\n \t\t   [--start-number <n>] [--numbered-files]\n-\t\t   [--in-reply-to=Message-Id] [--suffix=.<sfx>]\n+\t\t   [--in-reply-to=<Message-Id>] [--suffix=.<sfx>]\n \t\t   [--ignore-if-in-upstream]\n-\t\t   [--rfc] [--subject-prefix=Subject-Prefix]\n+\t\t   [--rfc] [--subject-prefix=<Subject-Prefix>]\n \t\t   [(--reroll-count|-v) <n>]\n \t\t   [--to=<email>] [--cc=<email>]\n \t\t   [--[no-]cover-letter] [--quiet] [--notes[=<ref>]]\n@@ -158,7 +158,7 @@ Beware that the default for 'git send-email' is to thread emails\n itself.  If you want `git format-patch` to take care of threading, you\n will want to ensure that threading is disabled for `git send-email`.\n \n---in-reply-to=Message-Id::\n+--in-reply-to=<Message-Id>::\n \tMake the first mail (or all the mails with `--no-thread`) appear as a\n \treply to the given Message-Id, which avoids breaking threads to\n \tprovide a new patch series.\n@@ -192,13 +192,17 @@ will want to ensure that threading is disabled for `git send-email`.\n \n --to=<email>::\n \tAdd a `To:` header to the email headers. This is in addition\n-\tto any configured headers, and may be used multiple times.\n+\tto any configured headers, and may be used multiple times. The\n+\temails given will be used along with any emails given by\n+\t`format.to` configurations.\n \tThe negated form `--no-to` discards all `To:` headers added so\n \tfar (from config or command line).\n \n --cc=<email>::\n \tAdd a `Cc:` header to the email headers. This is in addition\n-\tto any configured headers, and may be used multiple times.\n+\tto any configured headers, and may be used multiple times. The\n+\temails given will be used along with any emails given by\n+\t`format.cc` configurations.\n \tThe negated form `--no-cc` discards all `Cc:` headers added so\n \tfar (from config or command line).\n \n@@ -309,7 +313,8 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.\n --base=<commit>::\n \tRecord the base tree information to identify the state the\n \tpatch series applies to.  See the BASE TREE INFORMATION section\n-\tbelow for details.\n+\tbelow for details. If <commit> is equal to \"auto\", a base commit\n+\tis automatically chosen.\n \n --root::\n \tTreat the revision argument as a <revision range>, even if it\n@@ -325,8 +330,9 @@ CONFIGURATION\n -------------\n You can specify extra mail header lines to be added to each message,\n defaults for the subject prefix and file suffix, number patches when\n-outputting more than one patch, add \"To\" or \"Cc:\" headers, configure\n-attachments, and sign off patches with configuration variables.\n+outputting more than one patch, add \"To:\" or \"Cc:\" headers, configure\n+attachments, change the patch output directory, and sign off patches\n+with configuration variables.\n \n ------------\n [format]\n@@ -338,7 +344,8 @@ attachments, and sign off patches with configuration variables.\n \tcc = <email>\n \tattach [ = mime-boundary-string ]\n \tsignOff = true\n-\tcoverletter = auto\n+\toutputDirectory = <directory>\n+\tcoverLetter = auto\n ------------\n \n \n-- \n2.22.0.rc1.169.g49223abbf8\n\n"},{"id":"376040","messageId":"e3b8c96b1cfa3e5d642cde794bf6aac7d2ff0d57.1558492582.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558492582.git.liu.denton@gmail.com","subject":"[PATCH v3 3/8] format-patch: infer cover letter from branch description","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-22T02:44:30Z","receivedAt":"2019-05-22T02:44:35Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"We used to populate the subject of the cover letter generated by\ngit-format-patch with \"*** SUBJECT HERE ***\". However, if a user submits\nmultiple patchsets, they may want to keep a consistent subject between\nrerolls.\n\nIf git-format-patch is run with `--infer-cover-letter` or\n`format.inferCoverSubject`, infer the subject for the cover letter from\nthe top line(s) of a branch description, similar to how a subject is\nread from a commit message.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/config/format.txt    |  5 +++\n Documentation/git-format-patch.txt | 10 ++++++\n builtin/log.c                      | 58 ++++++++++++++++++------------\n t/t4014-format-patch.sh            | 33 +++++++++++++++++\n 4 files changed, 84 insertions(+), 22 deletions(-)\n\ndiff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\nindex dc77941c48..c8b28fe47f 100644\n--- a/Documentation/config/format.txt\n+++ b/Documentation/config/format.txt\n@@ -36,6 +36,11 @@ format.subjectPrefix::\n \tThe default for format-patch is to output files with the '[PATCH]'\n \tsubject prefix. Use this variable to change that prefix.\n \n+format.<branch-name>.inferCoverSubject::\n+\tA boolean that controls whether or not to infer the subject for\n+\tthe cover letter based on the branch's description. See the\n+\t--infer-cover-subject option in linkgit:git-format-patch[1].\n+\n format.signature::\n \tThe default for format-patch is to output a signature containing\n \tthe Git version number. Use this variable to change that default.\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 4e180bbfbb..f54aecf4bf 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -19,6 +19,7 @@ SYNOPSIS\n \t\t   [--start-number <n>] [--numbered-files]\n \t\t   [--in-reply-to=<Message-Id>] [--suffix=.<sfx>]\n \t\t   [--ignore-if-in-upstream]\n+\t\t   [--[no-]infer-cover-subject]\n \t\t   [--rfc] [--subject-prefix=<Subject-Prefix>]\n \t\t   [(--reroll-count|-v) <n>]\n \t\t   [--to=<email>] [--cc=<email>]\n@@ -170,6 +171,14 @@ will want to ensure that threading is disabled for `git send-email`.\n \tpatches being generated, and any patch that matches is\n \tignored.\n \n+--[no-]infer-cover-subject::\n+\tInstead of using the default \"*** SUBJECT HERE ***\" subject for\n+\tthe cover letter, infer the subject from the branch's\n+\tdescription.\n++\n+Similar to a commit message, the subject is inferred as the beginning of\n+the description up to and excluding the first blank line.\n+\n --subject-prefix=<Subject-Prefix>::\n \tInstead of the standard '[PATCH]' prefix in the subject\n \tline, instead use '[<Subject-Prefix>]'. This\n@@ -346,6 +355,7 @@ with configuration variables.\n \tsignOff = true\n \toutputDirectory = <directory>\n \tcoverLetter = auto\n+\tinferCoverSubject = true\n ------------\n \n \ndiff --git a/builtin/log.c b/builtin/log.c\nindex e43ee12fb1..617f074d60 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -769,6 +769,7 @@ static const char *signature = git_version_string;\n static const char *signature_file;\n static int config_cover_letter;\n static const char *config_output_directory;\n+static int infer_cover_subject;\n \n enum {\n \tCOVER_UNSET,\n@@ -864,6 +865,10 @@ static int git_format_config(const char *var, const char *value, void *cb)\n \t\t\tfrom = NULL;\n \t\treturn 0;\n \t}\n+\tif (!strcmp(var, \"format.infercoversubject\")) {\n+\t\tinfer_cover_subject = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n \n \treturn git_log_config(var, value, cb);\n }\n@@ -970,20 +975,6 @@ static void print_signature(FILE *file)\n \tputc('\\n', file);\n }\n \n-static void add_branch_description(struct strbuf *buf, const char *branch_name)\n-{\n-\tstruct strbuf desc = STRBUF_INIT;\n-\tif (!branch_name || !*branch_name)\n-\t\treturn;\n-\tread_branch_desc(&desc, branch_name);\n-\tif (desc.len) {\n-\t\tstrbuf_addch(buf, '\\n');\n-\t\tstrbuf_addbuf(buf, &desc);\n-\t\tstrbuf_addch(buf, '\\n');\n-\t}\n-\tstrbuf_release(&desc);\n-}\n-\n static char *find_branch_name(struct rev_info *rev)\n {\n \tint i, positive = -1;\n@@ -1034,13 +1025,17 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \t\t\t      struct commit *origin,\n \t\t\t      int nr, struct commit **list,\n \t\t\t      const char *branch_name,\n+\t\t\t      int infer_subject,\n \t\t\t      int quiet)\n {\n \tconst char *committer;\n-\tconst char *body = \"*** SUBJECT HERE ***\\n\\n*** BLURB HERE ***\\n\";\n-\tconst char *msg;\n+\tconst char *subject = \"*** SUBJECT HERE ***\";\n+\tconst char *body = \"*** BLURB HERE ***\";\n+\tconst char *description = NULL;\n \tstruct shortlog log;\n \tstruct strbuf sb = STRBUF_INIT;\n+\tstruct strbuf description_sb = STRBUF_INIT;\n+\tstruct strbuf subject_sb = STRBUF_INIT;\n \tint i;\n \tconst char *encoding = \"UTF-8\";\n \tint need_8bit_cte = 0;\n@@ -1068,17 +1063,34 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tif (!branch_name)\n \t\tbranch_name = find_branch_name(rev);\n \n-\tmsg = body;\n+\tif (branch_name && *branch_name)\n+\t\tread_branch_desc(&description_sb, branch_name);\n+\n+\tif (description_sb.len) {\n+\t\tif (infer_subject) {\n+\t\t\tdescription = format_subject(&subject_sb, description_sb.buf, \" \");\n+\t\t\tsubject = subject_sb.buf;\n+\t\t} else {\n+\t\t\tdescription = description_sb.buf;\n+\t\t}\n+\t}\n+\n \tpp.fmt = CMIT_FMT_EMAIL;\n \tpp.date_mode.type = DATE_RFC2822;\n \tpp.rev = rev;\n \tpp.print_email_subject = 1;\n \tpp_user_info(&pp, NULL, &sb, committer, encoding);\n-\tpp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);\n-\tpp_remainder(&pp, &msg, &sb, 0);\n-\tadd_branch_description(&sb, branch_name);\n+\tpp_title_line(&pp, &subject, &sb, encoding, need_8bit_cte);\n+\tpp_remainder(&pp, &body, &sb, 0);\n+\tif (description) {\n+\t\tstrbuf_addch(&sb, '\\n');\n+\t\tstrbuf_addstr(&sb, description);\n+\t\tstrbuf_addch(&sb, '\\n');\n+\t}\n \tfprintf(rev->diffopt.file, \"%s\\n\", sb.buf);\n \n+\tstrbuf_release(&description_sb);\n+\tstrbuf_release(&subject_sb);\n \tstrbuf_release(&sb);\n \n \tshortlog_init(&log);\n@@ -1554,6 +1566,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t{ OPTION_CALLBACK, 0, \"rfc\", &rev, NULL,\n \t\t\t    N_(\"Use [RFC PATCH] instead of [PATCH]\"),\n \t\t\t    PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback },\n+\t\tOPT_BOOL(0, \"infer-cover-subject\", &infer_cover_subject,\n+\t\t\t    N_(\"infer a cover letter subject from the branch description\")),\n \t\t{ OPTION_CALLBACK, 0, \"subject-prefix\", &rev, N_(\"prefix\"),\n \t\t\t    N_(\"Use [<prefix>] instead of [PATCH]\"),\n \t\t\t    PARSE_OPT_NONEG, subject_prefix_callback },\n@@ -1617,8 +1631,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \textra_to.strdup_strings = 1;\n \textra_cc.strdup_strings = 1;\n \tinit_log_defaults();\n-\tgit_config(git_format_config, NULL);\n \trepo_init_revisions(the_repository, &rev, prefix);\n+\tgit_config(git_format_config, NULL);\n \trev.commit_format = CMIT_FMT_EMAIL;\n \trev.expand_tabs_in_log_default = 0;\n \trev.verbose_header = 1;\n@@ -1893,7 +1907,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tif (thread)\n \t\t\tgen_message_id(&rev, \"cover\");\n \t\tmake_cover_letter(&rev, use_stdout,\n-\t\t\t\t  origin, nr, list, branch_name, quiet);\n+\t\t\t\t  origin, nr, list, branch_name, infer_cover_subject, quiet);\n \t\tprint_bases(&bases, rev.diffopt.file);\n \t\tprint_signature(rev.diffopt.file);\n \t\ttotal++;\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 3423f974bc..4cb6b9edfa 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -1469,6 +1469,39 @@ test_expect_success 'format patch ignores color.ui' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'cover letter with config subject' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\ttest_config format.inferCoverSubject true &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] config subject$\" actual &&\n+\tgrep \"^body\" actual\n+'\n+\n+test_expect_success 'cover letter with command-line subject' '\n+\ttest_config branch.rebuild-1.description \"command-line subject\n+\n+body\" &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter --infer-cover-subject master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] command-line subject$\" actual &&\n+\tgrep \"^body\" actual\n+'\n+\n+test_expect_success 'cover letter with command-line --no-infer-cover-subject overrides config' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\ttest_config format.inferCoverSubject true &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter --no-infer-cover-subject master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] \\*\\*\\* SUBJECT HERE \\*\\*\\*$\" actual &&\n+\tgrep \"^config subject\" actual &&\n+\tgrep \"^body\" actual\n+'\n+\n test_expect_success 'cover letter using branch description (1)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n-- \n2.22.0.rc1.169.g49223abbf8\n\n"},{"id":"376041","messageId":"52ee126825b7bc4cb788c307e1055753d9da7d51.1558492582.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558492582.git.liu.denton@gmail.com","subject":"[PATCH v3 4/8] format-patch: move extra_headers logic later","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-22T02:44:32Z","receivedAt":"2019-05-22T02:44:37Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future patch, we need to perform the addition of To: and Cc:\nto extra_headers after the branch_name logic. Simply transpose this\nlogic later in the function so that this happens. (This patch is best\nviewed with `git diff --color-moved`.)\n\nNote that this logic only depends on `git_config` and\n`repo_init_revisions` and is depended on by the patch creation logic\nwhich is directly below it so this move is effectively a no-op as no\ndependencies being reordered.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/log.c | 58 +++++++++++++++++++++++++--------------------------\n 1 file changed, 29 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 617f074d60..3f97f344df 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1667,35 +1667,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\trev.subject_prefix = strbuf_detach(&sprefix, NULL);\n \t}\n \n-\tfor (i = 0; i < extra_hdr.nr; i++) {\n-\t\tstrbuf_addstr(&buf, extra_hdr.items[i].string);\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t}\n-\n-\tif (extra_to.nr)\n-\t\tstrbuf_addstr(&buf, \"To: \");\n-\tfor (i = 0; i < extra_to.nr; i++) {\n-\t\tif (i)\n-\t\t\tstrbuf_addstr(&buf, \"    \");\n-\t\tstrbuf_addstr(&buf, extra_to.items[i].string);\n-\t\tif (i + 1 < extra_to.nr)\n-\t\t\tstrbuf_addch(&buf, ',');\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t}\n-\n-\tif (extra_cc.nr)\n-\t\tstrbuf_addstr(&buf, \"Cc: \");\n-\tfor (i = 0; i < extra_cc.nr; i++) {\n-\t\tif (i)\n-\t\t\tstrbuf_addstr(&buf, \"    \");\n-\t\tstrbuf_addstr(&buf, extra_cc.items[i].string);\n-\t\tif (i + 1 < extra_cc.nr)\n-\t\t\tstrbuf_addch(&buf, ',');\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t}\n-\n-\trev.extra_headers = strbuf_detach(&buf, NULL);\n-\n \tif (from) {\n \t\tif (split_ident_line(&rev.from_ident, from, strlen(from)))\n \t\t\tdie(_(\"invalid ident line: %s\"), from);\n@@ -1798,6 +1769,35 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \t}\n \n+\tfor (i = 0; i < extra_hdr.nr; i++) {\n+\t\tstrbuf_addstr(&buf, extra_hdr.items[i].string);\n+\t\tstrbuf_addch(&buf, '\\n');\n+\t}\n+\n+\tif (extra_to.nr)\n+\t\tstrbuf_addstr(&buf, \"To: \");\n+\tfor (i = 0; i < extra_to.nr; i++) {\n+\t\tif (i)\n+\t\t\tstrbuf_addstr(&buf, \"    \");\n+\t\tstrbuf_addstr(&buf, extra_to.items[i].string);\n+\t\tif (i + 1 < extra_to.nr)\n+\t\t\tstrbuf_addch(&buf, ',');\n+\t\tstrbuf_addch(&buf, '\\n');\n+\t}\n+\n+\tif (extra_cc.nr)\n+\t\tstrbuf_addstr(&buf, \"Cc: \");\n+\tfor (i = 0; i < extra_cc.nr; i++) {\n+\t\tif (i)\n+\t\t\tstrbuf_addstr(&buf, \"    \");\n+\t\tstrbuf_addstr(&buf, extra_cc.items[i].string);\n+\t\tif (i + 1 < extra_cc.nr)\n+\t\t\tstrbuf_addch(&buf, ',');\n+\t\tstrbuf_addch(&buf, '\\n');\n+\t}\n+\n+\trev.extra_headers = strbuf_detach(&buf, NULL);\n+\n \t/*\n \t * We cannot move this anywhere earlier because we do want to\n \t * know if --root was given explicitly from the command line.\n-- \n2.22.0.rc1.169.g49223abbf8\n\n"},{"id":"376042","messageId":"27c60c715c6e01197153583c6a55c865e0115be1.1558492582.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558492582.git.liu.denton@gmail.com","subject":"[PATCH v3 5/8] string-list: create string_list_append_all","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-22T02:44:35Z","receivedAt":"2019-05-22T02:44:40Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future patch, we'll need to take one string_list and append it to\nthe end of another. Create the `string_list_append_all` function which\ndoes this.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n string-list.c | 9 +++++++++\n string-list.h | 7 +++++++\n 2 files changed, 16 insertions(+)\n\ndiff --git a/string-list.c b/string-list.c\nindex a917955fbd..e63d58fbd2 100644\n--- a/string-list.c\n+++ b/string-list.c\n@@ -215,6 +215,15 @@ struct string_list_item *string_list_append(struct string_list *list,\n \t\t\tlist->strdup_strings ? xstrdup(string) : (char *)string);\n }\n \n+void string_list_append_all(struct string_list *list,\n+\t\t\t    const struct string_list *append_list)\n+{\n+\tstruct string_list_item *item;\n+\tALLOC_GROW(list->items, list->nr + append_list->nr, list->alloc);\n+\tfor_each_string_list_item(item, append_list)\n+\t\tstring_list_append(list, item->string);\n+}\n+\n /*\n  * Encapsulate the compare function pointer because ISO C99 forbids\n  * casting from void * to a function pointer and vice versa.\ndiff --git a/string-list.h b/string-list.h\nindex f964399949..8227e00e6a 100644\n--- a/string-list.h\n+++ b/string-list.h\n@@ -208,6 +208,13 @@ struct string_list_item *string_list_append(struct string_list *list, const char\n  */\n struct string_list_item *string_list_append_nodup(struct string_list *list, char *string);\n \n+/**\n+ * Add all strings in append_list to list.  If list->strdup_string is\n+ * set, then each string is copied; otherwise the new string_list_entry\n+ * refers to the entry in the append_list.\n+ */\n+void string_list_append_all(struct string_list *list, const struct string_list *append_list);\n+\n /**\n  * Sort the list's entries by string value in `strcmp()` order.\n  */\n-- \n2.22.0.rc1.169.g49223abbf8\n\n"},{"id":"376043","messageId":"f2a1546b2d43ee66d2bfa1834f9a81fb9f453281.1558492582.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558492582.git.liu.denton@gmail.com","subject":"[PATCH v3 6/8] format-patch: read branch-specific To: and Cc: headers","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-22T02:44:37Z","receivedAt":"2019-05-22T02:44:42Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"If a user wishes to keep track of whom to Cc: on individual patchsets,\nthey must manually keep track of each recipient and fill it in with the\n`--cc` option on git-format-patch each time. However, on the Git mailing\nlist, Cc:'s are typically never dropped. As a result, it would be nice\nto have a method to keep track of recipients on a per-branch basis.\n\nCurrently, git-format-patch gets its To: headers from the `--to` options\nand the `format.to` config variable. The Cc: header is derived\nsimilarly.\n\nIn addition to the above, read To: and Cc: headers from\n`format.<branch-name>.to` and `format.<branch-name>.cc` so that users\ncan have branch-specific configuration options.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/config/format.txt    |   7 +-\n Documentation/git-format-patch.txt |  12 +++-\n builtin/log.c                      |  63 +++++++++++++++--\n t/t4014-format-patch.sh            | 108 +++++++++++++++++++++++------\n 4 files changed, 162 insertions(+), 28 deletions(-)\n\ndiff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\nindex c8b28fe47f..95e255347a 100644\n--- a/Documentation/config/format.txt\n+++ b/Documentation/config/format.txt\n@@ -28,9 +28,12 @@ format.headers::\n \n format.to::\n format.cc::\n+format.<branch-name>.to::\n+format.<branch-name>.cc::\n \tAdditional recipients to include in a patch to be submitted\n-\tby mail.  See the --to and --cc options in\n-\tlinkgit:git-format-patch[1].\n+\tby mail.  For the <branch-name> options, the recipients are only\n+\tincluded if patches are generated for the given <branch-name>.\n+\tSee the --to and --cc options in linkgit:git-format-patch[1].\n \n format.subjectPrefix::\n \tThe default for format-patch is to output files with the '[PATCH]'\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex f54aecf4bf..76e61b746a 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -203,7 +203,7 @@ the description up to and excluding the first blank line.\n \tAdd a `To:` header to the email headers. This is in addition\n \tto any configured headers, and may be used multiple times. The\n \temails given will be used along with any emails given by\n-\t`format.to` configurations.\n+\t`format.to` and `format.<branch-name>.to` configurations.\n \tThe negated form `--no-to` discards all `To:` headers added so\n \tfar (from config or command line).\n \n@@ -211,7 +211,7 @@ the description up to and excluding the first blank line.\n \tAdd a `Cc:` header to the email headers. This is in addition\n \tto any configured headers, and may be used multiple times. The\n \temails given will be used along with any emails given by\n-\t`format.cc` configurations.\n+\t`format.cc` and `format.<branch-name>.cc` configurations.\n \tThe negated form `--no-cc` discards all `Cc:` headers added so\n \tfar (from config or command line).\n \n@@ -358,6 +358,14 @@ with configuration variables.\n \tinferCoverSubject = true\n ------------\n \n+In addition, for a specific branch, you can specify a custom cover\n+letter subject, and add additional \"To:\" or \"Cc:\" headers.\n+\n+------------\n+[format \"branch-name\"]\n+\tto = <email>\n+\tcc = <email>\n+------------\n \n DISCUSSION\n ----------\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 3f97f344df..cb9ccdc59c 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -738,6 +738,8 @@ static char *default_attach = NULL;\n static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;\n static struct string_list extra_to = STRING_LIST_INIT_NODUP;\n static struct string_list extra_cc = STRING_LIST_INIT_NODUP;\n+int to_cleared;\n+int cc_cleared;\n \n static void add_header(const char *value)\n {\n@@ -1021,6 +1023,55 @@ static void show_diffstat(struct rev_info *rev,\n \tfprintf(rev->diffopt.file, \"\\n\");\n }\n \n+static void add_branch_headers(struct rev_info *rev, const char *branch_name)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst struct string_list *values;\n+\n+\tif (!branch_name)\n+\t\tbranch_name = find_branch_name(rev);\n+\n+\tif (!branch_name || !*branch_name)\n+\t\treturn;\n+\n+\t/*\n+\t * HACK: We only use branch-specific recipients iff the list has not\n+\t * been cleared by an earlier --no-{to,cc} option on the command-line.\n+\t *\n+\t * When we get format.{to,cc} options, they can be cleared by\n+\t * --no-{to,cc} options since the `git_config` call comes before the\n+\t *  `parse_options` call.\n+\t *\n+\t *  However, in the case of branch.<name>.{to,cc}, this function needs\n+\t *  to be called after `setup_revisions`, which must be called after\n+\t *  `parse_options`. However, in order for the --no-{to,cc} logic to\n+\t *  clear the extra_{to,cc} string_list, this function should actually\n+\t *  be called _before_ `parse_options`. As a result, we have a circular\n+\t *  dependency.\n+\t *\n+\t *  The {to,cc}_cleared flag lets us workaround this by just no\n+\t *  including branch-specific recipients iff --no-{to,cc} has been\n+\t *  specified on the command-line.\n+\t */\n+\n+\tif (!to_cleared) {\n+\t\tstrbuf_addf(&buf, \"format.%s.to\", branch_name);\n+\t\tvalues = git_config_get_value_multi(buf.buf);\n+\t\tif (values)\n+\t\t\tstring_list_append_all(&extra_to, values);\n+\t}\n+\n+\tif (!cc_cleared) {\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addf(&buf, \"format.%s.cc\", branch_name);\n+\t\tvalues = git_config_get_value_multi(buf.buf);\n+\t\tif (values)\n+\t\t\tstring_list_append_all(&extra_cc, values);\n+\t}\n+\n+\tstrbuf_release(&buf);\n+}\n+\n static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \t\t\t      struct commit *origin,\n \t\t\t      int nr, struct commit **list,\n@@ -1293,18 +1344,20 @@ static int header_callback(const struct option *opt, const char *arg, int unset)\n \n static int to_callback(const struct option *opt, const char *arg, int unset)\n {\n-\tif (unset)\n+\tif (unset) {\n+\t\tto_cleared = 1;\n \t\tstring_list_clear(&extra_to, 0);\n-\telse\n+\t} else\n \t\tstring_list_append(&extra_to, arg);\n \treturn 0;\n }\n \n static int cc_callback(const struct option *opt, const char *arg, int unset)\n {\n-\tif (unset)\n+\tif (unset) {\n+\t\tcc_cleared = 1;\n \t\tstring_list_clear(&extra_cc, 0);\n-\telse\n+\t} else\n \t\tstring_list_append(&extra_cc, arg);\n \treturn 0;\n }\n@@ -1769,6 +1822,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \t}\n \n+\tadd_branch_headers(&rev, branch_name);\n+\n \tfor (i = 0; i < extra_hdr.nr; i++) {\n \t\tstrbuf_addstr(&buf, extra_hdr.items[i].string);\n \t\tstrbuf_addch(&buf, '\\n');\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 4cb6b9edfa..23c467e95b 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -234,6 +234,65 @@ test_expect_failure 'configuration To: header (rfc2047)' '\n \tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs9\n '\n \n+test_expect_success 'branch-specific configuration To: header (ascii)' '\n+\n+\ttest_unconfig format.to &&\n+\tgit config format.side.to \"R E Cipient <rcipient@example.com>\" &&\n+\tgit format-patch --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs10\n+'\n+\n+test_expect_failure 'branch-specific configuration To: header (rfc822)' '\n+\n+\tgit config format.side.to \"R. E. Cipient <rcipient@example.com>\" &&\n+\tgit format-patch --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" hdrs10\n+'\n+\n+test_expect_failure 'branch-specific configuration To: header (rfc2047)' '\n+\n+\tgit config format.side.to \"R Ä Cipient <rcipient@example.com>\" &&\n+\tgit format-patch --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs10\n+'\n+\n+test_expect_success 'all recipients included from all sources' '\n+\n+\tgit config format.to \"Format To1 <formatto1@example.com>\" &&\n+\tgit config --add format.to \"Format To2 <formatto2@example.com>\" &&\n+\tgit config format.cc \"Format Cc1 <formatcc1@example.com>\" &&\n+\tgit config --add format.cc \"Format Cc2 <formatcc2@example.com>\" &&\n+\tgit config format.side.to \"Branch To1 <branchto1@example.com>\" &&\n+\tgit config --add format.side.to \"Branch To2 <branchto2@example.com>\" &&\n+\tgit config format.side.cc \"Branch Cc1 <branchcc1@example.com>\" &&\n+\tgit config --add format.side.cc \"Branch Cc2 <branchcc2@example.com>\" &&\n+\tcat <<-\\EOF >expect &&\n+\tTo: Format To1 <formatto1@example.com>,\n+\t    Format To2 <formatto2@example.com>,\n+\t    Command-line To1 <commandlineto1@example.com>,\n+\t    Command-line To2 <commandlineto2@example.com>,\n+\t    Branch To1 <branchto1@example.com>,\n+\t    Branch To2 <branchto2@example.com>\n+\tCc: Format Cc1 <formatcc1@example.com>,\n+\t    Format Cc2 <formatcc2@example.com>,\n+\t    Command-line Cc1 <commandlinecc1@example.com>,\n+\t    Command-line Cc2 <commandlinecc2@example.com>,\n+\t    Branch Cc1 <branchcc1@example.com>,\n+\t    Branch Cc2 <branchcc2@example.com>\n+\n+\tEOF\n+\tgit format-patch --stdout \\\n+\t\t--to=\"Command-line To1 <commandlineto1@example.com>\" \\\n+\t\t--to=\"Command-line To2 <commandlineto2@example.com>\" \\\n+\t\t--cc=\"Command-line Cc1 <commandlinecc1@example.com>\" \\\n+\t\t--cc=\"Command-line Cc2 <commandlinecc2@example.com>\" \\\n+\t\tmaster..side | sed -ne \"/^To:/,/^$/p;/^$/q\" >patch10 &&\n+\ttest_cmp expect patch10\n+'\n+\n # check_patch <patch>: Verify that <patch> looks like a half-sane\n # patch email to avoid a false positive with !grep\n check_patch () {\n@@ -286,42 +345,51 @@ test_expect_success '--no-to overrides config.to' '\n \n \tgit config --replace-all format.to \\\n \t\t\"R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-to --stdout master..side >patch10 &&\n-\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n-\tcheck_patch hdrs10 &&\n-\t! grep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs10\n+\tgit config --replace-all format.side.to \\\n+\t\t\"B R Anch <branch@example.com>\" &&\n+\tgit format-patch --no-to --stdout master..side >patch11 &&\n+\tsed -e \"/^\\$/q\" patch11 >hdrs11 &&\n+\tcheck_patch hdrs11 &&\n+\t! grep \"R E Cipient <rcipient@example.com>\" hdrs11 &&\n+\t! grep \"B R Anch <branch@example.com>\" hdrs11\n '\n \n test_expect_success '--no-to and --to replaces config.to' '\n \n \tgit config --replace-all format.to \\\n \t\t\"Someone <someone@out.there>\" &&\n+\tgit config --replace-all format.side.to \\\n+\t\t\"B R Anch2 <branch2@example.com>\" &&\n \tgit format-patch --no-to --to=\"Someone Else <else@out.there>\" \\\n-\t\t--stdout master..side >patch11 &&\n-\tsed -e \"/^\\$/q\" patch11 >hdrs11 &&\n-\tcheck_patch hdrs11 &&\n-\t! grep \"^To: Someone <someone@out.there>\\$\" hdrs11 &&\n-\tgrep \"^To: Someone Else <else@out.there>\\$\" hdrs11\n+\t\t--stdout master..side >patch12 &&\n+\tsed -e \"/^\\$/q\" patch12 >hdrs12 &&\n+\tcheck_patch hdrs12 &&\n+\t! grep \"Someone <someone@out.there>\" hdrs12 &&\n+\t! grep \"B R Anch2 <branch2@example.com>\" hdrs12 &&\n+\tgrep \"^To: Someone Else <else@out.there>\\$\" hdrs12\n '\n \n test_expect_success '--no-cc overrides config.cc' '\n \n \tgit config --replace-all format.cc \\\n \t\t\"C E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-cc --stdout master..side >patch12 &&\n-\tsed -e \"/^\\$/q\" patch12 >hdrs12 &&\n-\tcheck_patch hdrs12 &&\n-\t! grep \"^Cc: C E Cipient <rcipient@example.com>\\$\" hdrs12\n+\tgit config --replace-all format.side.cc \\\n+\t\t\"B R Anch3 <branch3@example.com>\" &&\n+\tgit format-patch --no-cc --stdout master..side >patch13 &&\n+\tsed -e \"/^\\$/q\" patch13 >hdrs13 &&\n+\tcheck_patch hdrs13 &&\n+\t! grep \"C E Cipient <rcipient@example.com>\" hdrs13 &&\n+\t! grep \"B R Anch3 <branch3@example.com>\" hdrs13\n '\n \n test_expect_success '--no-add-header overrides config.headers' '\n \n \tgit config --replace-all format.headers \\\n \t\t\"Header1: B E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-add-header --stdout master..side >patch13 &&\n-\tsed -e \"/^\\$/q\" patch13 >hdrs13 &&\n-\tcheck_patch hdrs13 &&\n-\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" hdrs13\n+\tgit format-patch --no-add-header --stdout master..side >patch14 &&\n+\tsed -e \"/^\\$/q\" patch14 >hdrs14 &&\n+\tcheck_patch hdrs14 &&\n+\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" hdrs14\n '\n \n test_expect_success 'multiple files' '\n@@ -963,7 +1031,7 @@ test_expect_success 'format-patch wraps extremely long subject (ascii)' '\n \tgit add file &&\n \tgit commit -m \"$M512\" &&\n \tgit format-patch --stdout -1 >patch &&\n-\tsed -n \"/^Subject/p; /^ /p; /^$/q\" <patch >subject &&\n+\tsed -n \"/^Subject/p; /^ /p; /^Header1:/q\" <patch >subject &&\n \ttest_cmp expect subject\n '\n \n@@ -1002,7 +1070,7 @@ test_expect_success 'format-patch wraps extremely long subject (rfc2047)' '\n \tgit add file &&\n \tgit commit -m \"$M512\" &&\n \tgit format-patch --stdout -1 >patch &&\n-\tsed -n \"/^Subject/p; /^ /p; /^$/q\" <patch >subject &&\n+\tsed -n \"/^Subject/p; /^ /p; /^Header1:/q\" <patch >subject &&\n \ttest_cmp expect subject\n '\n \n@@ -1011,7 +1079,7 @@ check_author() {\n \tgit add file &&\n \tGIT_AUTHOR_NAME=$1 git commit -m author-check &&\n \tgit format-patch --stdout -1 >patch &&\n-\tsed -n \"/^From: /p; /^ /p; /^$/q\" <patch >actual &&\n+\tsed -n \"/^From: /p; /^ /p; /^Date:/q\" <patch >actual &&\n \ttest_cmp expect actual\n }\n \n-- \n2.22.0.rc1.169.g49223abbf8\n\n"},{"id":"376044","messageId":"70e28c54724984b88ccddf639282fd25f4e2fa06.1558492582.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558492582.git.liu.denton@gmail.com","subject":"[PATCH v3 7/8] format-patch: move output_directory logic later","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-22T02:44:39Z","receivedAt":"2019-05-22T02:44:43Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future patch, we need to create the output_directory after the\nbranch_name logic. Simply transpose this logic later in the function so\nthat this happens. (This patch is best viewed with `git diff\n--color-moved`.)\n\nNote that this logic only depends on `git_config` and the parseopt logic\nand is depended on by the patch creation logic which is directly below\nit so this move is effectively a no-op as no dependencies being\nreordered.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/git-format-patch.txt |  4 ++--\n builtin/log.c                      | 36 +++++++++++++++---------------\n 2 files changed, 20 insertions(+), 20 deletions(-)\n\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 76e61b746a..707b4bdc6b 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -358,8 +358,8 @@ with configuration variables.\n \tinferCoverSubject = true\n ------------\n \n-In addition, for a specific branch, you can specify a custom cover\n-letter subject, and add additional \"To:\" or \"Cc:\" headers.\n+In addition, for a specific branch, you can add additional \"To:\" or\n+\"Cc:\" headers.\n \n ------------\n [format \"branch-name\"]\ndiff --git a/builtin/log.c b/builtin/log.c\nindex cb9ccdc59c..97980881ec 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1771,24 +1771,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tif (rev.show_notes)\n \t\tinit_display_notes(&rev.notes_opt);\n \n-\tif (!output_directory && !use_stdout)\n-\t\toutput_directory = config_output_directory;\n-\n-\tif (!use_stdout)\n-\t\toutput_directory = set_outdir(prefix, output_directory);\n-\telse\n-\t\tsetup_pager();\n-\n-\tif (output_directory) {\n-\t\tif (rev.diffopt.use_color != GIT_COLOR_ALWAYS)\n-\t\t\trev.diffopt.use_color = GIT_COLOR_NEVER;\n-\t\tif (use_stdout)\n-\t\t\tdie(_(\"standard output, or directory, which one?\"));\n-\t\tif (mkdir(output_directory, 0777) < 0 && errno != EEXIST)\n-\t\t\tdie_errno(_(\"could not create directory '%s'\"),\n-\t\t\t\t  output_directory);\n-\t}\n-\n \tif (rev.pending.nr == 1) {\n \t\tint check_head = 0;\n \n@@ -1822,6 +1804,24 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \t}\n \n+\tif (!output_directory && !use_stdout)\n+\t\toutput_directory = config_output_directory;\n+\n+\tif (!use_stdout)\n+\t\toutput_directory = set_outdir(prefix, output_directory);\n+\telse\n+\t\tsetup_pager();\n+\n+\tif (output_directory) {\n+\t\tif (rev.diffopt.use_color != GIT_COLOR_ALWAYS)\n+\t\t\trev.diffopt.use_color = GIT_COLOR_NEVER;\n+\t\tif (use_stdout)\n+\t\t\tdie(_(\"standard output, or directory, which one?\"));\n+\t\tif (mkdir(output_directory, 0777) < 0 && errno != EEXIST)\n+\t\t\tdie_errno(_(\"could not create directory '%s'\"),\n+\t\t\t\t  output_directory);\n+\t}\n+\n \tadd_branch_headers(&rev, branch_name);\n \n \tfor (i = 0; i < extra_hdr.nr; i++) {\n-- \n2.22.0.rc1.169.g49223abbf8\n\n"},{"id":"376045","messageId":"b23c3c16f752242a25245cdbfb97b26598d47dc0.1558492582.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558492582.git.liu.denton@gmail.com","subject":"[PATCH v3 8/8] format-patch: read branch-specific output directory","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-22T02:44:41Z","receivedAt":"2019-05-22T02:44:45Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"If a user wishes to have a per-branch output directory for patches, they\nmust manually specify this on the command-line with `-o` for each\ninvocation of format-patch. However, this can be cumbersome for a user\nto keep track of.\n\nRead `format.<branch-name>.outputDirectory` to give a branch-specific\noutput directory that would override `format.outputDirectory`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/config/format.txt    |  5 ++++-\n Documentation/git-format-patch.txt |  3 ++-\n builtin/log.c                      | 26 ++++++++++++++++++++++++--\n t/t4014-format-patch.sh            | 18 +++++++++++++++---\n 4 files changed, 45 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\nindex 95e255347a..df67a83183 100644\n--- a/Documentation/config/format.txt\n+++ b/Documentation/config/format.txt\n@@ -87,8 +87,11 @@ format.coverLetter::\n \tgenerate a cover-letter only when there's more than one patch.\n \n format.outputDirectory::\n+format.<branch-name>.outputDirectory::\n \tSet a custom directory to store the resulting files instead of the\n-\tcurrent working directory.\n+\tcurrent working directory. If patches are being generated for\n+\t<branch-name>, the latter option takes priority if it exists,\n+\totherwise we will fallback to the former.\n \n format.useAutoBase::\n \tA boolean value which lets you enable the `--base=auto` option of\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 707b4bdc6b..346f1229d8 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -359,12 +359,13 @@ with configuration variables.\n ------------\n \n In addition, for a specific branch, you can add additional \"To:\" or\n-\"Cc:\" headers.\n+\"Cc:\" headers and change the patch output directory.\n \n ------------\n [format \"branch-name\"]\n \tto = <email>\n \tcc = <email>\n+\toutputDirectory = <directory>\n ------------\n \n DISCUSSION\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 97980881ec..a1fe8b994a 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1023,6 +1023,24 @@ static void show_diffstat(struct rev_info *rev,\n \tfprintf(rev->diffopt.file, \"\\n\");\n }\n \n+static const char *get_branch_output_dir(struct rev_info *rev, const char *branch_name)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *output_directory;\n+\n+\tif (!branch_name)\n+\t\tbranch_name = find_branch_name(rev);\n+\n+\tif (!branch_name || !*branch_name)\n+\t\treturn NULL;\n+\n+\tstrbuf_addf(&buf, \"format.%s.outputdirectory\", branch_name);\n+\tgit_config_get_string_const(buf.buf, &output_directory);\n+\tstrbuf_release(&buf);\n+\n+\treturn output_directory;\n+}\n+\n static void add_branch_headers(struct rev_info *rev, const char *branch_name)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -1804,8 +1822,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \t}\n \n-\tif (!output_directory && !use_stdout)\n-\t\toutput_directory = config_output_directory;\n+\tif (!use_stdout) {\n+\t\tif (!output_directory)\n+\t\t\toutput_directory = get_branch_output_dir(&rev, branch_name);\n+\t\tif (!output_directory)\n+\t\t\toutput_directory = config_output_directory;\n+\t}\n \n \tif (!use_stdout)\n \t\toutput_directory = set_outdir(prefix, output_directory);\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 23c467e95b..147934922c 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -1667,12 +1667,24 @@ test_expect_success 'format-patch format.outputDirectory option' '\n \ttest_line_count = $(ls patches | wc -l) list\n '\n \n-test_expect_success 'format-patch -o overrides format.outputDirectory' '\n+test_expect_success 'format-patch format.side.outputDirectory option overrides format.outputDirectory' '\n \ttest_config format.outputDirectory patches &&\n-\trm -fr patches patchset &&\n+\ttest_config format.side.outputDirectory sidepatches &&\n+\trm -fr patches sidepatches &&\n+\tgit format-patch master..side &&\n+\tgit rev-list master..side >list &&\n+\ttest_path_is_missing patches &&\n+\ttest_line_count = $(ls sidepatches | wc -l) list\n+'\n+\n+test_expect_success 'format-patch -o overrides format.outputDirectory and format.side.outputDirectory' '\n+\ttest_config format.outputDirectory patches &&\n+\ttest_config format.side.outputDirectory sidepatches &&\n+\trm -fr patches sidepatches patchset &&\n \tgit format-patch master..side -o patchset &&\n \ttest_path_is_missing patches &&\n-\ttest_path_is_dir patchset\n+\ttest_path_is_missing sidepatches &&\n+\ttest_line_count = $(ls patchset | wc -l) list\n '\n \n test_expect_success 'format-patch --base' '\n-- \n2.22.0.rc1.169.g49223abbf8\n\n"},{"id":"376463","messageId":"20190531003015.GA9976@archbookpro.localdomain","threadId":"51038","inReplyTo":"cover.1558492582.git.liu.denton@gmail.com","subject":"Re: [PATCH v3 0/8] teach branch-specific options for format-patch","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-31T00:30:15Z","receivedAt":"2019-05-31T00:30:20Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"On Tue, May 21, 2019 at 10:44:23PM -0400, Denton Liu wrote:\n> Hi Junio,\n> \n> I've gotten rid of all the `format.*.coverSubject` stuff and replaced it\n> with a generic `format.inferCoverSubject` that will read the subject\n> from the branch description. I've also made `git branch -d` stop\n> deleting the `format.<branch>.*` variables.\n> \n> A new addition in this patchset is that format-patch is now taught to\n> understand the `format.<branch-name>.outputDirectory` configuration.\n\n[...]\n\nQuick ping for this topic.\n\nThanks,\n\nDenton\n"},{"id":"376464","messageId":"7b60e58ba554768fd915e4f5c00a97737707ed42.1559263024.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"xmqqh8a5k9bu.fsf@gitster-ct.c.googlers.com","subject":"[RFC PATCH] config: learn the \"onbranch:\" includeIf condition","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-31T02:00:59Z","receivedAt":"2019-05-31T02:01:09Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Currently, if a user wishes to have individual settings per branch, they\nare required to manually keep track of the settings in their head and\nmanually set the options on the command-line or change the config at\neach branch.\n\nTeach config the \"onbranch:\" includeIf condition so that it can\nconditionally include configuration files if the branch is checked out\nin the current worktree.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n\nI decided to go ahead and implement the includeIf onbranch semantics for\nfun. For completeness, I'm sending it to the list but I'm not really\nsure if this should get merged, since I don't really have a use-case for\nthis, especially if we go the branch-specific format-patch config route.\n\nAnother thing to note is that this change doesn't completely cover all\nthe use-cases that the branch-specific format-patch does. In particular,\nif I run\n\n\t$ git checkout foo\n\t$ git format-patch master..bar\n\nwith the `format.bar.*`, we'd get bar-specific configs, whereas with\n`includeIf \"onbranch:bar\"`, we'd fail to include bar-specific configs\nand, more dangerously, we'd be including foo's configs.\n\nBut let's see how it goes.\n\n Documentation/config.txt  | 10 ++++++++++\n config.c                  | 16 ++++++++++++++++\n t/t1305-config-include.sh | 13 +++++++++++++\n 3 files changed, 39 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 7e2a6f61f5..3b9fbe1860 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -144,6 +144,11 @@ refer to linkgit:gitignore[5] for details. For convenience:\n \tThis is the same as `gitdir` except that matching is done\n \tcase-insensitively (e.g. on case-insensitive file sytems)\n \n+`onbranch`::\n+\tThe data that follows the keyword `onbranch:` is taken to be a\n+\tname of a local branch. If we are in a worktree where that\n+\tbranch is currently checked out, the include condition is met.\n+\n A few more notes on matching via `gitdir` and `gitdir/i`:\n \n  * Symlinks in `$GIT_DIR` are not resolved before matching.\n@@ -206,6 +211,11 @@ Example\n \t[includeIf \"gitdir:/path/to/group/\"]\n \t\tpath = foo.inc\n \n+\t; include only if we are in a worktree where foo-branch is\n+\t; currently checked out\n+\t[includeIf \"onbranch:foo-branch\"]\n+\t\tpath = foo.inc\n+\n Values\n ~~~~~~\n \ndiff --git a/config.c b/config.c\nindex 296a6d9cc4..954e84e13a 100644\n--- a/config.c\n+++ b/config.c\n@@ -19,6 +19,7 @@\n #include \"utf8.h\"\n #include \"dir.h\"\n #include \"color.h\"\n+#include \"refs.h\"\n \n struct config_source {\n \tstruct config_source *prev;\n@@ -264,6 +265,19 @@ static int include_by_gitdir(const struct config_options *opts,\n \treturn ret;\n }\n \n+static int include_by_branch(const char *cond, size_t cond_len)\n+{\n+\tint flags;\n+\tconst char *refname = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flags);\n+\tconst char *shortname;\n+\n+\tif (!refname || !(flags & REF_ISSYMREF)\t||\n+\t\t\t!skip_prefix(refname, \"refs/heads/\", &shortname))\n+\t\treturn 0;\n+\n+\treturn !strncmp(cond, shortname, cond_len);\n+}\n+\n static int include_condition_is_true(const struct config_options *opts,\n \t\t\t\t     const char *cond, size_t cond_len)\n {\n@@ -272,6 +286,8 @@ static int include_condition_is_true(const struct config_options *opts,\n \t\treturn include_by_gitdir(opts, cond, cond_len, 0);\n \telse if (skip_prefix_mem(cond, cond_len, \"gitdir/i:\", &cond, &cond_len))\n \t\treturn include_by_gitdir(opts, cond, cond_len, 1);\n+\telse if (skip_prefix_mem(cond, cond_len, \"onbranch:\", &cond, &cond_len))\n+\t\treturn include_by_branch(cond, cond_len);\n \n \t/* unknown conditionals are always false */\n \treturn 0;\ndiff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh\nindex 579a86b7f8..062db08ad9 100755\n--- a/t/t1305-config-include.sh\n+++ b/t/t1305-config-include.sh\n@@ -309,6 +309,19 @@ test_expect_success SYMLINKS 'conditional include, gitdir matching symlink, icas\n \t)\n '\n \n+test_expect_success 'conditional include, onbranch' '\n+\t(\n+\t\tcd bar &&\n+\t\techo \"[includeIf \\\"onbranch:foo-branch\\\"]path=bar9\" >>.git/config &&\n+\t\techo \"[test]nine=9\" >.git/bar9 &&\n+\t\ttest_must_fail git config test.nine &&\n+\t\tgit checkout -b foo-branch &&\n+\t\techo 9 >expect &&\n+\t\tgit config test.nine >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_expect_success 'include cycles are detected' '\n \tcat >.gitconfig <<-\\EOF &&\n \t[test]value = gitconfig\n-- \n2.22.0.rc1.169.g49223abbf8\n\n"},{"id":"376475","messageId":"nycvar.QRO.7.76.6.1905311453420.44@tvgsbejvaqbjf.bet","threadId":"51038","inReplyTo":"7b60e58ba554768fd915e4f5c00a97737707ed42.1559263024.git.liu.denton@gmail.com","subject":"Re: [RFC PATCH] config: learn the \"onbranch:\" includeIf condition","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-05-31T12:58:30Z","receivedAt":"2019-05-31T12:58:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 30 May 2019, Denton Liu wrote:\n\n> Currently, if a user wishes to have individual settings per branch, they\n> are required to manually keep track of the settings in their head and\n> manually set the options on the command-line or change the config at\n> each branch.\n>\n> Teach config the \"onbranch:\" includeIf condition so that it can\n> conditionally include configuration files if the branch is checked out\n> in the current worktree.\n\nWhat a coincidence. I actually wished for something like this, to have\nbranch-specific aliases.\n\nHowever, I would need this to handle patterns (via `wildmatch()`?) rather\nthan branch names.\n\n> I decided to go ahead and implement the includeIf onbranch semantics for\n> fun. For completeness, I'm sending it to the list but I'm not really\n> sure if this should get merged, since I don't really have a use-case for\n> this, especially if we go the branch-specific format-patch config route.\n>\n> Another thing to note is that this change doesn't completely cover all\n> the use-cases that the branch-specific format-patch does. In particular,\n> if I run\n>\n> \t$ git checkout foo\n> \t$ git format-patch master..bar\n>\n> with the `format.bar.*`, we'd get bar-specific configs, whereas with\n> `includeIf \"onbranch:bar\"`, we'd fail to include bar-specific configs\n> and, more dangerously, we'd be including foo's configs.\n\nI actually think that this is fine. \"on branch\" means that you are on the\nspecified branch, not that you merely mention the branch name on the\ncommand-line (in which case there would be the ambiguity \"did the user\nmean `master` or `bar`?\").\n\nCiao,\nDscho\n"},{"id":"376476","messageId":"20190531131626.GA30114@archbookpro.localdomain","threadId":"51038","inReplyTo":"nycvar.QRO.7.76.6.1905311453420.44@tvgsbejvaqbjf.bet","subject":"Re: [RFC PATCH] config: learn the \"onbranch:\" includeIf condition","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-31T13:16:26Z","receivedAt":"2019-05-31T13:16:31Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Johannes,\n\nOn Fri, May 31, 2019 at 02:58:30PM +0200, Johannes Schindelin wrote:\n> Hi,\n> \n> On Thu, 30 May 2019, Denton Liu wrote:\n> \n> > Currently, if a user wishes to have individual settings per branch, they\n> > are required to manually keep track of the settings in their head and\n> > manually set the options on the command-line or change the config at\n> > each branch.\n> >\n> > Teach config the \"onbranch:\" includeIf condition so that it can\n> > conditionally include configuration files if the branch is checked out\n> > in the current worktree.\n> \n> What a coincidence. I actually wished for something like this, to have\n> branch-specific aliases.\n> \n> However, I would need this to handle patterns (via `wildmatch()`?) rather\n> than branch names.\n\nDo you mean that we should be able to match a branch by pattern? So, for\nexample, if we had\n\n\t[includeIf \"onbranch:mas*\"]\n\nwe'd match if we were on \"master\"?\n\n> \n> > I decided to go ahead and implement the includeIf onbranch semantics for\n> > fun. For completeness, I'm sending it to the list but I'm not really\n> > sure if this should get merged, since I don't really have a use-case for\n> > this, especially if we go the branch-specific format-patch config route.\n> >\n> > Another thing to note is that this change doesn't completely cover all\n> > the use-cases that the branch-specific format-patch does. In particular,\n> > if I run\n> >\n> > \t$ git checkout foo\n> > \t$ git format-patch master..bar\n> >\n> > with the `format.bar.*`, we'd get bar-specific configs, whereas with\n> > `includeIf \"onbranch:bar\"`, we'd fail to include bar-specific configs\n> > and, more dangerously, we'd be including foo's configs.\n> \n> I actually think that this is fine. \"on branch\" means that you are on the\n> specified branch, not that you merely mention the branch name on the\n> command-line (in which case there would be the ambiguity \"did the user\n> mean `master` or `bar`?\").\n\nThe reason why I brought this up as a use case was because currently,\nwhen format-patch generates a cover letter, with the above, it'll use\nbar's branch description to populate it even if \"foo\" is checked out. As\na result, when implementing the branch-specific format-patch stuff, I\nwanted to make this consistent so that we wouldn't end up in a situation\nwhere the cover letter has the branch's description but is missing its\nCc's.\n\n> \n> Ciao,\n> Dscho\n"},{"id":"376487","messageId":"nycvar.QRO.7.76.6.1905311918510.44@tvgsbejvaqbjf.bet","threadId":"51038","inReplyTo":"20190531131626.GA30114@archbookpro.localdomain","subject":"Re: [RFC PATCH] config: learn the \"onbranch:\" includeIf condition","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-05-31T17:23:56Z","receivedAt":"2019-05-31T17:24:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Denton,\n\nOn Fri, 31 May 2019, Denton Liu wrote:\n\n> On Fri, May 31, 2019 at 02:58:30PM +0200, Johannes Schindelin wrote:\n>\n> > On Thu, 30 May 2019, Denton Liu wrote:\n> >\n> > > Currently, if a user wishes to have individual settings per branch,\n> > > they are required to manually keep track of the settings in their\n> > > head and manually set the options on the command-line or change the\n> > > config at each branch.\n> > >\n> > > Teach config the \"onbranch:\" includeIf condition so that it can\n> > > conditionally include configuration files if the branch is checked\n> > > out in the current worktree.\n> >\n> > What a coincidence. I actually wished for something like this, to have\n> > branch-specific aliases.\n> >\n> > However, I would need this to handle patterns (via `wildmatch()`?)\n> > rather than branch names.\n>\n> Do you mean that we should be able to match a branch by pattern? So, for\n> example, if we had\n>\n> \t[includeIf \"onbranch:mas*\"]\n>\n> we'd match if we were on \"master\"?\n\nYes, precisely.\n\nI would use this for the \"ever-green\" branches of Git for Windows, i.e.\nthe branches that are continuously rebasing Git for Windows' branch\nthicket on top of maint, master, next & pu. Those are called\n`shears/maint`, `shears/master`, etc (for historical reasons) in\nhttps://github.com/git-for-windows/git.\n\n> > > I decided to go ahead and implement the includeIf onbranch semantics\n> > > for fun. For completeness, I'm sending it to the list but I'm not\n> > > really sure if this should get merged, since I don't really have a\n> > > use-case for this, especially if we go the branch-specific\n> > > format-patch config route.\n> > >\n> > > Another thing to note is that this change doesn't completely cover\n> > > all the use-cases that the branch-specific format-patch does. In\n> > > particular, if I run\n> > >\n> > > \t$ git checkout foo\n> > > \t$ git format-patch master..bar\n> > >\n> > > with the `format.bar.*`, we'd get bar-specific configs, whereas with\n> > > `includeIf \"onbranch:bar\"`, we'd fail to include bar-specific configs\n> > > and, more dangerously, we'd be including foo's configs.\n> >\n> > I actually think that this is fine. \"on branch\" means that you are on the\n> > specified branch, not that you merely mention the branch name on the\n> > command-line (in which case there would be the ambiguity \"did the user\n> > mean `master` or `bar`?\").\n>\n> The reason why I brought this up as a use case was because currently,\n> when format-patch generates a cover letter, with the above, it'll use\n> bar's branch description to populate it even if \"foo\" is checked out. As\n> a result, when implementing the branch-specific format-patch stuff, I\n> wanted to make this consistent so that we wouldn't end up in a situation\n> where the cover letter has the branch's description but is missing its\n> Cc's.\n\nThat strikes me as a different use case than `includeIf`. I could imagine\nthat you'd want a setting like `formatpatch.detecttargetbranch = auto` or\nsome such that would pick up the `format.bar*` settings if there was *one*\nrev argument, and it was a commit range (or a tip commit), *and* it\nobviously referred to a single target branch.\n\nIt's just a scenario that is *very* specific to `git format-patch`.\n\nFor example, I would not, ever, want `git log ..next` to pick up a\nconfig specific to `next` just because I mentioned a commit range with\n`range` as the tip to start from.\n\nCiao,\nDscho\n"},{"id":"376497","messageId":"20190531184448.GA1175@archbookpro.localdomain","threadId":"51038","inReplyTo":"nycvar.QRO.7.76.6.1905311918510.44@tvgsbejvaqbjf.bet","subject":"Re: [RFC PATCH] config: learn the \"onbranch:\" includeIf condition","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-31T18:44:48Z","receivedAt":"2019-05-31T18:44:53Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"On Fri, May 31, 2019 at 07:23:56PM +0200, Johannes Schindelin wrote:\n> Hi Denton,\n> \n> On Fri, 31 May 2019, Denton Liu wrote:\n> \n> > On Fri, May 31, 2019 at 02:58:30PM +0200, Johannes Schindelin wrote:\n> >\n> > > On Thu, 30 May 2019, Denton Liu wrote:\n> > >\n\n[...]\n\n> \n> > > > I decided to go ahead and implement the includeIf onbranch semantics\n> > > > for fun. For completeness, I'm sending it to the list but I'm not\n> > > > really sure if this should get merged, since I don't really have a\n> > > > use-case for this, especially if we go the branch-specific\n> > > > format-patch config route.\n> > > >\n> > > > Another thing to note is that this change doesn't completely cover\n> > > > all the use-cases that the branch-specific format-patch does. In\n> > > > particular, if I run\n> > > >\n> > > > \t$ git checkout foo\n> > > > \t$ git format-patch master..bar\n> > > >\n> > > > with the `format.bar.*`, we'd get bar-specific configs, whereas with\n> > > > `includeIf \"onbranch:bar\"`, we'd fail to include bar-specific configs\n> > > > and, more dangerously, we'd be including foo's configs.\n> > >\n> > > I actually think that this is fine. \"on branch\" means that you are on the\n> > > specified branch, not that you merely mention the branch name on the\n> > > command-line (in which case there would be the ambiguity \"did the user\n> > > mean `master` or `bar`?\").\n> >\n> > The reason why I brought this up as a use case was because currently,\n> > when format-patch generates a cover letter, with the above, it'll use\n> > bar's branch description to populate it even if \"foo\" is checked out. As\n> > a result, when implementing the branch-specific format-patch stuff, I\n> > wanted to make this consistent so that we wouldn't end up in a situation\n> > where the cover letter has the branch's description but is missing its\n> > Cc's.\n> \n> That strikes me as a different use case than `includeIf`. I could imagine\n> that you'd want a setting like `formatpatch.detecttargetbranch = auto` or\n> some such that would pick up the `format.bar*` settings if there was *one*\n> rev argument, and it was a commit range (or a tip commit), *and* it\n> obviously referred to a single target branch.\n\nCorrect. For context, upthread I initially implemented the\nbranch-specific format-patch configs but Ævar suggested that we\nimplement the onbranch config semantics instead.\n\nSo I was just addressing the fact that this patch can't supercede the\nbranch-specific format-patch stuff since they have different use cases\nso the other patchset has to coexist with this one. I'm happy to see\nthat we're both in agreement about this.\n\n> \n> It's just a scenario that is *very* specific to `git format-patch`.\n> \n> For example, I would not, ever, want `git log ..next` to pick up a\n> config specific to `next` just because I mentioned a commit range with\n> `range` as the tip to start from.\n\nYeah, I'd never dream of implementing something that gross ;)\n\n> \n> Ciao,\n> Dscho\n"},{"id":"376503","messageId":"3573995264441c50ea9529b3ee926d1ed3ec4ac8.1559330905.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"7b60e58ba554768fd915e4f5c00a97737707ed42.1559263024.git.liu.denton@gmail.com","subject":"[PATCH v2] config: learn the \"onbranch:\" includeIf condition","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-05-31T19:33:42Z","receivedAt":"2019-05-31T19:33:48Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Currently, if a user wishes to have individual settings per branch, they\nare required to manually keep track of the settings in their head and\nmanually set the options on the command-line or change the config at\neach branch.\n\nTeach config the \"onbranch:\" includeIf condition so that it can\nconditionally include configuration files if the branch that is checked\nout in the current worktree matches the pattern given.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n\nThanks for taking a look, Johannes. I've modified the patch to match\nwith wildcards now.\n\nChanges since v1:\n\n* Allow onbranch: to specify a pattern instead of just a static\n  comparison\n\nInterdiff against v1:\n  diff --git a/Documentation/config.txt b/Documentation/config.txt\n  index 3b9fbe1860..93aa4323c6 100644\n  --- a/Documentation/config.txt\n  +++ b/Documentation/config.txt\n  @@ -145,9 +145,11 @@ refer to linkgit:gitignore[5] for details. For convenience:\n   \tcase-insensitively (e.g. on case-insensitive file sytems)\n   \n   `onbranch`::\n  -\tThe data that follows the keyword `onbranch:` is taken to be a\n  -\tname of a local branch. If we are in a worktree where that\n  -\tbranch is currently checked out, the include condition is met.\n  +\tThe data that follows the keyword `onbranch:` is taken to be a pattern\n  +\twith standard globbing wildcards and two additional ones, `**/` and\n  +\t`/**`, that can match multiple path components. If we are in a worktree\n  +\twhere the name of the branch that is currently checked out matches the\n  +\tpattern, the include condition is met.\n   \n   A few more notes on matching via `gitdir` and `gitdir/i`:\n   \n  diff --git a/config.c b/config.c\n  index 954e84e13a..48bb435739 100644\n  --- a/config.c\n  +++ b/config.c\n  @@ -268,6 +268,8 @@ static int include_by_gitdir(const struct config_options *opts,\n   static int include_by_branch(const char *cond, size_t cond_len)\n   {\n   \tint flags;\n  +\tint ret;\n  +\tstruct strbuf pattern = STRBUF_INIT;\n   \tconst char *refname = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flags);\n   \tconst char *shortname;\n   \n  @@ -275,7 +277,10 @@ static int include_by_branch(const char *cond, size_t cond_len)\n   \t\t\t!skip_prefix(refname, \"refs/heads/\", &shortname))\n   \t\treturn 0;\n   \n  -\treturn !strncmp(cond, shortname, cond_len);\n  +\tstrbuf_add(&pattern, cond, cond_len);\n  +\tret = !wildmatch(pattern.buf, shortname, WM_PATHNAME);\n  +\tstrbuf_release(&pattern);\n  +\treturn ret;\n   }\n   \n   static int include_condition_is_true(const struct config_options *opts,\n  diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh\n  index 062db08ad9..05c7def1f2 100755\n  --- a/t/t1305-config-include.sh\n  +++ b/t/t1305-config-include.sh\n  @@ -314,6 +314,7 @@ test_expect_success 'conditional include, onbranch' '\n   \t\tcd bar &&\n   \t\techo \"[includeIf \\\"onbranch:foo-branch\\\"]path=bar9\" >>.git/config &&\n   \t\techo \"[test]nine=9\" >.git/bar9 &&\n  +\t\tgit checkout -b master &&\n   \t\ttest_must_fail git config test.nine &&\n   \t\tgit checkout -b foo-branch &&\n   \t\techo 9 >expect &&\n  @@ -322,6 +323,25 @@ test_expect_success 'conditional include, onbranch' '\n   \t)\n   '\n   \n  +test_expect_success 'conditional include, onbranch, wildcard' '\n  +\t(\n  +\t\tcd bar &&\n  +\t\techo \"[includeIf \\\"onbranch:?oo-*/**\\\"]path=bar10\" >>.git/config &&\n  +\t\techo \"[test]ten=10\" >.git/bar10 &&\n  +\t\tgit checkout -b not-foo-branch/a &&\n  +\t\ttest_must_fail git config test.ten &&\n  +\n  +\t\techo 10 >expect &&\n  +\t\tgit checkout -b foo-branch/a/b/c &&\n  +\t\tgit config test.ten >actual &&\n  +\t\ttest_cmp expect actual &&\n  +\n  +\t\tgit checkout -b moo-bar/a &&\n  +\t\tgit config test.ten >actual &&\n  +\t\ttest_cmp expect actual\n  +\t)\n  +'\n  +\n   test_expect_success 'include cycles are detected' '\n   \tcat >.gitconfig <<-\\EOF &&\n   \t[test]value = gitconfig\n\n Documentation/config.txt  | 12 ++++++++++++\n config.c                  | 21 +++++++++++++++++++++\n t/t1305-config-include.sh | 33 +++++++++++++++++++++++++++++++++\n 3 files changed, 66 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 7e2a6f61f5..93aa4323c6 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -144,6 +144,13 @@ refer to linkgit:gitignore[5] for details. For convenience:\n \tThis is the same as `gitdir` except that matching is done\n \tcase-insensitively (e.g. on case-insensitive file sytems)\n \n+`onbranch`::\n+\tThe data that follows the keyword `onbranch:` is taken to be a pattern\n+\twith standard globbing wildcards and two additional ones, `**/` and\n+\t`/**`, that can match multiple path components. If we are in a worktree\n+\twhere the name of the branch that is currently checked out matches the\n+\tpattern, the include condition is met.\n+\n A few more notes on matching via `gitdir` and `gitdir/i`:\n \n  * Symlinks in `$GIT_DIR` are not resolved before matching.\n@@ -206,6 +213,11 @@ Example\n \t[includeIf \"gitdir:/path/to/group/\"]\n \t\tpath = foo.inc\n \n+\t; include only if we are in a worktree where foo-branch is\n+\t; currently checked out\n+\t[includeIf \"onbranch:foo-branch\"]\n+\t\tpath = foo.inc\n+\n Values\n ~~~~~~\n \ndiff --git a/config.c b/config.c\nindex 296a6d9cc4..48bb435739 100644\n--- a/config.c\n+++ b/config.c\n@@ -19,6 +19,7 @@\n #include \"utf8.h\"\n #include \"dir.h\"\n #include \"color.h\"\n+#include \"refs.h\"\n \n struct config_source {\n \tstruct config_source *prev;\n@@ -264,6 +265,24 @@ static int include_by_gitdir(const struct config_options *opts,\n \treturn ret;\n }\n \n+static int include_by_branch(const char *cond, size_t cond_len)\n+{\n+\tint flags;\n+\tint ret;\n+\tstruct strbuf pattern = STRBUF_INIT;\n+\tconst char *refname = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flags);\n+\tconst char *shortname;\n+\n+\tif (!refname || !(flags & REF_ISSYMREF)\t||\n+\t\t\t!skip_prefix(refname, \"refs/heads/\", &shortname))\n+\t\treturn 0;\n+\n+\tstrbuf_add(&pattern, cond, cond_len);\n+\tret = !wildmatch(pattern.buf, shortname, WM_PATHNAME);\n+\tstrbuf_release(&pattern);\n+\treturn ret;\n+}\n+\n static int include_condition_is_true(const struct config_options *opts,\n \t\t\t\t     const char *cond, size_t cond_len)\n {\n@@ -272,6 +291,8 @@ static int include_condition_is_true(const struct config_options *opts,\n \t\treturn include_by_gitdir(opts, cond, cond_len, 0);\n \telse if (skip_prefix_mem(cond, cond_len, \"gitdir/i:\", &cond, &cond_len))\n \t\treturn include_by_gitdir(opts, cond, cond_len, 1);\n+\telse if (skip_prefix_mem(cond, cond_len, \"onbranch:\", &cond, &cond_len))\n+\t\treturn include_by_branch(cond, cond_len);\n \n \t/* unknown conditionals are always false */\n \treturn 0;\ndiff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh\nindex 579a86b7f8..05c7def1f2 100755\n--- a/t/t1305-config-include.sh\n+++ b/t/t1305-config-include.sh\n@@ -309,6 +309,39 @@ test_expect_success SYMLINKS 'conditional include, gitdir matching symlink, icas\n \t)\n '\n \n+test_expect_success 'conditional include, onbranch' '\n+\t(\n+\t\tcd bar &&\n+\t\techo \"[includeIf \\\"onbranch:foo-branch\\\"]path=bar9\" >>.git/config &&\n+\t\techo \"[test]nine=9\" >.git/bar9 &&\n+\t\tgit checkout -b master &&\n+\t\ttest_must_fail git config test.nine &&\n+\t\tgit checkout -b foo-branch &&\n+\t\techo 9 >expect &&\n+\t\tgit config test.nine >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'conditional include, onbranch, wildcard' '\n+\t(\n+\t\tcd bar &&\n+\t\techo \"[includeIf \\\"onbranch:?oo-*/**\\\"]path=bar10\" >>.git/config &&\n+\t\techo \"[test]ten=10\" >.git/bar10 &&\n+\t\tgit checkout -b not-foo-branch/a &&\n+\t\ttest_must_fail git config test.ten &&\n+\n+\t\techo 10 >expect &&\n+\t\tgit checkout -b foo-branch/a/b/c &&\n+\t\tgit config test.ten >actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\tgit checkout -b moo-bar/a &&\n+\t\tgit config test.ten >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_expect_success 'include cycles are detected' '\n \tcat >.gitconfig <<-\\EOF &&\n \t[test]value = gitconfig\n-- \n2.22.0.rc1.169.g49223abbf8\n\n"},{"id":"376505","messageId":"nycvar.QRO.7.76.6.1905312214331.1775@tvgsbejvaqbjf.bet","threadId":"51038","inReplyTo":"3573995264441c50ea9529b3ee926d1ed3ec4ac8.1559330905.git.liu.denton@gmail.com","subject":"Re: [PATCH v2] config: learn the \"onbranch:\" includeIf condition","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-05-31T20:14:58Z","receivedAt":"2019-05-31T20:15:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Denton,\n\nOn Fri, 31 May 2019, Denton Liu wrote:\n\n> Currently, if a user wishes to have individual settings per branch, they\n> are required to manually keep track of the settings in their head and\n> manually set the options on the command-line or change the config at\n> each branch.\n>\n> Teach config the \"onbranch:\" includeIf condition so that it can\n> conditionally include configuration files if the branch that is checked\n> out in the current worktree matches the pattern given.\n\nThis looks good to me.\n\nThanks,\nDscho\n\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>\n> Thanks for taking a look, Johannes. I've modified the patch to match\n> with wildcards now.\n>\n> Changes since v1:\n>\n> * Allow onbranch: to specify a pattern instead of just a static\n>   comparison\n>\n> Interdiff against v1:\n>   diff --git a/Documentation/config.txt b/Documentation/config.txt\n>   index 3b9fbe1860..93aa4323c6 100644\n>   --- a/Documentation/config.txt\n>   +++ b/Documentation/config.txt\n>   @@ -145,9 +145,11 @@ refer to linkgit:gitignore[5] for details. For convenience:\n>    \tcase-insensitively (e.g. on case-insensitive file sytems)\n>\n>    `onbranch`::\n>   -\tThe data that follows the keyword `onbranch:` is taken to be a\n>   -\tname of a local branch. If we are in a worktree where that\n>   -\tbranch is currently checked out, the include condition is met.\n>   +\tThe data that follows the keyword `onbranch:` is taken to be a pattern\n>   +\twith standard globbing wildcards and two additional ones, `**/` and\n>   +\t`/**`, that can match multiple path components. If we are in a worktree\n>   +\twhere the name of the branch that is currently checked out matches the\n>   +\tpattern, the include condition is met.\n>\n>    A few more notes on matching via `gitdir` and `gitdir/i`:\n>\n>   diff --git a/config.c b/config.c\n>   index 954e84e13a..48bb435739 100644\n>   --- a/config.c\n>   +++ b/config.c\n>   @@ -268,6 +268,8 @@ static int include_by_gitdir(const struct config_options *opts,\n>    static int include_by_branch(const char *cond, size_t cond_len)\n>    {\n>    \tint flags;\n>   +\tint ret;\n>   +\tstruct strbuf pattern = STRBUF_INIT;\n>    \tconst char *refname = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flags);\n>    \tconst char *shortname;\n>\n>   @@ -275,7 +277,10 @@ static int include_by_branch(const char *cond, size_t cond_len)\n>    \t\t\t!skip_prefix(refname, \"refs/heads/\", &shortname))\n>    \t\treturn 0;\n>\n>   -\treturn !strncmp(cond, shortname, cond_len);\n>   +\tstrbuf_add(&pattern, cond, cond_len);\n>   +\tret = !wildmatch(pattern.buf, shortname, WM_PATHNAME);\n>   +\tstrbuf_release(&pattern);\n>   +\treturn ret;\n>    }\n>\n>    static int include_condition_is_true(const struct config_options *opts,\n>   diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh\n>   index 062db08ad9..05c7def1f2 100755\n>   --- a/t/t1305-config-include.sh\n>   +++ b/t/t1305-config-include.sh\n>   @@ -314,6 +314,7 @@ test_expect_success 'conditional include, onbranch' '\n>    \t\tcd bar &&\n>    \t\techo \"[includeIf \\\"onbranch:foo-branch\\\"]path=bar9\" >>.git/config &&\n>    \t\techo \"[test]nine=9\" >.git/bar9 &&\n>   +\t\tgit checkout -b master &&\n>    \t\ttest_must_fail git config test.nine &&\n>    \t\tgit checkout -b foo-branch &&\n>    \t\techo 9 >expect &&\n>   @@ -322,6 +323,25 @@ test_expect_success 'conditional include, onbranch' '\n>    \t)\n>    '\n>\n>   +test_expect_success 'conditional include, onbranch, wildcard' '\n>   +\t(\n>   +\t\tcd bar &&\n>   +\t\techo \"[includeIf \\\"onbranch:?oo-*/**\\\"]path=bar10\" >>.git/config &&\n>   +\t\techo \"[test]ten=10\" >.git/bar10 &&\n>   +\t\tgit checkout -b not-foo-branch/a &&\n>   +\t\ttest_must_fail git config test.ten &&\n>   +\n>   +\t\techo 10 >expect &&\n>   +\t\tgit checkout -b foo-branch/a/b/c &&\n>   +\t\tgit config test.ten >actual &&\n>   +\t\ttest_cmp expect actual &&\n>   +\n>   +\t\tgit checkout -b moo-bar/a &&\n>   +\t\tgit config test.ten >actual &&\n>   +\t\ttest_cmp expect actual\n>   +\t)\n>   +'\n>   +\n>    test_expect_success 'include cycles are detected' '\n>    \tcat >.gitconfig <<-\\EOF &&\n>    \t[test]value = gitconfig\n>\n>  Documentation/config.txt  | 12 ++++++++++++\n>  config.c                  | 21 +++++++++++++++++++++\n>  t/t1305-config-include.sh | 33 +++++++++++++++++++++++++++++++++\n>  3 files changed, 66 insertions(+)\n>\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index 7e2a6f61f5..93aa4323c6 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -144,6 +144,13 @@ refer to linkgit:gitignore[5] for details. For convenience:\n>  \tThis is the same as `gitdir` except that matching is done\n>  \tcase-insensitively (e.g. on case-insensitive file sytems)\n>\n> +`onbranch`::\n> +\tThe data that follows the keyword `onbranch:` is taken to be a pattern\n> +\twith standard globbing wildcards and two additional ones, `**/` and\n> +\t`/**`, that can match multiple path components. If we are in a worktree\n> +\twhere the name of the branch that is currently checked out matches the\n> +\tpattern, the include condition is met.\n> +\n>  A few more notes on matching via `gitdir` and `gitdir/i`:\n>\n>   * Symlinks in `$GIT_DIR` are not resolved before matching.\n> @@ -206,6 +213,11 @@ Example\n>  \t[includeIf \"gitdir:/path/to/group/\"]\n>  \t\tpath = foo.inc\n>\n> +\t; include only if we are in a worktree where foo-branch is\n> +\t; currently checked out\n> +\t[includeIf \"onbranch:foo-branch\"]\n> +\t\tpath = foo.inc\n> +\n>  Values\n>  ~~~~~~\n>\n> diff --git a/config.c b/config.c\n> index 296a6d9cc4..48bb435739 100644\n> --- a/config.c\n> +++ b/config.c\n> @@ -19,6 +19,7 @@\n>  #include \"utf8.h\"\n>  #include \"dir.h\"\n>  #include \"color.h\"\n> +#include \"refs.h\"\n>\n>  struct config_source {\n>  \tstruct config_source *prev;\n> @@ -264,6 +265,24 @@ static int include_by_gitdir(const struct config_options *opts,\n>  \treturn ret;\n>  }\n>\n> +static int include_by_branch(const char *cond, size_t cond_len)\n> +{\n> +\tint flags;\n> +\tint ret;\n> +\tstruct strbuf pattern = STRBUF_INIT;\n> +\tconst char *refname = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flags);\n> +\tconst char *shortname;\n> +\n> +\tif (!refname || !(flags & REF_ISSYMREF)\t||\n> +\t\t\t!skip_prefix(refname, \"refs/heads/\", &shortname))\n> +\t\treturn 0;\n> +\n> +\tstrbuf_add(&pattern, cond, cond_len);\n> +\tret = !wildmatch(pattern.buf, shortname, WM_PATHNAME);\n> +\tstrbuf_release(&pattern);\n> +\treturn ret;\n> +}\n> +\n>  static int include_condition_is_true(const struct config_options *opts,\n>  \t\t\t\t     const char *cond, size_t cond_len)\n>  {\n> @@ -272,6 +291,8 @@ static int include_condition_is_true(const struct config_options *opts,\n>  \t\treturn include_by_gitdir(opts, cond, cond_len, 0);\n>  \telse if (skip_prefix_mem(cond, cond_len, \"gitdir/i:\", &cond, &cond_len))\n>  \t\treturn include_by_gitdir(opts, cond, cond_len, 1);\n> +\telse if (skip_prefix_mem(cond, cond_len, \"onbranch:\", &cond, &cond_len))\n> +\t\treturn include_by_branch(cond, cond_len);\n>\n>  \t/* unknown conditionals are always false */\n>  \treturn 0;\n> diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh\n> index 579a86b7f8..05c7def1f2 100755\n> --- a/t/t1305-config-include.sh\n> +++ b/t/t1305-config-include.sh\n> @@ -309,6 +309,39 @@ test_expect_success SYMLINKS 'conditional include, gitdir matching symlink, icas\n>  \t)\n>  '\n>\n> +test_expect_success 'conditional include, onbranch' '\n> +\t(\n> +\t\tcd bar &&\n> +\t\techo \"[includeIf \\\"onbranch:foo-branch\\\"]path=bar9\" >>.git/config &&\n> +\t\techo \"[test]nine=9\" >.git/bar9 &&\n> +\t\tgit checkout -b master &&\n> +\t\ttest_must_fail git config test.nine &&\n> +\t\tgit checkout -b foo-branch &&\n> +\t\techo 9 >expect &&\n> +\t\tgit config test.nine >actual &&\n> +\t\ttest_cmp expect actual\n> +\t)\n> +'\n> +\n> +test_expect_success 'conditional include, onbranch, wildcard' '\n> +\t(\n> +\t\tcd bar &&\n> +\t\techo \"[includeIf \\\"onbranch:?oo-*/**\\\"]path=bar10\" >>.git/config &&\n> +\t\techo \"[test]ten=10\" >.git/bar10 &&\n> +\t\tgit checkout -b not-foo-branch/a &&\n> +\t\ttest_must_fail git config test.ten &&\n> +\n> +\t\techo 10 >expect &&\n> +\t\tgit checkout -b foo-branch/a/b/c &&\n> +\t\tgit config test.ten >actual &&\n> +\t\ttest_cmp expect actual &&\n> +\n> +\t\tgit checkout -b moo-bar/a &&\n> +\t\tgit config test.ten >actual &&\n> +\t\ttest_cmp expect actual\n> +\t)\n> +'\n> +\n>  test_expect_success 'include cycles are detected' '\n>  \tcat >.gitconfig <<-\\EOF &&\n>  \t[test]value = gitconfig\n> --\n> 2.22.0.rc1.169.g49223abbf8\n>\n>\n"},{"id":"376705","messageId":"nycvar.QRO.7.76.6.1906050953050.1775@tvgsbejvaqbjf.bet","threadId":"51038","inReplyTo":"nycvar.QRO.7.76.6.1905312214331.1775@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v2] config: learn the \"onbranch:\" includeIf condition","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-06-05T08:02:13Z","receivedAt":"2019-06-05T08:02:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Denton,\n\nOn Fri, 31 May 2019, Johannes Schindelin wrote:\n\n> On Fri, 31 May 2019, Denton Liu wrote:\n>\n> > Currently, if a user wishes to have individual settings per branch,\n> > they are required to manually keep track of the settings in their head\n> > and manually set the options on the command-line or change the config\n> > at each branch.\n> >\n> > Teach config the \"onbranch:\" includeIf condition so that it can\n> > conditionally include configuration files if the branch that is\n> > checked out in the current worktree matches the pattern given.\n>\n> This looks good to me.\n\n... actually...\n\n> > diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh\n> > index 579a86b7f8..05c7def1f2 100755\n> > --- a/t/t1305-config-include.sh\n> > +++ b/t/t1305-config-include.sh\n> > @@ -309,6 +309,39 @@ test_expect_success SYMLINKS 'conditional include, gitdir matching symlink, icas\n> >  \t)\n> >  '\n> >\n> > +test_expect_success 'conditional include, onbranch' '\n> > +\t(\n> > +\t\tcd bar &&\n\nThis `bar` is a side effect of an earlier test case. To be precise, of\nthis test case:\n\ntest_expect_success SYMLINKS 'conditional include, gitdir matching symlink' '\n\tln -s foo bar &&\n\t(\n\t\tcd bar &&\n\t\techo \"[includeIf \\\"gitdir:bar/\\\"]path=bar7\" >>.git/config\n&&\n\t\techo \"[test]seven=7\" >.git/bar7 &&\n\t\techo 7 >expect &&\n\t\tgit config test.seven >actual &&\n\t\ttest_cmp expect actual\n\t)\n'\n\nAs you can see, this test case is marked with the `SYMLINKS` prerequisite,\nand hence can be skipped.\n\nI got a crazy idea, though: how about `cd foo` instead of `cd bar`? It\nshould still work, as `bar` is just a symbolic link (unless that test case\nhas been skipped, in which case `bar` does not even exist) to `foo`.\n\nAnd while this still relies on a side effect of an earlier test case\n('conditional include, both unanchored', to be precise), at least that\ntest case cannot be skipped because of a missing prerequisite.\n\nThe same `s/cd bar/cd foo` applies to the second test case in this patch,\ntoo, of course.\n\nThanks,\nDscho\n\n> > +\t\techo \"[includeIf \\\"onbranch:foo-branch\\\"]path=bar9\" >>.git/config &&\n> > +\t\techo \"[test]nine=9\" >.git/bar9 &&\n> > +\t\tgit checkout -b master &&\n> > +\t\ttest_must_fail git config test.nine &&\n> > +\t\tgit checkout -b foo-branch &&\n> > +\t\techo 9 >expect &&\n> > +\t\tgit config test.nine >actual &&\n> > +\t\ttest_cmp expect actual\n> > +\t)\n> > +'\n> > +\n> > +test_expect_success 'conditional include, onbranch, wildcard' '\n> > +\t(\n> > +\t\tcd bar &&\n> > +\t\techo \"[includeIf \\\"onbranch:?oo-*/**\\\"]path=bar10\" >>.git/config &&\n> > +\t\techo \"[test]ten=10\" >.git/bar10 &&\n> > +\t\tgit checkout -b not-foo-branch/a &&\n> > +\t\ttest_must_fail git config test.ten &&\n> > +\n> > +\t\techo 10 >expect &&\n> > +\t\tgit checkout -b foo-branch/a/b/c &&\n> > +\t\tgit config test.ten >actual &&\n> > +\t\ttest_cmp expect actual &&\n> > +\n> > +\t\tgit checkout -b moo-bar/a &&\n> > +\t\tgit config test.ten >actual &&\n> > +\t\ttest_cmp expect actual\n> > +\t)\n> > +'\n> > +\n> >  test_expect_success 'include cycles are detected' '\n> >  \tcat >.gitconfig <<-\\EOF &&\n> >  \t[test]value = gitconfig\n> > --\n> > 2.22.0.rc1.169.g49223abbf8\n> >\n> >\n>\n>\n"},{"id":"376707","messageId":"CACsJy8BaYhcnj_nofxmDB3txBCgSe0aDeR+LYPNqkHYg4OroHg@mail.gmail.com","threadId":"51038","inReplyTo":"3573995264441c50ea9529b3ee926d1ed3ec4ac8.1559330905.git.liu.denton@gmail.com","subject":"Re: [PATCH v2] config: learn the \"onbranch:\" includeIf condition","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-06-05T10:08:04Z","receivedAt":"2019-06-05T10:08:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Jun 1, 2019 at 2:34 AM Denton Liu <liu.denton@gmail.com> wrote:\n>\n> Currently, if a user wishes to have individual settings per branch, they\n> are required to manually keep track of the settings in their head and\n> manually set the options on the command-line or change the config at\n> each branch.\n>\n> Teach config the \"onbranch:\" includeIf condition so that it can\n> conditionally include configuration files if the branch that is checked\n> out in the current worktree matches the pattern given.\n\nJust wondering, anybody wants special settings on detached head (and\nif so, what syntax should it be)?\n\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index 7e2a6f61f5..93aa4323c6 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -144,6 +144,13 @@ refer to linkgit:gitignore[5] for details. For convenience:\n>         This is the same as `gitdir` except that matching is done\n>         case-insensitively (e.g. on case-insensitive file sytems)\n>\n> +`onbranch`::\n> +       The data that follows the keyword `onbranch:` is taken to be a pattern\n> +       with standard globbing wildcards and two additional ones, `**/` and\n> +       `/**`, that can match multiple path components. If we are in a worktree\n> +       where the name of the branch that is currently checked out matches the\n> +       pattern, the include condition is met.\n\nSupporting transforming \"foo/\" to \"foo/**\" would be nice (gitdir and\ngitdir/i do this). Useful when you organize your branches into\n\"subdirs\".\n\n> @@ -264,6 +265,24 @@ static int include_by_gitdir(const struct config_options *opts,\n>         return ret;\n>  }\n>\n> +static int include_by_branch(const char *cond, size_t cond_len)\n> +{\n> +       int flags;\n> +       int ret;\n> +       struct strbuf pattern = STRBUF_INIT;\n> +       const char *refname = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flags);\n> +       const char *shortname;\n> +\n> +       if (!refname || !(flags & REF_ISSYMREF) ||\n> +                       !skip_prefix(refname, \"refs/heads/\", &shortname))\n\nWe probably don't even need to check more than !refname. Symbolic refs\nfrom HEAD must be refs/heads/* or it's an error (fsck will complain).\nBut it's probably also ok to stay strict.\n\n> +               return 0;\n> +\n> +       strbuf_add(&pattern, cond, cond_len);\n> +       ret = !wildmatch(pattern.buf, shortname, WM_PATHNAME);\n> +       strbuf_release(&pattern);\n> +       return ret;\n> +}\n-- \nDuy\n"},{"id":"376738","messageId":"49e22269d5a66d1975fca71a300e9099f46e1c40.1559769658.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"3573995264441c50ea9529b3ee926d1ed3ec4ac8.1559330905.git.liu.denton@gmail.com","subject":"[PATCH v3] config: learn the \"onbranch:\" includeIf condition","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-06-05T21:21:59Z","receivedAt":"2019-06-05T21:22:06Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Currently, if a user wishes to have individual settings per branch, they\nare required to manually keep track of the settings in their head and\nmanually set the options on the command-line or change the config at\neach branch.\n\nTeach config the \"onbranch:\" includeIf condition so that it can\nconditionally include configuration files if the branch that is checked\nout in the current worktree matches the pattern given.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n\nThanks for the review, Johannes and Duy. I've incorporated both of your\nsuggestions into this round.\n\nNotes:\n    Changes since v2:\n    \n    * Run test-cases outside foo/ since the subdirectory is not necessary\n    * Implicitly add `**` when there is a trailing `/`\n    \n    Changes since v1:\n    \n    * Allow onbranch: to specify a pattern instead of just a static\n      comparison\n\nInterdiff against v2:\n  diff --git a/Documentation/config.txt b/Documentation/config.txt\n  index 93aa4323c6..e3f5bc3396 100644\n  --- a/Documentation/config.txt\n  +++ b/Documentation/config.txt\n  @@ -145,11 +145,18 @@ refer to linkgit:gitignore[5] for details. For convenience:\n   \tcase-insensitively (e.g. on case-insensitive file sytems)\n   \n   `onbranch`::\n  -\tThe data that follows the keyword `onbranch:` is taken to be a pattern\n  -\twith standard globbing wildcards and two additional ones, `**/` and\n  -\t`/**`, that can match multiple path components. If we are in a worktree\n  -\twhere the name of the branch that is currently checked out matches the\n  -\tpattern, the include condition is met.\n  +\tThe data that follows the keyword `onbranch:` is taken to be a\n  +\tpattern with standard globbing wildcards and two additional\n  +\tones, `**/` and `/**`, that can match multiple path components.\n  +\tIf we are in a worktree where the name of the branch that is\n  +\tcurrently checked out matches the pattern, the include condition\n  +\tis met.\n  ++\n  +If the pattern ends with `/`, `**` will be automatically added. For\n  +example, the pattern `foo/` becomes `foo/**`. In other words, it matches\n  +all branches that begin with `foo/`. This is useful if your branches are\n  +organized hierarchically and you would like to apply a configuration to\n  +all the branches in that hierarchy.\n   \n   A few more notes on matching via `gitdir` and `gitdir/i`:\n   \n  diff --git a/config.c b/config.c\n  index 48bb435739..ad4166e243 100644\n  --- a/config.c\n  +++ b/config.c\n  @@ -171,6 +171,12 @@ static int handle_path_include(const char *path, struct config_include_data *inc\n   \treturn ret;\n   }\n   \n  +static void add_trailing_starstar_for_dir(struct strbuf *pat)\n  +{\n  +\tif (pat->len && is_dir_sep(pat->buf[pat->len - 1]))\n  +\t\tstrbuf_addstr(pat, \"**\");\n  +}\n  +\n   static int prepare_include_condition_pattern(struct strbuf *pat)\n   {\n   \tstruct strbuf path = STRBUF_INIT;\n  @@ -200,8 +206,7 @@ static int prepare_include_condition_pattern(struct strbuf *pat)\n   \t} else if (!is_absolute_path(pat->buf))\n   \t\tstrbuf_insert(pat, 0, \"**/\", 3);\n   \n  -\tif (pat->len && is_dir_sep(pat->buf[pat->len - 1]))\n  -\t\tstrbuf_addstr(pat, \"**\");\n  +\tadd_trailing_starstar_for_dir(pat);\n   \n   \tstrbuf_release(&path);\n   \treturn prefix;\n  @@ -278,6 +283,7 @@ static int include_by_branch(const char *cond, size_t cond_len)\n   \t\treturn 0;\n   \n   \tstrbuf_add(&pattern, cond, cond_len);\n  +\tadd_trailing_starstar_for_dir(&pattern);\n   \tret = !wildmatch(pattern.buf, shortname, WM_PATHNAME);\n   \tstrbuf_release(&pattern);\n   \treturn ret;\n  diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh\n  index 05c7def1f2..9571e366f8 100755\n  --- a/t/t1305-config-include.sh\n  +++ b/t/t1305-config-include.sh\n  @@ -310,36 +310,42 @@ test_expect_success SYMLINKS 'conditional include, gitdir matching symlink, icas\n   '\n   \n   test_expect_success 'conditional include, onbranch' '\n  -\t(\n  -\t\tcd bar &&\n  -\t\techo \"[includeIf \\\"onbranch:foo-branch\\\"]path=bar9\" >>.git/config &&\n  -\t\techo \"[test]nine=9\" >.git/bar9 &&\n  -\t\tgit checkout -b master &&\n  -\t\ttest_must_fail git config test.nine &&\n  -\t\tgit checkout -b foo-branch &&\n  -\t\techo 9 >expect &&\n  -\t\tgit config test.nine >actual &&\n  -\t\ttest_cmp expect actual\n  -\t)\n  +\techo \"[includeIf \\\"onbranch:foo-branch\\\"]path=bar9\" >>.git/config &&\n  +\techo \"[test]nine=9\" >.git/bar9 &&\n  +\tgit checkout -b master &&\n  +\ttest_must_fail git config test.nine &&\n  +\tgit checkout -b foo-branch &&\n  +\techo 9 >expect &&\n  +\tgit config test.nine >actual &&\n  +\ttest_cmp expect actual\n   '\n   \n   test_expect_success 'conditional include, onbranch, wildcard' '\n  -\t(\n  -\t\tcd bar &&\n  -\t\techo \"[includeIf \\\"onbranch:?oo-*/**\\\"]path=bar10\" >>.git/config &&\n  -\t\techo \"[test]ten=10\" >.git/bar10 &&\n  -\t\tgit checkout -b not-foo-branch/a &&\n  -\t\ttest_must_fail git config test.ten &&\n  -\n  -\t\techo 10 >expect &&\n  -\t\tgit checkout -b foo-branch/a/b/c &&\n  -\t\tgit config test.ten >actual &&\n  -\t\ttest_cmp expect actual &&\n  -\n  -\t\tgit checkout -b moo-bar/a &&\n  -\t\tgit config test.ten >actual &&\n  -\t\ttest_cmp expect actual\n  -\t)\n  +\techo \"[includeIf \\\"onbranch:?oo-*/**\\\"]path=bar10\" >>.git/config &&\n  +\techo \"[test]ten=10\" >.git/bar10 &&\n  +\tgit checkout -b not-foo-branch/a &&\n  +\ttest_must_fail git config test.ten &&\n  +\n  +\techo 10 >expect &&\n  +\tgit checkout -b foo-branch/a/b/c &&\n  +\tgit config test.ten >actual &&\n  +\ttest_cmp expect actual &&\n  +\n  +\tgit checkout -b moo-bar/a &&\n  +\tgit config test.ten >actual &&\n  +\ttest_cmp expect actual\n  +'\n  +\n  +test_expect_success 'conditional include, onbranch, implicit /** for /' '\n  +\techo \"[includeIf \\\"onbranch:foo-dir/\\\"]path=bar11\" >>.git/config &&\n  +\techo \"[test]eleven=11\" >.git/bar11 &&\n  +\tgit checkout -b not-foo-dir/a &&\n  +\ttest_must_fail git config test.eleven &&\n  +\n  +\techo 11 >expect &&\n  +\tgit checkout -b foo-dir/a/b/c &&\n  +\tgit config test.eleven >actual &&\n  +\ttest_cmp expect actual\n   '\n   \n   test_expect_success 'include cycles are detected' '\n\n Documentation/config.txt  | 19 +++++++++++++++++++\n config.c                  | 31 +++++++++++++++++++++++++++++--\n t/t1305-config-include.sh | 39 +++++++++++++++++++++++++++++++++++++++\n 3 files changed, 87 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 7e2a6f61f5..e3f5bc3396 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -144,6 +144,20 @@ refer to linkgit:gitignore[5] for details. For convenience:\n \tThis is the same as `gitdir` except that matching is done\n \tcase-insensitively (e.g. on case-insensitive file sytems)\n \n+`onbranch`::\n+\tThe data that follows the keyword `onbranch:` is taken to be a\n+\tpattern with standard globbing wildcards and two additional\n+\tones, `**/` and `/**`, that can match multiple path components.\n+\tIf we are in a worktree where the name of the branch that is\n+\tcurrently checked out matches the pattern, the include condition\n+\tis met.\n++\n+If the pattern ends with `/`, `**` will be automatically added. For\n+example, the pattern `foo/` becomes `foo/**`. In other words, it matches\n+all branches that begin with `foo/`. This is useful if your branches are\n+organized hierarchically and you would like to apply a configuration to\n+all the branches in that hierarchy.\n+\n A few more notes on matching via `gitdir` and `gitdir/i`:\n \n  * Symlinks in `$GIT_DIR` are not resolved before matching.\n@@ -206,6 +220,11 @@ Example\n \t[includeIf \"gitdir:/path/to/group/\"]\n \t\tpath = foo.inc\n \n+\t; include only if we are in a worktree where foo-branch is\n+\t; currently checked out\n+\t[includeIf \"onbranch:foo-branch\"]\n+\t\tpath = foo.inc\n+\n Values\n ~~~~~~\n \ndiff --git a/config.c b/config.c\nindex 296a6d9cc4..ad4166e243 100644\n--- a/config.c\n+++ b/config.c\n@@ -19,6 +19,7 @@\n #include \"utf8.h\"\n #include \"dir.h\"\n #include \"color.h\"\n+#include \"refs.h\"\n \n struct config_source {\n \tstruct config_source *prev;\n@@ -170,6 +171,12 @@ static int handle_path_include(const char *path, struct config_include_data *inc\n \treturn ret;\n }\n \n+static void add_trailing_starstar_for_dir(struct strbuf *pat)\n+{\n+\tif (pat->len && is_dir_sep(pat->buf[pat->len - 1]))\n+\t\tstrbuf_addstr(pat, \"**\");\n+}\n+\n static int prepare_include_condition_pattern(struct strbuf *pat)\n {\n \tstruct strbuf path = STRBUF_INIT;\n@@ -199,8 +206,7 @@ static int prepare_include_condition_pattern(struct strbuf *pat)\n \t} else if (!is_absolute_path(pat->buf))\n \t\tstrbuf_insert(pat, 0, \"**/\", 3);\n \n-\tif (pat->len && is_dir_sep(pat->buf[pat->len - 1]))\n-\t\tstrbuf_addstr(pat, \"**\");\n+\tadd_trailing_starstar_for_dir(pat);\n \n \tstrbuf_release(&path);\n \treturn prefix;\n@@ -264,6 +270,25 @@ static int include_by_gitdir(const struct config_options *opts,\n \treturn ret;\n }\n \n+static int include_by_branch(const char *cond, size_t cond_len)\n+{\n+\tint flags;\n+\tint ret;\n+\tstruct strbuf pattern = STRBUF_INIT;\n+\tconst char *refname = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flags);\n+\tconst char *shortname;\n+\n+\tif (!refname || !(flags & REF_ISSYMREF)\t||\n+\t\t\t!skip_prefix(refname, \"refs/heads/\", &shortname))\n+\t\treturn 0;\n+\n+\tstrbuf_add(&pattern, cond, cond_len);\n+\tadd_trailing_starstar_for_dir(&pattern);\n+\tret = !wildmatch(pattern.buf, shortname, WM_PATHNAME);\n+\tstrbuf_release(&pattern);\n+\treturn ret;\n+}\n+\n static int include_condition_is_true(const struct config_options *opts,\n \t\t\t\t     const char *cond, size_t cond_len)\n {\n@@ -272,6 +297,8 @@ static int include_condition_is_true(const struct config_options *opts,\n \t\treturn include_by_gitdir(opts, cond, cond_len, 0);\n \telse if (skip_prefix_mem(cond, cond_len, \"gitdir/i:\", &cond, &cond_len))\n \t\treturn include_by_gitdir(opts, cond, cond_len, 1);\n+\telse if (skip_prefix_mem(cond, cond_len, \"onbranch:\", &cond, &cond_len))\n+\t\treturn include_by_branch(cond, cond_len);\n \n \t/* unknown conditionals are always false */\n \treturn 0;\ndiff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh\nindex 579a86b7f8..9571e366f8 100755\n--- a/t/t1305-config-include.sh\n+++ b/t/t1305-config-include.sh\n@@ -309,6 +309,45 @@ test_expect_success SYMLINKS 'conditional include, gitdir matching symlink, icas\n \t)\n '\n \n+test_expect_success 'conditional include, onbranch' '\n+\techo \"[includeIf \\\"onbranch:foo-branch\\\"]path=bar9\" >>.git/config &&\n+\techo \"[test]nine=9\" >.git/bar9 &&\n+\tgit checkout -b master &&\n+\ttest_must_fail git config test.nine &&\n+\tgit checkout -b foo-branch &&\n+\techo 9 >expect &&\n+\tgit config test.nine >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'conditional include, onbranch, wildcard' '\n+\techo \"[includeIf \\\"onbranch:?oo-*/**\\\"]path=bar10\" >>.git/config &&\n+\techo \"[test]ten=10\" >.git/bar10 &&\n+\tgit checkout -b not-foo-branch/a &&\n+\ttest_must_fail git config test.ten &&\n+\n+\techo 10 >expect &&\n+\tgit checkout -b foo-branch/a/b/c &&\n+\tgit config test.ten >actual &&\n+\ttest_cmp expect actual &&\n+\n+\tgit checkout -b moo-bar/a &&\n+\tgit config test.ten >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'conditional include, onbranch, implicit /** for /' '\n+\techo \"[includeIf \\\"onbranch:foo-dir/\\\"]path=bar11\" >>.git/config &&\n+\techo \"[test]eleven=11\" >.git/bar11 &&\n+\tgit checkout -b not-foo-dir/a &&\n+\ttest_must_fail git config test.eleven &&\n+\n+\techo 11 >expect &&\n+\tgit checkout -b foo-dir/a/b/c &&\n+\tgit config test.eleven >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'include cycles are detected' '\n \tcat >.gitconfig <<-\\EOF &&\n \t[test]value = gitconfig\n-- \n2.22.0.rc1.169.g49223abbf8\n\n"},{"id":"376748","messageId":"nycvar.QRO.7.76.6.1906061452130.42@tvgsbejvaqbjf.bet","threadId":"51038","inReplyTo":"49e22269d5a66d1975fca71a300e9099f46e1c40.1559769658.git.liu.denton@gmail.com","subject":"Re: [PATCH v3] config: learn the \"onbranch:\" includeIf condition","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-06-06T12:52:42Z","receivedAt":"2019-06-06T12:52:47Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Denton,\n\nOn Wed, 5 Jun 2019, Denton Liu wrote:\n\n> Currently, if a user wishes to have individual settings per branch, they\n> are required to manually keep track of the settings in their head and\n> manually set the options on the command-line or change the config at\n> each branch.\n>\n> Teach config the \"onbranch:\" includeIf condition so that it can\n> conditionally include configuration files if the branch that is checked\n> out in the current worktree matches the pattern given.\n>\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>\n> Thanks for the review, Johannes and Duy. I've incorporated both of your\n> suggestions into this round.\n>\n> Notes:\n>     Changes since v2:\n>\n>     * Run test-cases outside foo/ since the subdirectory is not necessary\n\nThat should indeed take care of my concern. Thank you!\nDscho\n"},{"id":"377261","messageId":"cover.1560547500.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558492582.git.liu.denton@gmail.com","subject":"[RESEND PATCH v3 0/8] teach branch-specific options for format-patch","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-06-14T21:56:10Z","receivedAt":"2019-06-14T21:56:17Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi all,\n\nAlthough 'config: learn the \"onbranch:\" includeIf condition'[1] seems to be\non track for \"next\", the content in this patchset is not entirely\nsuperseded by it.\n\nPatches 1 and 2 should be relatively uncontroversial since they just do\na bit of cleanup.\n\nPatch 3 is a new feature that isn't included in the config onbranch\nstuff at all. In introduces the `--infer-cover-subject` command-line\noption which would allow a cover-letter's subject to be automatically\ninferred from the branch's description.\n\nFinally, patches 4-8 are all needed to implement branch-specific\nformat-patch options. Although this behaviour can be replicated by the\nconfig onbranch patches, it's not a direct replacement. Consider the\nfollowing:\n\n\t$ git checkout foo\n\t$ git format-patch master..bar\n\nWith config onbranch, this would generate patches using foo's settings\nbut with this patchset, we would generate patches using bar's settings,\nwhich seems more desirable and intuitive for users.\n\nI suppose if adding a `format.<branch>.*` option is controversial since\nwe won't be sure if we're adding other intermediate format keys, we\ncould follow onbranch convention and use `format.forbranch:<branch>.*`\nas configuration keys instead.\n\nThanks,\n\nDenton\n\n[1]: https://public-inbox.org/git/49e22269d5a66d1975fca71a300e9099f46e1c40.1559769658.git.liu.denton@gmail.com/\n\nChanges since v2:\n\n* Replaced `format.<branch-name>.coverSubject` and `--cover-subject`\n  with `format.inferCoverSubject` and `--infer-cover-subject` which\n  reads the subject from the branch description\n* Do not let `git branch -d` delete `format.<branch-name>.*` configs\n* More documentation cleanup\n* Taught format-patch to read `format.<branch-name>.outputDirectory` as\n  well\n\nChanges since v1:\n\n* Used format.<branch-name>.* variables instead of branch.<branch-name>.*\n\n\nDenton Liu (8):\n  t4014: clean up style\n  Doc: add more detail for git-format-patch\n  format-patch: infer cover letter from branch description\n  format-patch: move extra_headers logic later\n  string-list: create string_list_append_all\n  format-patch: read branch-specific To: and Cc: headers\n  format-patch: move output_directory logic later\n  format-patch: read branch-specific output directory\n\n Documentation/config/format.txt    |  17 +-\n Documentation/git-format-patch.txt |  44 +-\n builtin/log.c                      | 237 ++++++---\n string-list.c                      |   9 +\n string-list.h                      |   7 +\n t/t4014-format-patch.sh            | 739 +++++++++++++++++------------\n 6 files changed, 667 insertions(+), 386 deletions(-)\n\nRange-diff against v2:\n1:  75474e18f4 = 1:  650f04bc04 t4014: clean up style\n2:  9ae349bf8a ! 2:  14b40b1dec Doc: add more detail for git-format-patch\n    @@ -9,7 +9,10 @@\n     \n         In addition, document the special value of `--base=auto`.\n     \n    -    Finally, while we're at it, surround option arguments with <>.\n    +    Next, while we're at it, surround option arguments with <>.\n    +\n    +    Finally, document the `format.outputDirectory` config and change\n    +    `format.coverletter` to use camelcase.\n     \n         Signed-off-by: Denton Liu <liu.denton@gmail.com>\n     \n    @@ -67,3 +70,25 @@\n      \n      --root::\n      \tTreat the revision argument as a <revision range>, even if it\n    +@@\n    + -------------\n    + You can specify extra mail header lines to be added to each message,\n    + defaults for the subject prefix and file suffix, number patches when\n    +-outputting more than one patch, add \"To\" or \"Cc:\" headers, configure\n    +-attachments, and sign off patches with configuration variables.\n    ++outputting more than one patch, add \"To:\" or \"Cc:\" headers, configure\n    ++attachments, change the patch output directory, and sign off patches\n    ++with configuration variables.\n    + \n    + ------------\n    + [format]\n    +@@\n    + \tcc = <email>\n    + \tattach [ = mime-boundary-string ]\n    + \tsignOff = true\n    +-\tcoverletter = auto\n    ++\toutputDirectory = <directory>\n    ++\tcoverLetter = auto\n    + ------------\n    + \n    + \n3:  c0403f5139 < -:  ---------- format-patch: make cover letter subject configurable\n-:  ---------- > 3:  ca2b4c21c1 format-patch: infer cover letter from branch description\n4:  534c6fd48a ! 4:  82a56a0d76 format-patch: move extra_headers logic later\n    @@ -7,10 +7,10 @@\n         logic later in the function so that this happens. (This patch is best\n         viewed with `git diff --color-moved`.)\n     \n    -    Note that this logic only depends on the `git_config` and\n    +    Note that this logic only depends on `git_config` and\n         `repo_init_revisions` and is depended on by the patch creation logic\n    -    which is directly below it so this move is effectively a no-op as\n    -    no dependencies being reordered.\n    +    which is directly below it so this move is effectively a no-op as no\n    +    dependencies being reordered.\n     \n         Signed-off-by: Denton Liu <liu.denton@gmail.com>\n     \n5:  491297c40a = 5:  3554164deb string-list: create string_list_append_all\n6:  e2c71fa593 ! 6:  6ad1b25858 format-patch: read branch-specific To: and Cc: headers\n    @@ -59,29 +59,20 @@\n      \tfar (from config or command line).\n      \n     @@\n    - -------------\n    - You can specify extra mail header lines to be added to each message,\n    - defaults for the subject prefix and file suffix, number patches when\n    --outputting more than one patch, add \"To\" or \"Cc:\" headers, configure\n    -+outputting more than one patch, add \"To:\" or \"Cc:\" headers, configure\n    - attachments, and sign off patches with configuration variables.\n    - \n    - ------------\n    -@@\n    + \tinferCoverSubject = true\n      ------------\n      \n    - In addition, for a specific branch, you can specify a custom cover\n    --letter subject.\n    ++In addition, for a specific branch, you can specify a custom cover\n     +letter subject, and add additional \"To:\" or \"Cc:\" headers.\n    - \n    - ------------\n    - [format \"branch-name\"]\n    - \tcoverSubject = \"subject for branch-name only\"\n    ++\n    ++------------\n    ++[format \"branch-name\"]\n     +\tto = <email>\n     +\tcc = <email>\n    - ------------\n    ++------------\n      \n      DISCUSSION\n    + ----------\n     \n      diff --git a/builtin/log.c b/builtin/log.c\n      --- a/builtin/log.c\n    @@ -96,7 +87,7 @@\n      static void add_header(const char *value)\n      {\n     @@\n    - \treturn 0;\n    + \tfprintf(rev->diffopt.file, \"\\n\");\n      }\n      \n     +static void add_branch_headers(struct rev_info *rev, const char *branch_name)\n    @@ -186,25 +177,6 @@\n      \t\tstrbuf_addstr(&buf, extra_hdr.items[i].string);\n      \t\tstrbuf_addch(&buf, '\\n');\n     \n    - diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\n    - --- a/t/t3200-branch.sh\n    - +++ b/t/t3200-branch.sh\n    -@@\n    - \n    - test_expect_success 'test deleting branch deletes branch config' '\n    - \tgit config format.my7.coverSubject \"cover subject\" &&\n    -+\tgit config format.my7.to \"To Me <to@example.com>\" &&\n    -+\tgit config format.my7.cc \"Cc Me <cc@example.com>\" &&\n    - \tgit branch -d my7 &&\n    - \ttest -z \"$(git config branch.my7.remote)\" &&\n    - \ttest -z \"$(git config branch.my7.merge)\" &&\n    - \ttest -z \"$(git config format.my7.coverSubject)\"\n    -+\ttest -z \"$(git config format.my7.to)\" &&\n    -+\ttest -z \"$(git config format.my7.cc)\"\n    - '\n    - \n    - test_expect_success 'test deleting branch without config' '\n    -\n      diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\n      --- a/t/t4014-format-patch.sh\n      +++ b/t/t4014-format-patch.sh\n-:  ---------- > 7:  3bc1b95a4e format-patch: move output_directory logic later\n-:  ---------- > 8:  2cf7c77f59 format-patch: read branch-specific output directory\n-- \n2.22.0.355.g3bfa262345\n\n"},{"id":"377262","messageId":"650f04bc045edb6d9702df38269656cc6c0e0812.1560547501.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1560547500.git.liu.denton@gmail.com","subject":"[RESEND PATCH v3 1/8] t4014: clean up style","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-06-14T21:56:14Z","receivedAt":"2019-06-14T21:56:23Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In Git's tests, there is typically no space between the redirection\noperator and the filename. Remove these spaces.\n\nSince output is silenced when running without `-v` and debugging\noutput is useful with `-v`, remove redirections to /dev/null.\n\nChange here-docs from `<<\\EOF` to `<<-\\EOF` so that they can be indented\nalong with the rest of the test case.\n\nFinally, refactor to remove Git commands upstream of pipe. This way, if\nan invocation of a Git command fails, the return code won't be lost.\nKeep upstream non-Git commands since we have to assume a base level of\nsanity.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t4014-format-patch.sh | 614 +++++++++++++++++++++-------------------\n 1 file changed, 319 insertions(+), 295 deletions(-)\n\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex b6e2fdbc44..3423f974bc 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -34,7 +34,8 @@ test_expect_success setup '\n \tgit commit -m \"Side changes #3 with \\\\n backslash-n in it.\" &&\n \n \tgit checkout master &&\n-\tgit diff-tree -p C2 | git apply --index &&\n+\tgit diff-tree -p C2 >patch &&\n+\tgit apply --index <patch &&\n \ttest_tick &&\n \tgit commit -m \"Master accepts moral equivalent of #2\"\n \n@@ -77,7 +78,7 @@ test_expect_success \"format-patch doesn't consider merge commits\" '\n \tgit checkout -b merger master &&\n \ttest_tick &&\n \tgit merge --no-ff slave &&\n-\tcnt=$(git format-patch -3 --stdout | grep \"^From \" | wc -l) &&\n+\tcnt=$(git format-patch -3 --stdout >patch && grep \"^From \" patch | wc -l) &&\n \ttest $cnt = 3\n '\n \n@@ -85,21 +86,22 @@ test_expect_success \"format-patch result applies\" '\n \n \tgit checkout -b rebuild-0 master &&\n \tgit am -3 patch0 &&\n-\tcnt=$(git rev-list master.. | wc -l) &&\n-\ttest $cnt = 2\n+\tgit rev-list master.. >list &&\n+\ttest_line_count = 2 list\n '\n \n test_expect_success \"format-patch --ignore-if-in-upstream result applies\" '\n \n \tgit checkout -b rebuild-1 master &&\n \tgit am -3 patch1 &&\n-\tcnt=$(git rev-list master.. | wc -l) &&\n-\ttest $cnt = 2\n+\tgit rev-list master.. >list &&\n+\ttest_line_count = 2 list\n '\n \n test_expect_success 'commit did not screw up the log message' '\n \n-\tgit cat-file commit side | grep \"^Side .* with .* backslash-n\"\n+\tgit cat-file commit side >actual &&\n+\tgrep \"^Side .* with .* backslash-n\" actual\n \n '\n \n@@ -112,7 +114,8 @@ test_expect_success 'format-patch did not screw up the log message' '\n \n test_expect_success 'replay did not screw up the log message' '\n \n-\tgit cat-file commit rebuild-1 | grep \"^Side .* with .* backslash-n\"\n+\tgit cat-file commit rebuild-1 >actual &&\n+\tgrep \"^Side .* with .* backslash-n\" actual\n \n '\n \n@@ -122,8 +125,8 @@ test_expect_success 'extra headers' '\n \" &&\n \tgit config --add format.headers \"Cc: S E Cipient <scipient@example.com>\n \" &&\n-\tgit format-patch --stdout master..side > patch2 &&\n-\tsed -e \"/^\\$/q\" patch2 > hdrs2 &&\n+\tgit format-patch --stdout master..side >patch2 &&\n+\tsed -e \"/^\\$/q\" patch2 >hdrs2 &&\n \tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs2 &&\n \tgrep \"^Cc: S E Cipient <scipient@example.com>\\$\" hdrs2\n \n@@ -134,7 +137,7 @@ test_expect_success 'extra headers without newlines' '\n \tgit config --replace-all format.headers \"To: R E Cipient <rcipient@example.com>\" &&\n \tgit config --add format.headers \"Cc: S E Cipient <scipient@example.com>\" &&\n \tgit format-patch --stdout master..side >patch3 &&\n-\tsed -e \"/^\\$/q\" patch3 > hdrs3 &&\n+\tsed -e \"/^\\$/q\" patch3 >hdrs3 &&\n \tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs3 &&\n \tgrep \"^Cc: S E Cipient <scipient@example.com>\\$\" hdrs3\n \n@@ -144,8 +147,8 @@ test_expect_success 'extra headers with multiple To:s' '\n \n \tgit config --replace-all format.headers \"To: R E Cipient <rcipient@example.com>\" &&\n \tgit config --add format.headers \"To: S E Cipient <scipient@example.com>\" &&\n-\tgit format-patch --stdout master..side > patch4 &&\n-\tsed -e \"/^\\$/q\" patch4 > hdrs4 &&\n+\tgit format-patch --stdout master..side >patch4 &&\n+\tsed -e \"/^\\$/q\" patch4 >hdrs4 &&\n \tgrep \"^To: R E Cipient <rcipient@example.com>,\\$\" hdrs4 &&\n \tgrep \"^ *S E Cipient <scipient@example.com>\\$\" hdrs4\n '\n@@ -153,72 +156,82 @@ test_expect_success 'extra headers with multiple To:s' '\n test_expect_success 'additional command line cc (ascii)' '\n \n \tgit config --replace-all format.headers \"Cc: R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --cc=\"S E Cipient <scipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch5 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" patch5 &&\n-\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" patch5\n+\tgit format-patch --cc=\"S E Cipient <scipient@example.com>\" --stdout master..side >patch5 &&\n+\tsed -e \"/^\\$/q\" patch5 >hdrs5 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" hdrs5 &&\n+\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" hdrs5\n '\n \n test_expect_failure 'additional command line cc (rfc822)' '\n \n \tgit config --replace-all format.headers \"Cc: R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --cc=\"S. E. Cipient <scipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch5 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" patch5 &&\n-\tgrep \"^ *\\\"S. E. Cipient\\\" <scipient@example.com>\\$\" patch5\n+\tgit format-patch --cc=\"S. E. Cipient <scipient@example.com>\" --stdout master..side >patch5 &&\n+\tsed -e \"/^\\$/q\" patch5 >hdrs5 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" hdrs5 &&\n+\tgrep \"^ *\\\"S. E. Cipient\\\" <scipient@example.com>\\$\" hdrs5\n '\n \n test_expect_success 'command line headers' '\n \n \tgit config --unset-all format.headers &&\n-\tgit format-patch --add-header=\"Cc: R E Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch6 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>\\$\" patch6\n+\tgit format-patch --add-header=\"Cc: R E Cipient <rcipient@example.com>\" --stdout master..side >patch6 &&\n+\tsed -e \"/^\\$/q\" patch6 >hdrs6 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>\\$\" hdrs6\n '\n \n test_expect_success 'configuration headers and command line headers' '\n \n \tgit config --replace-all format.headers \"Cc: R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --add-header=\"Cc: S E Cipient <scipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch7 &&\n-\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" patch7 &&\n-\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" patch7\n+\tgit format-patch --add-header=\"Cc: S E Cipient <scipient@example.com>\" --stdout master..side >patch7 &&\n+\tsed -e \"/^\\$/q\" patch7 >hdrs7 &&\n+\tgrep \"^Cc: R E Cipient <rcipient@example.com>,\\$\" hdrs7 &&\n+\tgrep \"^ *S E Cipient <scipient@example.com>\\$\" hdrs7\n '\n \n test_expect_success 'command line To: header (ascii)' '\n \n \tgit config --unset-all format.headers &&\n-\tgit format-patch --to=\"R E Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch8 &&\n-\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" patch8\n+\tgit format-patch --to=\"R E Cipient <rcipient@example.com>\" --stdout master..side >patch8 &&\n+\tsed -e \"/^\\$/q\" patch8 >hdrs8 &&\n+\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs8\n '\n \n test_expect_failure 'command line To: header (rfc822)' '\n \n-\tgit format-patch --to=\"R. E. Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch8 &&\n-\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" patch8\n+\tgit format-patch --to=\"R. E. Cipient <rcipient@example.com>\" --stdout master..side >patch8 &&\n+\tsed -e \"/^\\$/q\" patch8 >hdrs8 &&\n+\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" hdrs8\n '\n \n test_expect_failure 'command line To: header (rfc2047)' '\n \n-\tgit format-patch --to=\"R Ä Cipient <rcipient@example.com>\" --stdout master..side | sed -e \"/^\\$/q\" >patch8 &&\n-\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" patch8\n+\tgit format-patch --to=\"R Ä Cipient <rcipient@example.com>\" --stdout master..side >patch8 &&\n+\tsed -e \"/^\\$/q\" patch8 >hdrs8 &&\n+\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs8\n '\n \n test_expect_success 'configuration To: header (ascii)' '\n \n \tgit config format.to \"R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --stdout master..side | sed -e \"/^\\$/q\" >patch9 &&\n-\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" patch9\n+\tgit format-patch --stdout master..side >patch9 &&\n+\tsed -e \"/^\\$/q\" patch9 >hdrs9 &&\n+\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs9\n '\n \n test_expect_failure 'configuration To: header (rfc822)' '\n \n \tgit config format.to \"R. E. Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --stdout master..side | sed -e \"/^\\$/q\" >patch9 &&\n-\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" patch9\n+\tgit format-patch --stdout master..side >patch9 &&\n+\tsed -e \"/^\\$/q\" patch9 >hdrs9 &&\n+\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" hdrs9\n '\n \n test_expect_failure 'configuration To: header (rfc2047)' '\n \n \tgit config format.to \"R Ä Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --stdout master..side | sed -e \"/^\\$/q\" >patch9 &&\n-\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" patch9\n+\tgit format-patch --stdout master..side >patch9 &&\n+\tsed -e \"/^\\$/q\" patch9 >hdrs9 &&\n+\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs9\n '\n \n # check_patch <patch>: Verify that <patch> looks like a half-sane\n@@ -231,52 +244,52 @@ check_patch () {\n \n test_expect_success 'format.from=false' '\n \n-\tgit -c format.from=false format-patch --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n+\tgit -c format.from=false format-patch --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n \tcheck_patch patch &&\n-\t! grep \"^From: C O Mitter <committer@example.com>\\$\" patch\n+\t! grep \"^From: C O Mitter <committer@example.com>\\$\" hdrs\n '\n \n test_expect_success 'format.from=true' '\n \n-\tgit -c format.from=true format-patch --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\tgrep \"^From: C O Mitter <committer@example.com>\\$\" patch\n+\tgit -c format.from=true format-patch --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\tgrep \"^From: C O Mitter <committer@example.com>\\$\" hdrs\n '\n \n test_expect_success 'format.from with address' '\n \n-\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\tgrep \"^From: F R Om <from@example.com>\\$\" patch\n+\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\tgrep \"^From: F R Om <from@example.com>\\$\" hdrs\n '\n \n test_expect_success '--no-from overrides format.from' '\n \n-\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --no-from --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\t! grep \"^From: F R Om <from@example.com>\\$\" patch\n+\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --no-from --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\t! grep \"^From: F R Om <from@example.com>\\$\" hdrs\n '\n \n test_expect_success '--from overrides format.from' '\n \n-\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --from --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch &&\n-\tcheck_patch patch &&\n-\t! grep \"^From: F R Om <from@example.com>\\$\" patch\n+\tgit -c format.from=\"F R Om <from@example.com>\" format-patch --from --stdout master..side >patch &&\n+\tsed -e \"/^\\$/q\" patch >hdrs &&\n+\tcheck_patch hdrs &&\n+\t! grep \"^From: F R Om <from@example.com>\\$\" hdrs\n '\n \n test_expect_success '--no-to overrides config.to' '\n \n \tgit config --replace-all format.to \\\n \t\t\"R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-to --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch10 &&\n-\tcheck_patch patch10 &&\n-\t! grep \"^To: R E Cipient <rcipient@example.com>\\$\" patch10\n+\tgit format-patch --no-to --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tcheck_patch hdrs10 &&\n+\t! grep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs10\n '\n \n test_expect_success '--no-to and --to replaces config.to' '\n@@ -284,31 +297,31 @@ test_expect_success '--no-to and --to replaces config.to' '\n \tgit config --replace-all format.to \\\n \t\t\"Someone <someone@out.there>\" &&\n \tgit format-patch --no-to --to=\"Someone Else <else@out.there>\" \\\n-\t\t--stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch11 &&\n-\tcheck_patch patch11 &&\n-\t! grep \"^To: Someone <someone@out.there>\\$\" patch11 &&\n-\tgrep \"^To: Someone Else <else@out.there>\\$\" patch11\n+\t\t--stdout master..side >patch11 &&\n+\tsed -e \"/^\\$/q\" patch11 >hdrs11 &&\n+\tcheck_patch hdrs11 &&\n+\t! grep \"^To: Someone <someone@out.there>\\$\" hdrs11 &&\n+\tgrep \"^To: Someone Else <else@out.there>\\$\" hdrs11\n '\n \n test_expect_success '--no-cc overrides config.cc' '\n \n \tgit config --replace-all format.cc \\\n \t\t\"C E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-cc --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch12 &&\n-\tcheck_patch patch12 &&\n-\t! grep \"^Cc: C E Cipient <rcipient@example.com>\\$\" patch12\n+\tgit format-patch --no-cc --stdout master..side >patch12 &&\n+\tsed -e \"/^\\$/q\" patch12 >hdrs12 &&\n+\tcheck_patch hdrs12 &&\n+\t! grep \"^Cc: C E Cipient <rcipient@example.com>\\$\" hdrs12\n '\n \n test_expect_success '--no-add-header overrides config.headers' '\n \n \tgit config --replace-all format.headers \\\n \t\t\"Header1: B E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-add-header --stdout master..side |\n-\tsed -e \"/^\\$/q\" >patch13 &&\n-\tcheck_patch patch13 &&\n-\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" patch13\n+\tgit format-patch --no-add-header --stdout master..side >patch13 &&\n+\tsed -e \"/^\\$/q\" patch13 >hdrs13 &&\n+\tcheck_patch hdrs13 &&\n+\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" hdrs13\n '\n \n test_expect_success 'multiple files' '\n@@ -338,7 +351,7 @@ test_expect_success 'reroll count (-v)' '\n check_threading () {\n \texpect=\"$1\" &&\n \tshift &&\n-\t(git format-patch --stdout \"$@\"; echo $? > status.out) |\n+\t(git format-patch --stdout \"$@\"; echo $? >status.out) |\n \t# Prints everything between the Message-ID and In-Reply-To,\n \t# and replaces all Message-ID-lookalikes by a sequence number\n \tperl -ne '\n@@ -353,12 +366,12 @@ check_threading () {\n \t\t\tprint;\n \t\t}\n \t\tprint \"---\\n\" if /^From /i;\n-\t' > actual &&\n+\t' >actual &&\n \ttest 0 = \"$(cat status.out)\" &&\n \ttest_cmp \"$expect\" actual\n }\n \n-cat >> expect.no-threading <<EOF\n+cat >>expect.no-threading <<EOF\n ---\n ---\n ---\n@@ -369,7 +382,7 @@ test_expect_success 'no threading' '\n \tcheck_threading expect.no-threading master\n '\n \n-cat > expect.thread <<EOF\n+cat >expect.thread <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -386,7 +399,7 @@ test_expect_success 'thread' '\n \tcheck_threading expect.thread --thread master\n '\n \n-cat > expect.in-reply-to <<EOF\n+cat >expect.in-reply-to <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -406,7 +419,7 @@ test_expect_success 'thread in-reply-to' '\n \t\t--thread master\n '\n \n-cat > expect.cover-letter <<EOF\n+cat >expect.cover-letter <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -427,7 +440,7 @@ test_expect_success 'thread cover-letter' '\n \tcheck_threading expect.cover-letter --cover-letter --thread master\n '\n \n-cat > expect.cl-irt <<EOF\n+cat >expect.cl-irt <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -459,7 +472,7 @@ test_expect_success 'thread explicit shallow' '\n \t\t--in-reply-to=\"<test.message>\" --thread=shallow master\n '\n \n-cat > expect.deep <<EOF\n+cat >expect.deep <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -477,7 +490,7 @@ test_expect_success 'thread deep' '\n \tcheck_threading expect.deep --thread=deep master\n '\n \n-cat > expect.deep-irt <<EOF\n+cat >expect.deep-irt <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -500,7 +513,7 @@ test_expect_success 'thread deep in-reply-to' '\n \t\t--in-reply-to=\"<test.message>\" master\n '\n \n-cat > expect.deep-cl <<EOF\n+cat >expect.deep-cl <<EOF\n ---\n Message-Id: <0>\n ---\n@@ -524,7 +537,7 @@ test_expect_success 'thread deep cover-letter' '\n \tcheck_threading expect.deep-cl --cover-letter --thread=deep master\n '\n \n-cat > expect.deep-cl-irt <<EOF\n+cat >expect.deep-cl-irt <<EOF\n ---\n Message-Id: <0>\n In-Reply-To: <1>\n@@ -606,7 +619,7 @@ test_expect_success 'cover-letter inherits diff options' '\n \n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n   This is an excessively long subject line for a message due to the\n     habit some projects have of not having a short, one-line subject at\n     the start of the commit message, but rather sticking a whole\n@@ -619,12 +632,12 @@ EOF\n test_expect_success 'shortlog of cover-letter wraps overly-long onelines' '\n \n \tgit format-patch --cover-letter -2 &&\n-\tsed -e \"1,/A U Thor/d\" -e \"/^\\$/q\" < 0000-cover-letter.patch > output &&\n+\tsed -e \"1,/A U Thor/d\" -e \"/^\\$/q\" <0000-cover-letter.patch >output &&\n \ttest_cmp expect output\n \n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n index $before..$after 100644\n --- a/file\n +++ b/file\n@@ -646,7 +659,7 @@ test_expect_success 'format-patch respects -U' '\n \n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n \n diff --git a/file b/file\n index $before..$after 100644\n@@ -662,7 +675,7 @@ EOF\n test_expect_success 'format-patch -p suppresses stat' '\n \n \tgit format-patch -p -2 &&\n-\tsed -e \"1,/^\\$/d\" -e \"/^+5/q\" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&\n+\tsed -e \"1,/^\\$/d\" -e \"/^+5/q\" <0001-This-is-an-excessively-long-subject-line-for-a-messa.patch >output &&\n \ttest_cmp expect output\n \n '\n@@ -717,7 +730,7 @@ test_expect_success 'format-patch from a subdirectory (3)' '\n '\n \n test_expect_success 'format-patch --in-reply-to' '\n-\tgit format-patch -1 --stdout --in-reply-to \"baz@foo.bar\" > patch8 &&\n+\tgit format-patch -1 --stdout --in-reply-to \"baz@foo.bar\" >patch8 &&\n \tgrep \"^In-Reply-To: <baz@foo.bar>\" patch8 &&\n \tgrep \"^References: <baz@foo.bar>\" patch8\n '\n@@ -738,20 +751,20 @@ test_expect_success 'format-patch --notes --signoff' '\n \tsed \"1,/^---$/d\" out | grep \"test message\"\n '\n \n-echo \"fatal: --name-only does not make sense\" > expect.name-only\n-echo \"fatal: --name-status does not make sense\" > expect.name-status\n-echo \"fatal: --check does not make sense\" > expect.check\n+echo \"fatal: --name-only does not make sense\" >expect.name-only\n+echo \"fatal: --name-status does not make sense\" >expect.name-status\n+echo \"fatal: --check does not make sense\" >expect.check\n \n test_expect_success 'options no longer allowed for format-patch' '\n-\ttest_must_fail git format-patch --name-only 2> output &&\n+\ttest_must_fail git format-patch --name-only 2>output &&\n \ttest_i18ncmp expect.name-only output &&\n-\ttest_must_fail git format-patch --name-status 2> output &&\n+\ttest_must_fail git format-patch --name-status 2>output &&\n \ttest_i18ncmp expect.name-status output &&\n-\ttest_must_fail git format-patch --check 2> output &&\n+\ttest_must_fail git format-patch --check 2>output &&\n \ttest_i18ncmp expect.check output'\n \n test_expect_success 'format-patch --numstat should produce a patch' '\n-\tgit format-patch --numstat --stdout master..side > output &&\n+\tgit format-patch --numstat --stdout master..side >output &&\n \ttest 5 = $(grep \"^diff --git a/\" output | wc -l)'\n \n test_expect_success 'format-patch -- <path>' '\n@@ -763,20 +776,22 @@ test_expect_success 'format-patch --ignore-if-in-upstream HEAD' '\n \tgit format-patch --ignore-if-in-upstream HEAD\n '\n \n-git_version=\"$(git --version | sed \"s/.* //\")\"\n+git_version=\"$(git --version >version && sed \"s/.* //\" version)\"\n \n signature() {\n \tprintf \"%s\\n%s\\n\\n\" \"-- \" \"${1:-$git_version}\"\n }\n \n test_expect_success 'format-patch default signature' '\n-\tgit format-patch --stdout -1 | tail -n 3 >output &&\n+\tgit format-patch --stdout -1 >patch &&\n+\ttail -n 3 patch >output &&\n \tsignature >expect &&\n \ttest_cmp expect output\n '\n \n test_expect_success 'format-patch --signature' '\n-\tgit format-patch --stdout --signature=\"my sig\" -1 | tail -n 3 >output &&\n+\tgit format-patch --stdout --signature=\"my sig\" -1 >patch &&\n+\ttail -n 3 patch >output &&\n \tsignature \"my sig\" >expect &&\n \ttest_cmp expect output\n '\n@@ -1167,282 +1182,282 @@ append_signoff()\n \n test_expect_success 'signoff: commit with no body' '\n \tappend_signoff </dev/null >actual &&\n-\tcat <<\\EOF | sed \"s/EOL$//\" >expected &&\n-4:Subject: [PATCH] EOL\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat <<-\\EOF | sed \"s/EOL$//\" >expected &&\n+\t4:Subject: [PATCH] EOL\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: commit with only subject' '\n \techo subject | append_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: commit with only subject that does not end with NL' '\n \tprintf subject | append_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: no existing signoffs' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tbody\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: no existing signoffs and no trailing NL' '\n \tprintf \"subject\\n\\nbody\" | append_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: some random signoff' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: my@house\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: my@house\n-12:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tSigned-off-by: my@house\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: my@house\n+\t12:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: misc conforming footer elements' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: my@house\n-(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)\n-Tested-by: Some One <someone@example.com>\n-Bug: 1234\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: my@house\n-15:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tSigned-off-by: my@house\n+\t(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)\n+\tTested-by: Some One <someone@example.com>\n+\tBug: 1234\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: my@house\n+\t15:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: some random signoff-alike' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n-Fooled-by-me: my@house\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-11:\n-12:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tbody\n+\tFooled-by-me: my@house\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t11:\n+\t12:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: not really a signoff' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-I want to mention about Signed-off-by: here.\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:I want to mention about Signed-off-by: here.\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tI want to mention about Signed-off-by: here.\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:I want to mention about Signed-off-by: here.\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: not really a signoff (2)' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-My unfortunate\n-Signed-off-by: example happens to be wrapped here.\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:Signed-off-by: example happens to be wrapped here.\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tMy unfortunate\n+\tSigned-off-by: example happens to be wrapped here.\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:Signed-off-by: example happens to be wrapped here.\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: valid S-o-b paragraph in the middle' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-Signed-off-by: my@house\n-Signed-off-by: your@house\n+\tSigned-off-by: my@house\n+\tSigned-off-by: your@house\n \n-A lot of houses.\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: my@house\n-10:Signed-off-by: your@house\n-11:\n-13:\n-14:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tA lot of houses.\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: my@house\n+\t10:Signed-off-by: your@house\n+\t11:\n+\t13:\n+\t14:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: the same signoff at the end' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: the same signoff at the end, no trailing NL' '\n \tprintf \"subject\\n\\nSigned-off-by: C O Mitter <committer@example.com>\" |\n \t\tappend_signoff >actual &&\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-9:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t9:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: the same signoff NOT at the end' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Signed-off-by: C O Mitter <committer@example.com>\n-Signed-off-by: my@house\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-12:Signed-off-by: my@house\n-EOF\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tSigned-off-by: my@house\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\t12:Signed-off-by: my@house\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: tolerate garbage in conforming footer' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Tested-by: my@house\n-Some Trash\n-Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-13:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tTested-by: my@house\n+\tSome Trash\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t13:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: respect trailer config' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-Myfooter: x\n-Some Trash\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-11:\n-12:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tMyfooter: x\n+\tSome Trash\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t11:\n+\t12:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual &&\n \n \ttest_config trailer.Myfooter.ifexists add &&\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-Myfooter: x\n-Some Trash\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-11:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tMyfooter: x\n+\tSome Trash\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t11:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'signoff: footer begins with non-signoff without @ sign' '\n-\tappend_signoff <<\\EOF >actual &&\n-subject\n+\tappend_signoff <<-\\EOF >actual &&\n+\tsubject\n \n-body\n+\tbody\n \n-Reviewed-id: Noone\n-Tested-by: my@house\n-Change-id: Ideadbeef\n-Signed-off-by: C O Mitter <committer@example.com>\n-Bug: 1234\n-EOF\n-\tcat >expected <<\\EOF &&\n-4:Subject: [PATCH] subject\n-8:\n-10:\n-14:Signed-off-by: C O Mitter <committer@example.com>\n-EOF\n+\tReviewed-id: Noone\n+\tTested-by: my@house\n+\tChange-id: Ideadbeef\n+\tSigned-off-by: C O Mitter <committer@example.com>\n+\tBug: 1234\n+\tEOF\n+\tcat >expected <<-\\EOF &&\n+\t4:Subject: [PATCH] subject\n+\t8:\n+\t10:\n+\t14:Signed-off-by: C O Mitter <committer@example.com>\n+\tEOF\n \ttest_cmp expected actual\n '\n \n@@ -1458,42 +1473,42 @@ test_expect_success 'cover letter using branch description (1)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter master >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (2)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter rebuild-1~2..rebuild-1 >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (3)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter ^master rebuild-1 >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (4)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter master.. >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (5)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter -2 HEAD >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter using branch description (6)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n \tgit format-patch --stdout --cover-letter -2 >actual &&\n-\tgrep hello actual >/dev/null\n+\tgrep hello actual\n '\n \n test_expect_success 'cover letter with nothing' '\n@@ -1547,7 +1562,8 @@ test_expect_success 'format-patch format.outputDirectory option' '\n \ttest_config format.outputDirectory patches &&\n \trm -fr patches &&\n \tgit format-patch master..side &&\n-\ttest $(git rev-list master..side | wc -l) -eq $(ls patches | wc -l)\n+\tgit rev-list master..side >list &&\n+\ttest_line_count = $(ls patches | wc -l) list\n '\n \n test_expect_success 'format-patch -o overrides format.outputDirectory' '\n@@ -1560,13 +1576,21 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '\n \n test_expect_success 'format-patch --base' '\n \tgit checkout side &&\n-\tgit format-patch --stdout --base=HEAD~3 -1 | tail -n 7 >actual1 &&\n-\tgit format-patch --stdout --base=HEAD~3 HEAD~.. | tail -n 7 >actual2 &&\n+\tgit format-patch --stdout --base=HEAD~3 -1 >patch &&\n+\ttail -n 7 patch >actual1 &&\n+\tgit format-patch --stdout --base=HEAD~3 HEAD~.. >patch &&\n+\ttail -n 7 patch >actual2 &&\n \techo >expected &&\n \techo \"base-commit: $(git rev-parse HEAD~3)\" >>expected &&\n-\techo \"prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n-\techo \"prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n-\tsignature >> expected &&\n+\tgit show --patch HEAD~2 >patch &&\n+\tgit patch-id --stable <patch >patch.id.raw &&\n+\tawk \"{print \\$1}\" <patch.id.raw >patch.id &&\n+\techo \"prerequisite-patch-id: $(cat patch.id)\" >>expected &&\n+\tgit show --patch HEAD~1 >patch &&\n+\tgit patch-id --stable <patch >patch.id.raw &&\n+\tawk \"{print \\$1}\" <patch.id.raw >patch.id &&\n+\techo \"prerequisite-patch-id: $(cat patch.id)\" >>expected &&\n+\tsignature >>expected &&\n \ttest_cmp expected actual1 &&\n \ttest_cmp expected actual2\n '\n-- \n2.22.0.355.g3bfa262345\n\n"},{"id":"377264","messageId":"14b40b1dec0b6d098abe99d4a64fe501cee56f43.1560547501.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1560547500.git.liu.denton@gmail.com","subject":"[RESEND PATCH v3 2/8] Doc: add more detail for git-format-patch","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-06-14T21:56:16Z","receivedAt":"2019-06-14T21:56:23Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In git-format-patch.txt, we were missing some key user information.\nFirst of all, using the `--to` and `--cc` options don't override\n`format.to` and `format.cc` variables, respectively. They add on to each\nother. Document this.\n\nIn addition, document the special value of `--base=auto`.\n\nNext, while we're at it, surround option arguments with <>.\n\nFinally, document the `format.outputDirectory` config and change\n`format.coverletter` to use camelcase.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/git-format-patch.txt | 25 ++++++++++++++++---------\n 1 file changed, 16 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 1af85d404f..4e180bbfbb 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -17,9 +17,9 @@ SYNOPSIS\n \t\t   [--signature-file=<file>]\n \t\t   [-n | --numbered | -N | --no-numbered]\n \t\t   [--start-number <n>] [--numbered-files]\n-\t\t   [--in-reply-to=Message-Id] [--suffix=.<sfx>]\n+\t\t   [--in-reply-to=<Message-Id>] [--suffix=.<sfx>]\n \t\t   [--ignore-if-in-upstream]\n-\t\t   [--rfc] [--subject-prefix=Subject-Prefix]\n+\t\t   [--rfc] [--subject-prefix=<Subject-Prefix>]\n \t\t   [(--reroll-count|-v) <n>]\n \t\t   [--to=<email>] [--cc=<email>]\n \t\t   [--[no-]cover-letter] [--quiet] [--notes[=<ref>]]\n@@ -158,7 +158,7 @@ Beware that the default for 'git send-email' is to thread emails\n itself.  If you want `git format-patch` to take care of threading, you\n will want to ensure that threading is disabled for `git send-email`.\n \n---in-reply-to=Message-Id::\n+--in-reply-to=<Message-Id>::\n \tMake the first mail (or all the mails with `--no-thread`) appear as a\n \treply to the given Message-Id, which avoids breaking threads to\n \tprovide a new patch series.\n@@ -192,13 +192,17 @@ will want to ensure that threading is disabled for `git send-email`.\n \n --to=<email>::\n \tAdd a `To:` header to the email headers. This is in addition\n-\tto any configured headers, and may be used multiple times.\n+\tto any configured headers, and may be used multiple times. The\n+\temails given will be used along with any emails given by\n+\t`format.to` configurations.\n \tThe negated form `--no-to` discards all `To:` headers added so\n \tfar (from config or command line).\n \n --cc=<email>::\n \tAdd a `Cc:` header to the email headers. This is in addition\n-\tto any configured headers, and may be used multiple times.\n+\tto any configured headers, and may be used multiple times. The\n+\temails given will be used along with any emails given by\n+\t`format.cc` configurations.\n \tThe negated form `--no-cc` discards all `Cc:` headers added so\n \tfar (from config or command line).\n \n@@ -309,7 +313,8 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.\n --base=<commit>::\n \tRecord the base tree information to identify the state the\n \tpatch series applies to.  See the BASE TREE INFORMATION section\n-\tbelow for details.\n+\tbelow for details. If <commit> is equal to \"auto\", a base commit\n+\tis automatically chosen.\n \n --root::\n \tTreat the revision argument as a <revision range>, even if it\n@@ -325,8 +330,9 @@ CONFIGURATION\n -------------\n You can specify extra mail header lines to be added to each message,\n defaults for the subject prefix and file suffix, number patches when\n-outputting more than one patch, add \"To\" or \"Cc:\" headers, configure\n-attachments, and sign off patches with configuration variables.\n+outputting more than one patch, add \"To:\" or \"Cc:\" headers, configure\n+attachments, change the patch output directory, and sign off patches\n+with configuration variables.\n \n ------------\n [format]\n@@ -338,7 +344,8 @@ attachments, and sign off patches with configuration variables.\n \tcc = <email>\n \tattach [ = mime-boundary-string ]\n \tsignOff = true\n-\tcoverletter = auto\n+\toutputDirectory = <directory>\n+\tcoverLetter = auto\n ------------\n \n \n-- \n2.22.0.355.g3bfa262345\n\n"},{"id":"377263","messageId":"ca2b4c21c1023e432bb543e227ab21b2317255ad.1560547501.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1560547500.git.liu.denton@gmail.com","subject":"[RESEND PATCH v3 3/8] format-patch: infer cover letter from branch description","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-06-14T21:56:18Z","receivedAt":"2019-06-14T21:56:24Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"We used to populate the subject of the cover letter generated by\ngit-format-patch with \"*** SUBJECT HERE ***\". However, if a user submits\nmultiple patchsets, they may want to keep a consistent subject between\nrerolls.\n\nIf git-format-patch is run with `--infer-cover-letter` or\n`format.inferCoverSubject`, infer the subject for the cover letter from\nthe top line(s) of a branch description, similar to how a subject is\nread from a commit message.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/config/format.txt    |  5 +++\n Documentation/git-format-patch.txt | 10 ++++++\n builtin/log.c                      | 58 ++++++++++++++++++------------\n t/t4014-format-patch.sh            | 33 +++++++++++++++++\n 4 files changed, 84 insertions(+), 22 deletions(-)\n\ndiff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\nindex dc77941c48..c8b28fe47f 100644\n--- a/Documentation/config/format.txt\n+++ b/Documentation/config/format.txt\n@@ -36,6 +36,11 @@ format.subjectPrefix::\n \tThe default for format-patch is to output files with the '[PATCH]'\n \tsubject prefix. Use this variable to change that prefix.\n \n+format.<branch-name>.inferCoverSubject::\n+\tA boolean that controls whether or not to infer the subject for\n+\tthe cover letter based on the branch's description. See the\n+\t--infer-cover-subject option in linkgit:git-format-patch[1].\n+\n format.signature::\n \tThe default for format-patch is to output a signature containing\n \tthe Git version number. Use this variable to change that default.\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 4e180bbfbb..f54aecf4bf 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -19,6 +19,7 @@ SYNOPSIS\n \t\t   [--start-number <n>] [--numbered-files]\n \t\t   [--in-reply-to=<Message-Id>] [--suffix=.<sfx>]\n \t\t   [--ignore-if-in-upstream]\n+\t\t   [--[no-]infer-cover-subject]\n \t\t   [--rfc] [--subject-prefix=<Subject-Prefix>]\n \t\t   [(--reroll-count|-v) <n>]\n \t\t   [--to=<email>] [--cc=<email>]\n@@ -170,6 +171,14 @@ will want to ensure that threading is disabled for `git send-email`.\n \tpatches being generated, and any patch that matches is\n \tignored.\n \n+--[no-]infer-cover-subject::\n+\tInstead of using the default \"*** SUBJECT HERE ***\" subject for\n+\tthe cover letter, infer the subject from the branch's\n+\tdescription.\n++\n+Similar to a commit message, the subject is inferred as the beginning of\n+the description up to and excluding the first blank line.\n+\n --subject-prefix=<Subject-Prefix>::\n \tInstead of the standard '[PATCH]' prefix in the subject\n \tline, instead use '[<Subject-Prefix>]'. This\n@@ -346,6 +355,7 @@ with configuration variables.\n \tsignOff = true\n \toutputDirectory = <directory>\n \tcoverLetter = auto\n+\tinferCoverSubject = true\n ------------\n \n \ndiff --git a/builtin/log.c b/builtin/log.c\nindex e43ee12fb1..617f074d60 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -769,6 +769,7 @@ static const char *signature = git_version_string;\n static const char *signature_file;\n static int config_cover_letter;\n static const char *config_output_directory;\n+static int infer_cover_subject;\n \n enum {\n \tCOVER_UNSET,\n@@ -864,6 +865,10 @@ static int git_format_config(const char *var, const char *value, void *cb)\n \t\t\tfrom = NULL;\n \t\treturn 0;\n \t}\n+\tif (!strcmp(var, \"format.infercoversubject\")) {\n+\t\tinfer_cover_subject = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n \n \treturn git_log_config(var, value, cb);\n }\n@@ -970,20 +975,6 @@ static void print_signature(FILE *file)\n \tputc('\\n', file);\n }\n \n-static void add_branch_description(struct strbuf *buf, const char *branch_name)\n-{\n-\tstruct strbuf desc = STRBUF_INIT;\n-\tif (!branch_name || !*branch_name)\n-\t\treturn;\n-\tread_branch_desc(&desc, branch_name);\n-\tif (desc.len) {\n-\t\tstrbuf_addch(buf, '\\n');\n-\t\tstrbuf_addbuf(buf, &desc);\n-\t\tstrbuf_addch(buf, '\\n');\n-\t}\n-\tstrbuf_release(&desc);\n-}\n-\n static char *find_branch_name(struct rev_info *rev)\n {\n \tint i, positive = -1;\n@@ -1034,13 +1025,17 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \t\t\t      struct commit *origin,\n \t\t\t      int nr, struct commit **list,\n \t\t\t      const char *branch_name,\n+\t\t\t      int infer_subject,\n \t\t\t      int quiet)\n {\n \tconst char *committer;\n-\tconst char *body = \"*** SUBJECT HERE ***\\n\\n*** BLURB HERE ***\\n\";\n-\tconst char *msg;\n+\tconst char *subject = \"*** SUBJECT HERE ***\";\n+\tconst char *body = \"*** BLURB HERE ***\";\n+\tconst char *description = NULL;\n \tstruct shortlog log;\n \tstruct strbuf sb = STRBUF_INIT;\n+\tstruct strbuf description_sb = STRBUF_INIT;\n+\tstruct strbuf subject_sb = STRBUF_INIT;\n \tint i;\n \tconst char *encoding = \"UTF-8\";\n \tint need_8bit_cte = 0;\n@@ -1068,17 +1063,34 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tif (!branch_name)\n \t\tbranch_name = find_branch_name(rev);\n \n-\tmsg = body;\n+\tif (branch_name && *branch_name)\n+\t\tread_branch_desc(&description_sb, branch_name);\n+\n+\tif (description_sb.len) {\n+\t\tif (infer_subject) {\n+\t\t\tdescription = format_subject(&subject_sb, description_sb.buf, \" \");\n+\t\t\tsubject = subject_sb.buf;\n+\t\t} else {\n+\t\t\tdescription = description_sb.buf;\n+\t\t}\n+\t}\n+\n \tpp.fmt = CMIT_FMT_EMAIL;\n \tpp.date_mode.type = DATE_RFC2822;\n \tpp.rev = rev;\n \tpp.print_email_subject = 1;\n \tpp_user_info(&pp, NULL, &sb, committer, encoding);\n-\tpp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);\n-\tpp_remainder(&pp, &msg, &sb, 0);\n-\tadd_branch_description(&sb, branch_name);\n+\tpp_title_line(&pp, &subject, &sb, encoding, need_8bit_cte);\n+\tpp_remainder(&pp, &body, &sb, 0);\n+\tif (description) {\n+\t\tstrbuf_addch(&sb, '\\n');\n+\t\tstrbuf_addstr(&sb, description);\n+\t\tstrbuf_addch(&sb, '\\n');\n+\t}\n \tfprintf(rev->diffopt.file, \"%s\\n\", sb.buf);\n \n+\tstrbuf_release(&description_sb);\n+\tstrbuf_release(&subject_sb);\n \tstrbuf_release(&sb);\n \n \tshortlog_init(&log);\n@@ -1554,6 +1566,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t{ OPTION_CALLBACK, 0, \"rfc\", &rev, NULL,\n \t\t\t    N_(\"Use [RFC PATCH] instead of [PATCH]\"),\n \t\t\t    PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback },\n+\t\tOPT_BOOL(0, \"infer-cover-subject\", &infer_cover_subject,\n+\t\t\t    N_(\"infer a cover letter subject from the branch description\")),\n \t\t{ OPTION_CALLBACK, 0, \"subject-prefix\", &rev, N_(\"prefix\"),\n \t\t\t    N_(\"Use [<prefix>] instead of [PATCH]\"),\n \t\t\t    PARSE_OPT_NONEG, subject_prefix_callback },\n@@ -1617,8 +1631,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \textra_to.strdup_strings = 1;\n \textra_cc.strdup_strings = 1;\n \tinit_log_defaults();\n-\tgit_config(git_format_config, NULL);\n \trepo_init_revisions(the_repository, &rev, prefix);\n+\tgit_config(git_format_config, NULL);\n \trev.commit_format = CMIT_FMT_EMAIL;\n \trev.expand_tabs_in_log_default = 0;\n \trev.verbose_header = 1;\n@@ -1893,7 +1907,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tif (thread)\n \t\t\tgen_message_id(&rev, \"cover\");\n \t\tmake_cover_letter(&rev, use_stdout,\n-\t\t\t\t  origin, nr, list, branch_name, quiet);\n+\t\t\t\t  origin, nr, list, branch_name, infer_cover_subject, quiet);\n \t\tprint_bases(&bases, rev.diffopt.file);\n \t\tprint_signature(rev.diffopt.file);\n \t\ttotal++;\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 3423f974bc..4cb6b9edfa 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -1469,6 +1469,39 @@ test_expect_success 'format patch ignores color.ui' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'cover letter with config subject' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\ttest_config format.inferCoverSubject true &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] config subject$\" actual &&\n+\tgrep \"^body\" actual\n+'\n+\n+test_expect_success 'cover letter with command-line subject' '\n+\ttest_config branch.rebuild-1.description \"command-line subject\n+\n+body\" &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter --infer-cover-subject master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] command-line subject$\" actual &&\n+\tgrep \"^body\" actual\n+'\n+\n+test_expect_success 'cover letter with command-line --no-infer-cover-subject overrides config' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\ttest_config format.inferCoverSubject true &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter --no-infer-cover-subject master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] \\*\\*\\* SUBJECT HERE \\*\\*\\*$\" actual &&\n+\tgrep \"^config subject\" actual &&\n+\tgrep \"^body\" actual\n+'\n+\n test_expect_success 'cover letter using branch description (1)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\n-- \n2.22.0.355.g3bfa262345\n\n"},{"id":"377265","messageId":"82a56a0d76c1028773ced5ffbbf832ee8dba7173.1560547501.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1560547500.git.liu.denton@gmail.com","subject":"[RESEND PATCH v3 4/8] format-patch: move extra_headers logic later","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-06-14T21:56:20Z","receivedAt":"2019-06-14T21:56:25Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future patch, we need to perform the addition of To: and Cc:\nto extra_headers after the branch_name logic. Simply transpose this\nlogic later in the function so that this happens. (This patch is best\nviewed with `git diff --color-moved`.)\n\nNote that this logic only depends on `git_config` and\n`repo_init_revisions` and is depended on by the patch creation logic\nwhich is directly below it so this move is effectively a no-op as no\ndependencies being reordered.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/log.c | 58 +++++++++++++++++++++++++--------------------------\n 1 file changed, 29 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 617f074d60..3f97f344df 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1667,35 +1667,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\trev.subject_prefix = strbuf_detach(&sprefix, NULL);\n \t}\n \n-\tfor (i = 0; i < extra_hdr.nr; i++) {\n-\t\tstrbuf_addstr(&buf, extra_hdr.items[i].string);\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t}\n-\n-\tif (extra_to.nr)\n-\t\tstrbuf_addstr(&buf, \"To: \");\n-\tfor (i = 0; i < extra_to.nr; i++) {\n-\t\tif (i)\n-\t\t\tstrbuf_addstr(&buf, \"    \");\n-\t\tstrbuf_addstr(&buf, extra_to.items[i].string);\n-\t\tif (i + 1 < extra_to.nr)\n-\t\t\tstrbuf_addch(&buf, ',');\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t}\n-\n-\tif (extra_cc.nr)\n-\t\tstrbuf_addstr(&buf, \"Cc: \");\n-\tfor (i = 0; i < extra_cc.nr; i++) {\n-\t\tif (i)\n-\t\t\tstrbuf_addstr(&buf, \"    \");\n-\t\tstrbuf_addstr(&buf, extra_cc.items[i].string);\n-\t\tif (i + 1 < extra_cc.nr)\n-\t\t\tstrbuf_addch(&buf, ',');\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t}\n-\n-\trev.extra_headers = strbuf_detach(&buf, NULL);\n-\n \tif (from) {\n \t\tif (split_ident_line(&rev.from_ident, from, strlen(from)))\n \t\t\tdie(_(\"invalid ident line: %s\"), from);\n@@ -1798,6 +1769,35 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \t}\n \n+\tfor (i = 0; i < extra_hdr.nr; i++) {\n+\t\tstrbuf_addstr(&buf, extra_hdr.items[i].string);\n+\t\tstrbuf_addch(&buf, '\\n');\n+\t}\n+\n+\tif (extra_to.nr)\n+\t\tstrbuf_addstr(&buf, \"To: \");\n+\tfor (i = 0; i < extra_to.nr; i++) {\n+\t\tif (i)\n+\t\t\tstrbuf_addstr(&buf, \"    \");\n+\t\tstrbuf_addstr(&buf, extra_to.items[i].string);\n+\t\tif (i + 1 < extra_to.nr)\n+\t\t\tstrbuf_addch(&buf, ',');\n+\t\tstrbuf_addch(&buf, '\\n');\n+\t}\n+\n+\tif (extra_cc.nr)\n+\t\tstrbuf_addstr(&buf, \"Cc: \");\n+\tfor (i = 0; i < extra_cc.nr; i++) {\n+\t\tif (i)\n+\t\t\tstrbuf_addstr(&buf, \"    \");\n+\t\tstrbuf_addstr(&buf, extra_cc.items[i].string);\n+\t\tif (i + 1 < extra_cc.nr)\n+\t\t\tstrbuf_addch(&buf, ',');\n+\t\tstrbuf_addch(&buf, '\\n');\n+\t}\n+\n+\trev.extra_headers = strbuf_detach(&buf, NULL);\n+\n \t/*\n \t * We cannot move this anywhere earlier because we do want to\n \t * know if --root was given explicitly from the command line.\n-- \n2.22.0.355.g3bfa262345\n\n"},{"id":"377266","messageId":"6ad1b258584d699aa49cdda39e41126ef5621343.1560547501.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1560547500.git.liu.denton@gmail.com","subject":"[RESEND PATCH v3 6/8] format-patch: read branch-specific To: and Cc: headers","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-06-14T21:56:25Z","receivedAt":"2019-06-14T21:56:30Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"If a user wishes to keep track of whom to Cc: on individual patchsets,\nthey must manually keep track of each recipient and fill it in with the\n`--cc` option on git-format-patch each time. However, on the Git mailing\nlist, Cc:'s are typically never dropped. As a result, it would be nice\nto have a method to keep track of recipients on a per-branch basis.\n\nCurrently, git-format-patch gets its To: headers from the `--to` options\nand the `format.to` config variable. The Cc: header is derived\nsimilarly.\n\nIn addition to the above, read To: and Cc: headers from\n`format.<branch-name>.to` and `format.<branch-name>.cc` so that users\ncan have branch-specific configuration options.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/config/format.txt    |   7 +-\n Documentation/git-format-patch.txt |  12 +++-\n builtin/log.c                      |  63 +++++++++++++++--\n t/t4014-format-patch.sh            | 108 +++++++++++++++++++++++------\n 4 files changed, 162 insertions(+), 28 deletions(-)\n\ndiff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\nindex c8b28fe47f..95e255347a 100644\n--- a/Documentation/config/format.txt\n+++ b/Documentation/config/format.txt\n@@ -28,9 +28,12 @@ format.headers::\n \n format.to::\n format.cc::\n+format.<branch-name>.to::\n+format.<branch-name>.cc::\n \tAdditional recipients to include in a patch to be submitted\n-\tby mail.  See the --to and --cc options in\n-\tlinkgit:git-format-patch[1].\n+\tby mail.  For the <branch-name> options, the recipients are only\n+\tincluded if patches are generated for the given <branch-name>.\n+\tSee the --to and --cc options in linkgit:git-format-patch[1].\n \n format.subjectPrefix::\n \tThe default for format-patch is to output files with the '[PATCH]'\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex f54aecf4bf..76e61b746a 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -203,7 +203,7 @@ the description up to and excluding the first blank line.\n \tAdd a `To:` header to the email headers. This is in addition\n \tto any configured headers, and may be used multiple times. The\n \temails given will be used along with any emails given by\n-\t`format.to` configurations.\n+\t`format.to` and `format.<branch-name>.to` configurations.\n \tThe negated form `--no-to` discards all `To:` headers added so\n \tfar (from config or command line).\n \n@@ -211,7 +211,7 @@ the description up to and excluding the first blank line.\n \tAdd a `Cc:` header to the email headers. This is in addition\n \tto any configured headers, and may be used multiple times. The\n \temails given will be used along with any emails given by\n-\t`format.cc` configurations.\n+\t`format.cc` and `format.<branch-name>.cc` configurations.\n \tThe negated form `--no-cc` discards all `Cc:` headers added so\n \tfar (from config or command line).\n \n@@ -358,6 +358,14 @@ with configuration variables.\n \tinferCoverSubject = true\n ------------\n \n+In addition, for a specific branch, you can specify a custom cover\n+letter subject, and add additional \"To:\" or \"Cc:\" headers.\n+\n+------------\n+[format \"branch-name\"]\n+\tto = <email>\n+\tcc = <email>\n+------------\n \n DISCUSSION\n ----------\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 3f97f344df..cb9ccdc59c 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -738,6 +738,8 @@ static char *default_attach = NULL;\n static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;\n static struct string_list extra_to = STRING_LIST_INIT_NODUP;\n static struct string_list extra_cc = STRING_LIST_INIT_NODUP;\n+int to_cleared;\n+int cc_cleared;\n \n static void add_header(const char *value)\n {\n@@ -1021,6 +1023,55 @@ static void show_diffstat(struct rev_info *rev,\n \tfprintf(rev->diffopt.file, \"\\n\");\n }\n \n+static void add_branch_headers(struct rev_info *rev, const char *branch_name)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst struct string_list *values;\n+\n+\tif (!branch_name)\n+\t\tbranch_name = find_branch_name(rev);\n+\n+\tif (!branch_name || !*branch_name)\n+\t\treturn;\n+\n+\t/*\n+\t * HACK: We only use branch-specific recipients iff the list has not\n+\t * been cleared by an earlier --no-{to,cc} option on the command-line.\n+\t *\n+\t * When we get format.{to,cc} options, they can be cleared by\n+\t * --no-{to,cc} options since the `git_config` call comes before the\n+\t *  `parse_options` call.\n+\t *\n+\t *  However, in the case of branch.<name>.{to,cc}, this function needs\n+\t *  to be called after `setup_revisions`, which must be called after\n+\t *  `parse_options`. However, in order for the --no-{to,cc} logic to\n+\t *  clear the extra_{to,cc} string_list, this function should actually\n+\t *  be called _before_ `parse_options`. As a result, we have a circular\n+\t *  dependency.\n+\t *\n+\t *  The {to,cc}_cleared flag lets us workaround this by just no\n+\t *  including branch-specific recipients iff --no-{to,cc} has been\n+\t *  specified on the command-line.\n+\t */\n+\n+\tif (!to_cleared) {\n+\t\tstrbuf_addf(&buf, \"format.%s.to\", branch_name);\n+\t\tvalues = git_config_get_value_multi(buf.buf);\n+\t\tif (values)\n+\t\t\tstring_list_append_all(&extra_to, values);\n+\t}\n+\n+\tif (!cc_cleared) {\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addf(&buf, \"format.%s.cc\", branch_name);\n+\t\tvalues = git_config_get_value_multi(buf.buf);\n+\t\tif (values)\n+\t\t\tstring_list_append_all(&extra_cc, values);\n+\t}\n+\n+\tstrbuf_release(&buf);\n+}\n+\n static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \t\t\t      struct commit *origin,\n \t\t\t      int nr, struct commit **list,\n@@ -1293,18 +1344,20 @@ static int header_callback(const struct option *opt, const char *arg, int unset)\n \n static int to_callback(const struct option *opt, const char *arg, int unset)\n {\n-\tif (unset)\n+\tif (unset) {\n+\t\tto_cleared = 1;\n \t\tstring_list_clear(&extra_to, 0);\n-\telse\n+\t} else\n \t\tstring_list_append(&extra_to, arg);\n \treturn 0;\n }\n \n static int cc_callback(const struct option *opt, const char *arg, int unset)\n {\n-\tif (unset)\n+\tif (unset) {\n+\t\tcc_cleared = 1;\n \t\tstring_list_clear(&extra_cc, 0);\n-\telse\n+\t} else\n \t\tstring_list_append(&extra_cc, arg);\n \treturn 0;\n }\n@@ -1769,6 +1822,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \t}\n \n+\tadd_branch_headers(&rev, branch_name);\n+\n \tfor (i = 0; i < extra_hdr.nr; i++) {\n \t\tstrbuf_addstr(&buf, extra_hdr.items[i].string);\n \t\tstrbuf_addch(&buf, '\\n');\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 4cb6b9edfa..23c467e95b 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -234,6 +234,65 @@ test_expect_failure 'configuration To: header (rfc2047)' '\n \tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs9\n '\n \n+test_expect_success 'branch-specific configuration To: header (ascii)' '\n+\n+\ttest_unconfig format.to &&\n+\tgit config format.side.to \"R E Cipient <rcipient@example.com>\" &&\n+\tgit format-patch --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tgrep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs10\n+'\n+\n+test_expect_failure 'branch-specific configuration To: header (rfc822)' '\n+\n+\tgit config format.side.to \"R. E. Cipient <rcipient@example.com>\" &&\n+\tgit format-patch --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tgrep \"^To: \\\"R. E. Cipient\\\" <rcipient@example.com>\\$\" hdrs10\n+'\n+\n+test_expect_failure 'branch-specific configuration To: header (rfc2047)' '\n+\n+\tgit config format.side.to \"R Ä Cipient <rcipient@example.com>\" &&\n+\tgit format-patch --stdout master..side >patch10 &&\n+\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n+\tgrep \"^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\\$\" hdrs10\n+'\n+\n+test_expect_success 'all recipients included from all sources' '\n+\n+\tgit config format.to \"Format To1 <formatto1@example.com>\" &&\n+\tgit config --add format.to \"Format To2 <formatto2@example.com>\" &&\n+\tgit config format.cc \"Format Cc1 <formatcc1@example.com>\" &&\n+\tgit config --add format.cc \"Format Cc2 <formatcc2@example.com>\" &&\n+\tgit config format.side.to \"Branch To1 <branchto1@example.com>\" &&\n+\tgit config --add format.side.to \"Branch To2 <branchto2@example.com>\" &&\n+\tgit config format.side.cc \"Branch Cc1 <branchcc1@example.com>\" &&\n+\tgit config --add format.side.cc \"Branch Cc2 <branchcc2@example.com>\" &&\n+\tcat <<-\\EOF >expect &&\n+\tTo: Format To1 <formatto1@example.com>,\n+\t    Format To2 <formatto2@example.com>,\n+\t    Command-line To1 <commandlineto1@example.com>,\n+\t    Command-line To2 <commandlineto2@example.com>,\n+\t    Branch To1 <branchto1@example.com>,\n+\t    Branch To2 <branchto2@example.com>\n+\tCc: Format Cc1 <formatcc1@example.com>,\n+\t    Format Cc2 <formatcc2@example.com>,\n+\t    Command-line Cc1 <commandlinecc1@example.com>,\n+\t    Command-line Cc2 <commandlinecc2@example.com>,\n+\t    Branch Cc1 <branchcc1@example.com>,\n+\t    Branch Cc2 <branchcc2@example.com>\n+\n+\tEOF\n+\tgit format-patch --stdout \\\n+\t\t--to=\"Command-line To1 <commandlineto1@example.com>\" \\\n+\t\t--to=\"Command-line To2 <commandlineto2@example.com>\" \\\n+\t\t--cc=\"Command-line Cc1 <commandlinecc1@example.com>\" \\\n+\t\t--cc=\"Command-line Cc2 <commandlinecc2@example.com>\" \\\n+\t\tmaster..side | sed -ne \"/^To:/,/^$/p;/^$/q\" >patch10 &&\n+\ttest_cmp expect patch10\n+'\n+\n # check_patch <patch>: Verify that <patch> looks like a half-sane\n # patch email to avoid a false positive with !grep\n check_patch () {\n@@ -286,42 +345,51 @@ test_expect_success '--no-to overrides config.to' '\n \n \tgit config --replace-all format.to \\\n \t\t\"R E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-to --stdout master..side >patch10 &&\n-\tsed -e \"/^\\$/q\" patch10 >hdrs10 &&\n-\tcheck_patch hdrs10 &&\n-\t! grep \"^To: R E Cipient <rcipient@example.com>\\$\" hdrs10\n+\tgit config --replace-all format.side.to \\\n+\t\t\"B R Anch <branch@example.com>\" &&\n+\tgit format-patch --no-to --stdout master..side >patch11 &&\n+\tsed -e \"/^\\$/q\" patch11 >hdrs11 &&\n+\tcheck_patch hdrs11 &&\n+\t! grep \"R E Cipient <rcipient@example.com>\" hdrs11 &&\n+\t! grep \"B R Anch <branch@example.com>\" hdrs11\n '\n \n test_expect_success '--no-to and --to replaces config.to' '\n \n \tgit config --replace-all format.to \\\n \t\t\"Someone <someone@out.there>\" &&\n+\tgit config --replace-all format.side.to \\\n+\t\t\"B R Anch2 <branch2@example.com>\" &&\n \tgit format-patch --no-to --to=\"Someone Else <else@out.there>\" \\\n-\t\t--stdout master..side >patch11 &&\n-\tsed -e \"/^\\$/q\" patch11 >hdrs11 &&\n-\tcheck_patch hdrs11 &&\n-\t! grep \"^To: Someone <someone@out.there>\\$\" hdrs11 &&\n-\tgrep \"^To: Someone Else <else@out.there>\\$\" hdrs11\n+\t\t--stdout master..side >patch12 &&\n+\tsed -e \"/^\\$/q\" patch12 >hdrs12 &&\n+\tcheck_patch hdrs12 &&\n+\t! grep \"Someone <someone@out.there>\" hdrs12 &&\n+\t! grep \"B R Anch2 <branch2@example.com>\" hdrs12 &&\n+\tgrep \"^To: Someone Else <else@out.there>\\$\" hdrs12\n '\n \n test_expect_success '--no-cc overrides config.cc' '\n \n \tgit config --replace-all format.cc \\\n \t\t\"C E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-cc --stdout master..side >patch12 &&\n-\tsed -e \"/^\\$/q\" patch12 >hdrs12 &&\n-\tcheck_patch hdrs12 &&\n-\t! grep \"^Cc: C E Cipient <rcipient@example.com>\\$\" hdrs12\n+\tgit config --replace-all format.side.cc \\\n+\t\t\"B R Anch3 <branch3@example.com>\" &&\n+\tgit format-patch --no-cc --stdout master..side >patch13 &&\n+\tsed -e \"/^\\$/q\" patch13 >hdrs13 &&\n+\tcheck_patch hdrs13 &&\n+\t! grep \"C E Cipient <rcipient@example.com>\" hdrs13 &&\n+\t! grep \"B R Anch3 <branch3@example.com>\" hdrs13\n '\n \n test_expect_success '--no-add-header overrides config.headers' '\n \n \tgit config --replace-all format.headers \\\n \t\t\"Header1: B E Cipient <rcipient@example.com>\" &&\n-\tgit format-patch --no-add-header --stdout master..side >patch13 &&\n-\tsed -e \"/^\\$/q\" patch13 >hdrs13 &&\n-\tcheck_patch hdrs13 &&\n-\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" hdrs13\n+\tgit format-patch --no-add-header --stdout master..side >patch14 &&\n+\tsed -e \"/^\\$/q\" patch14 >hdrs14 &&\n+\tcheck_patch hdrs14 &&\n+\t! grep \"^Header1: B E Cipient <rcipient@example.com>\\$\" hdrs14\n '\n \n test_expect_success 'multiple files' '\n@@ -963,7 +1031,7 @@ test_expect_success 'format-patch wraps extremely long subject (ascii)' '\n \tgit add file &&\n \tgit commit -m \"$M512\" &&\n \tgit format-patch --stdout -1 >patch &&\n-\tsed -n \"/^Subject/p; /^ /p; /^$/q\" <patch >subject &&\n+\tsed -n \"/^Subject/p; /^ /p; /^Header1:/q\" <patch >subject &&\n \ttest_cmp expect subject\n '\n \n@@ -1002,7 +1070,7 @@ test_expect_success 'format-patch wraps extremely long subject (rfc2047)' '\n \tgit add file &&\n \tgit commit -m \"$M512\" &&\n \tgit format-patch --stdout -1 >patch &&\n-\tsed -n \"/^Subject/p; /^ /p; /^$/q\" <patch >subject &&\n+\tsed -n \"/^Subject/p; /^ /p; /^Header1:/q\" <patch >subject &&\n \ttest_cmp expect subject\n '\n \n@@ -1011,7 +1079,7 @@ check_author() {\n \tgit add file &&\n \tGIT_AUTHOR_NAME=$1 git commit -m author-check &&\n \tgit format-patch --stdout -1 >patch &&\n-\tsed -n \"/^From: /p; /^ /p; /^$/q\" <patch >actual &&\n+\tsed -n \"/^From: /p; /^ /p; /^Date:/q\" <patch >actual &&\n \ttest_cmp expect actual\n }\n \n-- \n2.22.0.355.g3bfa262345\n\n"},{"id":"377267","messageId":"3554164deb27c93069ebe2ed9ee9cce7f55c242f.1560547501.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1560547500.git.liu.denton@gmail.com","subject":"[RESEND PATCH v3 5/8] string-list: create string_list_append_all","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-06-14T21:56:23Z","receivedAt":"2019-06-14T21:56:31Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future patch, we'll need to take one string_list and append it to\nthe end of another. Create the `string_list_append_all` function which\ndoes this.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n string-list.c | 9 +++++++++\n string-list.h | 7 +++++++\n 2 files changed, 16 insertions(+)\n\ndiff --git a/string-list.c b/string-list.c\nindex a917955fbd..e63d58fbd2 100644\n--- a/string-list.c\n+++ b/string-list.c\n@@ -215,6 +215,15 @@ struct string_list_item *string_list_append(struct string_list *list,\n \t\t\tlist->strdup_strings ? xstrdup(string) : (char *)string);\n }\n \n+void string_list_append_all(struct string_list *list,\n+\t\t\t    const struct string_list *append_list)\n+{\n+\tstruct string_list_item *item;\n+\tALLOC_GROW(list->items, list->nr + append_list->nr, list->alloc);\n+\tfor_each_string_list_item(item, append_list)\n+\t\tstring_list_append(list, item->string);\n+}\n+\n /*\n  * Encapsulate the compare function pointer because ISO C99 forbids\n  * casting from void * to a function pointer and vice versa.\ndiff --git a/string-list.h b/string-list.h\nindex f964399949..8227e00e6a 100644\n--- a/string-list.h\n+++ b/string-list.h\n@@ -208,6 +208,13 @@ struct string_list_item *string_list_append(struct string_list *list, const char\n  */\n struct string_list_item *string_list_append_nodup(struct string_list *list, char *string);\n \n+/**\n+ * Add all strings in append_list to list.  If list->strdup_string is\n+ * set, then each string is copied; otherwise the new string_list_entry\n+ * refers to the entry in the append_list.\n+ */\n+void string_list_append_all(struct string_list *list, const struct string_list *append_list);\n+\n /**\n  * Sort the list's entries by string value in `strcmp()` order.\n  */\n-- \n2.22.0.355.g3bfa262345\n\n"},{"id":"377268","messageId":"2cf7c77f5946f91659941c550b9599815d87c7b4.1560547501.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1560547500.git.liu.denton@gmail.com","subject":"[RESEND PATCH v3 8/8] format-patch: read branch-specific output directory","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-06-14T21:56:29Z","receivedAt":"2019-06-14T21:56:35Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"If a user wishes to have a per-branch output directory for patches, they\nmust manually specify this on the command-line with `-o` for each\ninvocation of format-patch. However, this can be cumbersome for a user\nto keep track of.\n\nRead `format.<branch-name>.outputDirectory` to give a branch-specific\noutput directory that would override `format.outputDirectory`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/config/format.txt    |  5 ++++-\n Documentation/git-format-patch.txt |  3 ++-\n builtin/log.c                      | 26 ++++++++++++++++++++++++--\n t/t4014-format-patch.sh            | 18 +++++++++++++++---\n 4 files changed, 45 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\nindex 95e255347a..df67a83183 100644\n--- a/Documentation/config/format.txt\n+++ b/Documentation/config/format.txt\n@@ -87,8 +87,11 @@ format.coverLetter::\n \tgenerate a cover-letter only when there's more than one patch.\n \n format.outputDirectory::\n+format.<branch-name>.outputDirectory::\n \tSet a custom directory to store the resulting files instead of the\n-\tcurrent working directory.\n+\tcurrent working directory. If patches are being generated for\n+\t<branch-name>, the latter option takes priority if it exists,\n+\totherwise we will fallback to the former.\n \n format.useAutoBase::\n \tA boolean value which lets you enable the `--base=auto` option of\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 707b4bdc6b..346f1229d8 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -359,12 +359,13 @@ with configuration variables.\n ------------\n \n In addition, for a specific branch, you can add additional \"To:\" or\n-\"Cc:\" headers.\n+\"Cc:\" headers and change the patch output directory.\n \n ------------\n [format \"branch-name\"]\n \tto = <email>\n \tcc = <email>\n+\toutputDirectory = <directory>\n ------------\n \n DISCUSSION\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 97980881ec..a1fe8b994a 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1023,6 +1023,24 @@ static void show_diffstat(struct rev_info *rev,\n \tfprintf(rev->diffopt.file, \"\\n\");\n }\n \n+static const char *get_branch_output_dir(struct rev_info *rev, const char *branch_name)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *output_directory;\n+\n+\tif (!branch_name)\n+\t\tbranch_name = find_branch_name(rev);\n+\n+\tif (!branch_name || !*branch_name)\n+\t\treturn NULL;\n+\n+\tstrbuf_addf(&buf, \"format.%s.outputdirectory\", branch_name);\n+\tgit_config_get_string_const(buf.buf, &output_directory);\n+\tstrbuf_release(&buf);\n+\n+\treturn output_directory;\n+}\n+\n static void add_branch_headers(struct rev_info *rev, const char *branch_name)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -1804,8 +1822,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \t}\n \n-\tif (!output_directory && !use_stdout)\n-\t\toutput_directory = config_output_directory;\n+\tif (!use_stdout) {\n+\t\tif (!output_directory)\n+\t\t\toutput_directory = get_branch_output_dir(&rev, branch_name);\n+\t\tif (!output_directory)\n+\t\t\toutput_directory = config_output_directory;\n+\t}\n \n \tif (!use_stdout)\n \t\toutput_directory = set_outdir(prefix, output_directory);\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 23c467e95b..147934922c 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -1667,12 +1667,24 @@ test_expect_success 'format-patch format.outputDirectory option' '\n \ttest_line_count = $(ls patches | wc -l) list\n '\n \n-test_expect_success 'format-patch -o overrides format.outputDirectory' '\n+test_expect_success 'format-patch format.side.outputDirectory option overrides format.outputDirectory' '\n \ttest_config format.outputDirectory patches &&\n-\trm -fr patches patchset &&\n+\ttest_config format.side.outputDirectory sidepatches &&\n+\trm -fr patches sidepatches &&\n+\tgit format-patch master..side &&\n+\tgit rev-list master..side >list &&\n+\ttest_path_is_missing patches &&\n+\ttest_line_count = $(ls sidepatches | wc -l) list\n+'\n+\n+test_expect_success 'format-patch -o overrides format.outputDirectory and format.side.outputDirectory' '\n+\ttest_config format.outputDirectory patches &&\n+\ttest_config format.side.outputDirectory sidepatches &&\n+\trm -fr patches sidepatches patchset &&\n \tgit format-patch master..side -o patchset &&\n \ttest_path_is_missing patches &&\n-\ttest_path_is_dir patchset\n+\ttest_path_is_missing sidepatches &&\n+\ttest_line_count = $(ls patchset | wc -l) list\n '\n \n test_expect_success 'format-patch --base' '\n-- \n2.22.0.355.g3bfa262345\n\n"},{"id":"377269","messageId":"3bc1b95a4e34a0957e1c4f671d1424448d4dbe7a.1560547501.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1560547500.git.liu.denton@gmail.com","subject":"[RESEND PATCH v3 7/8] format-patch: move output_directory logic later","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-06-14T21:56:27Z","receivedAt":"2019-06-14T21:56:38Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future patch, we need to create the output_directory after the\nbranch_name logic. Simply transpose this logic later in the function so\nthat this happens. (This patch is best viewed with `git diff\n--color-moved`.)\n\nNote that this logic only depends on `git_config` and the parseopt logic\nand is depended on by the patch creation logic which is directly below\nit so this move is effectively a no-op as no dependencies being\nreordered.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/git-format-patch.txt |  4 ++--\n builtin/log.c                      | 36 +++++++++++++++---------------\n 2 files changed, 20 insertions(+), 20 deletions(-)\n\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 76e61b746a..707b4bdc6b 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -358,8 +358,8 @@ with configuration variables.\n \tinferCoverSubject = true\n ------------\n \n-In addition, for a specific branch, you can specify a custom cover\n-letter subject, and add additional \"To:\" or \"Cc:\" headers.\n+In addition, for a specific branch, you can add additional \"To:\" or\n+\"Cc:\" headers.\n \n ------------\n [format \"branch-name\"]\ndiff --git a/builtin/log.c b/builtin/log.c\nindex cb9ccdc59c..97980881ec 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1771,24 +1771,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tif (rev.show_notes)\n \t\tinit_display_notes(&rev.notes_opt);\n \n-\tif (!output_directory && !use_stdout)\n-\t\toutput_directory = config_output_directory;\n-\n-\tif (!use_stdout)\n-\t\toutput_directory = set_outdir(prefix, output_directory);\n-\telse\n-\t\tsetup_pager();\n-\n-\tif (output_directory) {\n-\t\tif (rev.diffopt.use_color != GIT_COLOR_ALWAYS)\n-\t\t\trev.diffopt.use_color = GIT_COLOR_NEVER;\n-\t\tif (use_stdout)\n-\t\t\tdie(_(\"standard output, or directory, which one?\"));\n-\t\tif (mkdir(output_directory, 0777) < 0 && errno != EEXIST)\n-\t\t\tdie_errno(_(\"could not create directory '%s'\"),\n-\t\t\t\t  output_directory);\n-\t}\n-\n \tif (rev.pending.nr == 1) {\n \t\tint check_head = 0;\n \n@@ -1822,6 +1804,24 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \t}\n \n+\tif (!output_directory && !use_stdout)\n+\t\toutput_directory = config_output_directory;\n+\n+\tif (!use_stdout)\n+\t\toutput_directory = set_outdir(prefix, output_directory);\n+\telse\n+\t\tsetup_pager();\n+\n+\tif (output_directory) {\n+\t\tif (rev.diffopt.use_color != GIT_COLOR_ALWAYS)\n+\t\t\trev.diffopt.use_color = GIT_COLOR_NEVER;\n+\t\tif (use_stdout)\n+\t\t\tdie(_(\"standard output, or directory, which one?\"));\n+\t\tif (mkdir(output_directory, 0777) < 0 && errno != EEXIST)\n+\t\t\tdie_errno(_(\"could not create directory '%s'\"),\n+\t\t\t\t  output_directory);\n+\t}\n+\n \tadd_branch_headers(&rev, branch_name);\n \n \tfor (i = 0; i < extra_hdr.nr; i++) {\n-- \n2.22.0.355.g3bfa262345\n\n"},{"id":"384070","messageId":"cover.1571130298.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1558492582.git.liu.denton@gmail.com","subject":"[PATCH v6 0/3] format-patch: learn --infer-cover-subject option (also t4014 cleanup)","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-10-15T09:06:31Z","receivedAt":"2019-10-15T09:06:37Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Currently, format-patch only puts \"*** SUBJECT HERE ***\" when a cover\nletter is generated. However, it is already smart enough to be able to\npopulate the cover letter with the branch description so there's no\nreason why it cannot populate the subject as well.\n\nTeach format-patch the `--infer-cover-subject` option and corresponding\n`format.inferCoverSubject` configuration option which will read the\nsubject from the branch description using the same rules as for a commit\nmessage (that is, it will expect a subject line followed by a blank\nline).\n\nThis was based on patches 1-3 of an earlier patchset I sent[1].\n\nChanges since v5:\n\n* Remove speculation in log message of 1/3\n\n* Rename pp_from_desc() to prepare_cover_text()\n\nChanges since v4:\n\n* Modify 1/3 to more closely reflect intent of the original author\n\n* Incorporate Junio's suggestions into the documentation\n\n* Extract branch desc logic into pp_from_desc()\n\n* Fix broken tests\n\nChanges since v3:\n\n* Change --infer-cover-subject to --cover-from-description\n\n* No more test cleanup patches (they were merged in\n  'dl/format-patch-doc-test-cleanup')\n\nChanges since v2:\n\n* Break 1/4 into many different patches (one per paragraph of the\n  original patch)\n\n* Incorporate Eric's documentation/commit message suggestions\n\nChanges since v1:\n\n* Incorporate Eric's suggestions for cleanup in all patches\n\n* Add patch 3/4 to make it clear what is the default value for\n  format.coverLetter (since format.inferCoverSubject was borrowed from\n  this config but it also did not state what the default value was)\n\n* In 1/4, rename all instances of \"expected\" to \"expect\"\n\n[1]: https://public-inbox.org/git/cover.1558492582.git.liu.denton@gmail.com/\n\nDenton Liu (3):\n  format-patch: replace erroneous and condition\n  format-patch: use enum variables\n  format-patch: teach --cover-from-description option\n\n Documentation/config/format.txt    |   6 +\n Documentation/git-format-patch.txt |  22 ++++\n builtin/log.c                      | 125 +++++++++++++++------\n t/t4014-format-patch.sh            | 172 +++++++++++++++++++++++++++++\n t/t9902-completion.sh              |   5 +-\n 5 files changed, 296 insertions(+), 34 deletions(-)\n\nRange-diff against v5:\n1:  f89e56545b ! 1:  9d41068e73 format-patch: change erroneous and condition\n    @@ Metadata\n     Author: Denton Liu <liu.denton@gmail.com>\n     \n      ## Commit message ##\n    -    format-patch: change erroneous and condition\n    +    format-patch: replace erroneous and condition\n     \n         Commit 30984ed2e9 (format-patch: support deep threading, 2009-02-19),\n         introduced the following lines:\n    @@ Commit message\n                 thread = git_config_bool(var, value) && THREAD_SHALLOW;\n     \n         Since git_config_bool() returns a bool, the trailing `&& THREAD_SHALLOW`\n    -    is a no-op.\n    -\n    -    In Python, `x and y` is equivalent to `y if x else x`[1]. Since this\n    -    seems to be a Python-ism that's mistakenly leaked into our code, convert\n    -    this to the equivalent C expression.\n    -\n    -    [1]: https://docs.python.org/3/reference/expressions.html#boolean-operations\n    -\n    -    Signed-off-by: Denton Liu <liu.denton@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    is a no-op. Replace this errorneous and condition with a ternary\n    +    statement so that it is clear what the configured value is when a\n    +    boolean is given.\n     \n      ## builtin/log.c ##\n     @@ builtin/log.c: static int git_format_config(const char *var, const char *value, void *cb)\n2:  1fe780b5a4 = 2:  821e706bae format-patch: use enum variables\n3:  d5cd34a44b ! 3:  42b4a60fd2 format-patch: teach --cover-from-description option\n    @@ builtin/log.c: static void show_diffstat(struct rev_info *rev,\n      \tfprintf(rev->diffopt.file, \"\\n\");\n      }\n      \n    -+static void pp_from_desc(struct pretty_print_context *pp,\n    -+\t\t\t const char *branch_name,\n    -+\t\t\t struct strbuf *sb,\n    -+\t\t\t const char *encoding,\n    -+\t\t\t int need_8bit_cte)\n    ++static void prepare_cover_text(struct pretty_print_context *pp,\n    ++\t\t\t       const char *branch_name,\n    ++\t\t\t       struct strbuf *sb,\n    ++\t\t\t       const char *encoding,\n    ++\t\t\t       int need_8bit_cte)\n     +{\n     +\tconst char *subject = \"*** SUBJECT HERE ***\";\n     +\tconst char *body = \"*** BLURB HERE ***\";\n    @@ builtin/log.c: static void make_cover_letter(struct rev_info *rev, int use_stdou\n     -\tpp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);\n     -\tpp_remainder(&pp, &msg, &sb, 0);\n     -\tadd_branch_description(&sb, branch_name);\n    -+\tpp_from_desc(&pp, branch_name, &sb, encoding, need_8bit_cte);\n    ++\tprepare_cover_text(&pp, branch_name, &sb, encoding, need_8bit_cte);\n      \tfprintf(rev->diffopt.file, \"%s\\n\", sb.buf);\n      \n      \tstrbuf_release(&sb);\n-- \n2.23.0.17.gd2208d9060\n\n"},{"id":"384071","messageId":"9d41068e73f54cbac82267a6ebd2f13cf343c076.1571130298.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1571130298.git.liu.denton@gmail.com","subject":"[PATCH v6 1/3] format-patch: replace erroneous and condition","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-10-15T09:06:35Z","receivedAt":"2019-10-15T09:06:40Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Commit 30984ed2e9 (format-patch: support deep threading, 2009-02-19),\nintroduced the following lines:\n\n\t#define THREAD_SHALLOW 1\n\n\t[...]\n\n\tthread = git_config_bool(var, value) && THREAD_SHALLOW;\n\nSince git_config_bool() returns a bool, the trailing `&& THREAD_SHALLOW`\nis a no-op. Replace this errorneous and condition with a ternary\nstatement so that it is clear what the configured value is when a\nboolean is given.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/log.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 44b10b3415..351f4ffcfd 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -835,7 +835,7 @@ static int git_format_config(const char *var, const char *value, void *cb)\n \t\t\tthread = THREAD_SHALLOW;\n \t\t\treturn 0;\n \t\t}\n-\t\tthread = git_config_bool(var, value) && THREAD_SHALLOW;\n+\t\tthread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET;\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"format.signoff\")) {\n-- \n2.23.0.17.gd2208d9060\n\n"},{"id":"384072","messageId":"821e706bae6fd15a3d41022934ee0b290bb47ca5.1571130298.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1571130298.git.liu.denton@gmail.com","subject":"[PATCH v6 2/3] format-patch: use enum variables","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-10-15T09:06:37Z","receivedAt":"2019-10-15T09:06:42Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Before, `thread` and `config_cover_letter` were defined as ints even\nthough they behaved as enums. Define actual enums and change these\nvariables to use these new definitions.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/log.c | 30 +++++++++++++++++-------------\n 1 file changed, 17 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 351f4ffcfd..d212a8305d 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -764,24 +764,28 @@ static void add_header(const char *value)\n \titem->string[len] = '\\0';\n }\n \n-#define THREAD_SHALLOW 1\n-#define THREAD_DEEP 2\n-static int thread;\n+enum cover_setting {\n+\tCOVER_UNSET,\n+\tCOVER_OFF,\n+\tCOVER_ON,\n+\tCOVER_AUTO\n+};\n+\n+enum thread_level {\n+\tTHREAD_UNSET,\n+\tTHREAD_SHALLOW,\n+\tTHREAD_DEEP\n+};\n+\n+static enum thread_level thread;\n static int do_signoff;\n static int base_auto;\n static char *from;\n static const char *signature = git_version_string;\n static const char *signature_file;\n-static int config_cover_letter;\n+static enum cover_setting config_cover_letter;\n static const char *config_output_directory;\n \n-enum {\n-\tCOVER_UNSET,\n-\tCOVER_OFF,\n-\tCOVER_ON,\n-\tCOVER_AUTO\n-};\n-\n static int git_format_config(const char *var, const char *value, void *cb)\n {\n \tstruct rev_info *rev = cb;\n@@ -1248,9 +1252,9 @@ static int output_directory_callback(const struct option *opt, const char *arg,\n \n static int thread_callback(const struct option *opt, const char *arg, int unset)\n {\n-\tint *thread = (int *)opt->value;\n+\tenum thread_level *thread = (enum thread_level *)opt->value;\n \tif (unset)\n-\t\t*thread = 0;\n+\t\t*thread = THREAD_UNSET;\n \telse if (!arg || !strcmp(arg, \"shallow\"))\n \t\t*thread = THREAD_SHALLOW;\n \telse if (!strcmp(arg, \"deep\"))\n-- \n2.23.0.17.gd2208d9060\n\n"},{"id":"384073","messageId":"42b4a60fd29678f2de62c784027b9d355a913397.1571130298.git.liu.denton@gmail.com","threadId":"51038","inReplyTo":"cover.1571130298.git.liu.denton@gmail.com","subject":"[PATCH v6 3/3] format-patch: teach --cover-from-description option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-10-15T09:06:40Z","receivedAt":"2019-10-15T09:06:45Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Before, when format-patch generated a cover letter, only the body would\nbe populated with a branch's description while the subject would be\npopulated with placeholder text. However, users may want to have the\nsubject of their cover letter automatically populated in the same way.\n\nTeach format-patch to accept the `--cover-from-description` option and\ncorresponding `format.coverFromDescription` config, allowing users to\npopulate different parts of the cover letter (including the subject\nnow).\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config/format.txt    |   6 +\n Documentation/git-format-patch.txt |  22 ++++\n builtin/log.c                      |  95 ++++++++++++----\n t/t4014-format-patch.sh            | 172 +++++++++++++++++++++++++++++\n t/t9902-completion.sh              |   5 +-\n 5 files changed, 279 insertions(+), 21 deletions(-)\n\ndiff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\nindex cb629fa769..735dfcf827 100644\n--- a/Documentation/config/format.txt\n+++ b/Documentation/config/format.txt\n@@ -36,6 +36,12 @@ format.subjectPrefix::\n \tThe default for format-patch is to output files with the '[PATCH]'\n \tsubject prefix. Use this variable to change that prefix.\n \n+format.coverFromDescription::\n+\tThe default mode for format-patch to determine which parts of\n+\tthe cover letter will be populated using the branch's\n+\tdescription. See the `--cover-from-description` option in\n+\tlinkgit:git-format-patch[1].\n+\n format.signature::\n \tThe default for format-patch is to output a signature containing\n \tthe Git version number. Use this variable to change that default.\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 0ac56f4b70..6800e1ab9a 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -19,6 +19,7 @@ SYNOPSIS\n \t\t   [--start-number <n>] [--numbered-files]\n \t\t   [--in-reply-to=<message id>] [--suffix=.<sfx>]\n \t\t   [--ignore-if-in-upstream]\n+\t\t   [--cover-from-description=<mode>]\n \t\t   [--rfc] [--subject-prefix=<subject prefix>]\n \t\t   [(--reroll-count|-v) <n>]\n \t\t   [--to=<email>] [--cc=<email>]\n@@ -171,6 +172,26 @@ will want to ensure that threading is disabled for `git send-email`.\n \tpatches being generated, and any patch that matches is\n \tignored.\n \n+--cover-from-description=<mode>::\n+\tControls which parts of the cover letter will be automatically\n+\tpopulated using the branch's description.\n++\n+If `<mode>` is `message` or `default`, the cover letter subject will be\n+populated with placeholder text. The body of the cover letter will be\n+populated with the branch's description. This is the default mode when\n+no configuration nor command line option is specified.\n++\n+If `<mode>` is `subject`, the first paragraph of the branch description will\n+populate the cover letter subject. The remainder of the description will\n+populate the body of the cover letter.\n++\n+If `<mode>` is `auto`, if the first paragraph of the branch description\n+is greater than 100 bytes, then the mode will be `message`, otherwise\n+`subject` will be used.\n++\n+If `<mode>` is `none`, both the cover letter subject and body will be\n+populated with placeholder text.\n+\n --subject-prefix=<subject prefix>::\n \tInstead of the standard '[PATCH]' prefix in the subject\n \tline, instead use '[<subject prefix>]'. This\n@@ -347,6 +368,7 @@ with configuration variables.\n \tsignOff = true\n \toutputDirectory = <directory>\n \tcoverLetter = auto\n+\tcoverFromDescription = auto\n ------------\n \n \ndiff --git a/builtin/log.c b/builtin/log.c\nindex d212a8305d..04be559bd2 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -37,6 +37,7 @@\n #include \"range-diff.h\"\n \n #define MAIL_DEFAULT_WRAP 72\n+#define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100\n \n /* Set a default date-time format for git log (\"log.date\" config variable) */\n static const char *default_date_mode = NULL;\n@@ -777,6 +778,13 @@ enum thread_level {\n \tTHREAD_DEEP\n };\n \n+enum cover_from_description {\n+\tCOVER_FROM_NONE,\n+\tCOVER_FROM_MESSAGE,\n+\tCOVER_FROM_SUBJECT,\n+\tCOVER_FROM_AUTO\n+};\n+\n static enum thread_level thread;\n static int do_signoff;\n static int base_auto;\n@@ -785,6 +793,23 @@ static const char *signature = git_version_string;\n static const char *signature_file;\n static enum cover_setting config_cover_letter;\n static const char *config_output_directory;\n+static enum cover_from_description cover_from_description_mode = COVER_FROM_MESSAGE;\n+\n+static enum cover_from_description parse_cover_from_description(const char *arg)\n+{\n+\tif (!arg || !strcmp(arg, \"default\"))\n+\t\treturn COVER_FROM_MESSAGE;\n+\telse if (!strcmp(arg, \"none\"))\n+\t\treturn COVER_FROM_NONE;\n+\telse if (!strcmp(arg, \"message\"))\n+\t\treturn COVER_FROM_MESSAGE;\n+\telse if (!strcmp(arg, \"subject\"))\n+\t\treturn COVER_FROM_SUBJECT;\n+\telse if (!strcmp(arg, \"auto\"))\n+\t\treturn COVER_FROM_AUTO;\n+\telse\n+\t\tdie(_(\"%s: invalid cover from description mode\"), arg);\n+}\n \n static int git_format_config(const char *var, const char *value, void *cb)\n {\n@@ -891,6 +916,10 @@ static int git_format_config(const char *var, const char *value, void *cb)\n \t\t}\n \t\treturn 0;\n \t}\n+\tif (!strcmp(var, \"format.coverfromdescription\")) {\n+\t\tcover_from_description_mode = parse_cover_from_description(value);\n+\t\treturn 0;\n+\t}\n \n \treturn git_log_config(var, value, cb);\n }\n@@ -997,20 +1026,6 @@ static void print_signature(FILE *file)\n \tputc('\\n', file);\n }\n \n-static void add_branch_description(struct strbuf *buf, const char *branch_name)\n-{\n-\tstruct strbuf desc = STRBUF_INIT;\n-\tif (!branch_name || !*branch_name)\n-\t\treturn;\n-\tread_branch_desc(&desc, branch_name);\n-\tif (desc.len) {\n-\t\tstrbuf_addch(buf, '\\n');\n-\t\tstrbuf_addbuf(buf, &desc);\n-\t\tstrbuf_addch(buf, '\\n');\n-\t}\n-\tstrbuf_release(&desc);\n-}\n-\n static char *find_branch_name(struct rev_info *rev)\n {\n \tint i, positive = -1;\n@@ -1057,6 +1072,44 @@ static void show_diffstat(struct rev_info *rev,\n \tfprintf(rev->diffopt.file, \"\\n\");\n }\n \n+static void prepare_cover_text(struct pretty_print_context *pp,\n+\t\t\t       const char *branch_name,\n+\t\t\t       struct strbuf *sb,\n+\t\t\t       const char *encoding,\n+\t\t\t       int need_8bit_cte)\n+{\n+\tconst char *subject = \"*** SUBJECT HERE ***\";\n+\tconst char *body = \"*** BLURB HERE ***\";\n+\tstruct strbuf description_sb = STRBUF_INIT;\n+\tstruct strbuf subject_sb = STRBUF_INIT;\n+\n+\tif (cover_from_description_mode == COVER_FROM_NONE)\n+\t\tgoto do_pp;\n+\n+\tif (branch_name && *branch_name)\n+\t\tread_branch_desc(&description_sb, branch_name);\n+\tif (!description_sb.len)\n+\t\tgoto do_pp;\n+\n+\tif (cover_from_description_mode == COVER_FROM_SUBJECT ||\n+\t\t\tcover_from_description_mode == COVER_FROM_AUTO)\n+\t\tbody = format_subject(&subject_sb, description_sb.buf, \" \");\n+\n+\tif (cover_from_description_mode == COVER_FROM_MESSAGE ||\n+\t\t\t(cover_from_description_mode == COVER_FROM_AUTO &&\n+\t\t\t subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN))\n+\t\tbody = description_sb.buf;\n+\telse\n+\t\tsubject = subject_sb.buf;\n+\n+do_pp:\n+\tpp_title_line(pp, &subject, sb, encoding, need_8bit_cte);\n+\tpp_remainder(pp, &body, sb, 0);\n+\n+\tstrbuf_release(&description_sb);\n+\tstrbuf_release(&subject_sb);\n+}\n+\n static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \t\t\t      struct commit *origin,\n \t\t\t      int nr, struct commit **list,\n@@ -1064,8 +1117,6 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \t\t\t      int quiet)\n {\n \tconst char *committer;\n-\tconst char *body = \"*** SUBJECT HERE ***\\n\\n*** BLURB HERE ***\\n\";\n-\tconst char *msg;\n \tstruct shortlog log;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint i;\n@@ -1095,15 +1146,12 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tif (!branch_name)\n \t\tbranch_name = find_branch_name(rev);\n \n-\tmsg = body;\n \tpp.fmt = CMIT_FMT_EMAIL;\n \tpp.date_mode.type = DATE_RFC2822;\n \tpp.rev = rev;\n \tpp.print_email_subject = 1;\n \tpp_user_info(&pp, NULL, &sb, committer, encoding);\n-\tpp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);\n-\tpp_remainder(&pp, &msg, &sb, 0);\n-\tadd_branch_description(&sb, branch_name);\n+\tprepare_cover_text(&pp, branch_name, &sb, encoding, need_8bit_cte);\n \tfprintf(rev->diffopt.file, \"%s\\n\", sb.buf);\n \n \tstrbuf_release(&sb);\n@@ -1545,6 +1593,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tint use_patch_format = 0;\n \tint quiet = 0;\n \tint reroll_count = -1;\n+\tchar *cover_from_description_arg = NULL;\n \tchar *branch_name = NULL;\n \tchar *base_commit = NULL;\n \tstruct base_tree_info bases;\n@@ -1581,6 +1630,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t{ OPTION_CALLBACK, 0, \"rfc\", &rev, NULL,\n \t\t\t    N_(\"Use [RFC PATCH] instead of [PATCH]\"),\n \t\t\t    PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback },\n+\t\tOPT_STRING(0, \"cover-from-description\", &cover_from_description_arg,\n+\t\t\t    N_(\"cover-from-description-mode\"),\n+\t\t\t    N_(\"generate parts of a cover letter based on a branch's description\")),\n \t\t{ OPTION_CALLBACK, 0, \"subject-prefix\", &rev, N_(\"prefix\"),\n \t\t\t    N_(\"Use [<prefix>] instead of [PATCH]\"),\n \t\t\t    PARSE_OPT_NONEG, subject_prefix_callback },\n@@ -1672,6 +1724,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\t     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n+\tif (cover_from_description_arg)\n+\t\tcover_from_description_mode = parse_cover_from_description(cover_from_description_arg);\n+\n \tif (0 < reroll_count) {\n \t\tstruct strbuf sprefix = STRBUF_INIT;\n \t\tstrbuf_addf(&sprefix, \"%s v%d\",\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 72b09896cf..88db01308a 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -1517,6 +1517,178 @@ test_expect_success 'format patch ignores color.ui' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'cover letter with invalid --cover-from-description and config' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\ttest_must_fail git format-patch --cover-letter --cover-from-description garbage master &&\n+\ttest_config format.coverFromDescription garbage &&\n+\ttest_must_fail git format-patch --cover-letter master\n+'\n+\n+test_expect_success 'cover letter with format.coverFromDescription = default' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\ttest_config format.coverFromDescription default &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] \\*\\*\\* SUBJECT HERE \\*\\*\\*$\" actual &&\n+\t! grep \"^\\*\\*\\* BLURB HERE \\*\\*\\*$\" actual &&\n+\tgrep \"^config subject$\" actual &&\n+\tgrep \"^body$\" actual\n+'\n+\n+test_expect_success 'cover letter with --cover-from-description default' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter --cover-from-description default master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] \\*\\*\\* SUBJECT HERE \\*\\*\\*$\" actual &&\n+\t! grep \"^\\*\\*\\* BLURB HERE \\*\\*\\*$\" actual &&\n+\tgrep \"^config subject$\" actual &&\n+\tgrep \"^body$\" actual\n+'\n+\n+test_expect_success 'cover letter with format.coverFromDescription = none' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\ttest_config format.coverFromDescription none &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] \\*\\*\\* SUBJECT HERE \\*\\*\\*$\" actual &&\n+\tgrep \"^\\*\\*\\* BLURB HERE \\*\\*\\*$\" actual &&\n+\t! grep \"^config subject$\" actual &&\n+\t! grep \"^body$\" actual\n+'\n+\n+test_expect_success 'cover letter with --cover-from-description none' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter --cover-from-description none master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] \\*\\*\\* SUBJECT HERE \\*\\*\\*$\" actual &&\n+\tgrep \"^\\*\\*\\* BLURB HERE \\*\\*\\*$\" actual &&\n+\t! grep \"^config subject$\" actual &&\n+\t! grep \"^body$\" actual\n+'\n+\n+test_expect_success 'cover letter with format.coverFromDescription = message' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\ttest_config format.coverFromDescription message &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] \\*\\*\\* SUBJECT HERE \\*\\*\\*$\" actual &&\n+\t! grep \"^\\*\\*\\* BLURB HERE \\*\\*\\*$\" actual &&\n+\tgrep \"^config subject$\" actual &&\n+\tgrep \"^body$\" actual\n+'\n+\n+test_expect_success 'cover letter with --cover-from-description message' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter --cover-from-description message master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] \\*\\*\\* SUBJECT HERE \\*\\*\\*$\" actual &&\n+\t! grep \"^\\*\\*\\* BLURB HERE \\*\\*\\*$\" actual &&\n+\tgrep \"^config subject$\" actual &&\n+\tgrep \"^body$\" actual\n+'\n+\n+test_expect_success 'cover letter with format.coverFromDescription = subject' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\ttest_config format.coverFromDescription subject &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] config subject$\" actual &&\n+\t! grep \"^\\*\\*\\* BLURB HERE \\*\\*\\*$\" actual &&\n+\t! grep \"^config subject$\" actual &&\n+\tgrep \"^body$\" actual\n+'\n+\n+test_expect_success 'cover letter with --cover-from-description subject' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter --cover-from-description subject master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] config subject$\" actual &&\n+\t! grep \"^\\*\\*\\* BLURB HERE \\*\\*\\*$\" actual &&\n+\t! grep \"^config subject$\" actual &&\n+\tgrep \"^body$\" actual\n+'\n+\n+test_expect_success 'cover letter with format.coverFromDescription = auto (short subject line)' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\ttest_config format.coverFromDescription auto &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] config subject$\" actual &&\n+\t! grep \"^\\*\\*\\* BLURB HERE \\*\\*\\*$\" actual &&\n+\t! grep \"^config subject$\" actual &&\n+\tgrep \"^body$\" actual\n+'\n+\n+test_expect_success 'cover letter with --cover-from-description auto (short subject line)' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter --cover-from-description auto master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] config subject$\" actual &&\n+\t! grep \"^\\*\\*\\* BLURB HERE \\*\\*\\*$\" actual &&\n+\t! grep \"^config subject$\" actual &&\n+\tgrep \"^body$\" actual\n+'\n+\n+test_expect_success 'cover letter with format.coverFromDescription = auto (long subject line)' '\n+\ttest_config branch.rebuild-1.description \"this is a really long first line and it is over 100 characters long which is the threshold for long subjects\n+\n+body\" &&\n+\ttest_config format.coverFromDescription auto &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] \\*\\*\\* SUBJECT HERE \\*\\*\\*$\" actual &&\n+\t! grep \"^\\*\\*\\* BLURB HERE \\*\\*\\*$\" actual &&\n+\tgrep \"^this is a really long first line and it is over 100 characters long which is the threshold for long subjects$\" actual &&\n+\tgrep \"^body$\" actual\n+'\n+\n+test_expect_success 'cover letter with --cover-from-description auto (long subject line)' '\n+\ttest_config branch.rebuild-1.description \"this is a really long first line and it is over 100 characters long which is the threshold for long subjects\n+\n+body\" &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter --cover-from-description auto master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] \\*\\*\\* SUBJECT HERE \\*\\*\\*$\" actual &&\n+\t! grep \"^\\*\\*\\* BLURB HERE \\*\\*\\*$\" actual &&\n+\tgrep \"^this is a really long first line and it is over 100 characters long which is the threshold for long subjects$\" actual &&\n+\tgrep \"^body$\" actual\n+'\n+\n+test_expect_success 'cover letter with command-line --cover-from-description overrides config' '\n+\ttest_config branch.rebuild-1.description \"config subject\n+\n+body\" &&\n+\ttest_config format.coverFromDescription none &&\n+\tgit checkout rebuild-1 &&\n+\tgit format-patch --stdout --cover-letter --cover-from-description subject master >actual &&\n+\tgrep \"^Subject: \\[PATCH 0/2\\] config subject$\" actual &&\n+\t! grep \"^\\*\\*\\* BLURB HERE \\*\\*\\*$\" actual &&\n+\t! grep \"^config subject$\" actual &&\n+\tgrep \"^body$\" actual\n+'\n+\n test_expect_success 'cover letter using branch description (1)' '\n \tgit checkout rebuild-1 &&\n \ttest_config branch.rebuild-1.description hello &&\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex 75512c3403..5187e2ede5 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -1548,7 +1548,10 @@ test_expect_success 'complete tree filename with metacharacters' '\n '\n \n test_expect_success PERL 'send-email' '\n-\ttest_completion \"git send-email --cov\" \"--cover-letter \" &&\n+\ttest_completion \"git send-email --cov\" <<-\\EOF &&\n+\t--cover-from-description=Z\n+\t--cover-letter Z\n+\tEOF\n \ttest_completion \"git send-email ma\" \"master \"\n '\n \n-- \n2.23.0.17.gd2208d9060\n\n"},{"id":"384150","messageId":"xmqq4l09bhsp.fsf@gitster-ct.c.googlers.com","threadId":"51038","inReplyTo":"cover.1571130298.git.liu.denton@gmail.com","subject":"Re: [PATCH v6 0/3] format-patch: learn --infer-cover-subject option (also t4014 cleanup)","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-10-16T01:28:06Z","receivedAt":"2019-10-16T01:28:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thanks.  I think we are ready for 'next'.\n"}]}