From: Patrick Steinhardt <ps@pks.im>
To: Justin Tobler <jltobler@gmail.com>
Cc: git@vger.kernel.org, karthik.188@gmail.com
Subject: Re: [PATCH v2 5/6] builtin/repo: add keyvalue and nul format for stats
Date: Thu, 25 Sep 2025 07:39:03 +0200 [thread overview]
Message-ID: <aNTVdy4hhLDlMpVT@pks.im> (raw)
In-Reply-To: <20250924212426.2930029-6-jltobler@gmail.com>
On Wed, Sep 24, 2025 at 04:24:25PM -0500, Justin Tobler wrote:
> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
> index 0b8d74ed3e..db21b75522 100644
> --- a/Documentation/git-repo.adoc
> +++ b/Documentation/git-repo.adoc
> @@ -52,7 +52,26 @@ supported:
> * Reachable object counts categorized by type
>
> +
> -The table output format may change and is not intended for machine parsing.
> +The output format can be chosen through the flag `--format`. Three 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.
Let's mention that this is the default format.
> +`keyvalue`:::
> + Each line of output contains a key-value pair for a repository stat.
> + The '=' character is used to delimit between the key and the value.
> + Values containing "unusual" characters are quoted as explained for the
> + configuration variable `core.quotePath` (see linkgit:git-config[1]).
In the current state there is never any quoting, so this statement here
is a bit misleading. Should we maybe drop that part?
> +`nul`:::
> + Similar to `keyvalue`, but uses a NUL character to delimit between
> + key-value pairs instead of a newline. Also uses a newline character as
> + the delimiter between the key and value instead of '='. Unlike the
> + `keyvalue` format, values containing "unusual" characters are never
> + quoted.
Likewise.
> diff --git a/builtin/repo.c b/builtin/repo.c
> index 8f130bca66..fe7d43f78e 100644
> --- a/builtin/repo.c
> +++ b/builtin/repo.c
> @@ -312,6 +317,33 @@ static void stats_table_clear(struct stats_table *table)
> string_list_clear(&table->rows, 1);
> }
>
> +static void stats_keyvalue_print(struct repo_stats *stats, char key_delim,
> + char value_delim)
> +{
> + struct strbuf buf = STRBUF_INIT;
> +
> + strbuf_addf(&buf, "references.branches.count%c%" PRIuMAX "%c",
> + key_delim, (uintmax_t)stats->refs.branches, value_delim);
> + strbuf_addf(&buf, "references.tags.count%c%" PRIuMAX "%c",
> + key_delim, (uintmax_t)stats->refs.tags, value_delim);
> + strbuf_addf(&buf, "references.remotes.count%c%" PRIuMAX "%c",
> + key_delim, (uintmax_t)stats->refs.remotes, value_delim);
> + strbuf_addf(&buf, "references.others.count%c%" PRIuMAX "%c",
> + key_delim, (uintmax_t)stats->refs.others, value_delim);
> +
> + strbuf_addf(&buf, "objects.commits.count%c%" PRIuMAX "%c",
> + key_delim, (uintmax_t)stats->objects.commits, value_delim);
> + strbuf_addf(&buf, "objects.trees.count%c%" PRIuMAX "%c",
> + key_delim, (uintmax_t)stats->objects.trees, value_delim);
> + strbuf_addf(&buf, "objects.blobs.count%c%" PRIuMAX "%c",
> + key_delim, (uintmax_t)stats->objects.blobs, value_delim);
> + strbuf_addf(&buf, "objects.tags.count%c%" PRIuMAX "%c",
> + key_delim, (uintmax_t)stats->objects.tags, value_delim);
> +
> + fwrite(buf.buf, sizeof(char), buf.len, stdout);
> + strbuf_release(&buf);
> +}
Same question here regarding the buffering. Can't we print to stdout
directly, or is there a reason not to?
> @@ -389,17 +421,25 @@ static void stats_count_objects(struct object_stats *stats,
> path_walk_info_clear(&info);
> }
>
> -static int cmd_repo_stats(int argc UNUSED, const char **argv UNUSED,
> - const char *prefix, struct repository *repo)
> +static int cmd_repo_stats(int argc, const char **argv, const char *prefix,
> + struct repository *repo)
> {
> struct ref_filter filter = REF_FILTER_INIT;
> struct stats_table table = {
> .rows = STRING_LIST_INIT_DUP,
> };
> + enum output_format format = FORMAT_TABLE;
> struct repo_stats stats = { 0 };
> struct ref_array refs = { 0 };
> struct rev_info revs;
> + struct option options[] = {
> + OPT_CALLBACK_F(0, "format", &format, N_("format"),
> + N_("output format"),
> + PARSE_OPT_NONEG, parse_format_cb),
> + OPT_END()
> + };
>
> + parse_options(argc, argv, prefix, options, repo_usage, 0);
I think it would be sensible to introduce this call to `parse_options()`
right in the first commit that wires up the new subcommand. If we don't
do that we otherwise accept arbitrary arguments without raising any
error, and neither do we know to output help.
So we should move the addition to a previous commit and probably do the
following:
argc = parse_options(...);
if (argc)
usagef("too many arguments");
> @@ -407,8 +447,20 @@ static int cmd_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_keyvalue_print(&stats, '=', '\n');
> + break;
> + case FORMAT_NUL_TERMINATED:
> + stats_keyvalue_print(&stats, '\n', '\0');
> + break;
This reads much nicer now. The newline as key-value delimiter is a
curious choice, but you simply do what we already do in `git repo info`.
> diff --git a/t/t1901-repo-stats.sh b/t/t1901-repo-stats.sh
> index 315b9e1767..d2c1b6e307 100755
> --- a/t/t1901-repo-stats.sh
> +++ b/t/t1901-repo-stats.sh
> @@ -73,4 +73,37 @@ test_expect_success 'repository with references and objects' '
> )
> '
>
> +test_expect_success 'repository stats with keyvalue and nul format' '
> + test_when_finished "rm -rf repo" &&
> + git init repo &&
> + (
> + cd repo &&
> + test_commit_bulk 42 &&
> + git tag -a foo -m bar &&
> +
> + 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
> +
> + git repo stats --format=keyvalue >out 2>err &&
> +
> + test_cmp expect out &&
> + test_line_count = 0 err &&
> +
> + # Replace key and value delimiters for nul format.
> + tr "\n" "\0" <expect | tr "=" "\n" >expect_null &&
You can do this without the pipe:
tr "\n=" "\0\n" <expect >expect_nul
Also, let's call the file `expect_nul` (with a single 'l') to match the
format.
Patrick
next prev parent reply other threads:[~2025-09-25 5:39 UTC|newest]
Thread overview: 92+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-23 2:56 [PATCH 0/4] builtin/repo: introduce stats subcommand Justin Tobler
2025-09-23 2:56 ` [PATCH 1/4] " Justin Tobler
2025-09-23 10:52 ` Patrick Steinhardt
2025-09-23 15:10 ` Justin Tobler
2025-09-23 15:26 ` Patrick Steinhardt
2025-09-23 15:22 ` Karthik Nayak
2025-09-23 15:55 ` Justin Tobler
2025-09-23 2:56 ` [PATCH 2/4] builtin/repo: add object counts in stats output Justin Tobler
2025-09-23 10:52 ` Patrick Steinhardt
2025-09-23 15:19 ` Justin Tobler
2025-09-23 15:30 ` Karthik Nayak
2025-09-23 15:56 ` Justin Tobler
2025-09-23 2:56 ` [PATCH 3/4] builtin/repo: add keyvalue format for stats Justin Tobler
2025-09-23 10:53 ` Patrick Steinhardt
2025-09-23 15:26 ` Justin Tobler
2025-09-23 15:39 ` Karthik Nayak
2025-09-23 15:59 ` Justin Tobler
2025-09-23 2:57 ` [PATCH 4/4] builtin/repo: add nul " Justin Tobler
2025-09-23 10:53 ` Patrick Steinhardt
2025-09-23 15:33 ` Justin Tobler
2025-09-24 4:48 ` Patrick Steinhardt
2025-09-23 15:41 ` Karthik Nayak
2025-09-23 16:02 ` Justin Tobler
2025-09-24 21:24 ` [PATCH v2 0/6] builtin/repo: introduce stats subcommand Justin Tobler
2025-09-24 21:24 ` [PATCH v2 1/6] builtin/repo: rename repo_info() to cmd_repo_info() Justin Tobler
2025-09-24 21:24 ` [PATCH v2 2/6] ref-filter: allow NULL filter pattern Justin Tobler
2025-09-24 21:24 ` [PATCH v2 3/6] builtin/repo: introduce stats subcommand Justin Tobler
2025-09-25 5:38 ` Patrick Steinhardt
2025-09-25 13:01 ` Justin Tobler
2025-09-24 21:24 ` [PATCH v2 4/6] builtin/repo: add object counts in stats output Justin Tobler
2025-09-24 21:24 ` [PATCH v2 5/6] builtin/repo: add keyvalue and nul format for stats Justin Tobler
2025-09-25 5:39 ` Patrick Steinhardt [this message]
2025-09-25 13:16 ` Justin Tobler
2025-09-25 13:58 ` Patrick Steinhardt
2025-09-24 21:24 ` [PATCH v2 6/6] builtin/repo: add progress meter " Justin Tobler
2025-09-25 5:39 ` Patrick Steinhardt
2025-09-25 13:20 ` Justin Tobler
2025-09-25 23:29 ` [PATCH v3 0/7] builtin/repo: introduce stats subcommand Justin Tobler
2025-09-25 23:29 ` [PATCH v3 1/7] builtin/repo: rename repo_info() to cmd_repo_info() Justin Tobler
2025-09-25 23:29 ` [PATCH v3 2/7] ref-filter: allow NULL filter pattern Justin Tobler
2025-09-25 23:29 ` [PATCH v3 3/7] clang-format: exclude control macros from SpaceBeforeParens Justin Tobler
2025-09-25 23:29 ` [PATCH v3 4/7] builtin/repo: introduce stats subcommand Justin Tobler
2025-09-25 23:51 ` Eric Sunshine
2025-09-26 1:38 ` Justin Tobler
2025-09-25 23:29 ` [PATCH v3 5/7] builtin/repo: add object counts in stats output Justin Tobler
2025-09-25 23:29 ` [PATCH v3 6/7] builtin/repo: add keyvalue and nul format for stats Justin Tobler
2025-09-25 23:29 ` [PATCH v3 7/7] builtin/repo: add progress meter " Justin Tobler
2025-09-27 14:50 ` [PATCH v4 0/7] builtin/repo: introduce stats subcommand Justin Tobler
2025-09-27 14:50 ` [PATCH v4 1/7] builtin/repo: rename repo_info() to cmd_repo_info() Justin Tobler
2025-09-27 14:50 ` [PATCH v4 2/7] ref-filter: allow NULL filter pattern Justin Tobler
2025-09-27 14:50 ` [PATCH v4 3/7] clang-format: exclude control macros from SpaceBeforeParens Justin Tobler
2025-09-27 15:40 ` Junio C Hamano
2025-09-27 15:51 ` Justin Tobler
2025-09-27 23:49 ` Junio C Hamano
2025-09-27 14:50 ` [PATCH v4 4/7] builtin/repo: introduce stats subcommand Justin Tobler
2025-09-27 16:32 ` Junio C Hamano
2025-10-09 22:09 ` Justin Tobler
2025-10-10 0:42 ` Justin Tobler
2025-10-10 6:53 ` Patrick Steinhardt
2025-10-10 14:34 ` Justin Tobler
2025-10-13 6:13 ` Patrick Steinhardt
2025-09-27 14:50 ` [PATCH v4 5/7] builtin/repo: add object counts in stats output Justin Tobler
2025-09-27 14:50 ` [PATCH v4 6/7] builtin/repo: add keyvalue and nul format for stats Justin Tobler
2025-09-27 14:50 ` [PATCH v4 7/7] builtin/repo: add progress meter " Justin Tobler
2025-09-27 16:33 ` [PATCH v4 0/7] builtin/repo: introduce stats subcommand Junio C Hamano
2025-10-15 21:12 ` [PATCH v5 0/6] builtin/repo: introduce structure subcommand Justin Tobler
2025-10-15 21:12 ` [PATCH v5 1/6] builtin/repo: rename repo_info() to cmd_repo_info() Justin Tobler
2025-10-15 21:12 ` [PATCH v5 2/6] ref-filter: allow NULL filter pattern Justin Tobler
2025-10-15 21:12 ` [PATCH v5 3/6] builtin/repo: introduce structure subcommand Justin Tobler
2025-10-16 10:58 ` Patrick Steinhardt
2025-10-21 16:04 ` Justin Tobler
2025-10-15 21:12 ` [PATCH v5 4/6] builtin/repo: add object counts in structure output Justin Tobler
2025-10-15 21:12 ` [PATCH v5 5/6] builtin/repo: add keyvalue and nul format for structure stats Justin Tobler
2025-10-15 21:12 ` [PATCH v5 6/6] builtin/repo: add progress meter " Justin Tobler
2025-10-21 18:25 ` [PATCH v6 0/7] builtin/repo: introduce structure subcommand Justin Tobler
2025-10-21 18:25 ` [PATCH v6 1/7] builtin/repo: rename repo_info() to cmd_repo_info() Justin Tobler
2025-10-21 18:25 ` [PATCH v6 2/7] ref-filter: allow NULL filter pattern Justin Tobler
2025-10-21 18:25 ` [PATCH v6 3/7] ref-filter: export ref_kind_from_refname() Justin Tobler
2025-10-21 18:25 ` [PATCH v6 4/7] builtin/repo: introduce structure subcommand Justin Tobler
2025-10-22 5:01 ` Patrick Steinhardt
2025-10-22 13:50 ` Justin Tobler
2025-10-22 20:15 ` Lucas Seiki Oshiro
2025-10-22 23:42 ` Justin Tobler
2025-10-21 18:25 ` [PATCH v6 5/7] builtin/repo: add object counts in structure output Justin Tobler
2025-10-21 18:26 ` [PATCH v6 6/7] builtin/repo: add keyvalue and nul format for structure stats Justin Tobler
2025-10-22 20:34 ` Lucas Seiki Oshiro
2025-10-23 0:03 ` Justin Tobler
2025-10-21 18:26 ` [PATCH v6 7/7] builtin/repo: add progress meter " Justin Tobler
2025-10-22 19:23 ` [PATCH v6 0/7] builtin/repo: introduce structure subcommand Lucas Seiki Oshiro
2025-10-23 0:05 ` Justin Tobler
2025-10-23 20:54 ` Junio C Hamano
2025-10-24 5:14 ` Patrick Steinhardt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aNTVdy4hhLDlMpVT@pks.im \
--to=ps@pks.im \
--cc=git@vger.kernel.org \
--cc=jltobler@gmail.com \
--cc=karthik.188@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).