mailinfo: make ">From" in-body header check more robust

Since commit 81c5cf7 (mailinfo: skip bogus UNIX From line inside body, 2006-05-21), we have treated lines like ">From" in the body as headers. This makes "git am" work for people who erroneously paste the whole output from format-patch: From 12345abcd...fedcba543210 Mon Sep 17 00:00:00 2001 From: them Subject: [PATCH] whatever into their email body (assuming that an mbox writer then quotes "From" as ">From", as otherwise we would actually mailsplit on the in-body line). However, this has false positives if somebody actually has a commit body that starts with "From "; in this case we erroneously remove the line entirely from the commit message. We can make this check more robust by making sure the line actually looks like a real mbox "From" line. Inspect the line that begins with ">From " a more carefully to only skip lines that match the expected pattern (note that the datestamp part of the format-patch output is designed to be kept constant to help those who write magic(5) entries). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Sep 13, 2014 at 21:30 UTC 2da1f36671c3e3e28eba6a56d92249b6671c07d3
6 files changed +65 -1
builtin/mailinfo.c
+16 -1
@@ -288,6 +288,21 @@ static inline int cmp_header(const struct strbuf *line, const char *hdr)
288 line->buf[len] == ':' && isspace(line->buf[len + 1]);
289 }
290
291 +#define SAMPLE "From e6807f3efca28b30decfecb1732a56c7db1137ee Mon Sep 17 00:00:00 2001\n"
292 +static int is_format_patch_separator(const char *line, int len)
293 +{
294 + const char *cp;
295 +
296 + if (len != strlen(SAMPLE))
297 + return 0;
298 + if (!skip_prefix(line, "From ", &cp))
299 + return 0;
300 + if (strspn(cp, "0123456789abcdef") != 40)
301 + return 0;
302 + cp += 40;
303 + return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
304 +}
305 +
306 static int check_header(const struct strbuf *line,
307 struct strbuf *hdr_data[], int overwrite)
308 {
@@ -329,7 +344,7 @@ static int check_header(const struct strbuf *line,
344
345 /* for inbody stuff */
346 if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
332 - ret = 1; /* Should this return 0? */
347 + ret = is_format_patch_separator(line->buf + 1, line->len - 1);
348 goto check_header_out;
349 }
350 if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
t/t5100-mailinfo.sh
+18
@@ -89,4 +89,22 @@ test_expect_success 'mailinfo on from header without name works' '
89
90 '
91
92 +test_expect_success 'mailinfo finds headers after embedded From line' '
93 + mkdir embed-from &&
94 + git mailsplit -oembed-from "$TEST_DIRECTORY"/t5100/embed-from.in &&
95 + test_cmp "$TEST_DIRECTORY"/t5100/embed-from.in embed-from/0001 &&
96 + git mailinfo embed-from/msg embed-from/patch \
97 + <embed-from/0001 >embed-from/out &&
98 + test_cmp "$TEST_DIRECTORY"/t5100/embed-from.expect embed-from/out
99 +'
100 +
101 +test_expect_success 'mailinfo on message with quoted >From' '
102 + mkdir quoted-from &&
103 + git mailsplit -oquoted-from "$TEST_DIRECTORY"/t5100/quoted-from.in &&
104 + test_cmp "$TEST_DIRECTORY"/t5100/quoted-from.in quoted-from/0001 &&
105 + git mailinfo quoted-from/msg quoted-from/patch \
106 + <quoted-from/0001 >quoted-from/out &&
107 + test_cmp "$TEST_DIRECTORY"/t5100/quoted-from.expect quoted-from/msg
108 +'
109 +
110 test_done
t/t5100/embed-from.expect new
+5
@@ -0,0 +1,5 @@
1 +Author: Commit Author
2 +Email: commit@example.com
3 +Subject: patch subject
4 +Date: Sat, 13 Sep 2014 21:13:23 -0400
5 +
t/t5100/embed-from.in new
+13
@@ -0,0 +1,13 @@
1 +From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
2 +From: Email Author <email@example.com>
3 +Date: Sun, 25 May 2008 00:38:18 -0700
4 +Subject: [PATCH] email subject
5 +
6 +>From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
7 +From: Commit Author <commit@example.com>
8 +Date: Sat, 13 Sep 2014 21:13:23 -0400
9 +Subject: patch subject
10 +
11 +patch body
12 +---
13 +patch
t/t5100/quoted-from.expect new
+3
@@ -0,0 +1,3 @@
1 +>From the depths of history, we are stuck with the
2 +flaky mbox format.
3 +
t/t5100/quoted-from.in new
+10
@@ -0,0 +1,10 @@
1 +From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
2 +From: Author Name <somebody@example.com>
3 +Date: Sun, 25 May 2008 00:38:18 -0700
4 +Subject: [PATCH] testing quoted >From
5 +
6 +>From the depths of history, we are stuck with the
7 +flaky mbox format.
8 +
9 +---
10 +patch