]> git.ipfire.org Git - thirdparty/git.git/blobdiff - t/t4014-format-patch.sh
format-patch: teach --cover-from-description option
[thirdparty/git.git] / t / t4014-format-patch.sh
index c07d868491bbbd2887931d8afd19d662a4ac5272..88db01308a3f09f20c764617d44418172b459cbf 100755 (executable)
@@ -33,7 +33,8 @@ test_expect_success setup '
        git commit -m "Side changes #3 with \\n backslash-n in it." &&
 
        git checkout master &&
-       git diff-tree -p C2 | git apply --index &&
+       git diff-tree -p C2 >patch &&
+       git apply --index <patch &&
        test_tick &&
        git commit -m "Master accepts moral equivalent of #2" &&
 
@@ -60,23 +61,23 @@ test_expect_success setup '
 
 test_expect_success 'format-patch --ignore-if-in-upstream' '
        git format-patch --stdout master..side >patch0 &&
-       cnt=$(grep "^From " patch0 | wc -l) &&
-       test $cnt = 3
+       grep "^From " patch0 >from0 &&
+       test_line_count = 3 from0
 '
 
 test_expect_success 'format-patch --ignore-if-in-upstream' '
        git format-patch --stdout \
                --ignore-if-in-upstream master..side >patch1 &&
-       cnt=$(grep "^From " patch1 | wc -l) &&
-       test $cnt = 2
+       grep "^From " patch1 >from1 &&
+       test_line_count = 2 from1
 '
 
 test_expect_success 'format-patch --ignore-if-in-upstream handles tags' '
        git tag -a v1 -m tag side &&
        git tag -a v2 -m tag master &&
        git format-patch --stdout --ignore-if-in-upstream v2..v1 >patch1 &&
-       cnt=$(grep "^From " patch1 | wc -l) &&
-       test $cnt = 2
+       grep "^From " patch1 >from1 &&
+       test_line_count = 2 from1
 '
 
 test_expect_success "format-patch doesn't consider merge commits" '
@@ -90,26 +91,28 @@ test_expect_success "format-patch doesn't consider merge commits" '
        git checkout -b merger master &&
        test_tick &&
        git merge --no-ff slave &&
-       cnt=$(git format-patch -3 --stdout | grep "^From " | wc -l) &&
-       test $cnt = 3
+       git format-patch -3 --stdout >patch &&
+       grep "^From " patch >from &&
+       test_line_count = 3 from
 '
 
 test_expect_success 'format-patch result applies' '
        git checkout -b rebuild-0 master &&
        git am -3 patch0 &&
-       cnt=$(git rev-list master.. | wc -l) &&
-       test $cnt = 2
+       git rev-list master.. >list &&
+       test_line_count = 2 list
 '
 
 test_expect_success 'format-patch --ignore-if-in-upstream result applies' '
        git checkout -b rebuild-1 master &&
        git am -3 patch1 &&
-       cnt=$(git rev-list master.. | wc -l) &&
-       test $cnt = 2
+       git rev-list master.. >list &&
+       test_line_count = 2 list
 '
 
 test_expect_success 'commit did not screw up the log message' '
-       git cat-file commit side | grep "^Side .* with .* backslash-n"
+       git cat-file commit side >actual &&
+       grep "^Side .* with .* backslash-n" actual
 '
 
 test_expect_success 'format-patch did not screw up the log message' '
@@ -118,7 +121,8 @@ test_expect_success 'format-patch did not screw up the log message' '
 '
 
 test_expect_success 'replay did not screw up the log message' '
-       git cat-file commit rebuild-1 | grep "^Side .* with .* backslash-n"
+       git cat-file commit rebuild-1 >actual &&
+       grep "^Side .* with .* backslash-n" actual
 '
 
 test_expect_success 'extra headers' '
@@ -152,63 +156,73 @@ test_expect_success 'extra headers with multiple To:s' '
 
 test_expect_success 'additional command line cc (ascii)' '
        git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
-       git format-patch --cc="S E Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 &&
-       grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch5 &&
-       grep "^ *S E Cipient <scipient@example.com>\$" patch5
+       git format-patch --cc="S E Cipient <scipient@example.com>" --stdout master..side >patch5 &&
+       sed -e "/^\$/q" patch5 >hdrs5 &&
+       grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs5 &&
+       grep "^ *S E Cipient <scipient@example.com>\$" hdrs5
 '
 
 test_expect_failure 'additional command line cc (rfc822)' '
        git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
