[PATCH 3/4] builtin/repo: add keyvalue format for stats
- From
Justin Tobler <jltobler@gmail.com>
- Date
- Sep 23, 2025, 02:56 UTC
- Message-ID
- <20250923025700.3046260-4-jltobler@gmail.com>
- In-Reply-To
- <20250923025700.3046260-1-jltobler@gmail.com>
All repository stats are outputted in a human-friendly table form. This format is not suitable for machine parsing. Add a --format option that supports two output modes: `table` and `keyvalue`. The `table` mode is the default format and prints the same table output as before. With the `keyvalue` mode, each line of output contains a key-value pair of a repository stat. The '=' character is used to delimit between keys and values. This mode provides output that is more machine-friendly.
Signed-off-by: Justin Tobler <jltobler@gmail.com> --- Documentation/git-repo.adoc | 16 ++++++++-- builtin/repo.c | 61 ++++++++++++++++++++++++++++++++++--- t/t1901-repo-stats.sh | 25 +++++++++++++++ 3 files changed, 94 insertions(+), 8 deletions(-)
diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc index 2a67abfca8..7d0341e4f1 100644 --- a/Documentation/git-repo.adoc +++ b/Documentation/git-repo.adoc @@ -9,7 +9,7 @@ SYNOPSIS -------- [synopsis] git repo info [--format=(keyvalue|nul)] [-z] [<key>...] -git repo stats +git repo stats [--format=(table|keyvalue)] DESCRIPTION ----------- @@ -44,12 +44,22 @@ supported: + `-z` is an alias for `--format=nul`. -stats:: +`stats [--format=(table|keyvalue)]`:: Retrieve stats about the current repository. All references and reachable objects in the repository are categorized and counted accordingly. + -The table output format may change and is not intended for machine parsing. +The output format can be chosen through the flag `--format`. Two formats are +supported: ++ +`table`::: + Outputs repository stats in a human-friendly table and is used by + default. This format may change and is not intended for machine + parsing. + +`keyvalue`::: + Each line of output contains a key-value pair of a repostiory stat. The + '=' character is used to delimit between the key and the value. INFO KEYS --------- diff --git a/builtin/repo.c b/builtin/repo.c index a24ea0e66b..4c16a68e4e 100644 --- a/builtin/repo.c +++ b/builtin/repo.c @@ -14,13 +14,14 @@ static const char *const repo_usage[] = { "git repo info [--format=(keyvalue|nul)] [-z] [<key>...]", - "git repo stats", + "git repo stats [--format=(table|keyvalue)]", NULL }; typedef int get_value_fn(struct repository *repo, struct strbuf *buf); enum output_format { + FORMAT_TABLE, FORMAT_KEYVALUE, FORMAT_NUL_TERMINATED, }; @@ -135,6 +136,8 @@ static int parse_format_cb(const struct option *opt, *format = FORMAT_NUL_TERMINATED; else if (!strcmp(arg, "keyvalue")) *format = FORMAT_KEYVALUE; + else if (!strcmp(arg, "table")) + *format = FORMAT_TABLE; else die(_("invalid format '%s'"), arg); @@ -157,6 +160,8 @@ static int repo_info(int argc, const char **argv, const char *prefix, }; argc = parse_options(argc, argv, prefix, options, repo_usage, 0); + if (format == FORMAT_TABLE) + die(_("table format not supported")); return print_fields(argc, argv, repo, format); } @@ -286,6 +291,32 @@ static void stats_table_print(struct stats_table *table) strbuf_release(&buf); } +static void stats_print(struct stats *stats) +{ + struct strbuf buf = STRBUF_INIT; + + strbuf_addf(&buf, "references.branches.count=%" PRIuMAX "\n", + (uintmax_t)stats->refs.branches); + strbuf_addf(&buf, "references.tags.count=%" PRIuMAX "\n", + (uintmax_t)stats->refs.tags); + strbuf_addf(&buf, "references.remotes.count=%" PRIuMAX "\n", + (uintmax_t)stats->refs.remotes); + strbuf_addf(&buf, "references.others.count=%" PRIuMAX "\n", + (uintmax_t)stats->refs.others); + + strbuf_addf(&buf, "objects.commits.count=%" PRIuMAX "\n", + (uintmax_t)stats->objects.commits); + strbuf_addf(&buf, "objects.trees.count=%" PRIuMAX "\n", + (uintmax_t)stats->objects.trees); + strbuf_addf(&buf, "objects.blobs.count=%" PRIuMAX "\n", + (uintmax_t)stats->objects.blobs); + strbuf_addf(&buf, "objects.tags.count=%" PRIuMAX "\n", + (uintmax_t)stats->objects.tags); + + fwrite(buf.buf, sizeof(char), buf.len, stdout); + strbuf_release(&buf); +} + static void stats_count_references(struct ref_stats *stats, struct ref_array *refs) { for (int i = 0; i < refs->nr; i++) { @@ -359,9 +390,16 @@ static void stats_count_objects(struct object_stats *stats, path_walk_info_clear(&info); } -static int repo_stats(int argc UNUSED, const char **argv UNUSED, - const char *prefix, struct repository *repo) +static int repo_stats(int argc, const char **argv, const char *prefix, + struct repository *repo) { + enum output_format format = FORMAT_TABLE; + struct option options[] = { + OPT_CALLBACK_F(0, "format", &format, N_("format"), + N_("output format"), + PARSE_OPT_NONEG, parse_format_cb), + OPT_END() + }; struct ref_filter filter = REF_FILTER_INIT; struct strvec ref_patterns = STRVEC_INIT; struct stats_table table = { 0 }; @@ -369,6 +407,10 @@ static int repo_stats(int argc UNUSED, const char **argv UNUSED, struct stats stats = { 0 }; struct rev_info revs; + parse_options(argc, argv, prefix, options, repo_usage, 0); + if (format == FORMAT_NUL_TERMINATED) + die(_("nul format not yet supported")); + repo_init_revisions(repo, &revs, prefix); filter.name_patterns = ref_patterns.v; filter_refs(&refs, &filter, FILTER_REFS_REGULAR); @@ -376,8 +418,17 @@ static int repo_stats(int argc UNUSED, const char **argv UNUSED, stats_count_references(&stats.refs, &refs); stats_count_objects(&stats.objects, &refs, &revs); - stats_table_setup(&table, &stats); - stats_table_print(&table); + switch (format) { + case FORMAT_TABLE: + stats_table_setup(&table, &stats); + stats_table_print(&table); + break; + case FORMAT_KEYVALUE: + stats_print(&stats); + break; + default: + BUG("not a valid output format: %d", format); + } string_list_clear(&table.rows, 1); strvec_clear(&ref_patterns); diff --git a/t/t1901-repo-stats.sh b/t/t1901-repo-stats.sh index c6a7f08be5..5bc6d9d5c4 100755 --- a/t/t1901-repo-stats.sh +++ b/t/t1901-repo-stats.sh @@ -102,4 +102,29 @@ test_expect_success 'repository stats with objects' ' ) ' +test_expect_success 'repository stats with keyvalue format' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit_bulk 42 && + git tag -a foo -m bar && + git repo stats --format=keyvalue >out 2>err && + + cat >expect <<-EOF && + references.branches.count=1 + references.tags.count=1 + references.remotes.count=0 + references.others.count=0 + objects.commits.count=42 + objects.trees.count=42 + objects.blobs.count=42 + objects.tags.count=1 + EOF + + test_cmp expect out && + test_line_count = 0 err + ) +' + test_done
-- 2.51.0.193.g4975ec3473b