We used to populate the subject of the cover letter generated by
git-format-patch with "*** SUBJECT HERE ***". However, if a user submits
multiple patchsets, they may want to keep a consistent subject between
rerolls.

If git-format-patch is run with `--infer-cover-letter` or
`format.inferCoverSubject`, infer the subject for the cover letter from
the top line(s) of a branch description, similar to how a subject is
read from a commit message.

Signed-off-by: Denton Liu <liu.den...@gmail.com>
---
 Documentation/config/format.txt    |  5 +++
 Documentation/git-format-patch.txt | 10 ++++++
 builtin/log.c                      | 56 +++++++++++++++++++-----------
 t/t4014-format-patch.sh            | 33 ++++++++++++++++++
 4 files changed, 83 insertions(+), 21 deletions(-)

diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt
index 414a5a8a9d..84a40544c5 100644
--- a/Documentation/config/format.txt
+++ b/Documentation/config/format.txt
@@ -36,6 +36,11 @@ format.subjectPrefix::
        The default for format-patch is to output files with the '[PATCH]'
        subject prefix. Use this variable to change that prefix.
 
+format.inferCoverSubject::
+       A boolean that controls whether or not to infer the subject for
+       the cover letter based on the branch's description. See the
+       --infer-cover-subject option in linkgit:git-format-patch[1].
+
 format.signature::
        The default for format-patch is to output a signature containing
        the Git version number. Use this variable to change that default.
diff --git a/Documentation/git-format-patch.txt 
b/Documentation/git-format-patch.txt
index 0e2dd59c0f..757a74cf8a 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -19,6 +19,7 @@ SYNOPSIS
                   [--start-number <n>] [--numbered-files]
                   [--in-reply-to=<Message-Id>] [--suffix=.<sfx>]
                   [--ignore-if-in-upstream]
+                  [--[no-]infer-cover-subject]
                   [--rfc] [--subject-prefix=<Subject-Prefix>]
                   [(--reroll-count|-v) <n>]
                   [--to=<email>] [--cc=<email>]
@@ -171,6 +172,14 @@ will want to ensure that threading is disabled for `git 
send-email`.
        patches being generated, and any patch that matches is
        ignored.
 
+--[no-]infer-cover-subject::
+       Instead of using the default "*** SUBJECT HERE ***" subject for
+       the cover letter, infer the subject from the branch's
+       description.
++
+Similar to a commit message, the subject is inferred as the beginning of
+the description up to and excluding the first blank line.
+
 --subject-prefix=<Subject-Prefix>::
        Instead of the standard '[PATCH]' prefix in the subject
        line, instead use '[<Subject-Prefix>]'. This
@@ -351,6 +360,7 @@ with configuration variables.
        signOff = true
        outputDirectory = <directory>
        coverLetter = auto
+       inferCoverSubject = true
 ------------
 
 
diff --git a/builtin/log.c b/builtin/log.c
index 44b10b3415..b15754e282 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -774,6 +774,7 @@ static const char *signature = git_version_string;
 static const char *signature_file;
 static int config_cover_letter;
 static const char *config_output_directory;
+static int infer_cover_subject;
 
 enum {
        COVER_UNSET,
@@ -887,6 +888,10 @@ static int git_format_config(const char *var, const char 
*value, void *cb)
                }
                return 0;
        }
+       if (!strcmp(var, "format.infercoversubject")) {
+               infer_cover_subject = git_config_bool(var, value);
+               return 0;
+       }
 
        return git_log_config(var, value, cb);
 }
@@ -993,20 +998,6 @@ static void print_signature(FILE *file)
        putc('\n', file);
 }
 