-       git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 &&
-       grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch5 &&
-       grep "^ *\"S. E. Cipient\" <scipient@example.com>\$" patch5
+       git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side >patch5 &&
+       sed -e "/^\$/q" patch5 >hdrs5 &&
+       grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs5 &&
+       grep "^ *\"S. E. Cipient\" <scipient@example.com>\$" hdrs5
 '
 
 test_expect_success 'command line headers' '
        git config --unset-all format.headers &&
-       git format-patch --add-header="Cc: R E Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch6 &&
-       grep "^Cc: R E Cipient <rcipient@example.com>\$" patch6
+       git format-patch --add-header="Cc: R E Cipient <rcipient@example.com>" --stdout master..side >patch6 &&
+       sed -e "/^\$/q" patch6 >hdrs6 &&
+       grep "^Cc: R E Cipient <rcipient@example.com>\$" hdrs6
 '
 
 test_expect_success 'configuration headers and command line headers' '
        git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
-       git format-patch --add-header="Cc: S E Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch7 &&
-       grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch7 &&
-       grep "^ *S E Cipient <scipient@example.com>\$" patch7
+       git format-patch --add-header="Cc: S E Cipient <scipient@example.com>" --stdout master..side >patch7 &&
+       sed -e "/^\$/q" patch7 >hdrs7 &&
+       grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs7 &&
+       grep "^ *S E Cipient <scipient@example.com>\$" hdrs7
 '
 
 test_expect_success 'command line To: header (ascii)' '
        git config --unset-all format.headers &&
-       git format-patch --to="R E Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
-       grep "^To: R E Cipient <rcipient@example.com>\$" patch8
+       git format-patch --to="R E Cipient <rcipient@example.com>" --stdout master..side >patch8 &&
+       sed -e "/^\$/q" patch8 >hdrs8 &&
+       grep "^To: R E Cipient <rcipient@example.com>\$" hdrs8
 '
 
 test_expect_failure 'command line To: header (rfc822)' '
-       git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
-       grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" patch8
+       git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side >patch8 &&
+       sed -e "/^\$/q" patch8 >hdrs8 &&
+       grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" hdrs8
 '
 
 test_expect_failure 'command line To: header (rfc2047)' '
-       git format-patch --to="R Ä Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
-       grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" patch8
+       git format-patch --to="R Ä Cipient <rcipient@example.com>" --stdout master..side >patch8 &&
+       sed -e "/^\$/q" patch8 >hdrs8 &&
+       grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" hdrs8
 '
 
 test_expect_success 'configuration To: header (ascii)' '
        git config format.to "R E Cipient <rcipient@example.com>" &&
-       git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
-       grep "^To: R E Cipient <rcipient@example.com>\$" patch9
+       git format-patch --stdout master..side >patch9 &&
+       sed -e "/^\$/q" patch9 >hdrs9 &&
+       grep "^To: R E Cipient <rcipient@example.com>\$" hdrs9
 '
 
 test_expect_failure 'configuration To: header (rfc822)' '
        git config format.to "R. E. Cipient <rcipient@example.com>" &&
-       git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
-       grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" patch9
+       git format-patch --stdout master..side >patch9 &&
+       sed -e "/^\$/q" patch9 >hdrs9 &&
+       grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" hdrs9
 '
 
 test_expect_failure 'configuration To: header (rfc2047)' '
        git config format.to "R Ä Cipient <rcipient@example.com>" &&
-       git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
-       grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" patch9
+       git format-patch --stdout master..side >patch9 &&
+       sed -e "/^\$/q" patch9 >hdrs9 &&
+       grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" hdrs9
 '
 
 # check_patch <patch>: Verify that <patch> looks like a half-sane
@@ -220,76 +234,76 @@ check_patch () {
 }
 
 test_expect_success 'format.from=false' '
-       git -c format.from=false format-patch --stdout master..side |
-       sed -e "/^\$/q" >patch &&
+       git -c format.from=false format-patch --stdout master..side >patch &&
+       sed -e "/^\$/q" patch >hdrs &&
        check_patch patch &&
-       ! grep "^From: C O Mitter <committer@example.com>\$" patch
+       ! grep "^From: C O Mitter <committer@example.com>\$" hdrs
 '
 
 test_expect_success 'format.from=true' '
