From: Justin Tobler Date: Fri, 12 Dec 2025 22:36:40 GMT Subject: [PATCH v2 3/7] builtin/repo: humanise count values in structure output Message-ID: <20251212223644.3090879-4-jltobler@gmail.com> In-Reply-To: <20251212223644.3090879-1-jltobler@gmail.com> 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 --- builtin/repo.c | 45 +++++++++++++++++++++------- strbuf.c | 23 +++++++++++++++ strbuf.h | 7 +++++ t/t1901-repo-structure.sh | 62 +++++++++++++++++++-------------------- 4 files changed, 95 insertions(+), 42 deletions(-) diff --git a/builtin/repo.c b/builtin/repo.c index a69699857a..d3dfe416d0 100644 --- a/builtin/repo.c +++ b/builtin/repo.c @@ -223,6 +223,7 @@ struct stats_table { int name_col_width; int value_col_width; + int unit_col_width; }; /* @@ -230,6 +231,7 @@ struct stats_table { */ struct stats_table_entry { char *value; + char *unit; }; static void stats_table_vaddf(struct stats_table *table, @@ -250,11 +252,18 @@ static void stats_table_vaddf(struct stats_table *table, if (name_width > table->name_col_width) table->name_col_width = name_width; - if (entry) { + if (!entry) + return; + if (entry->value) { int value_width = utf8_strwidth(entry->value); if (value_width > table->value_col_width) table->value_col_width = value_width; } + if (entry->unit) { + int unit_width = utf8_strwidth(entry->unit); + if (unit_width > table->unit_col_width) + table->unit_col_width = unit_width; + } } static void stats_table_addf(struct stats_table *table, const char *format, ...) @@ -270,10 +279,13 @@ static void stats_table_count_addf(struct stats_table *table, size_t value, const char *format, ...) { struct stats_table_entry *entry; + struct strbuf buf = STRBUF_INIT; va_list ap; CALLOC_ARRAY(entry, 1); - entry->value = xstrfmt("%" PRIuMAX, (uintmax_t)value); + + entry->unit = strbuf_humanise_count_value(&buf, value); + entry->value = strbuf_detach(&buf, NULL); va_start(ap, format); stats_table_vaddf(table, entry, format, ap); @@ -324,20 +336,24 @@ static void stats_table_print_structure(const struct stats_table *table) { const char *name_col_title = _("Repository structure"); const char *value_col_title = _("Value"); - int name_col_width = utf8_strwidth(name_col_title); - int value_col_width = utf8_strwidth(value_col_title); + int title_name_width = utf8_strwidth(name_col_title); + int title_value_width = utf8_strwidth(value_col_title); + int name_col_width = table->name_col_width; + int value_col_width = table->value_col_width; + int unit_col_width = table->unit_col_width; struct string_list_item *item; struct strbuf buf = STRBUF_INIT; - if (table->name_col_width > name_col_width) - name_col_width = table->name_col_width; - if (table->value_col_width > value_col_width) - value_col_width = table->value_col_width; + if (title_name_width > name_col_width) + name_col_width = title_name_width; + if (title_value_width > value_col_width + unit_col_width + 1) + value_col_width = title_value_width - unit_col_width; strbuf_addstr(&buf, "| "); strbuf_utf8_align(&buf, ALIGN_LEFT, name_col_width, name_col_title); strbuf_addstr(&buf, " | "); - strbuf_utf8_align(&buf, ALIGN_LEFT, value_col_width, value_col_title); + strbuf_utf8_align(&buf, ALIGN_LEFT, + value_col_width + unit_col_width + 1, value_col_title); strbuf_addstr(&buf, " |"); printf("%s\n", buf.buf); @@ -345,17 +361,20 @@ static void stats_table_print_structure(const struct stats_table *table) for (int i = 0; i < name_col_width; i++) putchar('-'); printf(" | "); - for (int i = 0; i < value_col_width; i++) + for (int i = 0; i < value_col_width + unit_col_width + 1; i++) putchar('-'); printf(" |\n"); for_each_string_list_item(item, &table->rows) { struct stats_table_entry *entry = item->util; const char *value = ""; + const char *unit = ""; if (entry) { struct stats_table_entry *entry = item->util; value = entry->value; + if (entry->unit) + unit = entry->unit; } strbuf_reset(&buf); @@ -363,6 +382,8 @@ static void stats_table_print_structure(const struct stats_table *table) strbuf_utf8_align(&buf, ALIGN_LEFT, name_col_width, item->string); strbuf_addstr(&buf, " | "); strbuf_utf8_align(&buf, ALIGN_RIGHT, value_col_width, value); + strbuf_addch(&buf, ' '); + strbuf_utf8_align(&buf, ALIGN_LEFT, unit_col_width, unit); strbuf_addstr(&buf, " |"); printf("%s\n", buf.buf); } @@ -377,8 +398,10 @@ static void stats_table_clear(struct stats_table *table) for_each_string_list_item(item, &table->rows) { entry = item->util; - if (entry) + if (entry) { free(entry->value); + free(entry->unit); + } } string_list_clear(&table->rows, 1); diff --git a/strbuf.c b/strbuf.c index 1fb47bf21b..cebb1593ab 100644 --- a/strbuf.c +++ b/strbuf.c @@ -836,6 +836,29 @@ void strbuf_addstr_urlencode(struct strbuf *sb, const char *s, strbuf_add_urlencode(sb, s, strlen(s), allow_unencoded_fn); } +char *strbuf_humanise_count_value(struct strbuf *buf, size_t value) +{ + if (value >= 1000000000) { + uintmax_t x = (uintmax_t)value + 5000000; /* for rounding */ + strbuf_addf(buf, "%" PRIuMAX ".%02" PRIuMAX, + x / 1000000000, x % 1000000000 / 10000000); + return xstrfmt(_("G")); + } else if (value >= 1000000) { + uintmax_t x = (uintmax_t)value + 5000; /* for rounding */ + strbuf_addf(buf, "%" PRIuMAX ".%02" PRIuMAX, + x / 1000000, x % 1000000 / 10000); + return xstrfmt(_("M")); + } else if (value >= 1000) { + uintmax_t x = (uintmax_t)value + 5; /* for rounding */ + strbuf_addf(buf, "%" PRIuMAX ".%02" PRIuMAX, + x / 1000, x % 1000 / 10); + return xstrfmt(_("k")); + } else { + strbuf_addf(buf, "%" PRIuMAX, (uintmax_t)value); + return NULL; + } +} + char *strbuf_humanise_bytes_value(struct strbuf *buf, off_t bytes, unsigned flags) { int humanise_rate = flags & STRBUF_HUMANISE_RATE; diff --git a/strbuf.h b/strbuf.h index a5e3ab0cb4..7532eadd02 100644 --- a/strbuf.h +++ b/strbuf.h @@ -376,6 +376,13 @@ void strbuf_add_percentencode(struct strbuf *dst, const char *src, int flags); */ char *strbuf_humanise_bytes_value(struct strbuf *buf, off_t bytes, unsigned flags); +/** + * Append the given count value as a human-readable string that is downsacled by + * some factor. A string with the corresponding unit prefix is returned + * separately. + */ +char *strbuf_humanise_count_value(struct strbuf *buf, size_t value); + /** * Append the given byte size as a human-readable string (i.e. 12.23 KiB, * 3.50 MiB). diff --git a/t/t1901-repo-structure.sh b/t/t1901-repo-structure.sh index 36a71a144e..55fd13ad1b 100755 --- a/t/t1901-repo-structure.sh +++ b/t/t1901-repo-structure.sh @@ -10,21 +10,21 @@ test_expect_success 'empty repository' ' ( cd repo && cat >expect <<-\EOF && - | Repository structure | Value | - | -------------------- | ----- | - | * References | | - | * Count | 0 | - | * Branches | 0 | - | * Tags | 0 | - | * Remotes | 0 | - | * Others | 0 | - | | | - | * Reachable objects | | - | * Count | 0 | - | * Commits | 0 | - | * Trees | 0 | - | * Blobs | 0 | - | * Tags | 0 | + | Repository structure | Value | + | -------------------- | ------ | + | * References | | + | * Count | 0 | + | * Branches | 0 | + | * Tags | 0 | + | * Remotes | 0 | + | * Others | 0 | + | | | + | * Reachable objects | | + | * Count | 0 | + | * Commits | 0 | + | * Trees | 0 | + | * Blobs | 0 | + | * Tags | 0 | EOF git repo structure >out 2>err && @@ -39,7 +39,7 @@ test_expect_success 'repository with references and objects' ' git init repo && ( cd repo && - test_commit_bulk 42 && + test_commit_bulk 1005 && git tag -a foo -m bar && oid="$(git rev-parse HEAD)" && @@ -49,21 +49,21 @@ test_expect_success 'repository with references and objects' ' git notes add -m foo && cat >expect <<-\EOF && - | Repository structure | Value | - | -------------------- | ----- | - | * References | | - | * Count | 4 | - | * Branches | 1 | - | * Tags | 1 | - | * Remotes | 1 | - | * Others | 1 | - | | | - | * Reachable objects | | - | * Count | 130 | - | * Commits | 43 | - | * Trees | 43 | - | * Blobs | 43 | - | * Tags | 1 | + | 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 | EOF git repo structure >out 2>err && -- 2.52.0.209.ge85ae279b0