Merge branch 'jc/prettier-pretty-note'
authorJunio C Hamano <gitster@pobox.com>
Thu, 15 Nov 2012 18:25:05 +0000 (10:25 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 15 Nov 2012 18:25:05 +0000 (10:25 -0800)
Emit the notes attached to the commit in "format-patch --notes"
output after three-dashes.

* jc/prettier-pretty-note:
format-patch: add a blank line between notes and diffstat
Doc User-Manual: Patch cover letter, three dashes, and --notes
Doc format-patch: clarify --notes use case
Doc notes: Include the format-patch --notes option
Doc SubmittingPatches: Mention --notes option after "cover letter"
Documentation: decribe format-patch --notes
format-patch --notes: show notes after three-dashes
format-patch: append --signature after notes
pretty_print_commit(): do not append notes message
pretty: prepare notes message at a centralized place
format_note(): simplify API
pretty: remove reencode_commit_message()

1  2 
commit.h
notes.c
pretty.c
revision.c
t/t4014-format-patch.sh
diff --combined commit.h
index c4cd046160c7594c09bf0dd146e7cd862cb3fc0f,7b43e45017f9041cab074666a5813a5e666ae714..b6ad8f3f307a46cd5a0555ab346abaa20013b13a
+++ b/commit.h
@@@ -86,7 -86,7 +86,7 @@@ struct pretty_print_context 
        enum date_mode date_mode;
        unsigned date_mode_explicit:1;
        int need_8bit_cte;
-       int show_notes;
+       char *notes_message;
        struct reflog_walk_info *reflog_info;
        const char *output_encoding;
  };
