From patchwork Sun Sep 27 08:40:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff King X-Patchwork-Id: 11801983 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8239092C for ; Sun, 27 Sep 2020 08:40:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 73E0C23977 for ; Sun, 27 Sep 2020 08:40:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730583AbgI0IkG (ORCPT ); Sun, 27 Sep 2020 04:40:06 -0400 Received: from cloud.peff.net ([104.130.231.41]:42402 "EHLO cloud.peff.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730513AbgI0IkG (ORCPT ); Sun, 27 Sep 2020 04:40:06 -0400 Received: (qmail 29170 invoked by uid 109); 27 Sep 2020 08:40:05 -0000 Received: from Unknown (HELO peff.net) (10.0.1.2) by cloud.peff.net (qpsmtpd/0.94) with ESMTP; Sun, 27 Sep 2020 08:40:05 +0000 Authentication-Results: cloud.peff.net; auth=none Received: (qmail 4075 invoked by uid 111); 27 Sep 2020 08:40:07 -0000 Received: from coredump.intra.peff.net (HELO sigill.intra.peff.net) (10.0.0.2) by peff.net (qpsmtpd/0.94) with (TLS_AES_256_GCM_SHA384 encrypted) ESMTPS; Sun, 27 Sep 2020 04:40:07 -0400 Authentication-Results: peff.net; auth=none Date: Sun, 27 Sep 2020 04:40:04 -0400 From: Jeff King To: git@vger.kernel.org Cc: Eric Sunshine , Martin =?utf-8?b?w4VncmVu?= Subject: [PATCH v2 4/8] shortlog: match commit trailers with --group Message-ID: <20200927084004.GD2465761@coredump.intra.peff.net> References: <20200927083933.GA2222823@coredump.intra.peff.net> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20200927083933.GA2222823@coredump.intra.peff.net> Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org If a project uses commit trailers, this patch lets you use shortlog to see who is performing each action. For example, running: git shortlog -ns --group=trailer:reviewed-by in git.git shows who has reviewed. You can even use a custom format to see things like who has helped whom: git shortlog --format="...helped %an (%ad)" \ --group=trailer:helped-by Signed-off-by: Jeff King --- Documentation/git-shortlog.txt | 13 ++++++++++ builtin/shortlog.c | 44 +++++++++++++++++++++++++++++++++- shortlog.h | 2 ++ t/t4201-shortlog.sh | 14 +++++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt index 6496d313c1..edd6cda58a 100644 --- a/Documentation/git-shortlog.txt +++ b/Documentation/git-shortlog.txt @@ -53,6 +53,19 @@ OPTIONS + - `author`, commits are grouped by author - `committer`, commits are grouped by committer (the same as `-c`) + - `trailer:`, the `` is interpreted as a case-insensitive + commit message trailer (see linkgit:git-interpret-trailers[1]). For + example, if your project uses `Reviewed-by` trailers, you might want + to see who has been reviewing with + `git shortlog -ns --group=trailer:reviewed-by`. ++ +Note that commits that do not include the trailer will not be counted. +Likewise, commits with multiple trailers (e.g., multiple signoffs) may +be counted more than once. ++ +The contents of each trailer value are taken literally and completely. +No mailmap is applied, and the `-e` option has no effect (if the trailer +contains a username and email, they are both always shown). -c:: --committer:: diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 880ce19304..e1d9ee909f 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -9,6 +9,7 @@ #include "mailmap.h" #include "shortlog.h" #include "parse-options.h" +#include "trailer.h" static char const * const shortlog_usage[] = { N_("git shortlog [] [] [[--] ...]"), @@ -136,6 +137,8 @@ static void read_from_stdin(struct shortlog *log) case SHORTLOG_GROUP_COMMITTER: match = committer_match; break; + case SHORTLOG_GROUP_TRAILER: + die(_("using --group=trailer with stdin is not supported")); default: BUG("unhandled shortlog group"); } @@ -163,6 +166,37 @@ static void read_from_stdin(struct shortlog *log) strbuf_release(&oneline); } +static void insert_records_from_trailers(struct shortlog *log, + struct commit *commit, + struct pretty_print_context *ctx, + const char *oneline) +{ + struct trailer_iterator iter; + const char *commit_buffer, *body; + + /* + * Using format_commit_message("%B") would be simpler here, but + * this saves us copying the message. + */ + commit_buffer = logmsg_reencode(commit, NULL, ctx->output_encoding); + body = strstr(commit_buffer, "\n\n"); + if (!body) + return; + + trailer_iterator_init(&iter, body); + while (trailer_iterator_advance(&iter)) { + const char *value = iter.val.buf; + + if (strcasecmp(iter.key.buf, log->trailer)) + continue; + + insert_one_record(log, value, oneline); + } + trailer_iterator_release(&iter); + + unuse_commit_buffer(commit, commit_buffer); +} + void shortlog_add_commit(struct shortlog *log, struct commit *commit) { struct strbuf ident = STRBUF_INIT; @@ -197,6 +231,9 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit) &ident, &ctx); insert_one_record(log, ident.buf, oneline_str); break; + case SHORTLOG_GROUP_TRAILER: + insert_records_from_trailers(log, commit, &ctx, oneline_str); + break; } strbuf_release(&ident); @@ -263,12 +300,17 @@ static int parse_wrap_args(const struct option *opt, const char *arg, int unset) static int parse_group_option(const struct option *opt, const char *arg, int unset) { struct shortlog *log = opt->value; + const char *field; if (unset || !strcasecmp(arg, "author")) log->group = SHORTLOG_GROUP_AUTHOR; else if (!strcasecmp(arg, "committer")) log->group = SHORTLOG_GROUP_COMMITTER; - else + else if (skip_prefix(arg, "trailer:", &field)) { + log->group = SHORTLOG_GROUP_TRAILER; + free(log->trailer); + log->trailer = xstrdup(field); + } else return error(_("unknown group type: %s"), arg); return 0; diff --git a/shortlog.h b/shortlog.h index 876a52158d..89c2dbc5e6 100644 --- a/shortlog.h +++ b/shortlog.h @@ -19,7 +19,9 @@ struct shortlog { enum { SHORTLOG_GROUP_AUTHOR = 0, SHORTLOG_GROUP_COMMITTER, + SHORTLOG_GROUP_TRAILER, } group; + char *trailer; char *common_repo_prefix; int email; diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh index 65e4468746..e97d891a71 100755 --- a/t/t4201-shortlog.sh +++ b/t/t4201-shortlog.sh @@ -220,4 +220,18 @@ test_expect_success '--group=committer is the same as --committer' ' test_cmp expect actual ' +test_expect_success 'shortlog --group=trailer:signed-off-by' ' + git commit --allow-empty -m foo -s && + GIT_COMMITTER_NAME="SOB One" \ + GIT_COMMITTER_EMAIL=sob@example.com \ + git commit --allow-empty -m foo -s && + git commit --allow-empty --amend --no-edit -s && + cat >expect <<-\EOF && + 2 C O Mitter + 1 SOB One + EOF + git shortlog -ns --group=trailer:signed-off-by HEAD >actual && + test_cmp expect actual +' + test_done