Browse Source

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>
maint
Junio C Hamano 17 years ago
parent
commit
6bf4f1b4c9
  1. 2
      commit.h
  2. 7
      log-tree.c
  3. 20
      pretty.c
  4. 10
      t/t4021-format-patch-signer-mime.sh
  5. 30
      t/t4028-format-patch-mime-headers.sh

2
commit.h

@ -70,7 +70,7 @@ extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
struct strbuf *, struct strbuf *,
int abbrev, const char *subject, int abbrev, const char *subject,
const char *after_subject, enum date_mode, 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 /** Removes the first commit from a list sorted by date, and adds all
* of its parents. * of its parents.

7
log-tree.c

@ -146,6 +146,7 @@ void show_log(struct rev_info *opt, const char *sep)
int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40; int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
const char *extra; const char *extra;
const char *subject = NULL, *extra_headers = opt->extra_headers; const char *subject = NULL, *extra_headers = opt->extra_headers;
int need_8bit_cte = 0;


opt->loginfo = NULL; opt->loginfo = NULL;
if (!opt->verbose_header) { if (!opt->verbose_header) {
@ -214,6 +215,8 @@ void show_log(struct rev_info *opt, const char *sep)
if (opt->mime_boundary) { if (opt->mime_boundary) {
static char subject_buffer[1024]; static char subject_buffer[1024];
static char buffer[1024]; static char buffer[1024];

need_8bit_cte = -1; /* never */
snprintf(subject_buffer, sizeof(subject_buffer) - 1, snprintf(subject_buffer, sizeof(subject_buffer) - 1,
"%s" "%s"
"MIME-Version: 1.0\n" "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 * And then the pretty-printed message itself
*/ */
strbuf_init(&msgbuf, 0); 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, pretty_print_commit(opt->commit_format, commit, &msgbuf,
abbrev, subject, extra_headers, opt->date_mode, abbrev, subject, extra_headers, opt->date_mode,
has_non_ascii(opt->add_signoff)); need_8bit_cte);


if (opt->add_signoff) if (opt->add_signoff)
append_signoff(&msgbuf, opt->add_signoff); append_signoff(&msgbuf, opt->add_signoff);

20
pretty.c

@ -659,7 +659,7 @@ static void pp_title_line(enum cmit_fmt fmt,
const char *subject, const char *subject,
const char *after_subject, const char *after_subject,
const char *encoding, const char *encoding,
int plain_non_ascii) int need_8bit_cte)
{ {
struct strbuf title; struct strbuf title;


@ -692,7 +692,7 @@ static void pp_title_line(enum cmit_fmt fmt,
} }
strbuf_addch(sb, '\n'); strbuf_addch(sb, '\n');


if (plain_non_ascii) { if (need_8bit_cte > 0) {
const char *header_fmt = const char *header_fmt =
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=%s\n" "Content-Type: text/plain; charset=%s\n"
@ -743,7 +743,7 @@ static void pp_remainder(enum cmit_fmt fmt,
void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
struct strbuf *sb, int abbrev, struct strbuf *sb, int abbrev,
const char *subject, const char *after_subject, const char *subject, const char *after_subject,
enum date_mode dmode, int plain_non_ascii) enum date_mode dmode, int need_8bit_cte)
{ {
unsigned long beginning_of_body; unsigned long beginning_of_body;
int indent = 4; 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) if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
indent = 0; indent = 0;


/* After-subject is used to pass in Content-Type: multipart /*
* MIME header; in that case we do not have to do the * We need to check and emit Content-type: to mark it
* plaintext content type even if the commit message has * as 8-bit if we haven't done so.
* non 7-bit ASCII character. Otherwise, check if we need
* to say this is not a 7-bit ASCII.
*/ */
if (fmt == CMIT_FMT_EMAIL && !after_subject) { if (fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) {
int i, ch, in_body; int i, ch, in_body;


for (in_body = i = 0; (ch = msg[i]); i++) { 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; in_body = 1;
} }
else if (non_ascii(ch)) { else if (non_ascii(ch)) {
plain_non_ascii = 1; need_8bit_cte = 1;
break; break;
} }
} }
@ -813,7 +811,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
/* These formats treat the title line specially. */ /* These formats treat the title line specially. */
if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
pp_title_line(fmt, &msg, sb, subject, pp_title_line(fmt, &msg, sb, subject,
after_subject, encoding, plain_non_ascii); after_subject, encoding, need_8bit_cte);


beginning_of_body = sb->len; beginning_of_body = sb->len;
if (fmt != CMIT_FMT_ONELINE) if (fmt != CMIT_FMT_ONELINE)

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

@ -32,11 +32,19 @@ test_expect_success 'format with signoff without funny signer name' '


test_expect_success 'format with non ASCII signer name' ' 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 && git format-patch -s --stdout -1 >output &&
grep Content-Type 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 test_done



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

@ -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
Loading…
Cancel
Save