|
40 | 40 | #include "progress.h" |
41 | 41 | #include "commit-slab.h" |
42 | 42 | #include "advice.h" |
| 43 | +#include "utf8.h" |
43 | 44 |
|
44 | 45 | #include "commit-reach.h" |
45 | 46 | #include "range-diff.h" |
@@ -886,6 +887,7 @@ struct format_config { |
886 | 887 | char *signature; |
887 | 888 | char *signature_file; |
888 | 889 | enum cover_setting config_cover_letter; |
| 890 | + char *fmt_cover_letter_commit_list; |
889 | 891 | char *config_output_directory; |
890 | 892 | enum cover_from_description cover_from_description_mode; |
891 | 893 | int show_notes; |
@@ -930,6 +932,7 @@ static void format_config_release(struct format_config *cfg) |
930 | 932 | string_list_clear(&cfg->extra_cc, 0); |
931 | 933 | strbuf_release(&cfg->sprefix); |
932 | 934 | free(cfg->fmt_patch_suffix); |
| 935 | + free(cfg->fmt_cover_letter_commit_list); |
933 | 936 | } |
934 | 937 |
|
935 | 938 | static enum cover_from_description parse_cover_from_description(const char *arg) |
@@ -1052,6 +1055,10 @@ static int git_format_config(const char *var, const char *value, |
1052 | 1055 | cfg->config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF; |
1053 | 1056 | return 0; |
1054 | 1057 | } |
| 1058 | + if (!strcmp(var, "format.commitlistformat")) { |
| 1059 | + FREE_AND_NULL(cfg->fmt_cover_letter_commit_list); |
| 1060 | + return git_config_string(&cfg->fmt_cover_letter_commit_list, var, value); |
| 1061 | + } |
1055 | 1062 | if (!strcmp(var, "format.outputdirectory")) { |
1056 | 1063 | FREE_AND_NULL(cfg->config_output_directory); |
1057 | 1064 | return git_config_string(&cfg->config_output_directory, var, value); |
@@ -1335,13 +1342,59 @@ static void get_notes_args(struct strvec *arg, struct rev_info *rev) |
1335 | 1342 | } |
1336 | 1343 | } |
1337 | 1344 |
|
| 1345 | +static void generate_shortlog_cover_letter(struct shortlog *log, |
| 1346 | + struct rev_info *rev, |
| 1347 | + struct commit **list, |
| 1348 | + int nr) |
| 1349 | +{ |
| 1350 | + shortlog_init(log); |
| 1351 | + log->wrap_lines = 1; |
| 1352 | + log->wrap = MAIL_DEFAULT_WRAP; |
| 1353 | + log->in1 = 2; |
| 1354 | + log->in2 = 4; |
| 1355 | + log->file = rev->diffopt.file; |
| 1356 | + log->groups = SHORTLOG_GROUP_AUTHOR; |
| 1357 | + shortlog_finish_setup(log); |
| 1358 | + for (int i = 0; i < nr; i++) |
| 1359 | + shortlog_add_commit(log, list[i]); |
| 1360 | + |
| 1361 | + shortlog_output(log); |
| 1362 | +} |
| 1363 | + |
| 1364 | +static void generate_commit_list_cover(FILE *cover_file, const char *format, |
| 1365 | + struct commit **list, int n) |
| 1366 | +{ |
| 1367 | + struct strbuf commit_line = STRBUF_INIT; |
| 1368 | + struct strbuf wrapped_line = STRBUF_INIT; |
| 1369 | + struct pretty_print_context ctx = {0}; |
| 1370 | + struct rev_info rev = REV_INFO_INIT; |
| 1371 | + |
| 1372 | + rev.total = n; |
| 1373 | + ctx.rev = &rev; |
| 1374 | + for (int i = 1; i <= n; i++) { |
| 1375 | + rev.nr = i; |
| 1376 | + repo_format_commit_message(the_repository, list[n - i], format, |
| 1377 | + &commit_line, &ctx); |
| 1378 | + strbuf_add_wrapped_text(&wrapped_line, commit_line.buf, 0, 0, |
| 1379 | + MAIL_DEFAULT_WRAP); |
| 1380 | + fprintf(cover_file, "%s\n", wrapped_line.buf); |
| 1381 | + strbuf_reset(&commit_line); |
| 1382 | + strbuf_reset(&wrapped_line); |
| 1383 | + } |
| 1384 | + fprintf(cover_file, "\n"); |
| 1385 | + |
| 1386 | + strbuf_release(&commit_line); |
| 1387 | + strbuf_release(&wrapped_line); |
| 1388 | +} |
| 1389 | + |
1338 | 1390 | static void make_cover_letter(struct rev_info *rev, int use_separate_file, |
1339 | 1391 | struct commit *origin, |
1340 | 1392 | int nr, struct commit **list, |
1341 | 1393 | const char *description_file, |
1342 | 1394 | const char *branch_name, |
1343 | 1395 | int quiet, |
1344 | | - const struct format_config *cfg) |
| 1396 | + const struct format_config *cfg, |
| 1397 | + const char *format) |
1345 | 1398 | { |
1346 | 1399 | const char *from; |
1347 | 1400 | struct shortlog log; |
@@ -1388,18 +1441,17 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, |
1388 | 1441 | free(pp.after_subject); |
1389 | 1442 | strbuf_release(&sb); |
1390 | 1443 |
|
1391 | | - shortlog_init(&log); |
1392 | | - log.wrap_lines = 1; |
1393 | | - log.wrap = MAIL_DEFAULT_WRAP; |
1394 | | - log.in1 = 2; |
1395 | | - log.in2 = 4; |
1396 | | - log.file = rev->diffopt.file; |
1397 | | - log.groups = SHORTLOG_GROUP_AUTHOR; |
1398 | | - shortlog_finish_setup(&log); |
1399 | | - for (i = 0; i < nr; i++) |
1400 | | - shortlog_add_commit(&log, list[i]); |
1401 | | - |
1402 | | - shortlog_output(&log); |
| 1444 | + if (skip_prefix(format, "log:", &format)) |
| 1445 | + generate_commit_list_cover(rev->diffopt.file, format, list, nr); |
| 1446 | + else if (!strcmp(format, "shortlog")) |
| 1447 | + generate_shortlog_cover_letter(&log, rev, list, nr); |
| 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); |
1403 | 1455 |
|
1404 | 1456 | /* We can only do diffstat with a unique reference point */ |
1405 | 1457 | if (origin) |
@@ -1917,6 +1969,7 @@ int cmd_format_patch(int argc, |
1917 | 1969 | int just_numbers = 0; |
1918 | 1970 | int ignore_if_in_upstream = 0; |
1919 | 1971 | int cover_letter = -1; |
| 1972 | + const char *cover_letter_fmt = NULL; |
1920 | 1973 | int boundary_count = 0; |
1921 | 1974 | int no_binary_diff = 0; |
1922 | 1975 | int zero_commit = 0; |
@@ -1963,6 +2016,8 @@ int cmd_format_patch(int argc, |
1963 | 2016 | N_("print patches to standard out")), |
1964 | 2017 | OPT_BOOL(0, "cover-letter", &cover_letter, |
1965 | 2018 | N_("generate a cover letter")), |
| 2019 | + OPT_STRING(0, "commit-list-format", &cover_letter_fmt, N_("format-spec"), |
| 2020 | + N_("format spec used for the commit list in the cover letter")), |
1966 | 2021 | OPT_BOOL(0, "numbered-files", &just_numbers, |
1967 | 2022 | N_("use simple number sequence for output file names")), |
1968 | 2023 | OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"), |
@@ -2300,6 +2355,15 @@ int cmd_format_patch(int argc, |
2300 | 2355 | /* nothing to do */ |
2301 | 2356 | goto done; |
2302 | 2357 | total = list.nr; |
| 2358 | + |
| 2359 | + if (!cover_letter_fmt) { |
| 2360 | + cover_letter_fmt = cfg.fmt_cover_letter_commit_list; |
| 2361 | + if (!cover_letter_fmt) |
| 2362 | + cover_letter_fmt = "shortlog"; |
| 2363 | + } else if (cover_letter == -1) { |
| 2364 | + cover_letter = 1; |
| 2365 | + } |
| 2366 | + |
2303 | 2367 | if (cover_letter == -1) { |
2304 | 2368 | if (cfg.config_cover_letter == COVER_AUTO) |
2305 | 2369 | cover_letter = (total > 1); |
@@ -2386,12 +2450,14 @@ int cmd_format_patch(int argc, |
2386 | 2450 | } |
2387 | 2451 | rev.numbered_files = just_numbers; |
2388 | 2452 | rev.patch_suffix = fmt_patch_suffix; |
| 2453 | + |
2389 | 2454 | if (cover_letter) { |
2390 | 2455 | if (cfg.thread) |
2391 | 2456 | gen_message_id(&rev, "cover"); |
2392 | 2457 | make_cover_letter(&rev, !!output_directory, |
2393 | 2458 | origin, list.nr, list.items, |
2394 | | - description_file, branch_name, quiet, &cfg); |
| 2459 | + description_file, branch_name, quiet, &cfg, |
| 2460 | + cover_letter_fmt); |
2395 | 2461 | print_bases(&bases, rev.diffopt.file); |
2396 | 2462 | print_signature(signature, rev.diffopt.file); |
2397 | 2463 | total++; |
|
0 commit comments