Merge branch 'jc/format-patch-reroll'
authorJunio C Hamano <gitster@pobox.com>
Sat, 12 Jan 2013 02:34:10 +0000 (18:34 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sat, 12 Jan 2013 02:34:10 +0000 (18:34 -0800)
Teach "format-patch" to prefix v4- to its output files for the
fourth iteration of a patch series, to make it easier for the
submitter to keep separate copies for iterations.

* jc/format-patch-reroll:
format-patch: give --reroll-count a short synonym -v
format-patch: document and test --reroll-count
format-patch: add --reroll-count=$N option
get_patch_filename(): split into two functions
get_patch_filename(): drop "just-numbers" hack
get_patch_filename(): simplify function signature
builtin/log.c: stop using global patch_suffix
builtin/log.c: drop redundant "numbered_files" parameter from make_cover_letter()
builtin/log.c: drop unused "numbered" parameter from make_cover_letter()

1  2 
Documentation/git-format-patch.txt
builtin/log.c
log-tree.c
revision.h
t/t4014-format-patch.sh
index 259dce49945089de03ad2c0ac8ae8e56cfdc04e0,ae3212e8d8b1bcdbf19c37295b8d993c33bb3e9b..9a914d01597011d6703e6b560d707c221cba177e
@@@ -18,9 -18,9 +18,9 @@@ SYNOPSI
                   [--start-number <n>] [--numbered-files]
                   [--in-reply-to=Message-Id] [--suffix=.<sfx>]
                   [--ignore-if-in-upstream]
-                  [--subject-prefix=Subject-Prefix]
+                  [--subject-prefix=Subject-Prefix] [(--reroll-count|-v) <n>]
                   [--to=<email>] [--cc=<email>]
 -                 [--cover-letter] [--quiet]
 +                 [--cover-letter] [--quiet] [--notes[=<ref>]]
                   [<common diff options>]
                   [ <since> | <revision range> ]
  
@@@ -166,6 -166,15 +166,15 @@@ will want to ensure that threading is d
        allows for useful naming of a patch series, and can be
        combined with the `--numbered` option.
  
+ -v <n>::
+ --reroll-count=<n>::
+       Mark the series as the <n>-th iteration of the topic. The
+       output filenames have `v<n>` pretended to them, and the
+       subject prefix ("PATCH" by default, but configurable via the
+       `--subject-prefix` option) has ` v<n>` appended to it.  E.g.
+       `--reroll-count=4` may produce `v4-0001-add-makefile.patch`
+       file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
  --to=<email>::
        Add a `To:` header to the email headers. This is in addition
        to any configured headers, and may be used multiple times.
        containing the shortlog and the overall diffstat.  You can
        fill in a description in the file before sending it out.
  
 +--notes[=<ref>]::
 +      Append the notes (see linkgit:git-notes[1]) for the commit
 +      after the three-dash line.
 ++
 +The expected use case of this is to write supporting explanation for
 +the commit that does not belong to the commit log message proper,
 +and include it with the patch submission. While one can simply write
 +these explanations after `format-patch` has run but before sending,
 +keeping them as git notes allows them to be maintained between versions
 +of the patch series (but see the discussion of the `notes.rewrite`
 +configuration options in linkgit:git-notes[1] to use this workflow).
 +
  --[no]-signature=<signature>::
        Add a signature to each message produced. Per RFC 3676 the signature
        is separated from the body by a line with '-- ' on it. If the
diff --combined builtin/log.c
index 3899b1d43acc8c51e7609be94469eac057116440,08e8a9d2a3e5d19833cb0f48347f6ba214f280c9..5a4055eb0b0d4965785b6f69dbe92efe251c54dd
@@@ -351,8 -351,7 +351,8 @@@ static int git_log_config(const char *v
        }
        if (!prefixcmp(var, "color.decorate."))
                return parse_decorate_color_config(var, 15, value);
 -
 +      if (grep_config(var, value, cb) < 0)
 +              return -1;
        return git_diff_ui_config(var, value, cb);
  }
  
