format-patch: generate MIME header as needed even when there is format.header
authorJunio C Hamano <gitster@pobox.com>
Sat, 15 Mar 2008 00:10:09 +0000 (17:10 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sat, 15 Mar 2008 07:06:06 +0000 (00:06 -0700)
Earlier, the callchain from pretty_print_commit() down to pp_title_line()
had an unwarranted assumption that the presense of "after_subject"
parameter, means the caller has already output MIME headers for
attachments.  The parameter's primary purpose is to give extra header
lines the caller wants to place after pp_title_line() generates the
"Subject: " line.

This assumption does not hold when the user used the format.header
configuration variable to pass extra headers, and caused a message with
non-ASCII character to lack proper MIME headers (e.g.  8-bit CTE header).
The earlier logic also failed to suppress duplicated MIME headers when
"format-patch -s --attach" is asked for and the signer's name demanded
8-bit clean transport.

This patch fixes the logic by introducing a separate need_8bit_cte
parameter passed down the callchain.  This can have one of these values:

 -1 : we've already done MIME crap and we do not want to add extra header
      to say this is 8bit in pp_title_line();

  0 : we haven't done MIME and we have not seen anything that is 8bit yet;

  1 : we haven't done MIME and we have seen something that is 8bit;
      pp_title_line() must add MIME header.

It adds two tests by Jeff King who independently diagnosed this issue.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
commit.h
log-tree.c
pretty.c
t/t4021-format-patch-signer-mime.sh
t/t4028-format-patch-mime-headers.sh [new file with mode: 0755]

index 10e2b5d4cfdc7ac129ead711421ccc51d2667f02..000528a67b0f087793da2001d69993260bdaeb4a 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -70,7 +70,7 @@ extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
                                 struct strbuf *,
                                 int abbrev, const char *subject,
                                 const char *after_subject, enum date_mode,
-                               int non_ascii_present);
+                               int need_8bit_cte);
 
 /** Removes the first commit from a list sorted by date, and adds all
  * of its parents.
index 1f3fcf16ad7a101eb9eab53da84bd2640f97ab00..dd94f393a06a0c8a7fc6420af0b9487df47ec4a4 100644 (file)
@@ -146,6 +146,7 @@ void show_log(struct rev_info *opt, const char *sep)
        int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
        const char *extra;
        const char *subject = NULL, *extra_headers = opt->extra_headers;
+       int need_8bit_cte = 0;
 
        opt->loginfo = NULL;
        if (!opt->verbose_header) {
@@ -214,6 +215,8 @@ void show_log(struct rev_info *opt, const char *sep)
                if (opt->mime_boundary) {
                        static char subject_buffer[1024];
                        static char buffer[1024];
+
+                       need_8bit_cte = -1; /* never */
                        snprintf(subject_buffer, sizeof(subject_buffer) - 1,
                                 "%s"
                                 "MIME-Version: 1.0\n"
@@ -282,9 +285,11 @@ void show_log(struct rev_info *opt, const char *sep)
         * And then the pretty-printed message itself
         */
        strbuf_init(&msgbuf, 0);
+       if (need_8bit_cte >= 0)
+               need_8bit_cte = has_non_ascii(opt->add_signoff);
        pretty_print_commit(opt->commit_format, commit, &msgbuf,
                            abbrev, subject, extra_headers, opt->date_mode,
-                           has_non_ascii(opt->add_signoff));
+                           need_8bit_cte);
 
        if (opt->add_signoff)
                append_signoff(&msgbuf, opt->add_signoff);
