From: Mark C. Chu-Carroll Date: Wed, 21 Oct 2026 13:55:06 GMT Subject: [PATCH 2/3] repo: make concern thresholds configurable Message-ID: <20261021-b4-repo-structure-concern-v1-2-e3010552292c@fastmail.com> In-Reply-To: <20261021-b4-repo-structure-concern-v1-0-e3010552292c@fastmail.com> The concern reporting mechanism so far provides a measure of concern based on heuristic values estimated by analyzing a collection of (hopefully) representative repositories. But for different projects/usecases, the thresholds at which a value becomes concerning may vary. Provide a mechanism using git config to allow users to configure per-repo concern levels for "git repo structure" concern reporting. --- builtin/repo.c | 44 +++++++------ sideband.c | 2 +- t/t1901-repo-structure.sh | 155 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 178 insertions(+), 23 deletions(-) diff --git a/builtin/repo.c b/builtin/repo.c index a450497d4e..aa6efdfd25 100644 --- a/builtin/repo.c +++ b/builtin/repo.c @@ -2,6 +2,7 @@ #include "builtin.h" #include "commit.h" +#include "config.h" #include "environment.h" #include "hash.h" #include "hex.h" @@ -386,25 +387,27 @@ enum concern_stat { CONCERN_STAT_TREE_INFLATED_SIZE, CONCERN_STAT_BLOB_INFLATED_SIZE, CONCERN_STAT_TAG_INFLATED_SIZE, + CONCERN_STAT_MAXIMUM }; -struct concern_stat_threshold repo_structure_concern_thresholds[] = { - { .name = "commit_count", .threshold = 19 }, - {.name = "tree_count", .threshold = 21 }, - { .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} +struct concern_stat_threshold concern_stat_thresholds[] = { + { .name = "commit-count", .threshold = 19 }, + {.name = "tree-count", .threshold = 21 }, + { .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 = "tag-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}, + { 0 } }; /* @@ -450,7 +453,7 @@ static size_t approx_log2(long value) { static size_t repo_structure_concern_level(enum concern_stat stat_id, size_t value) { size_t log_value; - if (stat_id >= ARRAY_SIZE(concern_stat_thresholds)) + if (stat_id >= CONCERN_STAT_MAXIMUM) BUG("unexpected repository stat type"); log_value = approx_log2(value); if (log_value > concern_stat_thresholds[stat_id].threshold) @@ -977,7 +980,7 @@ static void structure_keyvalue_print(struct repo_structure *stats, print_keyvalue("objects.blobs.disk_size", key_delim, stats->objects.disk_sizes.blobs, value_delim); if (show_concern) - print_keyvalue("objects.blobs.blob_size.concern_level", key_delim, + print_keyvalue("objects.blobs.disk_size.concern_level", key_delim, repo_structure_concern_level(CONCERN_STAT_BLOB_SIZE, stats->objects.disk_sizes.blobs), value_delim); @@ -1243,7 +1246,8 @@ static int cmd_repo_structure(int argc, const char **argv, const char *prefix, argc = parse_options(argc, argv, prefix, options, repo_structure_usage, 0); if (argc) usage(_("too many arguments")); - + if (show_concern) + repo_config(repo, &config_concern_thresholds, NULL); repo_init_revisions(repo, &revs, prefix); if (show_progress < 0) diff --git a/sideband.c b/sideband.c index 0e75ab2317..374187402e 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" diff --git a/t/t1901-repo-structure.sh b/t/t1901-repo-structure.sh index f1e3fd2a75..49ecc081ad 100755 --- a/t/t1901-repo-structure.sh +++ b/t/t1901-repo-structure.sh @@ -67,13 +67,164 @@ test_expect_success 'empty repository' ' EOF git repo structure >out 2>err && - cp out /tmp/test1-out && test_cmp expect out && test_line_count = 0 err ) ' +test_expect_success SHA1 'repository with references and objects' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit_bulk 1005 && + git tag -a foo -m bar && + + oid="$(git rev-parse HEAD)" && + git update-ref refs/remotes/origin/foo "$oid" && + + # Also creates a commit, tree, and blob. + git notes add -m foo && + + # The tags disk size is handled specially due to the + # git-rev-list(1) --disk-usage=human option printing the full + # "byte/bytes" unit string instead of just "B". + cat >expect <<-EOF && + | Repository structure | Value | + | ------------------------- | ---------- | + | * References | | + | * Count | 4 | + | * Branches | 1 | + | * Tags | 1 | + | * Remotes | 1 | + | * Others | 1 | + | | | + | * Reachable objects | | + | * Count | 3.02 k | + | * Commits | 1.01 k | + | * Trees | 1.01 k | + | * Blobs | 1.01 k | + | * Tags | 1 | + | * Inflated size | 16.03 MiB | + | * Commits | 217.92 KiB | + | * Trees | 15.81 MiB | + | * Blobs | 11.68 KiB | + | * Tags | 132 B | + | * Disk size | $(object_type_disk_usage all true) | + | * Commits | $(object_type_disk_usage commit true) | + | * Trees | $(object_type_disk_usage tree true) | + | * Blobs | $(object_type_disk_usage blob true) | + | * Tags | $(object_type_disk_usage tag) B | + | | | + | * Largest objects | | + | * Commits | | + | * Maximum size [1] | 223 B | + | * Maximum parents [2] | 1 | + | * Trees | | + | * Maximum size [3] | 32.29 KiB | + | * Maximum entries [4] | 1.01 k | + | * Blobs | | + | * Maximum size [5] | 13 B | + | * Tags | | + | * Maximum size [6] | 132 B | + + [1] 0dc91eb18580102a3a216c8bfecedeba2b9f9b9a + [2] 0dc91eb18580102a3a216c8bfecedeba2b9f9b9a + [3] 60665251ab71dbd8c18d9bf2174f4ee0d58aa06c + [4] 60665251ab71dbd8c18d9bf2174f4ee0d58aa06c + [5] 97d808e45116bf02103490294d3d46dad7a2ac62 + [6] 4dae4f5954f5e6feb3577cfb1b181daa3fd3afd2 + EOF + + git repo structure >out 2>err && + + test_cmp expect out && + test_line_count = 0 err + ) +' + +test_expect_success SHA1 'concern flagging' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + # configure thresholds to get concern indications + git config repo.structure.concern.commit-count 7 && + git config repo.structure.concern.commit-size 14 && + git config repo.structure.concern.tree-size 10 && + git config repo.structure.concern.blob-size 8 && + git config repo.structure.concern.blob-inflated-size 10 && + git config repo.structure.concern.tag-size 2 && + git config repo.structure.concern.commit-largest 4 && + git config repo.structure.concern.blob-largest 2 && + touch configured && + test_commit_bulk 1005 && + git tag -a foo -m bar && + oid="$(git rev-parse HEAD)" && + git update-ref refs/remotes/origin/foo "$oid" && + + # Also creates a commit, tree, and blob. + git notes add -m foo && + + + # The tags disk size is handled specially due to the + # git-rev-list(1) --disk-usage=human option printing the full + # "byte/bytes" unit string instead of just "B". + cat >expect <<-EOF && + | Repository structure | Value | Concern | + | ------------------------- | ---------- | -------- | + | * References | | | + | * Count | 4 | | + | * Branches | 1 | | + | * Tags | 1 | | + | * Remotes | 1 | | + | * Others | 1 | | + | | | | + | * Reachable objects | | | + | * Count | 3.02 k | | + | * Commits | 1.01 k | ** | + | * Trees | 1.01 k | | + | * Blobs | 1.01 k | | + | * Tags | 1 | | + | * Inflated size | 16.03 MiB | | + | * Commits | 217.92 KiB | | + | * Trees | 15.81 MiB | | + | * Blobs | 11.68 KiB | *** | + | * Tags | 132 B | | + | * Disk size | $(object_type_disk_usage all true) | | + | * Commits | $(object_type_disk_usage commit true) | *** | + | * Trees | $(object_type_disk_usage tree true) | ******** | + | * Blobs | $(object_type_disk_usage blob true) | ****** | + | * Tags | $(object_type_disk_usage tag) B | **** | + | | | | + | * Largest objects | | | + | * Commits | | | + | * Maximum size [1] | 223 B | *** | + | * Maximum parents [2] | 1 | | + | * Trees | | | + | * Maximum size [3] | 32.29 KiB | | + | * Maximum entries [4] | 1.01 k | | + | * Blobs | | | + | * Maximum size [5] | 13 B | * | + | * Tags | | | + | * Maximum size [6] | 132 B | | + + [1] 0dc91eb18580102a3a216c8bfecedeba2b9f9b9a + [2] 0dc91eb18580102a3a216c8bfecedeba2b9f9b9a + [3] 60665251ab71dbd8c18d9bf2174f4ee0d58aa06c + [4] 60665251ab71dbd8c18d9bf2174f4ee0d58aa06c + [5] 97d808e45116bf02103490294d3d46dad7a2ac62 + [6] 4dae4f5954f5e6feb3577cfb1b181daa3fd3afd2 + EOF + + git repo structure --concern >actual 2>err && + + test_cmp expect actual && + test_line_count = 0 err + ) +' + test_expect_success SHA1 'lines and nul format' ' test_when_finished "rm -rf repo" && git init repo && @@ -172,7 +323,7 @@ test_expect_success SHA1 'lines format with concerns' ' objects.tags.inflated_size.concern_level=0 objects.commits.disk_size.concern_level=3 objects.trees.disk_size.concern_level=8 - objects.blobs.blob_size.concern_level=6 + objects.blobs.disk_size.concern_level=6 objects.tags.disk_size.concern_level=4 objects.commits.max_size.concern_level=3 objects.trees.max_size.concern_level=0 -- 2.53.0