@@@ -361,7 -360,6 +361,7 @@@ int cmd_whatchanged(int argc, const cha
        struct rev_info rev;
        struct setup_revision_opt opt;
  
 +      init_grep_defaults();
        git_config(git_log_config, NULL);
  
        init_revisions(&rev, prefix);
@@@ -452,7 -450,6 +452,7 @@@ int cmd_show(int argc, const char **arg
        struct pathspec match_all;
        int i, count, ret = 0;
  
 +      init_grep_defaults();
        git_config(git_log_config, NULL);
  
        init_pathspec(&match_all, NULL);
@@@ -533,7 -530,6 +533,7 @@@ int cmd_log_reflog(int argc, const cha
        struct rev_info rev;
        struct setup_revision_opt opt;
  
 +      init_grep_defaults();
        git_config(git_log_config, NULL);
  
        init_revisions(&rev, prefix);
@@@ -556,7 -552,6 +556,7 @@@ int cmd_log(int argc, const char **argv
        struct rev_info rev;
        struct setup_revision_opt opt;
  
 +      init_grep_defaults();
        git_config(git_log_config, NULL);
  
        init_revisions(&rev, prefix);
@@@ -678,7 -673,7 +678,7 @@@ static int reopen_stdout(struct commit 
                         struct rev_info *rev, int quiet)
  {
        struct strbuf filename = STRBUF_INIT;
-       int suffix_len = strlen(fmt_patch_suffix) + 1;
+       int suffix_len = strlen(rev->patch_suffix) + 1;
  
        if (output_directory) {
                strbuf_addstr(&filename, output_directory);
                        strbuf_addch(&filename, '/');
        }
  
-       get_patch_filename(commit, subject, rev->nr, fmt_patch_suffix, &filename);
+       if (rev->numbered_files)
+               strbuf_addf(&filename, "%d", rev->nr);
+       else if (commit)
+               fmt_output_commit(&filename, commit, rev);
+       else
+               fmt_output_subject(&filename, subject, rev);
  
        if (!quiet)
                fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
@@@ -773,7 -773,6 +778,6 @@@ static void add_branch_description(stru
  }
  
  static void make_cover_letter(struct rev_info *rev, int use_stdout,
-                             int numbered, int numbered_files,
                              struct commit *origin,
                              int nr, struct commit **list, struct commit *head,
                              const char *branch_name,
        committer = git_committer_info(0);
  
        if (!use_stdout &&
-           reopen_stdout(NULL, numbered_files ? NULL : "cover-letter", rev, quiet))
+           reopen_stdout(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
                return;
  
        log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
@@@ -1016,9 -1015,8 +1020,9 @@@ static char *find_branch_name(struct re
  {
        int i, positive = -1;
        unsigned char branch_sha1[20];
 -      struct strbuf buf = STRBUF_INIT;
 -      const char *branch;
 +      const unsigned char *tip_sha1;
 +      const char *ref;
 +      char *full_ref, *branch = NULL;
  
        for (i = 0; i < rev->cmdline.nr; i++) {
                if (rev->cmdline.rev[i].flags & UNINTERESTING)
                else
                        return NULL;
        }
 -      if (positive < 0)
 +      if (0 <= positive) {
 +              ref = rev->cmdline.rev[positive].name;
 +              tip_sha1 = rev->cmdline.rev[positive].item->sha1;
 +      } else if (!rev->cmdline.nr && rev->pending.nr == 1 &&
 +                 !strcmp(rev->pending.objects[0].name, "HEAD")) {
 +              /*
 +               * No actual ref from command line, but "HEAD" from
 +               * rev->def was added in setup_revisions()
 +               * e.g. format-patch --cover-letter -12
 +               */
 +              ref = "HEAD";
 +              tip_sha1 = rev->pending.objects[0].item->sha1;
 +      } else {
                return NULL;
 -      strbuf_addf(&buf, "refs/heads/%s", rev->cmdline.rev[positive].name);
 -      branch = resolve_ref_unsafe(buf.buf, branch_sha1, 1, NULL);
 -      if (!branch ||
 -          prefixcmp(branch, "refs/heads/") ||
 -          hashcmp(rev->cmdline.rev[positive].item->sha1, branch_sha1))
 -              branch = NULL;
 -      strbuf_release(&buf);
 -      if (branch)
 -              return xstrdup(rev->cmdline.rev[positive].name);
 -      return NULL;
 +      }
 +      if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
 +          !prefixcmp(full_ref, "refs/heads/") &&
 +          !hashcmp(tip_sha1, branch_sha1))
 +              branch = xstrdup(full_ref + strlen("refs/heads/"));
 +      free(full_ref);
 +      return branch;
  }
  
  int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int nr = 0, total, i;
        int use_stdout = 0;
        int start_number = -1;
-       int numbered_files = 0;         /* _just_ numbers */
+       int just_numbers = 0;
        int ignore_if_in_upstream = 0;
        int cover_letter = 0;
        int boundary_count = 0;
        struct strbuf buf = STRBUF_INIT;
        int use_patch_format = 0;
        int quiet = 0;
+       int reroll_count = -1;
        char *branch_name = NULL;
        const struct option builtin_format_patch_options[] = {
                { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
                            N_("print patches to standard out")),
                OPT_BOOLEAN(0, "cover-letter", &cover_letter,
                            N_("generate a cover letter")),
-               OPT_BOOLEAN(0, "numbered-files", &numbered_files,
+               OPT_BOOLEAN(0, "numbered-files", &just_numbers,
                            N_("use simple number sequence for output file names")),
                OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"),
                            N_("use <sfx> instead of '.patch'")),
                OPT_INTEGER(0, "start-number", &start_number,
                            N_("start numbering patches at <n> instead of 1")),
+               OPT_INTEGER('v', "reroll-count", &reroll_count,
+                           N_("mark the series as Nth re-roll")),
                { OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"),
                            N_("Use [<prefix>] instead of [PATCH]"),
                            PARSE_OPT_NONEG, subject_prefix_callback },
        extra_hdr.strdup_strings = 1;
        extra_to.strdup_strings = 1;
        extra_cc.strdup_strings = 1;
 +      init_grep_defaults();
        git_config(git_format_config, NULL);
        init_revisions(&rev, prefix);
        rev.commit_format = CMIT_FMT_EMAIL;
                             PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
                             PARSE_OPT_KEEP_DASHDASH);
  
+       if (0 < reroll_count) {
+               struct strbuf sprefix = STRBUF_INIT;
+               strbuf_addf(&sprefix, "%s v%d",
+                           rev.subject_prefix, reroll_count);
+               rev.reroll_count = reroll_count;
+               rev.subject_prefix = strbuf_detach(&sprefix, NULL);
+       }
        if (do_signoff) {
                const char *committer;
                const char *endpos;
                const char *msgid = clean_message_id(in_reply_to);
                string_list_append(rev.ref_message_ids, msgid);
        }
-       rev.numbered_files = numbered_files;
+       rev.numbered_files = just_numbers;
        rev.patch_suffix = fmt_patch_suffix;
        if (cover_letter) {
                if (thread)
                        gen_message_id(&rev, "cover");
-               make_cover_letter(&rev, use_stdout, numbered, numbered_files,
+               make_cover_letter(&rev, use_stdout,
                                  origin, nr, list, head, branch_name, quiet);
                total++;
                start_number--;
                }
  
                if (!use_stdout &&
-                   reopen_stdout(numbered_files ? NULL : commit, NULL, &rev, quiet))
+                   reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
                        die(_("Failed to create output files"));
                shown = log_tree_commit(&rev, commit);
                free(commit->buffer);
diff --combined log-tree.c
index 8876c736d4afbb92d862598861145abe0f408cec,5dc126b65c744b09bdde3a496c47c7ba2faeab8d..f8487f8a8a7694ce09f1b079c701bceabf688b20
@@@ -299,26 -299,34 +299,34 @@@ static unsigned int digits_in_number(un
        return result;
  }
  
- void get_patch_filename(struct commit *commit, const char *subject, int nr,
-                       const char *suffix, struct strbuf *buf)
+ void fmt_output_subject(struct strbuf *filename,
+                       const char *subject,
+                       struct rev_info *info)
  {
-       int suffix_len = strlen(suffix) + 1;
-       int start_len = buf->len;
-       strbuf_addf(buf, commit || subject ? "%04d-" : "%d", nr);
-       if (commit || subject) {
-               int max_len = start_len + FORMAT_PATCH_NAME_MAX - suffix_len;
-               struct pretty_print_context ctx = {0};
-               if (subject)
-                       strbuf_addstr(buf, subject);
-               else if (commit)
-                       format_commit_message(commit, "%f", buf, &ctx);
-               if (max_len < buf->len)
-                       strbuf_setlen(buf, max_len);
-               strbuf_addstr(buf, suffix);
-       }
+       const char *suffix = info->patch_suffix;
+       int nr = info->nr;
+       int start_len = filename->len;
+       int max_len = start_len + FORMAT_PATCH_NAME_MAX - (strlen(suffix) + 1);
+       if (0 < info->reroll_count)
+               strbuf_addf(filename, "v%d-", info->reroll_count);
+       strbuf_addf(filename, "%04d-%s", nr, subject);
+       if (max_len < filename->len)
+               strbuf_setlen(filename, max_len);
+       strbuf_addstr(filename, suffix);
+ }
+ void fmt_output_commit(struct strbuf *filename,
+                      struct commit *commit,
+                      struct rev_info *info)
+ {
+       struct pretty_print_context ctx = {0};
+       struct strbuf subject = STRBUF_INIT;
+       format_commit_message(commit, "%f", &subject, &ctx);
+       fmt_output_subject(filename, subject.buf, info);
+       strbuf_release(&subject);
  }
  
  void log_write_email_headers(struct rev_info *opt, struct commit *commit,
                         mime_boundary_leader, opt->mime_boundary);
                extra_headers = subject_buffer;
  
-               get_patch_filename(opt->numbered_files ? NULL : commit, NULL,
-                                  opt->nr, opt->patch_suffix, &filename);
+               if (opt->numbered_files)
+                       strbuf_addf(&filename, "%d", opt->nr);
+               else
+                       fmt_output_commit(&filename, commit, opt);
                snprintf(buffer, sizeof(buffer) - 1,
                         "\n--%s%s\n"
                         "Content-Type: text/x-patch;"
@@@ -540,6 -550,7 +550,6 @@@ void show_log(struct rev_info *opt
        struct pretty_print_context ctx = {0};
  
        opt->loginfo = NULL;
 -      ctx.show_notes = opt->show_notes;
        if (!opt->verbose_header) {
                graph_show_commit(opt->graph);
  
        if (!commit->buffer)
                return;
  
 +      if (opt->show_notes) {
 +              int raw;
 +              struct strbuf notebuf = STRBUF_INIT;
 +
 +              raw = (opt->commit_format == CMIT_FMT_USERFORMAT);
 +              format_display_notes(commit->object.sha1, &notebuf,
 +                                   get_log_output_encoding(), raw);
 +              ctx.notes_message = notebuf.len
 +                      ? strbuf_detach(&notebuf, NULL)
 +                      : xcalloc(1, 1);
 +      }
 +
        /*
         * And then the pretty-printed message itself
         */
        ctx.preserve_subject = opt->preserve_subject;
        ctx.reflog_info = opt->reflog_info;
        ctx.fmt = opt->commit_format;
 +      ctx.color = opt->diffopt.use_color;
        pretty_print_commit(&ctx, commit, &msgbuf);
  
        if (opt->add_signoff)
                append_signoff(&msgbuf, opt->add_signoff);
 +
 +      if ((ctx.fmt != CMIT_FMT_USERFORMAT) &&
 +          ctx.notes_message && *ctx.notes_message) {
 +              if (ctx.fmt == CMIT_FMT_EMAIL) {
 +                      strbuf_addstr(&msgbuf, "---\n");
 +                      opt->shown_dashes = 1;
 +              }
 +              strbuf_addstr(&msgbuf, ctx.notes_message);
 +      }
 +
        if (opt->show_log_size) {
                printf("log size %i\n", (int)msgbuf.len);
                graph_show_oneline(opt->graph);
        }
  
        strbuf_release(&msgbuf);
 +      free(ctx.notes_message);
  }
  
  int log_tree_diff_flush(struct rev_info *opt)
  {
 +      opt->shown_dashes = 0;
        diffcore_std(&opt->diffopt);
  
        if (diff_queue_is_empty()) {
        }
  
        if (opt->loginfo && !opt->no_commit_id) {
 -              /* When showing a verbose header (i.e. log message),
 -               * and not in --pretty=oneline format, we would want
 -               * an extra newline between the end of log and the
 -               * output for readability.
 -               */
                show_log(opt);
                if ((opt->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) &&
                    opt->verbose_header &&
                    opt->commit_format != CMIT_FMT_ONELINE) {
 +                      /*
 +                       * When showing a verbose header (i.e. log message),
 +                       * and not in --pretty=oneline format, we would want
 +                       * an extra newline between the end of log and the
 +                       * diff/diffstat output for readability.
 +                       */
                        int pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
                        if (opt->diffopt.output_prefix) {
                                struct strbuf *msg = NULL;
                                        opt->diffopt.output_prefix_data);
                                fwrite(msg->buf, msg->len, 1, stdout);
                        }
 -                      if ((pch & opt->diffopt.output_format) == pch) {
 +
 +                      /*
 +                       * We may have shown three-dashes line early
 +                       * between notes and the log message, in which
 +                       * case we only want a blank line after the
 +                       * notes without (an extra) three-dashes line.
 +                       * Otherwise, we show the three-dashes line if
 +                       * we are showing the patch with diffstat, but
 +                       * in that case, there is no extra blank line
 +                       * after the three-dashes line.
 +                       */
 +                      if (!opt->shown_dashes &&
 +                          (pch & opt->diffopt.output_format) == pch)
                                printf("---");
 -                      }
                        putchar('\n');
                }
        }
diff --combined revision.h
index 059bfff812da8033681aa11c5a9ca9085881747e,e4a912ae6b55326ab4a75534690b4fa592229318..a395c3639a30d4d0d55036a5fec316e4cdb9546f
@@@ -111,7 -111,6 +111,7 @@@ struct rev_info 
  
        /* Format info */
        unsigned int    shown_one:1,
 +                      shown_dashes:1,
                        show_merge:1,
                        show_notes:1,
                        show_notes_given:1,
        const char      *mime_boundary;
        const char      *patch_suffix;
        int             numbered_files;
+       int             reroll_count;
        char            *message_id;
        struct string_list *ref_message_ids;
        const char      *add_signoff;
diff --combined t/t4014-format-patch.sh
index dc0d8ae928229e3e0abbc1c486905f2a713075b5,aa9470dbf1fcca71998a41876709da4bf40c74ad..7fa3647514b616b1a40b233b23123a00c4d3ff6c
@@@ -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' '
        ls patches/0001-Side-changes-1.patch patches/0002-Side-changes-2.patch patches/0003-Side-changes-3-with-n-backslash-n-in-it.patch
  '
  
+ test_expect_success 'reroll count' '
+       rm -fr patches &&
+       git format-patch -o patches --cover-letter --reroll-count 4 master..side >list &&
+       ! grep -v "^patches/v4-000[0-3]-" list &&
+       sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+       ! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
+ '
+ test_expect_success 'reroll count (-v)' '
+       rm -fr patches &&
+       git format-patch -o patches --cover-letter -v4 master..side >list &&
+       ! grep -v "^patches/v4-000[0-3]-" list &&
+       sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+       ! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
+ '
  check_threading () {
        expect="$1" &&
        shift &&
@@@ -650,19 -632,8 +666,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
@@@ -797,14 -768,16 +813,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" &&
@@@ -817,31 -790,30 +833,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
@@@ -963,46 -907,4 +979,46 @@@ test_expect_success 'format patch ignor
        test_cmp expect actual
  '
  
 +test_expect_success 'cover letter using branch description (1)' '
 +      git checkout rebuild-1 &&
 +      test_config branch.rebuild-1.description hello &&
 +      git format-patch --stdout --cover-letter master >actual &&
 +      grep hello actual >/dev/null
 +'
 +
 +test_expect_success 'cover letter using branch description (2)' '
 +      git checkout rebuild-1 &&
 +      test_config branch.rebuild-1.description hello &&
 +      git format-patch --stdout --cover-letter rebuild-1~2..rebuild-1 >actual &&
 +      grep hello actual >/dev/null
 +'
 +
 +test_expect_success 'cover letter using branch description (3)' '
 +      git checkout rebuild-1 &&
 +      test_config branch.rebuild-1.description hello &&
 +      git format-patch --stdout --cover-letter ^master rebuild-1 >actual &&
 +      grep hello actual >/dev/null
 +'
 +
 +test_expect_success 'cover letter using branch description (4)' '
 +      git checkout rebuild-1 &&
 +      test_config branch.rebuild-1.description hello &&
 +      git format-patch --stdout --cover-letter master.. >actual &&
 +      grep hello actual >/dev/null
 +'
 +
 +test_expect_success 'cover letter using branch description (5)' '
 +      git checkout rebuild-1 &&
 +      test_config branch.rebuild-1.description hello &&
 +      git format-patch --stdout --cover-letter -2 HEAD >actual &&
 +      grep hello actual >/dev/null
 +'
 +
 +test_expect_success 'cover letter using branch description (6)' '
 +      git checkout rebuild-1 &&
 +      test_config branch.rebuild-1.description hello &&
 +      git format-patch --stdout --cover-letter -2 >actual &&
 +      grep hello actual >/dev/null
 +'
 +
  test_done