-       git -c format.from=true format-patch --stdout master..side |
-       sed -e "/^\$/q" >patch &&
-       check_patch patch &&
-       grep "^From: C O Mitter <committer@example.com>\$" patch
+       git -c format.from=true format-patch --stdout master..side >patch &&
+       sed -e "/^\$/q" patch >hdrs &&
+       check_patch hdrs &&
+       grep "^From: C O Mitter <committer@example.com>\$" hdrs
 '
 
 test_expect_success 'format.from with address' '
-       git -c format.from="F R Om <from@example.com>" format-patch --stdout master..side |
-       sed -e "/^\$/q" >patch &&
-       check_patch patch &&
-       grep "^From: F R Om <from@example.com>\$" patch
+       git -c format.from="F R Om <from@example.com>" format-patch --stdout master..side >patch &&
+       sed -e "/^\$/q" patch >hdrs &&
+       check_patch hdrs &&
+       grep "^From: F R Om <from@example.com>\$" hdrs
 '
 
 test_expect_success '--no-from overrides format.from' '
-       git -c format.from="F R Om <from@example.com>" format-patch --no-from --stdout master..side |
-       sed -e "/^\$/q" >patch &&
-       check_patch patch &&
-       ! grep "^From: F R Om <from@example.com>\$" patch
+       git -c format.from="F R Om <from@example.com>" format-patch --no-from --stdout master..side >patch &&
+       sed -e "/^\$/q" patch >hdrs &&
+       check_patch hdrs &&
+       ! grep "^From: F R Om <from@example.com>\$" hdrs
 '
 
 test_expect_success '--from overrides format.from' '
-       git -c format.from="F R Om <from@example.com>" format-patch --from --stdout master..side |
-       sed -e "/^\$/q" >patch &&
-       check_patch patch &&
-       ! grep "^From: F R Om <from@example.com>\$" patch
+       git -c format.from="F R Om <from@example.com>" format-patch --from --stdout master..side >patch &&
+       sed -e "/^\$/q" patch >hdrs &&
+       check_patch hdrs &&
+       ! grep "^From: F R Om <from@example.com>\$" hdrs
 '
 
 test_expect_success '--no-to overrides config.to' '
        git config --replace-all format.to \
                "R E Cipient <rcipient@example.com>" &&
-       git format-patch --no-to --stdout master..side |
-       sed -e "/^\$/q" >patch10 &&
-       check_patch patch10 &&
-       ! grep "^To: R E Cipient <rcipient@example.com>\$" patch10
+       git format-patch --no-to --stdout master..side >patch10 &&
+       sed -e "/^\$/q" patch10 >hdrs10 &&
+       check_patch hdrs10 &&
+       ! grep "^To: R E Cipient <rcipient@example.com>\$" hdrs10
 '
 
 test_expect_success '--no-to and --to replaces config.to' '
        git config --replace-all format.to \
                "Someone <someone@out.there>" &&
        git format-patch --no-to --to="Someone Else <else@out.there>" \
-               --stdout master..side |
-       sed -e "/^\$/q" >patch11 &&
-       check_patch patch11 &&
-       ! grep "^To: Someone <someone@out.there>\$" patch11 &&
-       grep "^To: Someone Else <else@out.there>\$" patch11
+               --stdout master..side >patch11 &&
+       sed -e "/^\$/q" patch11 >hdrs11 &&
+       check_patch hdrs11 &&
+       ! grep "^To: Someone <someone@out.there>\$" hdrs11 &&
+       grep "^To: Someone Else <else@out.there>\$" hdrs11
 '
 
 test_expect_success '--no-cc overrides config.cc' '
        git config --replace-all format.cc \
                "C E Cipient <rcipient@example.com>" &&
-       git format-patch --no-cc --stdout master..side |
-       sed -e "/^\$/q" >patch12 &&
-       check_patch patch12 &&
-       ! grep "^Cc: C E Cipient <rcipient@example.com>\$" patch12
+       git format-patch --no-cc --stdout master..side >patch12 &&
+       sed -e "/^\$/q" patch12 >hdrs12 &&
+       check_patch hdrs12 &&
+       ! grep "^Cc: C E Cipient <rcipient@example.com>\$" hdrs12
 '
 
 test_expect_success '--no-add-header overrides config.headers' '
        git config --replace-all format.headers \
                "Header1: B E Cipient <rcipient@example.com>" &&
