From: Patrick Steinhardt <ps@pks.im>
To: "Mark C. Chu-Carroll" <markchucarroll@fastmail.com>
Cc: git@vger.kernel.org
Subject: Re: [PATCH v2 1/3] repo: add level of concern reporting
Date: Tue, 27 Oct 2026 15:55:19 +0100 [thread overview]
Message-ID: <auC7TjiE_KdoTGyO@pks.im> (raw)
In-Reply-To: <20261026-b4-repo-structure-concern-v2-1-e0df34228dc8@fastmail.com>
On Mon, Oct 26, 2026 at 01:43:54PM -0400, Mark C. Chu-Carroll wrote:
> "git repo structure" computes a set of statistics
> that are useful for diagnosing performance issues in
> a git repository. But for a typical user who hasn't
> encountered any issues yet, it can be difficult to
> interpret the data - when should a value be considered
> concerning?
>
> Add an option to "git repo structure" that adds a "degree
> of concern" column to the structure report, which indicates
> when a stat values has reached a point where it might be
> concerning, and indicate how severe the concern should be.
Missing your Signed-off-by.
I think the commit message should also mention git-sizer(1) here, which
is the direct inspiration that we have for this additional data. To the
best of my knowledge, the level of concern is essentially the last item
that is missing for feature compatibility between these two tools.
Another interesting thing to document here is how you picked the level
of concern in the first place.
> diff --git a/builtin/repo.c b/builtin/repo.c
> index 84e012f83f..a450497d4e 100644
> --- a/builtin/repo.c
> +++ b/builtin/repo.c
> @@ -348,8 +348,118 @@ struct stats_table {
> int name_col_width;
> int value_col_width;
> int unit_col_width;
> + int concern_col_width;
> };
>
> +/*
> + * For computing level of concern about a stat, we need to have some
> + * baseline values. For each of these, we'define a minimum level
> + * of concern, which will appear as 1 star. Then we scale exponentially:
> + * each time that value doubles, we add another star.
> + *
> + * The threshold values for concern are expressed as log_2 values of the size
> + * at which the statistic becomes concerning. The default values for the
> + * threshold were selected based on observed values from a collection of
> + * representative-ish repositories of varying size, by finding the mean log_2
> + * value and standard deviation (sigma) for each statistic over the collection.
> + * The threshold for each metric is then set at mean(log_2) + sigma.
> + */
> +struct concern_stat_threshold {
> + const char *name;
> + size_t threshold;
> +};
> +
> +enum concern_stat {
> + CONCERN_STAT_COMMIT_COUNT,
> + CONCERN_STAT_TREE_COUNT,
> + CONCERN_STAT_BLOB_COUNT,
> + CONCERN_STAT_TAG_COUNT,
> + CONCERN_STAT_COMMIT_SIZE,
> + CONCERN_STAT_TREE_SIZE,
> + CONCERN_STAT_BLOB_SIZE,
> + CONCERN_STAT_TAG_SIZE,
> + CONCERN_STAT_COMMIT_LARGEST,
> + CONCERN_STAT_TREE_LARGEST,
> + CONCERN_STAT_BLOB_LARGEST,
> + CONCERN_STAT_TAG_LARGEST,
> + CONCERN_STAT_COMMIT_INFLATED_SIZE,
> + CONCERN_STAT_TREE_INFLATED_SIZE,
> + CONCERN_STAT_BLOB_INFLATED_SIZE,
> + CONCERN_STAT_TAG_INFLATED_SIZE,
> +};
> +
> +struct concern_stat_threshold repo_structure_concern_thresholds[] = {
> + { .name = "commit_count", .threshold = 19 },
> + {.name = "tree_count", .threshold = 21 },
Nit: missing space.
> + { .name = "blob_count", .threshold = 22 },
> + { .name = "tag_count", .threshold = 15 },
> + { .name = "commit_size", .threshold = 28 },
> + { .name = "tree_size", .threshold = 27 },
> + { .name = "blob_size", .threshold = 36 },
> + { .name = "tags_size", .threshold = 18 },
> + { .name = "commit_largest", .threshold = 18 },
> + { .name = "tree_largest", .threshold = 18 },
> + { .name = "blob_largest", .threshold = 26 },
> + { .name = "tag_largest", .threshold = 15 },
> + { .name = "commit_inflated_size", .threshold = 28 },
> + { .name = "tree_inflated_size", .threshold = 32 },
> + { .name = "blob_inflated_size", .threshold = 34 },
> + { .name = "tag_inflated_size", .threshold = 25}
> +};
I think this is a tiny bit unwieldy, as you now have to remember to
update multiple sites whenever you add another metric to this command.
Ideally, we'd have a single structure that knows to track all
information that we're about to print so that we can define this in a
single, central location.
For example, we could update `struct object_data` to have a configurable
threshold, and then do the same for other such structures. But that
might also get unwieldy by itself.
I think what bugs me a bit is that we have not only two, but three
locations because we have the enum, the thresholds and then also the
data structures themselves.
I'm not quite sure how to fix that myself though.
> +/*
> + * Concern thresholds aren't universal. We try to set reasonable default
> + * values, but projects may want to set these thresholds differently. To
> + * support that, they can set the thresholds for their project in the
> + * project's git config, in the section "repo.structure.concern".
> + */
> +static int config_concern_thresholds(const char *prefixed_var,
> + const char *str_val,
> + const struct config_context *ctx,
> + void *cb UNUSED) {
We should probably use `cb` to pass around the structure that you
populate so that we can avoid global variables.
> + const char *config_prefix = "repo.structure.concern.";
> + if (!strncmp(config_prefix, prefixed_var, strlen(config_prefix))) {
We have `starts_with()`. But also note that if this is a config key then
it should match case-insensitively, so it should rather be
`istarts_with()`.
The config keys are also a bit off as our format only has nested keys up
to three levels deep, not four.
> + const char *concern_name = &prefixed_var[strlen(config_prefix)];
> + for (int i = 0; concern_stat_thresholds[i].name != 0; i++) {
We typically don't compare with a zero value directly, and indices
should be of type `size_t` when iterating, so this should rather be:
for (size_t i = 0; concern_stat_thresholds[i].name; i++)
But the loop condition is a bit weird, because you don't have a final
entry there that has a guaranteed NULL name. So shouldn't we rather
iterate through `ARRAY_SIZE()`?
Also, I may be blind, but where is that struct getting defined? I can
only see `repo_structure_concern_thresholds`.
> + if (!strcmp(concern_stat_thresholds[i].name, concern_name)) {
> + int value = git_config_int(prefixed_var, str_val, ctx->kvi);
> + if (value < 0)
> + return -1;
> + concern_stat_thresholds[i].threshold = value;
> + return 0;
> + }
> + }
> + warning(_("Invalid concern threshold name: '%s'"), concern_name);
> + return 0;
> + }
> + return 0;
> +}
> +
> +static size_t approx_log2(long value) {
We have `log2u()` in "wrapper.h". Can we use that instead?
> @@ -681,12 +887,15 @@ static void print_object_data(const char *key, char key_delim,
> }
>
> static void structure_keyvalue_print(struct repo_structure *stats,
> - char key_delim, char value_delim)
> + char key_delim, char value_delim,
> + bool show_concern)
> {
> print_keyvalue("references.branches.count", key_delim,
> stats->refs.branches, value_delim);
> +
> print_keyvalue("references.tags.count", key_delim,
> stats->refs.tags, value_delim);
> +
Nit: stray newlines.
> diff --git a/sideband.c b/sideband.c
> index 374187402e..0e75ab2317 100644
> --- a/sideband.c
> +++ b/sideband.c
> @@ -1,4 +1,4 @@
> -#define USE_THE_REPOSITORY_VARIABLE
> +define USE_THE_REPOSITORY_VARIABLE
> #define DISABLE_SIGN_COMPARE_WARNINGS
>
> #include "git-compat-util.h"
This change probably shouldn't be here.
Patrick
next prev parent reply other threads:[~2026-10-27 14:55 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-21 13:55 [PATCH 0/3] repo: add degree-of-concern reporting to structure Mark C. Chu-Carroll
2026-10-21 13:55 ` [PATCH 1/3] repo: add level of concern reporting Mark C. Chu-Carroll
2026-10-21 13:55 ` [PATCH 2/3] repo: make concern thresholds configurable Mark C. Chu-Carroll
2026-10-21 13:55 ` [PATCH 3/3] repo: add documentation for level-of-concern Mark C. Chu-Carroll
2026-10-26 17:43 ` [PATCH v2 0/3] repo: add degree-of-concern reporting to structure Mark C. Chu-Carroll
2026-10-26 17:43 ` [PATCH v2 1/3] repo: add level of concern reporting Mark C. Chu-Carroll
2026-10-27 14:55 ` Patrick Steinhardt [this message]
2026-10-26 17:43 ` [PATCH v2 2/3] repo: make concern thresholds configurable Mark C. Chu-Carroll
2026-10-27 14:55 ` Patrick Steinhardt
2026-10-26 17:43 ` [PATCH v2 3/3] repo: add documentation for level-of-concern Mark C. Chu-Carroll
2026-10-27 14:55 ` Patrick Steinhardt
2026-10-28 13:23 ` Mark C. Chu-Carroll
2026-10-27 15:39 ` [PATCH v3 0/2] repo: add degree-of-concern reporting to structure Mark C. Chu-Carroll
2026-10-27 15:39 ` [PATCH v3 1/2] repo: add level of concern reporting Mark C. Chu-Carroll
2026-10-27 15:39 ` [PATCH v3 2/2] repo: make concern thresholds configurable Mark C. Chu-Carroll
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=auC7TjiE_KdoTGyO@pks.im \
--to=ps@pks.im \
--cc=git@vger.kernel.org \
--cc=markchucarroll@fastmail.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