-static void add_branch_description(struct strbuf *buf, const char *branch_name)
-{
-       struct strbuf desc = STRBUF_INIT;
-       if (!branch_name || !*branch_name)
-               return;
-       read_branch_desc(&desc, branch_name);
-       if (desc.len) {
-               strbuf_addch(buf, '\n');
-               strbuf_addbuf(buf, &desc);
-               strbuf_addch(buf, '\n');
-       }
-       strbuf_release(&desc);
-}
-
 static char *find_branch_name(struct rev_info *rev)
 {
        int i, positive = -1;
@@ -1057,13 +1048,17 @@ static void make_cover_letter(struct rev_info *rev, int 
use_stdout,
                              struct commit *origin,
                              int nr, struct commit **list,
                              const char *branch_name,
+                             int infer_subject,
                              int quiet)
 {
        const char *committer;
-       const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
-       const char *msg;
+       const char *subject = "*** SUBJECT HERE ***";
+       const char *body = "*** BLURB HERE ***";
+       const char *description = NULL;
        struct shortlog log;
        struct strbuf sb = STRBUF_INIT;
+       struct strbuf description_sb = STRBUF_INIT;
+       struct strbuf subject_sb = STRBUF_INIT;
        int i;
        const char *encoding = "UTF-8";
        int need_8bit_cte = 0;
@@ -1091,17 +1086,34 @@ static void make_cover_letter(struct rev_info *rev, int 
use_stdout,
        if (!branch_name)
                branch_name = find_branch_name(rev);
 
-       msg = body;
+       if (branch_name && *branch_name)
+               read_branch_desc(&description_sb, branch_name);
+
+       if (description_sb.len) {
+               if (infer_subject) {
+                       description = format_subject(&subject_sb, 
description_sb.buf, " ");
+                       subject = subject_sb.buf;
+               } else {
+                       description = description_sb.buf;
+               }
+       }
+
        pp.fmt = CMIT_FMT_EMAIL;
        pp.date_mode.type = DATE_RFC2822;
        pp.rev = rev;
        pp.print_email_subject = 1;
        pp_user_info(&pp, NULL, &sb, committer, encoding);
-       pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
-       pp_remainder(&pp, &msg, &sb, 0);
-       add_branch_description(&sb, branch_name);
+       pp_title_line(&pp, &subject, &sb, encoding, need_8bit_cte);
+       pp_remainder(&pp, &body, &sb, 0);
+       if (description) {
+               strbuf_addch(&sb, '\n');
+               strbuf_addstr(&sb, description);
+               strbuf_addch(&sb, '\n');
+       }
        fprintf(rev->diffopt.file, "%s\n", sb.buf);
 
+       strbuf_release(&description_sb);
+       strbuf_release(&subject_sb);
        strbuf_release(&sb);
 
        shortlog_init(&log);
@@ -1577,6 +1589,8 @@ int cmd_format_patch(int argc, const char **argv, const 
char *prefix)
                { OPTION_CALLBACK, 0, "rfc", &rev, NULL,
                            N_("Use [RFC PATCH] instead of [PATCH]"),
                            PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback },
+               OPT_BOOL(0, "infer-cover-subject", &infer_cover_subject,
+                           N_("infer a cover letter subject from the branch 
description")),
                { OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"),
                            N_("Use [<prefix>] instead of [PATCH]"),
                            PARSE_OPT_NONEG, subject_prefix_callback },
@@ -1916,7 +1930,7 @@ int cmd_format_patch(int argc, const char **argv, const 
char *prefix)
                if (thread)
                        gen_message_id(&rev, "cover");
                make_cover_letter(&rev, use_stdout,
-                                 origin, nr, list, branch_name, quiet);
+                                 origin, nr, list, branch_name, 
infer_cover_subject, quiet);
                print_bases(&bases, rev.diffopt.file);
                print_signature(rev.diffopt.file);
                total++;
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index d6aa41b8c6..a544d0c704 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -1558,6 +1558,39 @@ test_expect_success 'format patch ignores color.ui' '
        test_cmp expect actual
 '
 
+test_expect_success 'cover letter with config subject' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_config format.inferCoverSubject true &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter master >actual &&
+       grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
+       grep "^body" actual
+'
+
+test_expect_success 'cover letter with command-line subject' '
+       test_config branch.rebuild-1.description "command-line subject
+
+body" &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --infer-cover-subject master 
>actual &&
+       grep "^Subject: \[PATCH 0/2\] command-line subject$" actual &&
+       grep "^body" actual
+'
+
+test_expect_success 'cover letter with command-line --no-infer-cover-subject 
overrides config' '
+       test_config branch.rebuild-1.description "config subject
+
+body" &&
+       test_config format.inferCoverSubject true &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --no-infer-cover-subject 
master >actual &&
+       grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
+       grep "^config subject" actual &&
+       grep "^body" actual
+'
+
 test_expect_success 'cover letter using branch description (1)' '
        git checkout rebuild-1 &&
        test_config branch.rebuild-1.description hello &&
-- 
2.23.0.248.g3a9dd8fb08

Reply via email to