format-patch: --commit-list-format without prefix

Having to prefix a custom format-string with "log:" when passed from the CLI can be annoying. It would be great if this prefix wasn't required. Teach make_cover_letter() to accept custom format-strings without the "log:" prefix if a placeholder is detected. Note that both here and in "git log --format" the check is done naively by just checking for the presence of a '%'. Signed-off-by: Mirko Faina <mroik@delayed.space> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Mirko Faina committed Mar 23, 2026 at 17:57 UTC 36c16a5b7fff7806b475b5fa07eca3a5179d7fa6
3 files changed +29 -1
Documentation/git-format-patch.adoc
+3 -1
@@ -326,8 +326,10 @@ feeding the result to `git send-email`.
326 --commit-list-format=<format-spec>::
327 Specify the format in which to generate the commit list of the patch
328 series. The accepted values for format-spec are `shortlog`, `modern` or a
329 - format string prefixed with `log:`.
329 + format-string prefixed with `log:`.
330 e.g. `log: %s (%an)`
331 + The user is allowed to drop the prefix if the format-string contains a
332 + `%<placeholder>`.
333 If not given, defaults to the `format.commitListFormat` configuration
334 variable.
335 This option implies the use of `--cover-letter` unless
builtin/log.c
+2
@@ -1448,6 +1448,8 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
1448 else if (!strcmp(format, "modern"))
1449 generate_commit_list_cover(rev->diffopt.file, "[%(count)/%(total)] %s",
1450 list, nr);
1451 + else if (strchr(format, '%'))
1452 + generate_commit_list_cover(rev->diffopt.file, format, list, nr);
1453 else
1454 die(_("'%s' is not a valid format string"), format);
1455
t/t4014-format-patch.sh
+24
@@ -392,6 +392,30 @@ test_expect_success 'cover letter with subject, author and count' '
392 test_grep "^\[1/1\] This is a subject (A U Thor)$" patches/0000-cover-letter.patch
393 '
394
395 +test_expect_success 'cover letter with custom format no prefix' '
396 + rm -rf patches &&
397 + test_when_finished "git reset --hard HEAD~1" &&
398 + test_when_finished "rm -rf patches test_file" &&
399 + touch test_file &&
400 + git add test_file &&
401 + git commit -m "This is a subject" &&
402 + git format-patch --commit-list-format="[%(count)/%(total)] %s (%an)" \
403 + -o patches HEAD~1 &&
404 + test_grep "^\[1/1\] This is a subject (A U Thor)$" patches/0000-cover-letter.patch
405 +'
406 +
407 +test_expect_success 'cover letter fail when no prefix and no placeholder' '
408 + rm -rf patches &&
409 + test_when_finished "git reset --hard HEAD~1" &&
410 + test_when_finished "rm -rf patches test_file err" &&
411 + touch test_file &&
412 + git add test_file &&
413 + git commit -m "This is a subject" &&
414 + test_must_fail git format-patch --commit-list-format="this should fail" \
415 + -o patches HEAD~1 2>err &&
416 + test_grep "is not a valid format string" err
417 +'
418 +
419 test_expect_success 'cover letter modern format' '
420 test_when_finished "git reset --hard HEAD~1" &&
421 test_when_finished "rm -rf patches test_file" &&