@@@ -99,8 -99,6 +99,6 @@@ extern int has_non_ascii(const char *te
  struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
  extern char *logmsg_reencode(const struct commit *commit,
                             const char *output_encoding);
- extern char *reencode_commit_message(const struct commit *commit,
-                                    const char **encoding_p);
  extern void get_commit_format(const char *arg, struct rev_info *);
  extern const char *format_subject(struct strbuf *sb, const char *msg,
                                  const char *line_separator);
@@@ -222,8 -220,4 +220,8 @@@ struct commit *get_merge_parent(const c
  
  extern int parse_signed_commit(const unsigned char *sha1,
                               struct strbuf *message, struct strbuf *signature);
 +extern void print_commit_list(struct commit_list *list,
 +                            const char *format_cur,
 +                            const char *format_last);
 +
  #endif /* COMMIT_H */
diff --combined notes.c
index 19b0eaada28ffc603d0bbc1914303fd1de6bf22c,97097db9dc45a6dc81a29844aea61330c59cab11..f63fd572d6db125559e68556a8fc152a53700644
+++ b/notes.c
@@@ -848,16 -848,15 +848,16 @@@ int combine_notes_ignore(unsigned char 
        return 0;
  }
  
 -static int string_list_add_note_lines(struct string_list *sort_uniq_list,
 +/*
 + * Add the lines from the named object to list, with trailing
 + * newlines removed.
 + */
 +static int string_list_add_note_lines(struct string_list *list,
                                      const unsigned char *sha1)
  {
        char *data;
        unsigned long len;
        enum object_type t;
 -      struct strbuf buf = STRBUF_INIT;
 -      struct strbuf **lines = NULL;
 -      int i, list_index;
  
        if (is_null_sha1(sha1))
                return 0;
                return t != OBJ_BLOB || !data;
        }
  
 -      strbuf_attach(&buf, data, len, len + 1);
 -      lines = strbuf_split(&buf, '\n');
 -
 -      for (i = 0; lines[i]; i++) {
 -              if (lines[i]->buf[lines[i]->len - 1] == '\n')
 -                      strbuf_setlen(lines[i], lines[i]->len - 1);
 -              if (!lines[i]->len)
 -                      continue; /* skip empty lines */
 -              list_index = string_list_find_insert_index(sort_uniq_list,
 -                                                         lines[i]->buf, 0);
 -              if (list_index < 0)
 -                      continue; /* skip duplicate lines */
 -              string_list_insert_at_index(sort_uniq_list, list_index,
 -                                          lines[i]->buf);
 -      }
 -
 -      strbuf_list_free(lines);
 -      strbuf_release(&buf);
 +      /*
 +       * If the last line of the file is EOL-terminated, this will
 +       * add an empty string to the list.  But it will be removed
 +       * later, along with any empty strings that came from empty
 +       * lines within the file.
 +       */
 +      string_list_split(list, data, '\n', -1);
 +      free(data);
        return 0;
  }
  
@@@ -892,7 -901,7 +892,7 @@@ static int string_list_join_lines_helpe
  int combine_notes_cat_sort_uniq(unsigned char *cur_sha1,
                const unsigned char *new_sha1)
  {
 -      struct string_list sort_uniq_list = { NULL, 0, 0, 1 };
 +      struct string_list sort_uniq_list = STRING_LIST_INIT_DUP;
        struct strbuf buf = STRBUF_INIT;
        int ret = 1;
  
                goto out;
        if (string_list_add_note_lines(&sort_uniq_list, new_sha1))
                goto out;
 +      string_list_remove_empty_items(&sort_uniq_list, 0);
 +      sort_string_list(&sort_uniq_list);
 +      string_list_remove_duplicates(&sort_uniq_list, 0);
  
        /* create a new blob object from sort_uniq_list */
        if (for_each_string_list(&sort_uniq_list,
@@@ -943,18 -949,23 +943,18 @@@ void string_list_add_refs_by_glob(struc
  void string_list_add_refs_from_colon_sep(struct string_list *list,
                                         const char *globs)
  {
 -      struct strbuf globbuf = STRBUF_INIT;
 -      struct strbuf **split;
 +      struct string_list split = STRING_LIST_INIT_NODUP;
 +      char *globs_copy = xstrdup(globs);
        int i;
  
 -      strbuf_addstr(&globbuf, globs);
 -      split = strbuf_split(&globbuf, ':');
 +      string_list_split_in_place(&split, globs_copy, ':', -1);
 +      string_list_remove_empty_items(&split, 0);
  
 -      for (i = 0; split[i]; i++) {
 -              if (!split[i]->len)
 -                      continue;
 -              if (split[i]->buf[split[i]->len-1] == ':')
 -                      strbuf_setlen(split[i], split[i]->len-1);
 -              string_list_add_refs_by_glob(list, split[i]->buf);
 -      }
 +      for (i = 0; i < split.nr; i++)
 +              string_list_add_refs_by_glob(list, split.items[i].string);
  
 -      strbuf_list_free(split);
 -      strbuf_release(&globbuf);
 +      string_list_clear(&split, 0);
 +      free(globs_copy);
  }
  
  static int notes_display_config(const char *k, const char *v, void *cb)
@@@ -1193,10 -1204,11 +1193,11 @@@ void free_notes(struct notes_tree *t
   * If the given notes_tree is NULL, the internal/default notes_tree will be
   * used instead.
   *
-  * 'flags' is a bitwise combination of the flags for format_display_notes.
+  * (raw != 0) gives the %N userformat; otherwise, the note message is given
+  * for human consumption.
   */
  static void format_note(struct notes_tree *t, const unsigned char *object_sha1,
-                       struct strbuf *sb, const char *output_encoding, int flags)
+                       struct strbuf *sb, const char *output_encoding, int raw)
  {
        static const char utf8[] = "utf-8";
        const unsigned char *sha1;
        }
  
        if (output_encoding && *output_encoding &&
 -                      strcmp(utf8, output_encoding)) {
 +          !is_encoding_utf8(output_encoding)) {
                char *reencoded = reencode_string(msg, output_encoding, utf8);
                if (reencoded) {
                        free(msg);
        if (msglen && msg[msglen - 1] == '\n')
                msglen--;
  
-       if (flags & NOTES_SHOW_HEADER) {
+       if (!raw) {
                const char *ref = t->ref;
                if (!ref || !strcmp(ref, GIT_NOTES_DEFAULT_REF)) {
                        strbuf_addstr(sb, "\nNotes:\n");
        for (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {
                linelen = strchrnul(msg_p, '\n') - msg_p;
  
-               if (flags & NOTES_INDENT)
+               if (!raw)
                        strbuf_addstr(sb, "    ");
                strbuf_add(sb, msg_p, linelen);
                strbuf_addch(sb, '\n');
  }
  
  void format_display_notes(const unsigned char *object_sha1,
-                         struct strbuf *sb, const char *output_encoding, int flags)
+                         struct strbuf *sb, const char *output_encoding, int raw)
  {
        int i;
        assert(display_notes_trees);
        for (i = 0; display_notes_trees[i]; i++)
                format_note(display_notes_trees[i], object_sha1, sb,
-                           output_encoding, flags);
+                           output_encoding, raw);
  }
  
  int copy_note(struct notes_tree *t,
diff --combined pretty.c
index dba682828c2e005b71c0ccbb325fb666915984bc,1925e9c3e479f711f6f9f35b3e570e455ba45ac5..5bdc2e70bcd1e8fc0c7e625c3eac26397d8c96d2
+++ b/pretty.c
@@@ -231,7 -231,7 +231,7 @@@ static int is_rfc822_special(char ch
        }
  }
  
 -static int has_rfc822_specials(const char *s, int len)
 +static int needs_rfc822_quoting(const char *s, int len)
  {
        int i;
        for (i = 0; i < len; i++)
        return 0;
  }
  
 +static int last_line_length(struct strbuf *sb)
 +{
 +      int i;
 +
 +      /* How many bytes are already used on the last line? */
 +      for (i = sb->len - 1; i >= 0; i--)
 +              if (sb->buf[i] == '\n')
 +                      break;
 +      return sb->len - (i + 1);
 +}
 +
  static void add_rfc822_quoted(struct strbuf *out, const char *s, int len)
  {
        int i;
        strbuf_addch(out, '"');
  }
  
 -static int is_rfc2047_special(char ch)
 +enum rfc2047_type {
 +      RFC2047_SUBJECT,
 +      RFC2047_ADDRESS,
 +};
 +
 +static int is_rfc2047_special(char ch, enum rfc2047_type type)
  {
 -      return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
 +      /*
 +       * rfc2047, section 4.2:
 +       *
 +       *    8-bit values which correspond to printable ASCII characters other
 +       *    than "=", "?", and "_" (underscore), MAY be represented as those
 +       *    characters.  (But see section 5 for restrictions.)  In
 +       *    particular, SPACE and TAB MUST NOT be represented as themselves
 +       *    within encoded words.
 +       */
 +
 +      /*
 +       * rule out non-ASCII characters and non-printable characters (the
 +       * non-ASCII check should be redundant as isprint() is not localized
 +       * and only knows about ASCII, but be defensive about that)
 +       */
 +      if (non_ascii(ch) || !isprint(ch))
 +              return 1;
 +
 +      /*
 +       * rule out special printable characters (' ' should be the only
 +       * whitespace character considered printable, but be defensive and use
 +       * isspace())
 +       */
 +      if (isspace(ch) || ch == '=' || ch == '?' || ch == '_')
 +              return 1;
 +
 +      /*
 +       * rfc2047, section 5.3:
 +       *
 +       *    As a replacement for a 'word' entity within a 'phrase', for example,
 +       *    one that precedes an address in a From, To, or Cc header.  The ABNF
 +       *    definition for 'phrase' from RFC 822 thus becomes:
 +       *
 +       *    phrase = 1*( encoded-word / word )
 +       *
 +       *    In this case the set of characters that may be used in a "Q"-encoded
 +       *    'encoded-word' is restricted to: <upper and lower case ASCII
 +       *    letters, decimal digits, "!", "*", "+", "-", "/", "=", and "_"
 +       *    (underscore, ASCII 95.)>.  An 'encoded-word' that appears within a
 +       *    'phrase' MUST be separated from any adjacent 'word', 'text' or
 +       *    'special' by 'linear-white-space'.
 +       */
 +
 +      if (type != RFC2047_ADDRESS)
 +              return 0;
 +
 +      /* '=' and '_' are special cases and have been checked above */
 +      return !(isalnum(ch) || ch == '!' || ch == '*' || ch == '+' || ch == '-' || ch == '/');
  }
  
 -static void add_rfc2047(struct strbuf *sb, const char *line, int len,
 -                     const char *encoding)
 +static int needs_rfc2047_encoding(const char *line, int len,
 +                                enum rfc2047_type type)
  {
 -      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];
                if (non_ascii(ch) || ch == '\n')
 -                      goto needquote;
 +                      return 1;
                if ((i + 1 < len) && (ch == '=' && line[i+1] == '?'))
 -                      goto needquote;
 +                      return 1;
        }
 -      strbuf_add_wrapped_bytes(sb, line, len, 0, 1, max_length - line_len);
 -      return;
  
 -needquote:
 +      return 0;
 +}
 +
 +static void add_rfc2047(struct strbuf *sb, const char *line, int len,
 +                     const char *encoding, enum rfc2047_type type)
 +{
 +      static const int max_encoded_length = 76; /* per rfc2047 */
 +      int i;
 +      int line_len = last_line_length(sb);
 +
        strbuf_grow(sb, len * 3 + strlen(encoding) + 100);
        strbuf_addf(sb, "=?%s?q?", encoding);
        line_len += strlen(encoding) + 5; /* 5 for =??q? */
        for (i = 0; i < len; i++) {
                unsigned ch = line[i] & 0xFF;
 +              int is_special = is_rfc2047_special(ch, type);
 +
 +              /*
 +               * According to RFC 2047, we could encode the special character
 +               * ' ' (space) with '_' (underscore) for readability. But many
 +               * programs do not understand this and just leave the
 +               * underscore in place. Thus, we do nothing special here, which
 +               * causes ' ' to be encoded as '=20', avoiding this problem.
 +               */
  
 -              if (line_len >= max_length - 2) {
 +              if (line_len + 2 + (is_special ? 3 : 1) > max_encoded_length) {
                        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,
 -               * many programs do not understand this and just
 -               * leave the underscore in place.
 -               */
 -              if (is_rfc2047_special(ch) || ch == ' ' || ch == '\n') {
 +              if (is_special) {
                        strbuf_addf(sb, "=%02X", ch);
                        line_len += 3;
 -              }
 -              else {
 +              } else {
                        strbuf_addch(sb, ch);
                        line_len++;
                }
@@@ -387,7 -323,6 +387,7 @@@ void pp_user_info(const struct pretty_p
                  const char *what, struct strbuf *sb,
                  const char *line, const char *encoding)
  {
 +      int max_length = 78; /* per rfc2822 */
        char *date;
        int namelen;
        unsigned long time;
        if (pp->fmt == CMIT_FMT_EMAIL) {
                char *name_tail = strchr(line, '<');
                int display_name_length;
 -              int final_line;
                if (!name_tail)
                        return;
                while (line < name_tail && isspace(name_tail[-1]))
                        name_tail--;
                display_name_length = name_tail - line;
                strbuf_addstr(sb, "From: ");
 -              if (!has_rfc822_specials(line, display_name_length)) {
 -                      add_rfc2047(sb, line, display_name_length, encoding);
 -              } else {
 +              if (needs_rfc2047_encoding(line, display_name_length, RFC2047_ADDRESS)) {
 +                      add_rfc2047(sb, line, display_name_length,
 +                                              encoding, RFC2047_ADDRESS);
 +                      max_length = 76; /* per rfc2047 */
 +              } else if (needs_rfc822_quoting(line, display_name_length)) {
                        struct strbuf quoted = STRBUF_INIT;
                        add_rfc822_quoted(&quoted, line, display_name_length);
 -                      add_rfc2047(sb, quoted.buf, quoted.len, encoding);
 +                      strbuf_add_wrapped_bytes(sb, quoted.buf, quoted.len,
 +                                                      -6, 1, max_length);
                        strbuf_release(&quoted);
 +              } else {
 +                      strbuf_add_wrapped_bytes(sb, line, display_name_length,
 +                                                      -6, 1, max_length);
                }
 -              for (final_line = 0; final_line < sb->len; final_line++)
 -                      if (sb->buf[sb->len - final_line - 1] == '\n')
 -                              break;
 -              if (namelen - display_name_length + final_line > 78) {
 +              if (namelen - display_name_length + last_line_length(sb) > max_length) {
                        strbuf_addch(sb, '\n');
                        if (!isspace(name_tail[0]))
                                strbuf_addch(sb, ' ');
@@@ -571,7 -504,7 +571,7 @@@ char *logmsg_reencode(const struct comm
                return NULL;
        encoding = get_header(commit, "encoding");
        use_encoding = encoding ? encoding : utf8;
 -      if (!strcmp(use_encoding, output_encoding))
 +      if (same_encoding(use_encoding, output_encoding))
                if (encoding) /* we'll strip encoding header later */
                        out = xstrdup(commit->buffer);
                else
@@@ -1100,9 -1033,8 +1100,8 @@@ static size_t format_commit_one(struct 
                }
                return 0;       /* unknown %g placeholder */
        case 'N':
-               if (c->pretty_ctx->show_notes) {
-                       format_display_notes(commit->object.sha1, sb,
-                                   get_log_output_encoding(), 0);
+               if (c->pretty_ctx->notes_message) {
+                       strbuf_addstr(sb, c->pretty_ctx->notes_message);
                        return 1;
                }
                return 0;
@@@ -1345,7 -1277,6 +1344,7 @@@ void pp_title_line(const struct pretty_
                   const char *encoding,
                   int need_8bit_cte)
  {
 +      static const int max_length = 78; /* per rfc2047 */
        struct strbuf title;
  
        strbuf_init(&title, 80);
        strbuf_grow(sb, title.len + 1024);
        if (pp->subject) {
                strbuf_addstr(sb, pp->subject);
 -              add_rfc2047(sb, title.buf, title.len, encoding);
 +              if (needs_rfc2047_encoding(title.buf, title.len, RFC2047_SUBJECT))
 +                      add_rfc2047(sb, title.buf, title.len,
 +                                              encoding, RFC2047_SUBJECT);
 +              else
 +                      strbuf_add_wrapped_bytes(sb, title.buf, title.len,
 +                                       -last_line_length(sb), 1, max_length);
        } else {
                strbuf_addbuf(sb, &title);
        }
@@@ -1414,16 -1340,6 +1413,6 @@@ void pp_remainder(const struct pretty_p
        }
  }
  
- char *reencode_commit_message(const struct commit *commit, const char **encoding_p)
- {
-       const char *encoding;
-       encoding = get_log_output_encoding();
-       if (encoding_p)
-               *encoding_p = encoding;
-       return logmsg_reencode(commit, encoding);
- }
  void pretty_print_commit(const struct pretty_print_context *pp,
                         const struct commit *commit,
                         struct strbuf *sb)
                return;
        }
  
-       reencoded = reencode_commit_message(commit, &encoding);
+       encoding = get_log_output_encoding();
+       reencoded = logmsg_reencode(commit, encoding);
        if (reencoded) {
                msg = reencoded;
        }
        if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
  
-       if (pp->show_notes)
-               format_display_notes(commit->object.sha1, sb, encoding,
-                                    NOTES_SHOW_HEADER | NOTES_INDENT);
        free(reencoded);
  }
  
diff --combined revision.c
index d7d621cdbf224f5a2bc1780e9602dcd3d08c1d57,ddfba11ca806e13990123432453e3d1cc56e9feb..95d21e6472921ab993373ef1848357379440d46e
@@@ -1048,9 -1048,9 +1048,9 @@@ void init_revisions(struct rev_info *re
  
        revs->commit_format = CMIT_FMT_DEFAULT;
  
 +      init_grep_defaults();
 +      grep_init(&revs->grep_filter, prefix);
        revs->grep_filter.status_only = 1;
 -      revs->grep_filter.pattern_tail = &(revs->grep_filter.pattern_list);
 -      revs->grep_filter.header_tail = &(revs->grep_filter.header_list);
        revs->grep_filter.regflags = REG_NEWLINE;
  
        diff_setup(&revs->diffopt);
@@@ -1603,17 -1603,13 +1603,17 @@@ static int handle_revision_opt(struct r
                return argcount;
        } else if (!strcmp(arg, "--grep-debug")) {
                revs->grep_filter.debug = 1;
 +      } else if (!strcmp(arg, "--basic-regexp")) {
 +              grep_set_pattern_type_option(GREP_PATTERN_TYPE_BRE, &revs->grep_filter);
        } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
 -              revs->grep_filter.regflags |= REG_EXTENDED;
 +              grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, &revs->grep_filter);
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
                revs->grep_filter.regflags |= REG_ICASE;
                DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
        } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
 -              revs->grep_filter.fixed = 1;
 +              grep_set_pattern_type_option(GREP_PATTERN_TYPE_FIXED, &revs->grep_filter);
 +      } else if (!strcmp(arg, "--perl-regexp")) {
 +              grep_set_pattern_type_option(GREP_PATTERN_TYPE_PCRE, &revs->grep_filter);
        } else if (!strcmp(arg, "--all-match")) {
                revs->grep_filter.all_match = 1;
        } else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
@@@ -1897,8 -1893,6 +1897,8 @@@ int setup_revisions(int argc, const cha
        revs->diffopt.abbrev = revs->abbrev;
        diff_setup_done(&revs->diffopt);
  
 +      grep_commit_pattern_type(GREP_PATTERN_TYPE_UNSPECIFIED,
 +                               &revs->grep_filter);
        compile_grep_patterns(&revs->grep_filter);
  
        if (revs->reverse && revs->reflog_info)
@@@ -2242,7 -2236,7 +2242,7 @@@ static int commit_match(struct commit *
                if (!buf.len)
                        strbuf_addstr(&buf, commit->buffer);
                format_display_notes(commit->object.sha1, &buf,
-                                    get_log_output_encoding(), 0);
+                                    get_log_output_encoding(), 1);
        }
  
        /* Find either in the commit object, or in the temporary */
diff --combined t/t4014-format-patch.sh
index ad9f69e6074311cc3828896bd8409822344d6f62,9750ba69962a2d5adf393882402480b9a94091c8..16a4ca1d6061122cf7ed3abd68390e27794f79b0
@@@ -110,107 -110,73 +110,107 @@@ test_expect_success 'replay did not scr
  
  test_expect_success 'extra headers' '
  
 -      git config format.headers "To: R. E. Cipient <rcipient@example.com>
 +      git config format.headers "To: R E Cipient <rcipient@example.com>
  " &&
 -      git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>
 +      git config --add format.headers "Cc: S E Cipient <scipient@example.com>
  " &&
        git format-patch --stdout master..side > patch2 &&
        sed -e "/^\$/q" patch2 > hdrs2 &&
 -      grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs2 &&
 -      grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs2
 +      grep "^To: R E Cipient <rcipient@example.com>\$" hdrs2 &&
 +      grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs2
  
  '
  
  test_expect_success 'extra headers without newlines' '
  
 -      git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
 -      git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>" &&
 +      git config --replace-all format.headers "To: R E Cipient <rcipient@example.com>" &&
 +      git config --add format.headers "Cc: S E Cipient <scipient@example.com>" &&
        git format-patch --stdout master..side >patch3 &&
        sed -e "/^\$/q" patch3 > hdrs3 &&
 -      grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs3 &&
 -      grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs3
 +      grep "^To: R E Cipient <rcipient@example.com>\$" hdrs3 &&
 +      grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs3
  
  '
  
  test_expect_success 'extra headers with multiple To:s' '
  
 -      git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
 -      git config --add format.headers "To: S. E. Cipient <scipient@example.com>" &&
 +      git config --replace-all format.headers "To: R E Cipient <rcipient@example.com>" &&
 +      git config --add format.headers "To: S E Cipient <scipient@example.com>" &&
        git format-patch --stdout master..side > patch4 &&
        sed -e "/^\$/q" patch4 > hdrs4 &&
 -      grep "^To: R. E. Cipient <rcipient@example.com>,\$" hdrs4 &&
 -      grep "^ *S. E. Cipient <scipient@example.com>\$" hdrs4
 +      grep "^To: R E Cipient <rcipient@example.com>,\$" hdrs4 &&
 +      grep "^ *S E Cipient <scipient@example.com>\$" hdrs4
  '
  
 -test_expect_success 'additional command line cc' '
 +test_expect_success 'additional command line cc (ascii)' '
  
 -      git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
 +      git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
 +      git format-patch --cc="S E Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 &&
 +      grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch5 &&
 +      grep "^ *S E Cipient <scipient@example.com>\$" patch5
 +'
 +
 +test_expect_failure 'additional command line cc (rfc822)' '
 +
 +      git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
        git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 &&
 -      grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch5 &&
 -      grep "^ *S. E. Cipient <scipient@example.com>\$" patch5
 +      grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch5 &&
 +      grep "^ *"S. E. Cipient" <scipient@example.com>\$" patch5
  '
  
  test_expect_success 'command line headers' '
  
        git config --unset-all format.headers &&
 -      git format-patch --add-header="Cc: R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch6 &&
 -      grep "^Cc: R. E. Cipient <rcipient@example.com>\$" patch6
 +      git format-patch --add-header="Cc: R E Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch6 &&
 +      grep "^Cc: R E Cipient <rcipient@example.com>\$" patch6
  '
  
  test_expect_success 'configuration headers and command line headers' '
  
 -      git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
 -      git format-patch --add-header="Cc: S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch7 &&
 -      grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch7 &&
 -      grep "^ *S. E. Cipient <scipient@example.com>\$" patch7
 +      git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
 +      git format-patch --add-header="Cc: S E Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch7 &&
 +      grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch7 &&
 +      grep "^ *S E Cipient <scipient@example.com>\$" patch7
  '
  
 -test_expect_success 'command line To: header' '
 +test_expect_success 'command line To: header (ascii)' '
  
        git config --unset-all format.headers &&
 +      git format-patch --to="R E Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
 +      grep "^To: R E Cipient <rcipient@example.com>\$" patch8
 +'
 +
 +test_expect_failure 'command line To: header (rfc822)' '
 +
        git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
 -      grep "^To: R. E. Cipient <rcipient@example.com>\$" patch8
 +      grep "^To: "R. E. Cipient" <rcipient@example.com>\$" patch8
  '
  
 -test_expect_success 'configuration To: header' '
 +test_expect_failure 'command line To: header (rfc2047)' '
 +
 +      git format-patch --to="R Ä Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
 +      grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" patch8
 +'
 +
 +test_expect_success 'configuration To: header (ascii)' '
 +
 +      git config format.to "R E Cipient <rcipient@example.com>" &&
 +      git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
 +      grep "^To: R E Cipient <rcipient@example.com>\$" patch9
 +'
 +
 +test_expect_failure 'configuration To: header (rfc822)' '
  
        git config format.to "R. E. Cipient <rcipient@example.com>" &&
        git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
 -      grep "^To: R. E. Cipient <rcipient@example.com>\$" patch9
 +      grep "^To: "R. E. Cipient" <rcipient@example.com>\$" patch9
 +'
 +
 +test_expect_failure 'configuration To: header (rfc2047)' '
 +
 +      git config format.to "R Ä Cipient <rcipient@example.com>" &&
 +      git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
 +      grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" patch9
  '
  
  # check_patch <patch>: Verify that <patch> looks like a half-sane
@@@ -224,11 -190,11 +224,11 @@@ check_patch () 
  test_expect_success '--no-to overrides config.to' '
  
        git config --replace-all format.to \
 -              "R. E. Cipient <rcipient@example.com>" &&
 +              "R E Cipient <rcipient@example.com>" &&
        git format-patch --no-to --stdout master..side |
        sed -e "/^\$/q" >patch10 &&
        check_patch patch10 &&
 -      ! grep "^To: R. E. Cipient <rcipient@example.com>\$" patch10
 +      ! grep "^To: R E Cipient <rcipient@example.com>\$" patch10
  '
  
  test_expect_success '--no-to and --to replaces config.to' '
  test_expect_success '--no-cc overrides config.cc' '
  
        git config --replace-all format.cc \
 -              "C. E. Cipient <rcipient@example.com>" &&
 +              "C E Cipient <rcipient@example.com>" &&
        git format-patch --no-cc --stdout master..side |
        sed -e "/^\$/q" >patch12 &&
        check_patch patch12 &&
 -      ! grep "^Cc: C. E. Cipient <rcipient@example.com>\$" patch12
 +      ! grep "^Cc: C E Cipient <rcipient@example.com>\$" patch12
  '
  
  test_expect_success '--no-add-header overrides config.headers' '
  
        git config --replace-all format.headers \
 -              "Header1: B. E. Cipient <rcipient@example.com>" &&
 +              "Header1: B E Cipient <rcipient@example.com>" &&
        git format-patch --no-add-header --stdout master..side |
        sed -e "/^\$/q" >patch13 &&
        check_patch patch13 &&
 -      ! grep "^Header1: B. E. Cipient <rcipient@example.com>\$" patch13
 +      ! grep "^Header1: B E Cipient <rcipient@example.com>\$" patch13
  '
  
  test_expect_success 'multiple files' '
@@@ -650,8 -616,19 +650,19 @@@ test_expect_success 'format-patch --in-
  '
  
  test_expect_success 'format-patch --signoff' '
-       git format-patch -1 --signoff --stdout |
-       grep "^Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+       git format-patch -1 --signoff --stdout >out &&
+       grep "^Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" out
+ '
+ test_expect_success 'format-patch --notes --signoff' '
+       git notes --ref test add -m "test message" HEAD &&
+       git format-patch -1 --signoff --stdout --notes=test >out &&
+       # Three dashes must come after S-o-b
+       ! sed "/^Signed-off-by: /q" out | grep "test message" &&
+       sed "1,/^Signed-off-by: /d" out | grep "test message" &&
+       # Notes message must come after three dashes
+       ! sed "/^---$/q" out | grep "test message" &&
+       sed "1,/^---$/d" out | grep "test message"
  '
  
  echo "fatal: --name-only does not make sense" > expect.name-only
@@@ -786,14 -763,16 +797,14 @@@ M64=$M8$M8$M8$M8$M8$M8$M8$M
  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
 + 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)' '
 +test_expect_success 'format-patch wraps extremely long subject (ascii)' '
        echo content >>file &&
        git add file &&
        git commit -m "$M512" &&
@@@ -806,31 -785,30 +817,31 @@@ 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?=
 +Subject: [PATCH] =?UTF-8?q?f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
 + =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
 + =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?=
 + =?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?=
 + =?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3?=
 + =?UTF-8?q?=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?=
 + =?UTF-8?q?=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
 + =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
 + =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?=
 + =?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?=
 + =?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3?=
 + =?UTF-8?q?=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?=
 + =?UTF-8?q?=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
 + =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
 + =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?=
 + =?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?=
 + =?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3?=
 + =?UTF-8?q?=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?=
 + =?UTF-8?q?=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
 + =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
  EOF
 -test_expect_success 'format-patch wraps extremely long headers (rfc2047)' '
 +test_expect_success 'format-patch wraps extremely long subject (rfc2047)' '
        rm -rf patches/ &&
        echo content >>file &&
        git add file &&
        test_cmp expect subject
  '
  
 -M8="foo_bar_"
 -M64=$M8$M8$M8$M8$M8$M8$M8$M8
 -cat >expect <<EOF
 -From: $M64
 - <foobar@foo.bar>
 -EOF
 -test_expect_success 'format-patch wraps non-quotable headers' '
 -      rm -rf patches/ &&
 -      echo content >>file &&
 -      git add file &&
 -      git commit -mfoo --author "$M64 <foobar@foo.bar>" &&
 -      git format-patch --stdout -1 >patch &&
 -      sed -n "/^From: /p; /^ /p; /^$/q" <patch >from &&
 -      test_cmp expect from
 -'
 -
  check_author() {
        echo content >>file &&
        git add file &&
        GIT_AUTHOR_NAME=$1 git commit -m author-check &&
        git format-patch --stdout -1 >patch &&
 -      grep ^From: patch >actual &&
 +      sed -n "/^From: /p; /^ /p; /^$/q" <patch >actual &&
        test_cmp expect actual
  }
  
  cat >expect <<'EOF'
  From: "Foo B. Bar" <author@example.com>
  EOF
 -test_expect_success 'format-patch quotes dot in headers' '
 +test_expect_success 'format-patch quotes dot in from-headers' '
        check_author "Foo B. Bar"
  '
  
  cat >expect <<'EOF'
  From: "Foo \"The Baz\" Bar" <author@example.com>
  EOF
 -test_expect_success 'format-patch quotes double-quote in headers' '
 +test_expect_success 'format-patch quotes double-quote in from-headers' '
        check_author "Foo \"The Baz\" Bar"
  '
  
  cat >expect <<'EOF'
 -From: =?UTF-8?q?"F=C3=B6o=20B.=20Bar"?= <author@example.com>
 +From: =?UTF-8?q?F=C3=B6o=20Bar?= <author@example.com>
 +EOF
 +test_expect_success 'format-patch uses rfc2047-encoded from-headers when necessary' '
 +      check_author "Föo Bar"
 +'
 +
 +cat >expect <<'EOF'
 +From: =?UTF-8?q?F=C3=B6o=20B=2E=20Bar?= <author@example.com>
  EOF
 -test_expect_success 'rfc2047-encoded headers also double-quote 822 specials' '
 +test_expect_success 'rfc2047-encoded from-headers leave no rfc822 specials' '
        check_author "Föo B. Bar"
  '
  
 +cat >expect <<EOF
 +From: foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_
 + <author@example.com>
 +EOF
 +test_expect_success 'format-patch wraps moderately long from-header (ascii)' '
 +      check_author "foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_"
 +'
 +
 +cat >expect <<'EOF'
 +From: 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 <author@example.com>
 +EOF
 +test_expect_success 'format-patch wraps extremely long from-header (ascii)' '
 +      check_author "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"
 +'
 +
 +cat >expect <<'EOF'
 +From: "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" <author@example.com>
 +EOF
 +test_expect_success 'format-patch wraps extremely long from-header (rfc822)' '
 +      check_author "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"
 +'
 +
 +cat >expect <<'EOF'
 +From: =?UTF-8?q?Fo=C3=B6=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo?=
 + =?UTF-8?q?=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20?=
 + =?UTF-8?q?Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar?=
 + =?UTF-8?q?=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20?=
 + =?UTF-8?q?Foo=20Bar=20Foo=20Bar?= <author@example.com>
 +EOF
 +test_expect_success 'format-patch wraps extremely long from-header (rfc2047)' '
 +      check_author "Foö 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"
 +'
 +
  cat >expect <<'EOF'
  Subject: header with . in it
  EOF