Thread (15 messages) 15 messages, 2 authors, 10h ago

[PATCH v3 2/2] repo: make concern thresholds configurable

flat view
HOTtoday

From: Mark C. Chu-Carroll <hidden>
Date: 2026-10-27 15:39:46
Subsystem: documentation, the rest · Maintainers: Jonathan Corbet, Linus Torvalds

Revision v3 (3 revisions archived).

Revisions (3)
  1. v1
  2. v2 [diff against previous]
  3. v3 current [diff against previous]
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.
---
 Documentation/config/repo.adoc |  59 ++++++++++++++
 Documentation/git-repo.adoc    |  11 +++
 builtin/repo.c                 |  62 +++++++-------
 sideband.c                     |   2 +-
 t/t1901-repo-structure.sh      | 180 ++++++++++++++++++++++++++++++++++++++---
 5 files changed, 273 insertions(+), 41 deletions(-)
diff --git a/Documentation/config/repo.adoc b/Documentation/config/repo.adoc
new file mode 100644
index 0000000000..811b099d72
--- /dev/null
+++ b/Documentation/config/repo.adoc
@@ -0,0 +1,59 @@
+`repo.concern.commit-count`::
+	Sets the value of the concern threshold for commit count.
+
+`repo.concern.tree-count`::
+	Sets the value of the concern threshold for tree count.
+
+`repo.concern.blob-count`::
+	Sets the value of the concern threshold for blob count.
+
+`repo.concern.tag-count`::
+	Sets the value of the concern threshold for tag count.
+
+`repo.concern.commit-size`::
+	Sets the value of the concern threshold for total commit size.
+
+`repo.concern.tree-size`::
+	Sets the value of the concern threshold for total tree size.
+
+`repo.concern.blob-size`::
+	Sets the value of the concern threshold for total blob size.
+
+`repo.concern.tag-size`::
+	Sets the value of the concern threshold for total tag size.
+
+`repo.concern.commit-inflated-size`::
+	Sets the value of the concern threshold for total size
+	of all inflated commits.
+
+`repo.concern.tree-inflated-size`::
+	Sets the value of the concern threshold for total size of
+	all inflated trees.
+
+`repo.concern.blob-inflated-size`::
+	Sets the value of the concern threshold for total size of
+	all inflated blobs.
+
+`repo.concern.tag-inflated-size`::
+	Sets the value of the concern threshold for the total size
+	of all inflated tags.
+
+`repo.concern.commit-largest`::
+	Sets the value of the concern threshold the size of the largest
+	commit.
+
+`repo.concern.tree-largest`::
+	Sets the value of the concern threshold for the size of the
+	largest tree.
+
+`repo.concern.blob-largest`::
+	Sets the value of the concern threshold for the size of the
+	largest blob.
+
+`repo.concern.tag-largest`::
+	Sets the value of the concern threshold for the size of the
+	largest tag.
+
+All of the concern thresholds are integer values for the
+floor of the base-2 log of the threshold value. The default values
+of each threshold are set to heuristically determined values.
diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index f186b27d87..516403e17d 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -101,6 +101,8 @@ statistic value. The value is considered concerning if it exceeds a heuristicall
 chosen threshold value; the degree of concern is the difference between the
 log_2 of the value, and the log_2 of the corresponding threshold.
 
+The threshold values are configurable; see the configuration section below.
+
 INFO KEYS
 ---------
 In order to obtain a set of values from `git repo info`, you should provide
@@ -153,6 +155,15 @@ using the `nul` format:
 git repo info --format=nul layout.bare layout.shallow
 ------------
 
+CONFIGURATION
+-------------
+
+include::includes/cmd-config-section-all.adoc[]
+
+:git-repo: 1
+include::config/repo.adoc[]
+
+
 SEE ALSO
 --------
 linkgit:git-rev-parse[1]
diff --git a/builtin/repo.c b/builtin/repo.c
index a450497d4e..ed579e6685 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"
@@ -29,6 +30,8 @@
 #define REPO_STRUCTURE_USAGE \
 	"git repo structure [--format=(table|lines|nul) | -z] [--[no-]concern]"
 
