@matheusbsbs / matheus-bagliano / commits / a1f6baa5c9
Fork 0

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>

Jeff King committed Feb 23, 2011 at 04:58 UTC a1f6baa5c97abc8b579fa7ac7c4dc21971bdc048
2 files changed +108 -8
pretty.c
+24 -8
@@ -216,7 +216,15 @@ static int is_rfc2047_special(char ch)
216 static void add_rfc2047(struct strbuf *sb, const char *line, int len,
217 const char *encoding)
218 {
219 - int i, last;
219 + static const int max_length = 78; /* per rfc2822 */
220 + int i;
221 + int line_len;
222 +
223 + /* How many bytes are already used on the current line? */
224 + for (i = sb->len - 1; i >= 0; i--)
225 + if (sb->buf[i] == '\n')
226 + break;
227 + line_len = sb->len - (i+1);
228
229 for (i = 0; i < len; i++) {
230 int ch = line[i];
@@ -225,14 +233,21 @@ static void add_rfc2047(struct strbuf *sb, const char *line, int len,
233 if ((i + 1 < len) && (ch == '=' && line[i+1] == '?'))
234 goto needquote;
235 }
228 - strbuf_add(sb, line, len);
236 + strbuf_add_wrapped_bytes(sb, line, len, 0, 1, max_length - line_len);
237 return;
238
239 needquote:
240 strbuf_grow(sb, len * 3 + strlen(encoding) + 100);
241 strbuf_addf(sb, "=?%s?q?", encoding);
234 - for (i = last = 0; i < len; i++) {
242 + line_len += strlen(encoding) + 5; /* 5 for =??q? */
243 + for (i = 0; i < len; i++) {
244 unsigned ch = line[i] & 0xFF;
245 +
246 + if (line_len >= max_length - 2) {
247 + strbuf_addf(sb, "?=\n =?%s?q?", encoding);
248 + line_len = strlen(encoding) + 5 + 1; /* =??q? plus SP */
249 + }
250 +
251 /*
252 * We encode ' ' using '=20' even though rfc2047
253 * allows using '_' for readability. Unfortunately,
@@ -240,12 +255,14 @@ needquote:
255 * leave the underscore in place.
256 */
257 if (is_rfc2047_special(ch) || ch == ' ') {
243 - strbuf_add(sb, line + last, i - last);
258 strbuf_addf(sb, "=%02X", ch);
245 - last = i + 1;
259 + line_len += 3;
260 + }
261 + else {
262 + strbuf_addch(sb, ch);
263 + line_len++;
264 }
265 }
248 - strbuf_add(sb, line + last, len - last);
266 strbuf_addstr(sb, "?=");
267 }
268
@@ -1106,11 +1123,10 @@ void pp_title_line(enum cmit_fmt fmt,
1123 const char *encoding,
1124 int need_8bit_cte)
1125 {
1109 - const char *line_separator = (fmt == CMIT_FMT_EMAIL) ? "\n " : " ";
1126 struct strbuf title;
1127
1128 strbuf_init(&title, 80);
1113 - *msg_p = format_subject(&title, *msg_p, line_separator);
1129 + *msg_p = format_subject(&title, *msg_p, " ");
1130
1131 strbuf_grow(sb, title.len + 1024);
1132 if (subject) {
t/t4014-format-patch.sh
+84
@@ -709,4 +709,88 @@ test_expect_success TTY 'format-patch --stdout paginates' '
709 test_path_is_missing .git/pager_used
710 '
711
712 +test_expect_success 'format-patch handles multi-line subjects' '
713 + rm -rf patches/ &&
714 + echo content >>file &&
715 + for i in one two three; do echo $i; done >msg &&
716 + git add file &&
717 + git commit -F msg &&
718 + git format-patch -o patches -1 &&
719 + grep ^Subject: patches/0001-one.patch >actual &&
720 + echo "Subject: [PATCH] one two three" >expect &&
721 + test_cmp expect actual
722 +'
723 +
724 +test_expect_success 'format-patch handles multi-line encoded subjects' '
725 + rm -rf patches/ &&
726 + echo content >>file &&
727 + for i in en två tre; do echo $i; done >msg &&
728 + git add file &&
729 + git commit -F msg &&
730 + git format-patch -o patches -1 &&
731 + grep ^Subject: patches/0001-en.patch >actual &&
732 + echo "Subject: [PATCH] =?UTF-8?q?en=20tv=C3=A5=20tre?=" >expect &&
733 + test_cmp expect actual
734 +'
735 +
736 +M8="foo bar "
737 +M64=$M8$M8$M8$M8$M8$M8$M8$M8
738 +M512=$M64$M64$M64$M64$M64$M64$M64$M64
739 +cat >expect <<'EOF'
740 +Subject: [PATCH] foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
741 + bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
742 + foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
743 + bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
744 + foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
745 + bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
746 + foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
747 + bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
748 + foo bar foo bar foo bar foo bar
749 +EOF
750 +test_expect_success 'format-patch wraps extremely long headers (ascii)' '
751 + echo content >>file &&
752 + git add file &&
753 + git commit -m "$M512" &&
754 + git format-patch --stdout -1 >patch &&
755 + sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject &&
756 + test_cmp expect subject
757 +'
758 +
759 +M8="föö bar "
760 +M64=$M8$M8$M8$M8$M8$M8$M8$M8
761 +M512=$M64$M64$M64$M64$M64$M64$M64$M64
762 +cat >expect <<'EOF'
763 +Subject: [PATCH] =?UTF-8?q?f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
764 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
765 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
766 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
767 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
768 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
769 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
770 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
771 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
772 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
773 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
774 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
775 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
776 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
777 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
778 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
779 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
780 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
781 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
782 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
783 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
784 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
785 +EOF
786 +test_expect_success 'format-patch wraps extremely long headers (rfc2047)' '
787 + rm -rf patches/ &&
788 + echo content >>file &&
789 + git add file &&
790 + git commit -m "$M512" &&
791 + git format-patch --stdout -1 >patch &&
792 + sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject &&
793 + test_cmp expect subject
794 +'
795 +
796 test_done