Merge branch 'ew/mboxrd-format-am'
authorJunio C Hamano <gitster@pobox.com>
Wed, 6 Jul 2016 20:38:11 +0000 (13:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 6 Jul 2016 20:38:11 +0000 (13:38 -0700)
Teach format-patch and mailsplit (hence "am") how a line that
happens to begin with "From " in the e-mail message is quoted with
">", so that these lines can be restored to their original shape.

* ew/mboxrd-format-am:
am: support --patch-format=mboxrd
mailsplit: support unescaping mboxrd messages
pretty: support "mboxrd" output format

1  2 
builtin/log.c
pretty.c
t/t4014-format-patch.sh
diff --combined builtin/log.c
index 8eef94f335489eeb91c247251b2dacddbcbb726b,6d6f368175f0edfef2953461ec7ca6790513e572..de47b4e89ae4d9aa1fbdd7187854d8fefc5ebe55
@@@ -674,9 -674,9 +674,9 @@@ static int auto_number = 1
  
  static char *default_attach = NULL;
  
 -static struct string_list extra_hdr;
 -static struct string_list extra_to;
 -static struct string_list extra_cc;
 +static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;
 +static struct string_list extra_to = STRING_LIST_INIT_NODUP;
 +static struct string_list extra_cc = STRING_LIST_INIT_NODUP;
  
  static void add_header(const char *value)
  {
@@@ -953,7 -953,7 +953,7 @@@ static void make_cover_letter(struct re
        struct pretty_print_context pp = {0};
        struct commit *head = list[0];
  
-       if (rev->commit_format != CMIT_FMT_EMAIL)
+       if (!cmit_fmt_is_mail(rev->commit_format))
                die(_("Cover letter needs email format"));
  
        committer = git_committer_info(0);
diff --combined pretty.c
index c3ec430220a5667685dbdeca5252df48bd65cd41,6abd8a12151635206d587f078015ad8b88b80b6f..929e5aef9760cd3a111f1c6339d012b301173bd8
+++ b/pretty.c
@@@ -92,6 -92,7 +92,7 @@@ static void setup_commit_formats(void
                { "medium",     CMIT_FMT_MEDIUM,        0,      8 },
                { "short",      CMIT_FMT_SHORT,         0,      0 },
                { "email",      CMIT_FMT_EMAIL,         0,      0 },
+               { "mboxrd",     CMIT_FMT_MBOXRD,        0,      0 },
                { "fuller",     CMIT_FMT_FULLER,        0,      8 },
                { "full",       CMIT_FMT_FULL,          0,      8 },
                { "oneline",    CMIT_FMT_ONELINE,       1,      0 }
@@@ -444,7 -445,7 +445,7 @@@ void pp_user_info(struct pretty_print_c
        if (pp->mailmap)
                map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
  
-       if (pp->fmt == CMIT_FMT_EMAIL) {
+       if (cmit_fmt_is_mail(pp->fmt)) {
                if (pp->from_ident && ident_cmp(pp->from_ident, &ident)) {
                        struct strbuf buf = STRBUF_INIT;
  
                            show_ident_date(&ident, &pp->date_mode));
                break;
        case CMIT_FMT_EMAIL:
+       case CMIT_FMT_MBOXRD:
                strbuf_addf(sb, "Date: %s\n",
                            show_ident_date(&ident, DATE_MODE(RFC2822)));
                break;
@@@ -535,7 -537,7 +537,7 @@@ static void add_merge_info(const struc
  {
        struct commit_list *parent = commit->parents;
  
-       if ((pp->fmt == CMIT_FMT_ONELINE) || (pp->fmt == CMIT_FMT_EMAIL) ||
+       if ((pp->fmt == CMIT_FMT_ONELINE) || (cmit_fmt_is_mail(pp->fmt)) ||
            !parent || !parent->next)
                return;
  
@@@ -1063,7 -1065,7 +1065,7 @@@ static size_t format_commit_one(struct 
        switch (placeholder[0]) {
        case 'C':
                if (starts_with(placeholder + 1, "(auto)")) {
 -                      c->auto_color = 1;
 +                      c->auto_color = want_color(c->pretty_ctx->color);
                        return 7; /* consumed 7 bytes, "C(auto)" */
                } else {
                        int ret = parse_color(sb, placeholder, c);
@@@ -1614,7 -1616,7 +1616,7 @@@ void pp_title_line(struct pretty_print_
        if (pp->after_subject) {
                strbuf_addstr(sb, pp->after_subject);
        }
-       if (pp->fmt == CMIT_FMT_EMAIL) {
+       if (cmit_fmt_is_mail(pp->fmt)) {
                strbuf_addch(sb, '\n');
        }
  
@@@ -1697,6 -1699,16 +1699,16 @@@ static void pp_handle_indent(struct pre
                strbuf_add(sb, line, linelen);
  }
  
+ static int is_mboxrd_from(const char *line, int len)
+ {
+       /*
+        * a line matching /^From $/ here would only have len == 4
+        * at this point because is_empty_line would've trimmed all
+        * trailing space
+        */
+       return len > 4 && starts_with(line + strspn(line, ">"), "From ");
+ }
  void pp_remainder(struct pretty_print_context *pp,
                  const char **msg_p,
                  struct strbuf *sb,
                else if (pp->expand_tabs_in_log)
                        strbuf_add_tabexpand(sb, pp->expand_tabs_in_log,
                                             line, linelen);
-               else
+               else {
+                       if (pp->fmt == CMIT_FMT_MBOXRD &&
+                                       is_mboxrd_from(line, linelen))
+                               strbuf_addch(sb, '>');
                        strbuf_add(sb, line, linelen);
+               }
                strbuf_addch(sb, '\n');
        }
  }
@@@ -1750,14 -1767,14 +1767,14 @@@ void pretty_print_commit(struct pretty_
        encoding = get_log_output_encoding();
        msg = reencoded = logmsg_reencode(commit, NULL, encoding);
  
-       if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
+       if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt))
                indent = 0;
  
        /*
         * We need to check and emit Content-type: to mark it
         * as 8-bit if we haven't done so.
         */
-       if (pp->fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) {
+       if (cmit_fmt_is_mail(pp->fmt) && need_8bit_cte == 0) {
                int i, ch, in_body;
  
                for (in_body = i = 0; (ch = msg[i]); i++) {
        msg = skip_empty_lines(msg);
  
        /* These formats treat the title line specially. */
-       if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
+       if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt))
                pp_title_line(pp, &msg, sb, encoding, need_8bit_cte);
  
        beginning_of_body = sb->len;
         * format.  Make sure we did not strip the blank line
         * between the header and the body.
         */
-       if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
+       if (cmit_fmt_is_mail(pp->fmt) && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
  
        unuse_commit_buffer(commit, reencoded);
diff --combined t/t4014-format-patch.sh
index 805dc9012d5f765eb40a67e58c1572b1fe316bd6,8a1cab5892a5549f22003f5f23823452aca61b78..1206c48392c36db04ef422813e76b370eeef20b2
@@@ -1072,7 -1072,7 +1072,7 @@@ test_expect_success '--from omits redun
  '
  
  test_expect_success 'in-body headers trigger content encoding' '
 -      GIT_AUTHOR_NAME="éxötìc" test_commit exotic &&
 +      test_env GIT_AUTHOR_NAME="éxötìc" test_commit exotic &&
        test_when_finished "git reset --hard HEAD^" &&
        git format-patch -1 --stdout --from >patch &&
        cat >expect <<-\EOF &&
@@@ -1565,4 -1565,45 +1565,45 @@@ test_expect_success 'format-patch --bas
        test_cmp expected actual
  '
  
+ test_expect_success 'format-patch --pretty=mboxrd' '
+       sp=" " &&
+       cat >msg <<-INPUT_END &&
+       mboxrd should escape the body
+       From could trip up a loose mbox parser
+       >From extra escape for reversibility
+       >>From extra escape for reversibility 2
+       from lower case not escaped
+       Fromm bad speling not escaped
+        From with leading space not escaped
+       F
+       From
+       From$sp
+       From    $sp
+       From    $sp
+       INPUT_END
+       cat >expect <<-INPUT_END &&
+       >From could trip up a loose mbox parser
+       >>From extra escape for reversibility
+       >>>From extra escape for reversibility 2
+       from lower case not escaped
+       Fromm bad speling not escaped
+        From with leading space not escaped
+       F
+       From
+       From
+       From
+       From
+       INPUT_END
+       C=$(git commit-tree HEAD^^{tree} -p HEAD <msg) &&
+       git format-patch --pretty=mboxrd --stdout -1 $C~1..$C >patch &&
+       git grep -h --no-index -A11 \
+               "^>From could trip up a loose mbox parser" patch >actual &&
+       test_cmp expect actual
+ '
  test_done