Commits

Anonymous committed 6bf4f1b

format-patch: generate MIME header as needed even when there is format.header

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>

  • Participants
  • Parent commits a0b54e7

Comments (0)

Files changed (5)

                                 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.
 	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) {
 		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"
 	 * 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);
 			  const char *subject,
 			  const char *after_subject,
 			  const char *encoding,
-			  int plain_non_ascii)
+			  int need_8bit_cte)
 {
 	struct strbuf title;
 
 	}
 	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"
 }
 
 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;
 	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++) {
 					in_body = 1;
 			}
 			else if (non_ascii(ch)) {
-				plain_non_ascii = 1;
+				need_8bit_cte = 1;
 				break;
 			}
 		}
 	/* 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)

t/t4021-format-patch-signer-mime.sh

 
 test_expect_success 'format with non ASCII signer name' '
 
-	GIT_COMMITTER_NAME="$B$O$^$N(B $B$U$K$*$&(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
 

t/t4028-format-patch-mime-headers.sh

+#!/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