+#define REPO_STRUCTURE_CONFIG_PREFIX "repo.concern."
+
 static const char *const repo_usage[] = {
 	REPO_INFO_USAGE,
 	REPO_STRUCTURE_USAGE,
@@ -386,40 +389,43 @@ 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 }
 };
 
 /*
  * 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".
+ * project's git config, in the section "repo.concern".
   */
 static int config_concern_thresholds(const char *prefixed_var,
 				     const char *str_val,
 				     const struct config_context *ctx,
 				     void *cb UNUSED) {
-	const char *config_prefix = "repo.structure.concern.";
-	if (!strncmp(config_prefix, prefixed_var, strlen(config_prefix))) {
-		const char *concern_name = &prefixed_var[strlen(config_prefix)];
+	if (!strncmp(REPO_STRUCTURE_CONFIG_PREFIX, prefixed_var,
+	      strlen(REPO_STRUCTURE_CONFIG_PREFIX))) {
+		const char *concern_name =
+			&prefixed_var[strlen(REPO_STRUCTURE_CONFIG_PREFIX)];
 		for (int i = 0; concern_stat_thresholds[i].name != 0; i++) {
 			if (!strcmp(concern_stat_thresholds[i].name, concern_name)) {
 				int value = git_config_int(prefixed_var, str_val, ctx->kvi);
@@ -435,7 +441,7 @@ static int config_concern_thresholds(const char *prefixed_var,
 	return 0;
 }
 
-static size_t approx_log2(long value) {
+static size_t approx_log2(size_t value) {
 	int position = 0;
 	if (value <= 0) {
 		return 0;
@@ -450,7 +456,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)
@@ -844,6 +850,7 @@ static void stats_table_print_structure(const struct stats_table *table,
 			strbuf_utf8_align(&buf, ALIGN_LEFT, concern_col_width, concern_str.buf);
 			strbuf_addstr(&buf, " |");
 		}
+		strbuf_release(&concern_str);
 		printf("%s\n", buf.buf);
 	}
 
@@ -977,7 +984,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);
@@ -997,7 +1004,7 @@ static void structure_keyvalue_print(struct repo_structure *stats,
 							    stats->objects.largest.commit_size.value),
 			      value_delim);
 	print_object_data("objects.trees.max_size", key_delim,
-			  &stats->objects.largest.tree_size, value_delim);
+			 &stats->objects.largest.tree_size, value_delim);
 	if (show_concern)
 		print_keyvalue("objects.trees.max_size.concern_level", key_delim,
 			       repo_structure_concern_level(CONCERN_STAT_TREE_LARGEST,
@@ -1011,7 +1018,7 @@ static void structure_keyvalue_print(struct repo_structure *stats,
 							    stats->objects.largest.blob_size.value),
 			      value_delim);
 	print_object_data("objects.tags.max_size", key_delim,
-			  &stats->objects.largest.tag_size, value_delim);
+			   &stats->objects.largest.tag_size, value_delim);
 	if (show_concern)
 		print_keyvalue("objects.tags.max_size.concern_level", key_delim,
 			       repo_structure_concern_level(CONCERN_STAT_TAG_LARGEST,
@@ -1243,7 +1250,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..e8f1269ad4 100755
--- a/t/t1901-repo-structure.sh
+++ b/t/t1901-repo-structure.sh
@@ -67,13 +67,166 @@ 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 &&
+		# differences between zlib and zlib-ng can cause this
+		# test to fail; so we just turn it off.
+		git config core.compression 0 &&
+		# configure thresholds to get concern indications
+		git config repo.concern.commit-count 7 &&
+		git config repo.concern.commit-size 14 &&
+		git config repo.concern.tree-size 10 &&
+		git config repo.concern.blob-size 8 &&
+		git config repo.concern.blob-inflated-size 10 &&
+		git config repo.concern.tag-size 2 &&
+		git config repo.concern.commit-largest 4 &&
+		git config repo.concern.blob-largest 2 &&
+		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 &&
@@ -139,16 +292,18 @@ test_expect_success SHA1 'lines format with concerns' '
 	git init repo &&
 	(
 		cd repo &&
+		# differences between zlib and zlib-ng can cause this
+		# test to fail; so we just turn it off.
+		git config core.compression 0 &&
 		# 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 &&
+		git config repo.concern.commit-count 7 &&
+		git config repo.concern.commit-size 14 &&
+		git config repo.concern.tree-size 10 &&
+		git config repo.concern.blob-size 8 &&
+		git config repo.concern.blob-inflated-size 10 &&
+		git config repo.concern.tag-size 2 &&
+		git config repo.concern.commit-largest 4 &&
+		git config repo.concern.blob-largest 2 &&
 		test_commit_bulk 1005 &&
 		git tag -a foo -m bar &&
 		oid="$(git rev-parse HEAD)" &&
@@ -157,7 +312,6 @@ test_expect_success SHA1 'lines format with concerns' '
 		# 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".
@@ -172,8 +326,8 @@ 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.tags.disk_size.concern_level=4
+		objects.blobs.disk_size.concern_level=6
+		objects.tags.disk_size.concern_level=5
 		objects.commits.max_size.concern_level=3
 		objects.trees.max_size.concern_level=0
 		objects.blobs.max_size.concern_level=1
-- 
2.53.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help