-       git format-patch --no-add-header --stdout master..side |
-       sed -e "/^\$/q" >patch13 &&
-       check_patch patch13 &&
-       ! grep "^Header1: B E Cipient <rcipient@example.com>\$" patch13
+       git format-patch --no-add-header --stdout master..side >patch13 &&
+       sed -e "/^\$/q" patch13 >hdrs13 &&
+       check_patch hdrs13 &&
+       ! grep "^Header1: B E Cipient <rcipient@example.com>\$" hdrs13
 '
 
 test_expect_success 'multiple files' '
@@ -318,7 +332,7 @@ test_expect_success 'reroll count (-v)' '
 check_threading () {
        expect="$1" &&
        shift &&
-       (git format-patch --stdout "$@"; echo $? >status.out) |
+       git format-patch --stdout "$@" >patch &&
        # Prints everything between the Message-ID and In-Reply-To,
        # and replaces all Message-ID-lookalikes by a sequence number
        perl -ne '
@@ -333,8 +347,7 @@ check_threading () {
                        print;
                }
                print "---\n" if /^From /i;
-       ' >actual &&
-       test 0 = "$(cat status.out)" &&
+       ' <patch >actual &&
        test_cmp "$expect" actual
 }
 
@@ -596,7 +609,7 @@ EOF
 
 test_expect_success 'shortlog of cover-letter wraps overly-long onelines' '
        git format-patch --cover-letter -2 &&
-       sed -e "1,/A U Thor/d" -e "/^\$/q" <0000-cover-letter.patch >output &&
+       sed -e "1,/A U Thor/d" -e "/^\$/q" 0000-cover-letter.patch >output &&
        test_cmp expect output
 '
 
@@ -635,7 +648,7 @@ EOF
 
 test_expect_success 'format-patch -p suppresses stat' '
        git format-patch -p -2 &&
-       sed -e "1,/^\$/d" -e "/^+5/q" <0001-This-is-an-excessively-long-subject-line-for-a-messa.patch >output &&
+       sed -e "1,/^\$/d" -e "/^+5/q" 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch >output &&
        test_cmp expect output
 '
 
@@ -795,7 +808,8 @@ test_expect_success 'options no longer allowed for format-patch' '
 
 test_expect_success 'format-patch --numstat should produce a patch' '
        git format-patch --numstat --stdout master..side >output &&
-       test 5 = $(grep "^diff --git a/" output | wc -l)
+       grep "^diff --git a/" output >diff &&
+       test_line_count = 5 diff
 '
 
 test_expect_success 'format-patch -- <path>' '
@@ -807,20 +821,25 @@ test_expect_success 'format-patch --ignore-if-in-upstream HEAD' '
        git format-patch --ignore-if-in-upstream HEAD
 '
 
