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>
This commit is contained in:
Justin Tobler
2025-12-09 16:58:16 -06:00
committed by Junio C Hamano
parent b3f15b90b6
commit 9deb65ae23
2 changed files with 82 additions and 41 deletions

View File

@@ -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;
const 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, ...)
@@ -266,6 +275,10 @@ static void stats_table_addf(struct stats_table *table, const char *format, ...)
va_end(ap);
}
static const char *unit_k = "k";
static const char *unit_M = "M";
static const char *unit_G = "G";
static void stats_table_count_addf(struct stats_table *table, size_t value,
const char *format, ...)
{
@@ -273,7 +286,26 @@ static void stats_table_count_addf(struct stats_table *table, size_t value,
va_list ap;
CALLOC_ARRAY(entry, 1);
entry->value = xstrfmt("%" PRIuMAX, (uintmax_t)value);
if (value >= 1000000000) {
uintmax_t x = (uintmax_t)value + 5000000;
entry->value = xstrfmt("%" PRIuMAX ".%02" PRIuMAX,
x / 1000000000,
x % 1000000000 / 10000000);
entry->unit = unit_G;
} else if (value >= 1000000) {
uintmax_t x = (uintmax_t)value + 5000;
entry->value = xstrfmt("%" PRIuMAX ".%02" PRIuMAX,
x / 1000000, x % 1000000 / 10000);
entry->unit = unit_M;
} else if (value >= 1000) {
uintmax_t x = (uintmax_t)value + 5;
entry->value = xstrfmt("%" PRIuMAX ".%02" PRIuMAX,
x / 1000, x % 1000 / 10);
entry->unit = unit_k;
} else {
entry->value = xstrfmt("%" PRIuMAX, (uintmax_t)value);
}
va_start(ap, format);
stats_table_vaddf(table, entry, format, ap);
@@ -324,20 +356,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 +381,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 +402,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);
}

View File

@@ -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 &&