Skip to content

Commit 47beb37

Browse files
peffgitster
authored andcommitted
shortlog: match commit trailers with --group
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f0939a0 commit 47beb37

File tree

4 files changed

+72
-1
lines changed

4 files changed

+72
-1
lines changed

Documentation/git-shortlog.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@ OPTIONS
5353
+
5454
- `author`, commits are grouped by author
5555
- `committer`, commits are grouped by committer (the same as `-c`)
56+
- `trailer:<field>`, the `<field>` is interpreted as a case-insensitive
57+
commit message trailer (see linkgit:git-interpret-trailers[1]). For
58+
example, if your project uses `Reviewed-by` trailers, you might want
59+
to see who has been reviewing with
60+
`git shortlog -ns --group=trailer:reviewed-by`.
61+
+
62+
Note that commits that do not include the trailer will not be counted.
63+
Likewise, commits with multiple trailers (e.g., multiple signoffs) may
64+
be counted more than once.
65+
+
66+
The contents of each trailer value are taken literally and completely.
67+
No mailmap is applied, and the `-e` option has no effect (if the trailer
68+
contains a username and email, they are both always shown).
5669

5770
-c::
5871
--committer::

builtin/shortlog.c

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "mailmap.h"
1010
#include "shortlog.h"
1111
#include "parse-options.h"
12+
#include "trailer.h"
1213

1314
static char const * const shortlog_usage[] = {
1415
N_("git shortlog [<options>] [<revision-range>] [[--] <path>...]"),
@@ -136,6 +137,8 @@ static void read_from_stdin(struct shortlog *log)
136137
case SHORTLOG_GROUP_COMMITTER:
137138
match = committer_match;
138139
break;
140+
case SHORTLOG_GROUP_TRAILER:
141+
die(_("using --group=trailer with stdin is not supported"));
139142
default:
140143
BUG("unhandled shortlog group");
141144
}
@@ -163,6 +166,37 @@ static void read_from_stdin(struct shortlog *log)
163166
strbuf_release(&oneline);
164167
}
165168

169+
static void insert_records_from_trailers(struct shortlog *log,
170+
struct commit *commit,
171+
struct pretty_print_context *ctx,
172+
const char *oneline)
173+
{
174+
struct trailer_iterator iter;
175+
const char *commit_buffer, *body;
176+
177+
/*
178+
* Using format_commit_message("%B") would be simpler here, but
179+
* this saves us copying the message.
180+
*/
181+
commit_buffer = logmsg_reencode(commit, NULL, ctx->output_encoding);
182+
body = strstr(commit_buffer, "\n\n");
183+
if (!body)
184+
return;
185+
186+
trailer_iterator_init(&iter, body);
187+
while (trailer_iterator_advance(&iter)) {
188+
const char *value = iter.val.buf;
189+
190+
if (strcasecmp(iter.key.buf, log->trailer))
191+
continue;
192+
193+
insert_one_record(log, value, oneline);
194+
}
195+
trailer_iterator_release(&iter);
196+
197+
unuse_commit_buffer(commit, commit_buffer);
198+
}
199+
166200
void shortlog_add_commit(struct shortlog *log, struct commit *commit)
167201
{
168202
struct strbuf ident = STRBUF_INIT;
@@ -197,6 +231,9 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
197231
&ident, &ctx);
198232
insert_one_record(log, ident.buf, oneline_str);
199233
break;
234+
case SHORTLOG_GROUP_TRAILER:
235+
insert_records_from_trailers(log, commit, &ctx, oneline_str);
236+
break;
200237
}
201238

202239
strbuf_release(&ident);
@@ -263,12 +300,17 @@ static int parse_wrap_args(const struct option *opt, const char *arg, int unset)
263300
static int parse_group_option(const struct option *opt, const char *arg, int unset)
264301
{
265302
struct shortlog *log = opt->value;
303+
const char *field;
266304

267305
if (unset || !strcasecmp(arg, "author"))
268306
log->group = SHORTLOG_GROUP_AUTHOR;
269307
else if (!strcasecmp(arg, "committer"))
270308
log->group = SHORTLOG_GROUP_COMMITTER;
271-
else
309+
else if (skip_prefix(arg, "trailer:", &field)) {
310+
log->group = SHORTLOG_GROUP_TRAILER;
311+
free(log->trailer);
312+
log->trailer = xstrdup(field);
313+
} else
272314
return error(_("unknown group type: %s"), arg);
273315

274316
return 0;

shortlog.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ struct shortlog {
1919
enum {
2020
SHORTLOG_GROUP_AUTHOR = 0,
2121
SHORTLOG_GROUP_COMMITTER,
22+
SHORTLOG_GROUP_TRAILER,
2223
} group;
24+
char *trailer;
2325

2426
char *common_repo_prefix;
2527
int email;

t/t4201-shortlog.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,18 @@ test_expect_success '--group=committer is the same as --committer' '
220220
test_cmp expect actual
221221
'
222222

223+
test_expect_success 'shortlog --group=trailer:signed-off-by' '
224+
git commit --allow-empty -m foo -s &&
225+
GIT_COMMITTER_NAME="SOB One" \
226+
227+
git commit --allow-empty -m foo -s &&
228+
git commit --allow-empty --amend --no-edit -s &&
229+
cat >expect <<-\EOF &&
230+
2 C O Mitter <[email protected]>
231+
1 SOB One <[email protected]>
232+
EOF
233+
git shortlog -ns --group=trailer:signed-off-by HEAD >actual &&
234+
test_cmp expect actual
235+
'
236+
223237
test_done

0 commit comments

Comments
 (0)