-git_version="$(git --version | sed "s/.* //")"
+test_expect_success 'get git version' '
+       git_version=$(git --version) &&
+       git_version=${git_version##* }
+'
 
 signature() {
        printf "%s\n%s\n\n" "-- " "${1:-$git_version}"
 }
 
 test_expect_success 'format-patch default signature' '
-       git format-patch --stdout -1 | tail -n 3 >output &&
+       git format-patch --stdout -1 >patch &&
+       tail -n 3 patch >output &&
        signature >expect &&
        test_cmp expect output
 '
 
 test_expect_success 'format-patch --signature' '
-       git format-patch --stdout --signature="my sig" -1 | tail -n 3 >output &&
+       git format-patch --stdout --signature="my sig" -1 >patch &&
+       tail -n 3 patch >output &&
        signature "my sig" >expect &&
        test_cmp expect output
 '
@@ -852,8 +871,8 @@ test_expect_success 'format-patch --signature --cover-letter' '
        git config --unset-all format.signature &&
        git format-patch --stdout --signature="my sig" --cover-letter \
                -1 >output &&
-       grep "my sig" output &&
-       test 2 = $(grep "my sig" output | wc -l)
+       grep "my sig" output >sig &&
+       test_line_count = 2 sig
 '
 
 test_expect_success 'format.signature="" suppresses signatures' '
@@ -890,7 +909,7 @@ test_expect_success 'prepare mail-signature input' '
 test_expect_success '--signature-file=file works' '
        git format-patch --stdout --signature-file=mail-signature -1 >output &&
        check_patch output &&
-       sed -e "1,/^-- \$/d" <output >actual &&
+       sed -e "1,/^-- \$/d" output >actual &&
        {
                cat mail-signature && echo
        } >expect &&
@@ -901,7 +920,7 @@ test_expect_success 'format.signaturefile works' '
        test_config format.signaturefile mail-signature &&
        git format-patch --stdout -1 >output &&
        check_patch output &&
-       sed -e "1,/^-- \$/d" <output >actual &&
+       sed -e "1,/^-- \$/d" output >actual &&
        {
                cat mail-signature && echo
        } >expect &&
@@ -923,7 +942,7 @@ test_expect_success '--signature-file overrides format.signaturefile' '
        git format-patch --stdout \
                        --signature-file=other-mail-signature -1 >output &&
        check_patch output &&
-       sed -e "1,/^-- \$/d" <output >actual &&
+       sed -e "1,/^-- \$/d" output >actual &&
        {
                cat other-mail-signature && echo
        } >expect &&
@@ -992,7 +1011,7 @@ test_expect_success 'format-patch wraps extremely long subject (ascii)' '
        git add file &&
        git commit -m "$M512" &&
        git format-patch --stdout -1 >patch &&
-       sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject &&
+       sed -n "/^Subject/p; /^ /p; /^$/q" patch >subject &&
        test_cmp expect subject
 '
 
@@ -1031,7 +1050,7 @@ test_expect_success 'format-patch wraps extremely long subject (rfc2047)' '
        git add file &&
        git commit -m "$M512" &&
        git format-patch --stdout -1 >patch &&
-       sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject &&
+       sed -n "/^Subject/p; /^ /p; /^$/q" patch >subject &&
        test_cmp expect subject
 '
 
@@ -1040,7 +1059,7 @@ check_author() {
        git add file &&
        GIT_AUTHOR_NAME=$1 git commit -m author-check &&
        git format-patch --stdout -1 >patch &&
-       sed -n "/^From: /p; /^ /p; /^$/q" <patch >actual &&
+       sed -n "/^From: /p; /^ /p; /^$/q" patch >actual &&
        test_cmp expect actual
 }
 
@@ -1160,7 +1179,7 @@ test_expect_success '--from=ident replaces author' '
        From: A U Thor <author@example.com>
 
        EOF
-       sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+       sed -ne "/^From:/p; /^$/p; /^---$/q" patch >patch.head &&
        test_cmp expect patch.head
 '
 
@@ -1172,7 +1191,7 @@ test_expect_success '--from uses committer ident' '
        From: A U Thor <author@example.com>
 
        EOF
-       sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+       sed -ne "/^From:/p; /^$/p; /^---$/q" patch >patch.head &&
        test_cmp expect patch.head
 '
 
@@ -1182,7 +1201,7 @@ test_expect_success '--from omits redundant in-body header' '
        From: A U Thor <author@example.com>
 
        EOF
-       sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+       sed -ne "/^From:/p; /^$/p; /^---$/q" patch >patch.head &&
        test_cmp expect patch.head
 '
 
@@ -1197,7 +1216,7 @@ test_expect_success 'in-body headers trigger content encoding' '
        From: éxötìc <author@example.com>
 
        EOF
-       sed -ne "/^From:/p; /^$/p; /^Content-Type/p; /^---$/q" <patch >patch.head &&
+       sed -ne "/^From:/p; /^$/p; /^Content-Type/p; /^---$/q" patch >patch.head &&
        test_cmp expect patch.head
 '
 
@@ -1498,46 +1517,218 @@ test_expect_success 'format patch ignores color.ui' '
        test_cmp expect actual
 '
 
+test_expect_success 'cover letter with invalid --cover-from-description and config' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_must_fail git format-patch --cover-letter --cover-from-description garbage master &&
+       test_config format.coverFromDescription garbage &&
+       test_must_fail git format-patch --cover-letter master
+'
+
+test_expect_success 'cover letter with format.coverFromDescription = default' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_config format.coverFromDescription default &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with --cover-from-description default' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description default master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with format.coverFromDescription = none' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_config format.coverFromDescription none &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       ! grep "^config subject$" actual &&
+       ! grep "^body$" actual
+'
+
+test_expect_success 'cover letter with --cover-from-description none' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description none master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       ! grep "^config subject$" actual &&
+       ! grep "^body$" actual
+'
+
+test_expect_success 'cover letter with format.coverFromDescription = message' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_config format.coverFromDescription message &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with --cover-from-description message' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description message master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with format.coverFromDescription = subject' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_config format.coverFromDescription subject &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter master >actual &&
+       grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       ! grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with --cover-from-description subject' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description subject master >actual &&
+       grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       ! grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with format.coverFromDescription = auto (short subject line)' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_config format.coverFromDescription auto &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter master >actual &&
+       grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       ! grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with --cover-from-description auto (short subject line)' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description auto master >actual &&
+       grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       ! grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with format.coverFromDescription = auto (long subject line)' '
+       test_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
+
+body" &&
+       test_config format.coverFromDescription auto &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       grep "^this is a really long first line and it is over 100 characters long which is the threshold for long subjects$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with --cover-from-description auto (long subject line)' '
+       test_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
+
+body" &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description auto master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       grep "^this is a really long first line and it is over 100 characters long which is the threshold for long subjects$" actual &&
+       grep "^body$" actual
+'
+
+test_expect_success 'cover letter with command-line --cover-from-description overrides config' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_config format.coverFromDescription none &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description subject master >actual &&
+       grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
+       ! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
+       ! grep "^config subject$" actual &&
+       grep "^body$" actual
+'
+
 test_expect_success 'cover letter using branch description (1)' '
        git checkout rebuild-1 &&
        test_config branch.rebuild-1.description hello &&
        git format-patch --stdout --cover-letter master >actual &&
-       grep hello actual >/dev/null
+       grep hello actual
 '
 
 test_expect_success 'cover letter using branch description (2)' '
        git checkout rebuild-1 &&
        test_config branch.rebuild-1.description hello &&
        git format-patch --stdout --cover-letter rebuild-1~2..rebuild-1 >actual &&
-       grep hello actual >/dev/null
+       grep hello actual
 '
 
 test_expect_success 'cover letter using branch description (3)' '
        git checkout rebuild-1 &&
        test_config branch.rebuild-1.description hello &&
        git format-patch --stdout --cover-letter ^master rebuild-1 >actual &&
-       grep hello actual >/dev/null
+       grep hello actual
 '
 
 test_expect_success 'cover letter using branch description (4)' '
        git checkout rebuild-1 &&
        test_config branch.rebuild-1.description hello &&
        git format-patch --stdout --cover-letter master.. >actual &&
-       grep hello actual >/dev/null
+       grep hello actual
 '
 
 test_expect_success 'cover letter using branch description (5)' '
        git checkout rebuild-1 &&
        test_config branch.rebuild-1.description hello &&
        git format-patch --stdout --cover-letter -2 HEAD >actual &&
-       grep hello actual >/dev/null
+       grep hello actual
 '
 
 test_expect_success 'cover letter using branch description (6)' '
        git checkout rebuild-1 &&
        test_config branch.rebuild-1.description hello &&
        git format-patch --stdout --cover-letter -2 >actual &&
-       grep hello actual >/dev/null
+       grep hello actual
 '
 
 test_expect_success 'cover letter with nothing' '
@@ -1591,7 +1782,9 @@ test_expect_success 'format-patch format.outputDirectory option' '
        test_config format.outputDirectory patches &&
        rm -fr patches &&
        git format-patch master..side &&
-       test $(git rev-list master..side | wc -l) -eq $(ls patches | wc -l)
+       count=$(git rev-list --count master..side) &&
+       ls patches >list &&
+       test_line_count = $count list
 '
 
 test_expect_success 'format-patch -o overrides format.outputDirectory' '
@@ -1604,19 +1797,40 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '
 
 test_expect_success 'format-patch --base' '
        git checkout patchid &&
-       git format-patch --stdout --base=HEAD~3 -1 | tail -n 7 >actual1 &&
-       git format-patch --stdout --base=HEAD~3 HEAD~.. | tail -n 7 >actual2 &&
+
+       git format-patch --stdout --base=HEAD~3 -1 >patch &&
+       tail -n 7 patch >actual1 &&
+
+       git format-patch --stdout --base=HEAD~3 HEAD~.. >patch &&
+       tail -n 7 patch >actual2 &&
+
        echo >expect &&
-       echo "base-commit: $(git rev-parse HEAD~3)" >>expect &&
-       echo "prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --stable | awk "{print \$1}")" >>expect &&
-       echo "prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --stable | awk "{print \$1}")" >>expect &&
+       git rev-parse HEAD~3 >commit-id-base &&
+       echo "base-commit: $(cat commit-id-base)" >>expect &&
+
+       git show --patch HEAD~2 >patch &&
+       git patch-id --stable <patch >patch.id.raw &&
+       awk "{print \"prerequisite-patch-id:\", \$1}" <patch.id.raw >>expect &&
+
+       git show --patch HEAD~1 >patch &&
+       git patch-id --stable <patch >patch.id.raw &&
+       awk "{print \"prerequisite-patch-id:\", \$1}" <patch.id.raw >>expect &&
+
        signature >>expect &&
        test_cmp expect actual1 &&
        test_cmp expect actual2 &&
+
        echo >fail &&
-       echo "base-commit: $(git rev-parse HEAD~3)" >>fail &&
-       echo "prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --unstable | awk "{print \$1}")" >>fail &&
-       echo "prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --unstable | awk "{print \$1}")" >>fail &&
+       echo "base-commit: $(cat commit-id-base)" >>fail &&
+
+       git show --patch HEAD~2 >patch &&
+       git patch-id --unstable <patch >patch.id.raw &&
+       awk "{print \"prerequisite-patch-id:\", \$1}" <patch.id.raw >>fail &&
+
+       git show --patch HEAD~1 >patch &&
+       git patch-id --unstable <patch >patch.id.raw &&
+       awk "{print \"prerequisite-patch-id:\", \$1}" <patch.id.raw >>fail &&
+
        signature >>fail &&
        ! test_cmp fail actual1 &&
        ! test_cmp fail actual2
@@ -1627,7 +1841,8 @@ test_expect_success 'format-patch --base errors out when base commit is in revis
        test_must_fail git format-patch --base=HEAD~1 -2 &&
        git format-patch --stdout --base=HEAD~2 -2 >patch &&
        grep "^base-commit:" patch >actual &&
-       echo "base-commit: $(git rev-parse HEAD~2)" >expect &&
+       git rev-parse HEAD~2 >commit-id-base &&
+       echo "base-commit: $(cat commit-id-base)" >expect &&
        test_cmp expect actual
 '
 
@@ -1666,7 +1881,8 @@ test_expect_success 'format-patch --base=auto' '
        test_commit N2 &&
        git format-patch --stdout --base=auto -2 >patch &&
        grep "^base-commit:" patch >actual &&
-       echo "base-commit: $(git rev-parse upstream)" >expect &&
+       git rev-parse upstream >commit-id-base &&
+       echo "base-commit: $(cat commit-id-base)" >expect &&
        test_cmp expect actual
 '
 
@@ -1703,7 +1919,8 @@ test_expect_success 'format-patch format.useAutoBaseoption' '
        git config format.useAutoBase true &&
        git format-patch --stdout -1 >patch &&
        grep "^base-commit:" patch >actual &&
-       echo "base-commit: $(git rev-parse upstream)" >expect &&
+       git rev-parse upstream >commit-id-base &&
+       echo "base-commit: $(cat commit-id-base)" >expect &&
        test_cmp expect actual
 '
 
@@ -1712,7 +1929,8 @@ test_expect_success 'format-patch --base overrides format.useAutoBase' '
        git config format.useAutoBase true &&
        git format-patch --stdout --base=HEAD~1 -1 >patch &&
        grep "^base-commit:" patch >actual &&
-       echo "base-commit: $(git rev-parse HEAD~1)" >expect &&
+       git rev-parse HEAD~1 >commit-id-base &&
+       echo "base-commit: $(cat commit-id-base)" >expect &&
        test_cmp expect actual
 '
 
@@ -1788,7 +2006,7 @@ test_expect_success 'interdiff: cover-letter' '
        git format-patch --cover-letter --interdiff=boop~2 -1 boop &&
        test_i18ngrep "^Interdiff:$" 0000-cover-letter.patch &&
        test_i18ngrep ! "^Interdiff:$" 0001-fleep.patch &&
-       sed "1,/^@@ /d; /^-- $/q" <0000-cover-letter.patch >actual &&
+       sed "1,/^@@ /d; /^-- $/q" 0000-cover-letter.patch >actual &&
        test_cmp expect actual
 '
 
@@ -1804,7 +2022,7 @@ test_expect_success 'interdiff: solo-patch' '
        EOF
        git format-patch --interdiff=boop~2 -1 boop &&
        test_i18ngrep "^Interdiff:$" 0001-fleep.patch &&
-       sed "1,/^  @@ /d; /^$/q" <0001-fleep.patch >actual &&
+       sed "1,/^  @@ /d; /^$/q" 0001-fleep.patch >actual &&
        test_cmp expect actual
 '