diff options
Diffstat (limited to 'builtin/shortlog.c')
| -rw-r--r-- | builtin/shortlog.c | 98 |
1 files changed, 68 insertions, 30 deletions
diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 26c5c0cf93..27a87167e1 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -81,8 +81,10 @@ static void insert_one_record(struct shortlog *log, format_subject(&subject, oneline, " "); buffer = strbuf_detach(&subject, NULL); - if (item->util == NULL) - item->util = xcalloc(1, sizeof(struct string_list)); + if (!item->util) { + item->util = xmalloc(sizeof(struct string_list)); + string_list_init_nodup(item->util); + } string_list_append(item->util, buffer); } } @@ -130,7 +132,9 @@ static void read_from_stdin(struct shortlog *log) match = committer_match; break; case SHORTLOG_GROUP_TRAILER: - die(_("using --group=trailer with stdin is not supported")); + die(_("using %s with stdin is not supported"), "--group=trailer"); + case SHORTLOG_GROUP_FORMAT: + die(_("using %s with stdin is not supported"), "--group=format"); default: BUG("unhandled shortlog group"); } @@ -168,6 +172,9 @@ static void insert_records_from_trailers(struct shortlog *log, const char *commit_buffer, *body; struct strbuf ident = STRBUF_INIT; + if (!log->trailers.nr) + return; + /* * Using format_commit_message("%B") would be simpler here, but * this saves us copying the message. @@ -198,9 +205,34 @@ static void insert_records_from_trailers(struct shortlog *log, unuse_commit_buffer(commit, commit_buffer); } +static int shortlog_needs_dedup(const struct shortlog *log) +{ + return HAS_MULTI_BITS(log->groups) || log->format.nr > 1 || log->trailers.nr; +} + +static void insert_records_from_format(struct shortlog *log, + struct strset *dups, + struct commit *commit, + struct pretty_print_context *ctx, + const char *oneline) +{ + struct strbuf buf = STRBUF_INIT; + struct string_list_item *item; + + for_each_string_list_item(item, &log->format) { + strbuf_reset(&buf); + + format_commit_message(commit, item->string, &buf, ctx); + + if (!shortlog_needs_dedup(log) || strset_add(dups, buf.buf)) + insert_one_record(log, buf.buf, oneline); + } + + strbuf_release(&buf); +} + void shortlog_add_commit(struct shortlog *log, struct commit *commit) { - struct strbuf ident = STRBUF_INIT; struct strbuf oneline = STRBUF_INIT; struct strset dups = STRSET_INIT; struct pretty_print_context ctx = {0}; @@ -209,7 +241,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit) ctx.fmt = CMIT_FMT_USERFORMAT; ctx.abbrev = log->abbrev; ctx.print_email_subject = 1; - ctx.date_mode.type = DATE_NORMAL; + ctx.date_mode = log->date_mode; ctx.output_encoding = get_log_output_encoding(); if (!log->summary) { @@ -220,30 +252,10 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit) } oneline_str = oneline.len ? oneline.buf : "<none>"; - if (log->groups & SHORTLOG_GROUP_AUTHOR) { - strbuf_reset(&ident); - format_commit_message(commit, - log->email ? "%aN <%aE>" : "%aN", - &ident, &ctx); - if (!HAS_MULTI_BITS(log->groups) || - strset_add(&dups, ident.buf)) - insert_one_record(log, ident.buf, oneline_str); - } - if (log->groups & SHORTLOG_GROUP_COMMITTER) { - strbuf_reset(&ident); - format_commit_message(commit, - log->email ? "%cN <%cE>" : "%cN", - &ident, &ctx); - if (!HAS_MULTI_BITS(log->groups) || - strset_add(&dups, ident.buf)) - insert_one_record(log, ident.buf, oneline_str); - } - if (log->groups & SHORTLOG_GROUP_TRAILER) { - insert_records_from_trailers(log, &dups, commit, &ctx, oneline_str); - } + insert_records_from_trailers(log, &dups, commit, &ctx, oneline_str); + insert_records_from_format(log, &dups, commit, &ctx, oneline_str); strset_clear(&dups); - strbuf_release(&ident); strbuf_release(&oneline); } @@ -312,6 +324,7 @@ static int parse_group_option(const struct option *opt, const char *arg, int uns if (unset) { log->groups = 0; string_list_clear(&log->trailers, 0); + string_list_clear(&log->format, 0); } else if (!strcasecmp(arg, "author")) log->groups |= SHORTLOG_GROUP_AUTHOR; else if (!strcasecmp(arg, "committer")) @@ -319,8 +332,15 @@ static int parse_group_option(const struct option *opt, const char *arg, int uns else if (skip_prefix(arg, "trailer:", &field)) { log->groups |= SHORTLOG_GROUP_TRAILER; string_list_append(&log->trailers, field); - } else + } else if (skip_prefix(arg, "format:", &field)) { + log->groups |= SHORTLOG_GROUP_FORMAT; + string_list_append(&log->format, field); + } else if (strchr(arg, '%')) { + log->groups |= SHORTLOG_GROUP_FORMAT; + string_list_append(&log->format, arg); + } else { return error(_("unknown group type: %s"), arg); + } return 0; } @@ -338,6 +358,19 @@ void shortlog_init(struct shortlog *log) log->in2 = DEFAULT_INDENT2; log->trailers.strdup_strings = 1; log->trailers.cmp = strcasecmp; + log->format.strdup_strings = 1; +} + +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"); + + string_list_sort(&log->trailers); } int cmd_shortlog(int argc, const char **argv, const char *prefix) @@ -379,6 +412,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) break; case PARSE_OPT_HELP: case PARSE_OPT_ERROR: + case PARSE_OPT_SUBCOMMAND: exit(129); case PARSE_OPT_COMPLETE: exit(0); @@ -404,10 +438,11 @@ parse_done: log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT; log.abbrev = rev.abbrev; log.file = rev.diffopt.file; + log.date_mode = rev.date_mode; if (!log.groups) log.groups = SHORTLOG_GROUP_AUTHOR; - string_list_sort(&log.trailers); + shortlog_finish_setup(&log); /* assume HEAD if from a tty */ if (!nongit && !rev.pending.nr && isatty(0)) @@ -420,6 +455,8 @@ parse_done: else get_from_rev(&rev, &log); + release_revisions(&rev); + shortlog_output(&log); if (log.file != stdout) fclose(log.file); @@ -439,7 +476,7 @@ void shortlog_output(struct shortlog *log) struct strbuf sb = STRBUF_INIT; if (log->sort_by_number) - QSORT(log->list.items, log->list.nr, + STABLE_QSORT(log->list.items, log->list.nr, log->summary ? compare_by_counter : compare_by_list); for (i = 0; i < log->list.nr; i++) { const struct string_list_item *item = &log->list.items[i]; @@ -474,4 +511,5 @@ void shortlog_output(struct shortlog *log) log->list.strdup_strings = 1; string_list_clear(&log->list, 1); clear_mailmap(&log->mailmap); + string_list_clear(&log->format, 0); } |
