When a shortlog caller wants to group output by, say, author email, they can easily express this with: $ git shortlog --group=format:%ae and restrict output to specific email(s) with the new `--group-filter` option introduced by the previous commit. But they are not able to apply the same treatment to identities that appear in trailers. Doing: $ git shortlog -e --group=format:%ae --group=trailer:Co-authored-by will produce funky results, interspersing proper emails with full "Name <email>" identities from the Co-authored-by trailer (or anything else that might appear there), like: 461 me@xxxxxxxxxxxx 11 Taylor Blau <me@xxxxxxxxxxxx> So if the caller wants to restrict output to a set of matching email addresses (say, "me@xxxxxxxxxxxx"), they cannot do it with a `--group-filter`, since it would discard the group "Taylor Blau <me@xxxxxxxxxxxx>". Introduce a new `--email-only` option, which extracts the email component of an identity from all shortlog groups, including trailers. It behaves similarly to the `-e` option, but replaces its output with just the email component, instead of adding it on to the end. Now, `shortlog` callers can perform: $ git shortlog -s --group=author --group=trailer:Co-authored-by \ --email-only --group-filter="<me@xxxxxxxxxxxx>" 472 <me@xxxxxxxxxxxx> to obtain the output they want. Signed-off-by: Taylor Blau <me@xxxxxxxxxxxx> --- Documentation/git-shortlog.txt | 5 +++++ builtin/shortlog.c | 33 ++++++++++++++++++++++++--------- shortlog.h | 1 + t/t4201-shortlog.sh | 28 ++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 9 deletions(-) diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt index dab6d09648..160c11aead 100644 --- a/Documentation/git-shortlog.txt +++ b/Documentation/git-shortlog.txt @@ -39,6 +39,11 @@ OPTIONS --email:: Show the email address of each author. +--email-only:: + Show only the email address of each author, or committer. If + using a different kind of group (e.g. trailers, custom format, + etc.), only values which contain name/email-pairs will be shown. + --format[=<format>]:: Instead of the commit subject, use some other information to describe each commit. '<format>' can be any string accepted diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 679db22c57..bbe01a376f 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -108,9 +108,13 @@ static int parse_ident(struct shortlog *log, maillen = ident.mail_end - ident.mail_begin; map_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen); - strbuf_add(out, namebuf, namelen); - if (log->email) - strbuf_addf(out, " <%.*s>", (int)maillen, mailbuf); + if (log->email_only) { + strbuf_addf(out, "<%.*s>", (int)maillen, mailbuf); + } else { + strbuf_add(out, namebuf, namelen); + if (log->email) + strbuf_addf(out, " <%.*s>", (int)maillen, mailbuf); + } return 0; } @@ -198,6 +202,8 @@ static void insert_records_from_trailers(struct shortlog *log, strbuf_reset(&ident); if (!parse_ident(log, &ident, value)) value = ident.buf; + else if (log->email_only) + continue; if (!strset_add(dups, value)) continue; @@ -370,12 +376,19 @@ void shortlog_init(struct shortlog *log) void shortlog_finish_setup(struct shortlog *log) { - if (log->groups & SHORTLOG_GROUP_AUTHOR) - string_list_append(&log->format, - log->email ? "%aN <%aE>" : "%aN"); - if (log->groups & SHORTLOG_GROUP_COMMITTER) - string_list_append(&log->format, - log->email ? "%cN <%cE>" : "%cN"); + if (log->email_only) { + if (log->groups & SHORTLOG_GROUP_AUTHOR) + string_list_append(&log->format, "<%aE>"); + if (log->groups & SHORTLOG_GROUP_COMMITTER) + string_list_append(&log->format, "<%cE>"); + } else { + if (log->groups & SHORTLOG_GROUP_AUTHOR) + string_list_append(&log->format, + log->email ? "%aN <%aE>" : "%aN"); + if (log->groups & SHORTLOG_GROUP_COMMITTER) + string_list_append(&log->format, + log->email ? "%cN <%cE>" : "%cN"); + } string_list_sort(&log->trailers); string_list_sort(&log->group_filter); @@ -397,6 +410,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) N_("suppress commit descriptions, only provides commit count")), OPT_BOOL('e', "email", &log.email, N_("show the email address of each author")), + OPT_BOOL(0, "email-only", &log.email_only, + N_("only show the email address of each author")), OPT_CALLBACK_F('w', NULL, &log, N_("<w>[,<i1>[,<i2>]]"), N_("linewrap output"), PARSE_OPT_OPTARG, &parse_wrap_args), diff --git a/shortlog.h b/shortlog.h index 8ebee0e2d6..3fb28639c3 100644 --- a/shortlog.h +++ b/shortlog.h @@ -30,6 +30,7 @@ struct shortlog { struct string_list format; int email; + int email_only; struct string_list mailmap; FILE *file; }; diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh index 0695c42ca8..d747a402ff 100755 --- a/t/t4201-shortlog.sh +++ b/t/t4201-shortlog.sh @@ -400,6 +400,34 @@ test_expect_success '--no-group-filter reset group filters' ' test_cmp expect actual ' +test_expect_success '--email-only shows emails from author' ' + cat >expect <<-\EOF && + 2 <author@xxxxxxxxxxx> + EOF + git shortlog -ns --group=author --email-only -2 HEAD >actual && + test_cmp expect actual +' + +test_expect_success '--email-only shows emails from committer' ' + cat >expect <<-\EOF && + 2 <committer@xxxxxxxxxxx> + EOF + git shortlog -ns --group=committer --email-only -2 HEAD >actual && + test_cmp expect actual +' + +test_expect_success '--email-only shows emails from trailers with idents' ' + cat >expect <<-\EOF && + 1 <a@xxxxxxxxxxx> + 1 <b@xxxxxxxxxxx> + EOF + # at this point, HEAD~3 has a trailer "Repeated-trailer: Foo", + # which is not shown here since it cannot be parsed as an ident + git shortlog -ns --group=trailer:some-trailer --email-only -3 \ + HEAD >actual && + test_cmp expect actual +' + test_expect_success 'shortlog can match multiple format groups' ' GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME" \ git commit --allow-empty -m "identical names" && -- 2.41.0.1.gf123b68cb8