Skip to content

Commit 3dc95e0

Browse files
ttaylorrgitster
authored andcommitted
shortlog: support arbitrary commit format --groups
In addition to generating a shortlog based on committer, author, or the identity in one or more specified trailers, it can be useful to generate a shortlog based on an arbitrary commit format. This can be used, for example, to generate a distribution of commit activity over time, like so: $ git shortlog --group='%cd' --date='format:%Y-%m' -s v2.37.0.. 117 2022-06 274 2022-07 324 2022-08 263 2022-09 7 2022-10 Arbitrary commit formats can be used. In fact, `git shortlog`'s default behavior (to count by commit authors) can be emulated as follows: $ git shortlog --group='%aN <%aE>' ... and future patches will make the default behavior (as well as `--committer`, and `--group=trailer:<trailer>`) special cases of the more flexible `--group` option. Note also that the SHORTLOG_GROUP_FORMAT enum value is used only to designate that `--group:<format>` is in use when in stdin mode to declare that the combination is invalid. Signed-off-by: Taylor Blau <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b017d3d commit 3dc95e0

File tree

4 files changed

+78
-2
lines changed

4 files changed

+78
-2
lines changed

Documentation/git-shortlog.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ OPTIONS
5050
--date=<format>::
5151
Show dates formatted according to the given date string. (See
5252
the `--date` option in the "Commit Formatting" section of
53-
linkgit:git-log[1]).
53+
linkgit:git-log[1]). Useful with `--group=format:<format>`.
5454

5555
--group=<type>::
5656
Group commits based on `<type>`. If no `--group` option is
@@ -64,6 +64,9 @@ OPTIONS
6464
example, if your project uses `Reviewed-by` trailers, you might want
6565
to see who has been reviewing with
6666
`git shortlog -ns --group=trailer:reviewed-by`.
67+
- `format:<format>`, any string accepted by the `--format` option of
68+
'git log'. (See the "PRETTY FORMATS" section of
69+
linkgit:git-log[1].)
6770
+
6871
Note that commits that do not include the trailer will not be counted.
6972
Likewise, commits with multiple trailers (e.g., multiple signoffs) may

builtin/shortlog.c

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ static void read_from_stdin(struct shortlog *log)
133133
break;
134134
case SHORTLOG_GROUP_TRAILER:
135135
die(_("using %s with stdin is not supported"), "--group=trailer");
136+
case SHORTLOG_GROUP_FORMAT:
137+
die(_("using %s with stdin is not supported"), "--group=format");
136138
default:
137139
BUG("unhandled shortlog group");
138140
}
@@ -203,6 +205,32 @@ static void insert_records_from_trailers(struct shortlog *log,
203205
unuse_commit_buffer(commit, commit_buffer);
204206
}
205207

208+
static int shortlog_needs_dedup(const struct shortlog *log)
209+
{
210+
return HAS_MULTI_BITS(log->groups) || log->format.nr > 1 || log->trailers.nr;
211+
}
212+
213+
static void insert_records_from_format(struct shortlog *log,
214+
struct strset *dups,
215+
struct commit *commit,
216+
struct pretty_print_context *ctx,
217+
const char *oneline)
218+
{
219+
struct strbuf buf = STRBUF_INIT;
220+
struct string_list_item *item;
221+
222+
for_each_string_list_item(item, &log->format) {
223+
strbuf_reset(&buf);
224+
225+
format_commit_message(commit, item->string, &buf, ctx);
226+
227+
if (!shortlog_needs_dedup(log) || strset_add(dups, buf.buf))
228+
insert_one_record(log, buf.buf, oneline);
229+
}
230+
231+
strbuf_release(&buf);
232+
}
233+
206234
void shortlog_add_commit(struct shortlog *log, struct commit *commit)
207235
{
208236
struct strbuf ident = STRBUF_INIT;
@@ -244,6 +272,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
244272
insert_one_record(log, ident.buf, oneline_str);
245273
}
246274
insert_records_from_trailers(log, &dups, commit, &ctx, oneline_str);
275+
insert_records_from_format(log, &dups, commit, &ctx, oneline_str);
247276

