format-patch: add --description-file option

This patch makes it possible to directly feed a branch description to derive the cover letter from. The use case is formatting dynamically created temporary commits which are not referenced anywhere. The most obvious alternative would be creating a temporary branch and setting a description on it, but that doesn't seem particularly elegant. Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Oswald Buddenhagen committed Aug 21, 2023 at 19:07 UTC 67f4b36e339d91c06ff9cdc254864c830158e10f
3 files changed +36 -3
Documentation/git-format-patch.txt
+4
@@ -215,6 +215,10 @@ is greater than 100 bytes, then the mode will be `message`, otherwise
215 If `<mode>` is `none`, both the cover letter subject and body will be
216 populated with placeholder text.
217
218 +--description-file=<file>::
219 + Use the contents of <file> instead of the branch's description
220 + for generating the cover letter.
221 +
222 --subject-prefix=<subject prefix>::
223 Instead of the standard '[PATCH]' prefix in the subject
224 line, instead use '[<subject prefix>]'. This
builtin/log.c
+18 -3
@@ -1253,7 +1253,15 @@ static void show_diffstat(struct rev_info *rev,
1253 fprintf(rev->diffopt.file, "\n");
1254 }
1255
1256 +static void read_desc_file(struct strbuf *buf, const char *desc_file)
1257 +{
1258 + if (strbuf_read_file(buf, desc_file, 0) < 0)
1259 + die_errno(_("unable to read branch description file '%s'"),
1260 + desc_file);
1261 +}
1262 +
1263 static void prepare_cover_text(struct pretty_print_context *pp,
1264 + const char *description_file,
1265 const char *branch_name,
1266 struct strbuf *sb,
1267 const char *encoding,
@@ -1267,7 +1275,9 @@ static void prepare_cover_text(struct pretty_print_context *pp,
1275 if (cover_from_description_mode == COVER_FROM_NONE)
1276 goto do_pp;
1277
1270 - if (branch_name && *branch_name)
1278 + if (description_file && *description_file)
1279 + read_desc_file(&description_sb, description_file);
1280 + else if (branch_name && *branch_name)
1281 read_branch_desc(&description_sb, branch_name);
1282 if (!description_sb.len)
1283 goto do_pp;
@@ -1313,6 +1323,7 @@ static void get_notes_args(struct strvec *arg, struct rev_info *rev)
1323 static void make_cover_letter(struct rev_info *rev, int use_separate_file,
1324 struct commit *origin,
1325 int nr, struct commit **list,
1326 + const char *description_file,
1327 const char *branch_name,
1328 int quiet)
1329 {
@@ -1352,7 +1363,8 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
1363 pp.rev = rev;
1364 pp.print_email_subject = 1;
1365 pp_user_info(&pp, NULL, &sb, committer, encoding);
1355 - prepare_cover_text(&pp, branch_name, &sb, encoding, need_8bit_cte);
1366 + prepare_cover_text(&pp, description_file, branch_name, &sb,
1367 + encoding, need_8bit_cte);
1368 fprintf(rev->diffopt.file, "%s\n", sb.buf);
1369
1370 strbuf_release(&sb);
@@ -1893,6 +1905,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
1905 int quiet = 0;
1906 const char *reroll_count = NULL;
1907 char *cover_from_description_arg = NULL;
1908 + char *description_file = NULL;
1909 char *branch_name = NULL;
1910 char *base_commit = NULL;
1911 struct base_tree_info bases;
@@ -1936,6 +1949,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
1949 OPT_STRING(0, "cover-from-description", &cover_from_description_arg,
1950 N_("cover-from-description-mode"),
1951 N_("generate parts of a cover letter based on a branch's description")),
1952 + OPT_FILENAME(0, "description-file", &description_file,
1953 + N_("use branch description from file")),
1954 OPT_CALLBACK_F(0, "subject-prefix", &rev, N_("prefix"),
1955 N_("use [<prefix>] instead of [PATCH]"),
1956 PARSE_OPT_NONEG, subject_prefix_callback),
@@ -2321,7 +2336,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
2336 if (thread)
2337 gen_message_id(&rev, "cover");
2338 make_cover_letter(&rev, !!output_directory,
2324 - origin, nr, list, branch_name, quiet);
2339 + origin, nr, list, description_file, branch_name, quiet);
2340 print_bases(&bases, rev.diffopt.file);
2341 print_signature(rev.diffopt.file);
2342 total++;
t/t4014-format-patch.sh
+14
@@ -1991,6 +1991,20 @@ test_expect_success 'cover letter using branch description (6)' '
1991 grep hello actual
1992 '
1993
1994 +test_expect_success 'cover letter with --description-file' '
1995 + test_when_finished "rm -f description.txt" &&
1996 + cat >description.txt <<-\EOF &&
1997 + subject from file
1998 +
1999 + body from file
2000 + EOF
2001 + git checkout rebuild-1 &&
2002 + git format-patch --stdout --cover-letter --cover-from-description auto \
2003 + --description-file description.txt main >actual &&
2004 + grep "^Subject: \[PATCH 0/2\] subject from file$" actual &&
2005 + grep "^body from file$" actual
2006 +'
2007 +
2008 test_expect_success 'cover letter with nothing' '
2009 git format-patch --stdout --cover-letter >actual &&
2010 test_line_count = 0 actual