index b987ff245b310a6693dc69ba8c71ef2915da7864..0963bb08c125c8fc0c4d8ee5e8ff293145678158 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -659,7 +659,7 @@ static void pp_title_line(enum cmit_fmt fmt,
                          const char *subject,
                          const char *after_subject,
                          const char *encoding,
-                         int plain_non_ascii)
+                         int need_8bit_cte)
 {
        struct strbuf title;
 
@@ -692,7 +692,7 @@ static void pp_title_line(enum cmit_fmt fmt,
        }
        strbuf_addch(sb, '\n');
 
-       if (plain_non_ascii) {
+       if (need_8bit_cte > 0) {
                const char *header_fmt =
                        "MIME-Version: 1.0\n"
                        "Content-Type: text/plain; charset=%s\n"
@@ -741,9 +741,9 @@ static void pp_remainder(enum cmit_fmt fmt,
 }
 
 void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
-                                 struct strbuf *sb, int abbrev,
-                                 const char *subject, const char *after_subject,
-                                 enum date_mode dmode, int plain_non_ascii)
+                        struct strbuf *sb, int abbrev,
+                        const char *subject, const char *after_subject,
+                        enum date_mode dmode, int need_8bit_cte)
 {
        unsigned long beginning_of_body;
        int indent = 4;
@@ -769,13 +769,11 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
        if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
                indent = 0;
 
-       /* After-subject is used to pass in Content-Type: multipart
-        * MIME header; in that case we do not have to do the
-        * plaintext content type even if the commit message has
-        * non 7-bit ASCII character.  Otherwise, check if we need
-        * to say this is not a 7-bit ASCII.
+       /*
+        * We need to check and emit Content-type: to mark it
+        * as 8-bit if we haven't done so.
         */
-       if (fmt == CMIT_FMT_EMAIL && !after_subject) {
+       if (fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) {
                int i, ch, in_body;
 
                for (in_body = i = 0; (ch = msg[i]); i++) {
@@ -788,7 +786,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
                                        in_body = 1;
                        }
                        else if (non_ascii(ch)) {
-                               plain_non_ascii = 1;
+                               need_8bit_cte = 1;
                                break;
                        }
                }
@@ -813,7 +811,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
        /* These formats treat the title line specially. */
        if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
                pp_title_line(fmt, &msg, sb, subject,
-                             after_subject, encoding, plain_non_ascii);
+                             after_subject, encoding, need_8bit_cte);
 
        beginning_of_body = sb->len;
        if (fmt != CMIT_FMT_ONELINE)
index 67a70fadabbdcaca15832c86d76f1e194a923a75..ba43f185494630c50fc2a168df8dcd45c0b2421b 100755 (executable)
@@ -32,11 +32,19 @@ test_expect_success 'format with signoff without funny signer name' '
 
 test_expect_success 'format with non ASCII signer name' '
 
-       GIT_COMMITTER_NAME="\e$B$O$^$N\e(B \e$B$U$K$*$&\e(B" \
+       GIT_COMMITTER_NAME="はまの ふにおう" \
        git format-patch -s --stdout -1 >output &&
        grep Content-Type output
 
 '
 
+test_expect_success 'attach and signoff do not duplicate mime headers' '
+
+       GIT_COMMITTER_NAME="はまの ふにおう" \
+       git format-patch -s --stdout -1 --attach >output &&
+       test `grep -ci ^MIME-Version: output` = 1
+
+'
+
 test_done
 
diff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh
new file mode 100755 (executable)
index 0000000..204ba67
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+test_description='format-patch mime headers and extra headers do not conflict'
+. ./test-lib.sh
+
+test_expect_success 'create commit with utf-8 body' '
+       echo content >file &&
+       git add file &&
+       git commit -m one &&
+       echo more >>file &&
+       git commit -a -m "two
+
+       utf-8 body: ñ"
+'
+
+test_expect_success 'patch has mime headers' '
+       rm -f 0001-two.patch &&
+       git format-patch HEAD^ &&
+       grep -i "content-type: text/plain; charset=utf-8" 0001-two.patch
+'
+
+test_expect_success 'patch has mime and extra headers' '
+       rm -f 0001-two.patch &&
+       git config format.headers "x-foo: bar" &&
+       git format-patch HEAD^ &&
+       grep -i "x-foo: bar" 0001-two.patch &&
+       grep -i "content-type: text/plain; charset=utf-8" 0001-two.patch
+'
+
+test_done