248277
strset_clear(&dups);
249278
strbuf_release(&ident);
@@ -315,15 +344,23 @@ static int parse_group_option(const struct option *opt, const char *arg, int uns
315344
if (unset) {
316345
log->groups = 0;
317346
string_list_clear(&log->trailers, 0);
347+
string_list_clear(&log->format, 0);
318348
} else if (!strcasecmp(arg, "author"))
319349
log->groups |= SHORTLOG_GROUP_AUTHOR;
320350
else if (!strcasecmp(arg, "committer"))
321351
log->groups |= SHORTLOG_GROUP_COMMITTER;
322352
else if (skip_prefix(arg, "trailer:", &field)) {
323353
log->groups |= SHORTLOG_GROUP_TRAILER;
324354
string_list_append(&log->trailers, field);
325-
} else
355+
} else if (skip_prefix(arg, "format:", &field)) {
356+
log->groups |= SHORTLOG_GROUP_FORMAT;
357+
string_list_append(&log->format, field);
358+
} else if (strchr(arg, '%')) {
359+
log->groups |= SHORTLOG_GROUP_FORMAT;
360+
string_list_append(&log->format, arg);
361+
} else {
326362
return error(_("unknown group type: %s"), arg);
363+
}
327364

328365
return 0;
329366
}
@@ -341,6 +378,7 @@ void shortlog_init(struct shortlog *log)
341378
log->in2 = DEFAULT_INDENT2;
342379
log->trailers.strdup_strings = 1;
343380
log->trailers.cmp = strcasecmp;
381+
log->format.strdup_strings = 1;
344382
}
345383

346384
int cmd_shortlog(int argc, const char **argv, const char *prefix)
@@ -481,4 +519,5 @@ void shortlog_output(struct shortlog *log)
481519
log->list.strdup_strings = 1;
482520
string_list_clear(&log->list, 1);
483521
clear_mailmap(&log->mailmap);
522+
string_list_clear(&log->format, 0);
484523
}

shortlog.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ struct shortlog {
2222
SHORTLOG_GROUP_AUTHOR = (1 << 0),
2323
SHORTLOG_GROUP_COMMITTER = (1 << 1),
2424
SHORTLOG_GROUP_TRAILER = (1 << 2),
25+
SHORTLOG_GROUP_FORMAT = (1 << 3),
2526
} groups;
2627
struct string_list trailers;
28+
struct string_list format;
2729

2830
int email;
2931
struct string_list mailmap;

t/t4201-shortlog.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,26 @@ test_expect_success 'shortlog --group=trailer:signed-off-by' '
244244
test_cmp expect actual
245245
'
246246

247+
test_expect_success 'shortlog --group=format' '
248+
git shortlog -s --date="format:%Y" --group="format:%cN (%cd)" \
249+
HEAD >actual &&
250+
cat >expect <<-\EOF &&
251+
4 C O Mitter (2005)
252+
1 Sin Nombre (2005)
253+
EOF
254+
test_cmp expect actual
255+
'
256+
257+
test_expect_success 'shortlog --group=<format> DWIM' '
258+
git shortlog -s --date="format:%Y" --group="%cN (%cd)" HEAD >actual &&
259+
test_cmp expect actual
260+
'
261+
262+
test_expect_success 'shortlog bogus --group' '
263+
test_must_fail git shortlog --group=bogus HEAD 2>err &&
264+
grep "unknown group type" err
265+
'
266+
247267
test_expect_success 'trailer idents are split' '
248268
cat >expect <<-\EOF &&
249269
2 C O Mitter
@@ -326,6 +346,18 @@ test_expect_success 'shortlog can match multiple groups' '
326346
test_cmp expect actual
327347
'
328348

349+
test_expect_success 'shortlog can match multiple format groups' '
350+
GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME" \
351+
git commit --allow-empty -m "identical names" &&
352+
test_tick &&
353+
cat >expect <<-\EOF &&
354+
2 A U Thor
355+
1 C O Mitter
356+
EOF
357+
git shortlog -ns --group="%cn" --group="%an" -2 HEAD >actual &&
358+
test_cmp expect actual
359+
'
360+
329361
test_expect_success 'set up option selection tests' '
330362
git commit --allow-empty -F - <<-\EOF
331363
subject

0 commit comments

Comments
 (0)