builtin/repo: humanise count values in structure output
The table output format for the git-repo(1) structure subcommand is used by default and intended to provide output to users in a human-friendly manner. When the reference/object count values in a repository are large, it becomes more cumbersome for users to read the values. For larger values, update the table output format to instead produce more human-friendly count values that are scaled down with the appropriate unit prefix. Output for the keyvalue and nul formats remains unchanged. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Justin Tobler committed
Dec 17, 2025 at 11:54 UTC
54731320cc3db337f9a3e3920f707e9de3596c60
4 files changed
+91
-41
builtin/repo.c
+28
-10
@@ -223,6 +223,7 @@ struct stats_table {
223
224
int name_col_width;
225
int value_col_width;
226
+ int unit_col_width;
227
};
228
229
/*
@@ -230,6 +231,7 @@ struct stats_table {
231
*/
232
struct stats_table_entry {
233
char *value;
234
+ const char *unit;
235
};
236
237
static void stats_table_vaddf(struct stats_table *table,
@@ -250,11 +252,18 @@ static void stats_table_vaddf(struct stats_table *table,
252
253
if (name_width > table->name_col_width)
254
table->name_col_width = name_width;
253
- if (entry) {
255
+ if (!entry)
256
+ return;
257
+ if (entry->value) {
258
int value_width = utf8_strwidth(entry->value);
259
if (value_width > table->value_col_width)
260
table->value_col_width = value_width;
261
}
262
+ if (entry->unit) {
263
+ int unit_width = utf8_strwidth(entry->unit);
264
+ if (unit_width > table->unit_col_width)
265
+ table->unit_col_width = unit_width;
266
+ }
267
}
268
269
static void stats_table_addf(struct stats_table *table, const char *format, ...)
@@ -273,7 +282,7 @@ static void stats_table_count_addf(struct stats_table *table, size_t value,
282
va_list ap;
283
284
CALLOC_ARRAY(entry, 1);
276
- entry->value = xstrfmt("%" PRIuMAX, (uintmax_t)value);
285
+ humanise_count(value, &entry->value, &entry->unit);
286
287
va_start(ap, format);
288
stats_table_vaddf(table, entry, format, ap);
@@ -324,20 +333,24 @@ static void stats_table_print_structure(const struct stats_table *table)
333
{
334
const char *name_col_title = _("Repository structure");
335
const char *value_col_title = _("Value");
327
- int name_col_width = utf8_strwidth(name_col_title);
328
- int value_col_width = utf8_strwidth(value_col_title);
336
+ int title_name_width = utf8_strwidth(name_col_title);
337
+ int title_value_width = utf8_strwidth(value_col_title);
338
+ int name_col_width = table->name_col_width;
339
+ int value_col_width = table->value_col_width;
340
+ int unit_col_width = table->unit_col_width;
341
struct string_list_item *item;
342
struct strbuf buf = STRBUF_INIT;
343
332
- if (table->name_col_width > name_col_width)
333
- name_col_width = table->name_col_width;
334
- if (table->value_col_width > value_col_width)
335
- value_col_width = table->value_col_width;
344
+ if (title_name_width > name_col_width)
345
+ name_col_width = title_name_width;
346
+ if (title_value_width > value_col_width + unit_col_width + 1)
347
+ value_col_width = title_value_width - unit_col_width;
348
349
strbuf_addstr(&buf, "| ");
350
strbuf_utf8_align(&buf, ALIGN_LEFT, name_col_width, name_col_title);
351
strbuf_addstr(&buf, " | ");
340
- strbuf_utf8_align(&buf, ALIGN_LEFT, value_col_width, value_col_title);
352
+ strbuf_utf8_align(&buf, ALIGN_LEFT,
353
+ value_col_width + unit_col_width + 1, value_col_title);
354
strbuf_addstr(&buf, " |");
355
printf("%s\n", buf.buf);
356
@@ -345,17 +358,20 @@ static void stats_table_print_structure(const struct stats_table *table)
358
for (int i = 0; i < name_col_width; i++)
359
putchar('-');
360
printf(" | ");
348
- for (int i = 0; i < value_col_width; i++)
361
+ for (int i = 0; i < value_col_width + unit_col_width + 1; i++)
362
putchar('-');
363
printf(" |\n");
364
365
for_each_string_list_item(item, &table->rows) {
366
struct stats_table_entry *entry = item->util;
367
const char *value = "";
368
+ const char *unit = "";
369
370
if (entry) {
371
struct stats_table_entry *entry = item->util;
372
value = entry->value;
373
+ if (entry->unit)
374
+ unit = entry->unit;
375
}
376
377
strbuf_reset(&buf);
@@ -363,6 +379,8 @@ static void stats_table_print_structure(const struct stats_table *table)
379
strbuf_utf8_align(&buf, ALIGN_LEFT, name_col_width, item->string);
380
strbuf_addstr(&buf, " | ");
381
strbuf_utf8_align(&buf, ALIGN_RIGHT, value_col_width, value);
382
+ strbuf_addch(&buf, ' ');
383
+ strbuf_utf8_align(&buf, ALIGN_LEFT, unit_col_width, unit);
384
strbuf_addstr(&buf, " |");
385
printf("%s\n", buf.buf);
386
}
strbuf.c
+26
@@ -836,6 +836,32 @@ void strbuf_addstr_urlencode(struct strbuf *sb, const char *s,
836
strbuf_add_urlencode(sb, s, strlen(s), allow_unencoded_fn);
837
}
838
839
+void humanise_count(size_t count, char **value, const char **unit)
840
+{
841
+ if (count >= 1000000000) {
842
+ size_t x = count + 5000000; /* for rounding */
843
+ *value = xstrfmt(_("%u.%2.2u"), (unsigned)(x / 1000000000),
844
+ (unsigned)(x % 1000000000 / 10000000));
845
+ /* TRANSLATORS: SI decimal prefix symbol for 10^9 */
846
+ *unit = _("G");
847
+ } else if (count >= 1000000) {
848
+ size_t x = count + 5000; /* for rounding */
849
+ *value = xstrfmt(_("%u.%2.2u"), (unsigned)(x / 1000000),
850
+ (unsigned)(x % 1000000 / 10000));
851
+ /* TRANSLATORS: SI decimal prefix symbol for 10^6 */
852
+ *unit = _("M");
853
+ } else if (count >= 1000) {
854
+ size_t x = count + 5; /* for rounding */
855
+ *value = xstrfmt(_("%u.%2.2u"), (unsigned)(x / 1000),
856
+ (unsigned)(x % 1000 / 10));
857
+ /* TRANSLATORS: SI decimal prefix symbol for 10^3 */
858
+ *unit = _("k");
859
+ } else {
860
+ *value = xstrfmt("%u", (unsigned)count);
861
+ *unit = NULL;
862
+ }
863
+}
864
+
865
void humanise_bytes(off_t bytes, char **value, const char **unit,
866
unsigned flags)
867
{
strbuf.h
+6
@@ -381,6 +381,12 @@ enum humanise_flags {
381
void humanise_bytes(off_t bytes, char **value, const char **unit,
382
unsigned flags);
383
384
+/**
385
+ * Converts the given count into a downscaled human-readable value and
386
+ * corresponding unit as two separate strings.
387
+ */
388
+void humanise_count(size_t count, char **value, const char **unit);
389
+
390
/**
391
* Append the given byte size as a human-readable string (i.e. 12.23 KiB,
392
* 3.50 MiB).
t/t1901-repo-structure.sh
+31
-31
@@ -10,21 +10,21 @@ test_expect_success 'empty repository' '
10
(
11
cd repo &&
12
cat >expect <<-\EOF &&
13
- | Repository structure | Value |
14
- | -------------------- | ----- |
15
- | * References | |
16
- | * Count | 0 |
17
- | * Branches | 0 |
18
- | * Tags | 0 |
19
- | * Remotes | 0 |
20
- | * Others | 0 |
21
- | | |
22
- | * Reachable objects | |
23
- | * Count | 0 |
24
- | * Commits | 0 |
25
- | * Trees | 0 |
26
- | * Blobs | 0 |
27
- | * Tags | 0 |
13
+ | Repository structure | Value |
14
+ | -------------------- | ------ |
15
+ | * References | |
16
+ | * Count | 0 |
17
+ | * Branches | 0 |
18
+ | * Tags | 0 |
19
+ | * Remotes | 0 |
20
+ | * Others | 0 |
21
+ | | |
22
+ | * Reachable objects | |
23
+ | * Count | 0 |
24
+ | * Commits | 0 |
25
+ | * Trees | 0 |
26
+ | * Blobs | 0 |
27
+ | * Tags | 0 |
28
EOF
29
30
git repo structure >out 2>err &&
@@ -39,7 +39,7 @@ test_expect_success 'repository with references and objects' '
39
git init repo &&
40
(
41
cd repo &&
42
- test_commit_bulk 42 &&
42
+ test_commit_bulk 1005 &&
43
git tag -a foo -m bar &&
44
45
oid="$(git rev-parse HEAD)" &&
@@ -49,21 +49,21 @@ test_expect_success 'repository with references and objects' '
49
git notes add -m foo &&
50
51
cat >expect <<-\EOF &&
52
- | Repository structure | Value |
53
- | -------------------- | ----- |
54
- | * References | |
55
- | * Count | 4 |
56
- | * Branches | 1 |
57
- | * Tags | 1 |
58
- | * Remotes | 1 |
59
- | * Others | 1 |
60
- | | |
61
- | * Reachable objects | |
62
- | * Count | 130 |
63
- | * Commits | 43 |
64
- | * Trees | 43 |
65
- | * Blobs | 43 |
66
- | * Tags | 1 |
52
+ | Repository structure | Value |
53
+ | -------------------- | ------ |
54
+ | * References | |
55
+ | * Count | 4 |
56
+ | * Branches | 1 |
57
+ | * Tags | 1 |
58
+ | * Remotes | 1 |
59
+ | * Others | 1 |
60
+ | | |
61
+ | * Reachable objects | |
62
+ | * Count | 3.02 k |
63
+ | * Commits | 1.01 k |
64
+ | * Trees | 1.01 k |
65
+ | * Blobs | 1.01 k |
66
+ | * Tags | 1 |
67
EOF
68
69
git repo structure >out 2>err &&