builtin/repo: add OID annotations to table output
The "structure" output for git-repo(1) does not show the corresponding OIDs for the largest objects in its "table" output. Update the output to include a list of OID annotations with an index to the corresponding row in the table. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Justin Tobler committed
Mar 2, 2026 at 15:45 UTC
e00bb8c76e18357da3a2098cdac2a3c2c312c17d
2 files changed
+143
-80
builtin/repo.c
+68
-10
@@ -238,6 +238,7 @@ struct repo_structure {
238
239
struct stats_table {
240
struct string_list rows;
241
+ struct string_list annotations;
242
243
int name_col_width;
244
int value_col_width;
@@ -250,6 +251,8 @@ struct stats_table {
251
struct stats_table_entry {
252
char *value;
253
const char *unit;
254
+ size_t index;
255
+ struct object_id *oid;
256
};
257
258
static void stats_table_vaddf(struct stats_table *table,
@@ -272,6 +275,12 @@ static void stats_table_vaddf(struct stats_table *table,
275
table->name_col_width = name_width;
276
if (!entry)
277
return;
278
+ if (entry->oid) {
279
+ entry->index = table->annotations.nr + 1;
280
+ strbuf_addf(&buf, "[%" PRIuMAX "] %s", (uintmax_t)entry->index,
281
+ oid_to_hex(entry->oid));
282
+ string_list_append_nodup(&table->annotations, strbuf_detach(&buf, NULL));
283
+ }
284
if (entry->value) {
285
int value_width = utf8_strwidth(entry->value);
286
if (value_width > table->value_col_width)
@@ -282,6 +291,8 @@ static void stats_table_vaddf(struct stats_table *table,
291
if (unit_width > table->unit_col_width)
292
table->unit_col_width = unit_width;
293
}
294
+
295
+ strbuf_release(&buf);
296
}
297
298
static void stats_table_addf(struct stats_table *table, const char *format, ...)
@@ -321,6 +332,27 @@ static void stats_table_size_addf(struct stats_table *table, size_t value,
332
va_end(ap);
333
}
334
335
+static void stats_table_object_size_addf(struct stats_table *table,
336
+ struct object_id *oid, size_t value,
337
+ const char *format, ...)
338
+{
339
+ struct stats_table_entry *entry;
340
+ va_list ap;
341
+
342
+ CALLOC_ARRAY(entry, 1);
343
+ humanise_bytes(value, &entry->value, &entry->unit, HUMANISE_COMPACT);
344
+
345
+ /*
346
+ * A NULL OID should not have a table annotation.
347
+ */
348
+ if (!is_null_oid(oid))
349
+ entry->oid = oid;
350
+
351
+ va_start(ap, format);
352
+ stats_table_vaddf(table, entry, format, ap);
353
+ va_end(ap);
354
+}
355
+
356
static inline size_t get_total_reference_count(struct ref_stats *stats)
357
{
358
return stats->branches + stats->remotes + stats->tags + stats->others;
@@ -389,19 +421,29 @@ static void stats_table_setup_structure(struct stats_table *table,
421
stats_table_addf(table, "");
422
stats_table_addf(table, "* %s", _("Largest objects"));
423
stats_table_addf(table, " * %s", _("Commits"));
392
- stats_table_size_addf(table, objects->largest.commit_size.value,
393
- " * %s", _("Maximum size"));
424
+ stats_table_object_size_addf(table,
425
+ &objects->largest.commit_size.oid,
426
+ objects->largest.commit_size.value,
427
+ " * %s", _("Maximum size"));
428
stats_table_addf(table, " * %s", _("Trees"));
395
- stats_table_size_addf(table, objects->largest.tree_size.value,
396
- " * %s", _("Maximum size"));
429
+ stats_table_object_size_addf(table,
430
+ &objects->largest.tree_size.oid,
431
+ objects->largest.tree_size.value,
432
+ " * %s", _("Maximum size"));
433
stats_table_addf(table, " * %s", _("Blobs"));
398
- stats_table_size_addf(table, objects->largest.blob_size.value,
399
- " * %s", _("Maximum size"));
434
+ stats_table_object_size_addf(table,
435
+ &objects->largest.blob_size.oid,
436
+ objects->largest.blob_size.value,
437
+ " * %s", _("Maximum size"));
438
stats_table_addf(table, " * %s", _("Tags"));
401
- stats_table_size_addf(table, objects->largest.tag_size.value,
402
- " * %s", _("Maximum size"));
439
+ stats_table_object_size_addf(table,
440
+ &objects->largest.tag_size.oid,
441
+ objects->largest.tag_size.value,
442
+ " * %s", _("Maximum size"));
443
}
444
445
+#define INDEX_WIDTH 4
446
+
447
static void stats_table_print_structure(const struct stats_table *table)
448
{
449
const char *name_col_title = _("Repository structure");
@@ -420,7 +462,8 @@ static void stats_table_print_structure(const struct stats_table *table)
462
value_col_width = title_value_width - unit_col_width;
463
464
strbuf_addstr(&buf, "| ");
423
- strbuf_utf8_align(&buf, ALIGN_LEFT, name_col_width, name_col_title);
465
+ strbuf_utf8_align(&buf, ALIGN_LEFT, name_col_width + INDEX_WIDTH,
466
+ name_col_title);
467
strbuf_addstr(&buf, " | ");
468
strbuf_utf8_align(&buf, ALIGN_LEFT,
469
value_col_width + unit_col_width + 1, value_col_title);
@@ -428,7 +471,7 @@ static void stats_table_print_structure(const struct stats_table *table)
471
printf("%s\n", buf.buf);
472
473
printf("| ");
431
- for (int i = 0; i < name_col_width; i++)
474
+ for (int i = 0; i < name_col_width + INDEX_WIDTH; i++)
475
putchar('-');
476
printf(" | ");
477
for (int i = 0; i < value_col_width + unit_col_width + 1; i++)
@@ -450,6 +493,13 @@ static void stats_table_print_structure(const struct stats_table *table)
493
strbuf_reset(&buf);
494
strbuf_addstr(&buf, "| ");
495
strbuf_utf8_align(&buf, ALIGN_LEFT, name_col_width, item->string);
496
+
497
+ if (entry && entry->oid)
498
+ strbuf_addf(&buf, " [%" PRIuMAX "]",
499
+ (uintmax_t)entry->index);
500
+ else
501
+ strbuf_addchars(&buf, ' ', INDEX_WIDTH);
502
+
503
strbuf_addstr(&buf, " | ");
504
strbuf_utf8_align(&buf, ALIGN_RIGHT, value_col_width, value);
505
strbuf_addch(&buf, ' ');
@@ -458,6 +508,12 @@ static void stats_table_print_structure(const struct stats_table *table)
508
printf("%s\n", buf.buf);
509
}
510
511
+ if (table->annotations.nr) {
512
+ printf("\n");
513
+ for_each_string_list_item(item, &table->annotations)
514
+ printf("%s\n", item->string);
515
+ }
516
+
517
strbuf_release(&buf);
518
}
519
@@ -473,6 +529,7 @@ static void stats_table_clear(struct stats_table *table)
529
}
530
531
string_list_clear(&table->rows, 1);
532
+ string_list_clear(&table->annotations, 1);
533
}
534
535
static inline void print_keyvalue(const char *key, char key_delim, size_t value,
@@ -702,6 +759,7 @@ static int cmd_repo_structure(int argc, const char **argv, const char *prefix,
759
{
760
struct stats_table table = {
761
.rows = STRING_LIST_INIT_DUP,
762
+ .annotations = STRING_LIST_INIT_DUP,
763
};
764
enum output_format format = FORMAT_TABLE;
765
struct repo_structure stats = { 0 };
t/t1901-repo-structure.sh
+75
-70
@@ -27,41 +27,41 @@ test_expect_success 'empty repository' '
27
(
28
cd repo &&
29
cat >expect <<-\EOF &&
30
- | Repository structure | Value |
31
- | -------------------- | ------ |
32
- | * References | |
33
- | * Count | 0 |
34
- | * Branches | 0 |
35
- | * Tags | 0 |
36
- | * Remotes | 0 |
37
- | * Others | 0 |
38
- | | |
39
- | * Reachable objects | |
40
- | * Count | 0 |
41
- | * Commits | 0 |
42
- | * Trees | 0 |
43
- | * Blobs | 0 |
44
- | * Tags | 0 |
45
- | * Inflated size | 0 B |
46
- | * Commits | 0 B |
47
- | * Trees | 0 B |
48
- | * Blobs | 0 B |
49
- | * Tags | 0 B |
50
- | * Disk size | 0 B |
51
- | * Commits | 0 B |
52
- | * Trees | 0 B |
53
- | * Blobs | 0 B |
54
- | * Tags | 0 B |
55
- | | |
56
- | * Largest objects | |
57
- | * Commits | |
58
- | * Maximum size | 0 B |
59
- | * Trees | |
60
- | * Maximum size | 0 B |
61
- | * Blobs | |
62
- | * Maximum size | 0 B |
63
- | * Tags | |
64
- | * Maximum size | 0 B |
30
+ | Repository structure | Value |
31
+ | ------------------------ | ------ |
32
+ | * References | |
33
+ | * Count | 0 |
34
+ | * Branches | 0 |
35
+ | * Tags | 0 |
36
+ | * Remotes | 0 |
37
+ | * Others | 0 |
38
+ | | |
39
+ | * Reachable objects | |
40
+ | * Count | 0 |
41
+ | * Commits | 0 |
42
+ | * Trees | 0 |
43
+ | * Blobs | 0 |
44
+ | * Tags | 0 |
45
+ | * Inflated size | 0 B |
46
+ | * Commits | 0 B |
47
+ | * Trees | 0 B |
48
+ | * Blobs | 0 B |
49
+ | * Tags | 0 B |
50
+ | * Disk size | 0 B |
51
+ | * Commits | 0 B |
52
+ | * Trees | 0 B |
53
+ | * Blobs | 0 B |
54
+ | * Tags | 0 B |
55
+ | | |
56
+ | * Largest objects | |
57
+ | * Commits | |
58
+ | * Maximum size | 0 B |
59
+ | * Trees | |
60
+ | * Maximum size | 0 B |
61
+ | * Blobs | |
62
+ | * Maximum size | 0 B |
63
+ | * Tags | |
64
+ | * Maximum size | 0 B |
65
EOF
66
67
git repo structure >out 2>err &&
@@ -89,41 +89,46 @@ test_expect_success SHA1 'repository with references and objects' '
89
# git-rev-list(1) --disk-usage=human option printing the full
90
# "byte/bytes" unit string instead of just "B".
91
cat >expect <<-EOF &&
92
- | Repository structure | Value |
93
- | -------------------- | ---------- |
94
- | * References | |
95
- | * Count | 4 |
96
- | * Branches | 1 |
97
- | * Tags | 1 |
98
- | * Remotes | 1 |
99
- | * Others | 1 |
100
- | | |
101
- | * Reachable objects | |
102
- | * Count | 3.02 k |
103
- | * Commits | 1.01 k |
104
- | * Trees | 1.01 k |
105
- | * Blobs | 1.01 k |
106
- | * Tags | 1 |
107
- | * Inflated size | 16.03 MiB |
108
- | * Commits | 217.92 KiB |
109
- | * Trees | 15.81 MiB |
110
- | * Blobs | 11.68 KiB |
111
- | * Tags | 132 B |
112
- | * Disk size | $(object_type_disk_usage all true) |
113
- | * Commits | $(object_type_disk_usage commit true) |
114
- | * Trees | $(object_type_disk_usage tree true) |
115
- | * Blobs | $(object_type_disk_usage blob true) |
116
- | * Tags | $(object_type_disk_usage tag) B |
117
- | | |
118
- | * Largest objects | |
119
- | * Commits | |
120
- | * Maximum size | 223 B |
121
- | * Trees | |
122
- | * Maximum size | 32.29 KiB |
123
- | * Blobs | |
124
- | * Maximum size | 13 B |
125
- | * Tags | |
126
- | * Maximum size | 132 B |
92
+ | Repository structure | Value |
93
+ | ------------------------ | ---------- |
94
+ | * References | |
95
+ | * Count | 4 |
96
+ | * Branches | 1 |
97
+ | * Tags | 1 |
98
+ | * Remotes | 1 |
99
+ | * Others | 1 |
100
+ | | |
101
+ | * Reachable objects | |
102
+ | * Count | 3.02 k |
103
+ | * Commits | 1.01 k |
104
+ | * Trees | 1.01 k |
105
+ | * Blobs | 1.01 k |
106
+ | * Tags | 1 |
107
+ | * Inflated size | 16.03 MiB |
108
+ | * Commits | 217.92 KiB |
109
+ | * Trees | 15.81 MiB |
110
+ | * Blobs | 11.68 KiB |
111
+ | * Tags | 132 B |
112
+ | * Disk size | $(object_type_disk_usage all true) |
113
+ | * Commits | $(object_type_disk_usage commit true) |
114
+ | * Trees | $(object_type_disk_usage tree true) |
115
+ | * Blobs | $(object_type_disk_usage blob true) |
116
+ | * Tags | $(object_type_disk_usage tag) B |
117
+ | | |
118
+ | * Largest objects | |
119
+ | * Commits | |
120
+ | * Maximum size [1] | 223 B |
121
+ | * Trees | |
122
+ | * Maximum size [2] | 32.29 KiB |
123
+ | * Blobs | |
124
+ | * Maximum size [3] | 13 B |
125
+ | * Tags | |
126
+ | * Maximum size [4] | 132 B |
127
+
128
+ [1] 0dc91eb18580102a3a216c8bfecedeba2b9f9b9a
129
+ [2] 60665251ab71dbd8c18d9bf2174f4ee0d58aa06c
130
+ [3] 97d808e45116bf02103490294d3d46dad7a2ac62
131
+ [4] 4dae4f5954f5e6feb3577cfb1b181daa3fd3afd2
132
EOF
133
134
git repo structure >out 2>err &&