format-patch: wrap long header lines
Subject and identity headers may be arbitrarily long. In the past, we just assumed that single-line headers would be reasonably short. For multi-line subjects that we squish into a single line, we just "pre-folded" the data in pp_title_line by adding a newline and indentation. There were two problems. One is that, although rare, single-line messages can actually be longer than the recommended line-length limits. The second is that the pre-folding interacted badly with rfc2047 encoding, leading to malformed headers. Instead, let's stop pre-folding the subject lines, and just fold everything based on length in add_rfc2047, whether it is encoded or not. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
98acc837a1
commit
a1f6baa5c9
32
pretty.c
32
pretty.c
@ -216,7 +216,15 @@ static int is_rfc2047_special(char ch)
|
||||
static void add_rfc2047(struct strbuf *sb, const char *line, int len,
|
||||
const char *encoding)
|
||||
{
|
||||
int i, last;
|
||||
static const int max_length = 78; /* per rfc2822 */
|
||||
int i;
|
||||
int line_len;
|
||||
|
||||
/* How many bytes are already used on the current line? */
|
||||
for (i = sb->len - 1; i >= 0; i--)
|
||||
if (sb->buf[i] == '\n')
|
||||
break;
|
||||
line_len = sb->len - (i+1);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
int ch = line[i];
|
||||
@ -225,14 +233,21 @@ static void add_rfc2047(struct strbuf *sb, const char *line, int len,
|
||||
if ((i + 1 < len) && (ch == '=' && line[i+1] == '?'))
|
||||
goto needquote;
|
||||
}
|
||||
strbuf_add(sb, line, len);
|
||||
strbuf_add_wrapped_bytes(sb, line, len, 0, 1, max_length - line_len);
|
||||
return;
|
||||
|
||||
needquote:
|
||||
strbuf_grow(sb, len * 3 + strlen(encoding) + 100);
|
||||
strbuf_addf(sb, "=?%s?q?", encoding);
|
||||
for (i = last = 0; i < len; i++) {
|
||||
line_len += strlen(encoding) + 5; /* 5 for =??q? */
|
||||
for (i = 0; i < len; i++) {
|
||||
unsigned ch = line[i] & 0xFF;
|
||||
|
||||
if (line_len >= max_length - 2) {
|
||||
strbuf_addf(sb, "?=\n =?%s?q?", encoding);
|
||||
line_len = strlen(encoding) + 5 + 1; /* =??q? plus SP */
|
||||
}
|
||||
|
||||
/*
|
||||
* We encode ' ' using '=20' even though rfc2047
|
||||
* allows using '_' for readability. Unfortunately,
|
||||
@ -240,12 +255,14 @@ needquote:
|
||||
* leave the underscore in place.
|
||||
*/
|
||||
if (is_rfc2047_special(ch) || ch == ' ') {
|
||||
strbuf_add(sb, line + last, i - last);
|
||||
strbuf_addf(sb, "=%02X", ch);
|
||||
last = i + 1;
|
||||
line_len += 3;
|
||||
}
|
||||
else {
|
||||
strbuf_addch(sb, ch);
|
||||
line_len++;
|
||||
}
|
||||
}
|
||||
strbuf_add(sb, line + last, len - last);
|
||||
strbuf_addstr(sb, "?=");
|
||||
}
|
||||
|
||||
@ -1106,11 +1123,10 @@ void pp_title_line(enum cmit_fmt fmt,
|
||||
const char *encoding,
|
||||
int need_8bit_cte)
|
||||
{
|
||||
const char *line_separator = (fmt == CMIT_FMT_EMAIL) ? "\n " : " ";
|
||||
struct strbuf title;
|
||||
|
||||
strbuf_init(&title, 80);
|
||||
*msg_p = format_subject(&title, *msg_p, line_separator);
|
||||
*msg_p = format_subject(&title, *msg_p, " ");
|
||||
|
||||
strbuf_grow(sb, title.len + 1024);
|
||||
if (subject) {
|
||||
|
@ -709,4 +709,88 @@ test_expect_success TTY 'format-patch --stdout paginates' '
|
||||
test_path_is_missing .git/pager_used
|
||||
'
|
||||
|
||||
test_expect_success 'format-patch handles multi-line subjects' '
|
||||
rm -rf patches/ &&
|
||||
echo content >>file &&
|
||||
for i in one two three; do echo $i; done >msg &&
|
||||
git add file &&
|
||||
git commit -F msg &&
|
||||
git format-patch -o patches -1 &&
|
||||
grep ^Subject: patches/0001-one.patch >actual &&
|
||||
echo "Subject: [PATCH] one two three" >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'format-patch handles multi-line encoded subjects' '
|
||||
rm -rf patches/ &&
|
||||
echo content >>file &&
|
||||
for i in en två tre; do echo $i; done >msg &&
|
||||
git add file &&
|
||||
git commit -F msg &&
|
||||
git format-patch -o patches -1 &&
|
||||
grep ^Subject: patches/0001-en.patch >actual &&
|
||||
echo "Subject: [PATCH] =?UTF-8?q?en=20tv=C3=A5=20tre?=" >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
M8="foo bar "
|
||||
M64=$M8$M8$M8$M8$M8$M8$M8$M8
|
||||
M512=$M64$M64$M64$M64$M64$M64$M64$M64
|
||||
cat >expect <<'EOF'
|
||||
Subject: [PATCH] foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
|
||||
bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
|
||||
foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
|
||||
bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
|
||||
foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
|
||||
bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
|
||||
foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
|
||||
bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
|
||||
foo bar foo bar foo bar foo bar
|
||||
EOF
|
||||
test_expect_success 'format-patch wraps extremely long headers (ascii)' '
|
||||
echo content >>file &&
|
||||
git add file &&
|
||||
git commit -m "$M512" &&
|
||||
git format-patch --stdout -1 >patch &&
|
||||
sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject &&
|
||||
test_cmp expect subject
|
||||
'
|
||||
|
||||
M8="föö bar "
|
||||
M64=$M8$M8$M8$M8$M8$M8$M8$M8
|
||||
M512=$M64$M64$M64$M64$M64$M64$M64$M64
|
||||
cat >expect <<'EOF'
|
||||
Subject: [PATCH] =?UTF-8?q?f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
|
||||
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
|
||||
EOF
|
||||
test_expect_success 'format-patch wraps extremely long headers (rfc2047)' '
|
||||
rm -rf patches/ &&
|
||||
echo content >>file &&
|
||||
git add file &&
|
||||
git commit -m "$M512" &&
|
||||
git format-patch --stdout -1 >patch &&
|
||||
sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject &&
|
||||
test_cmp expect subject
|
||||
'
|
||||
|
||||
test_done
|
||||
|
Loading…
Reference in New Issue
Block a user