Skip to content

Commit 3d42929

Browse files
committed
builtin/repo: add keyvalue and nul format for structure stats
All repository structure stats are outputted in a human-friendly table form. This format is not suitable for machine parsing. Add a --format option that supports three output modes: `table`, `keyvalue`, and `nul`. The `table` mode is the default format and prints the same table output as before. With the `keyvalue` mode, each line of output contains a key-value pair of a repository stat. The '=' character is used to delimit between keys and values. The `nul` mode is similar to `keyvalue`, but key-values are delimited by a NUL character instead of a newline. Also, instead of a '=' character to delimit between keys and values, a newline character is used. This allows stat values to support special characters without having to cquote them. These two new modes provides output that is more machine-friendly. Signed-off-by: Justin Tobler <[email protected]>
1 parent 4d37f65 commit 3d42929

File tree

3 files changed

+106
-7
lines changed

3 files changed

+106
-7
lines changed

Documentation/git-repo.adoc

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ SYNOPSIS
99
--------
1010
[synopsis]
1111
git repo info [--format=(keyvalue|nul)] [-z] [<key>...]
12-
git repo structure
12+
git repo structure [--format=(table|keyvalue|nul)]
1313

1414
DESCRIPTION
1515
-----------
@@ -44,15 +44,34 @@ supported:
4444
+
4545
`-z` is an alias for `--format=nul`.
4646

47-
`structure`::
47+
`structure [--format=(table|keyvalue|nul)]`::
4848
Retrieve statistics about the current repository structure. The
4949
following kinds of information are reported:
5050
+
5151
* Reference counts categorized by type
5252
* Reachable object counts categorized by type
5353

5454
+
55-
The table output format may change and is not intended for machine parsing.
55+
The output format can be chosen through the flag `--format`. Three formats are
56+
supported:
57+
+
58+
`table`:::
59+
Outputs repository stats in a human-friendly table. This format may
60+
change and is not intended for machine parsing. This is the default
61+
format.
62+
63+
`keyvalue`:::
64+
Each line of output contains a key-value pair for a repository stat.
65+
The '=' character is used to delimit between the key and the value.
66+
Values containing "unusual" characters are quoted as explained for the
67+
configuration variable `core.quotePath` (see linkgit:git-config[1]).
68+
69+
`nul`:::
70+
Similar to `keyvalue`, but uses a NUL character to delimit between
71+
key-value pairs instead of a newline. Also uses a newline character as
72+
the delimiter between the key and value instead of '='. Unlike the
73+
`keyvalue` format, values containing "unusual" characters are never
74+
quoted.
5675

5776
INFO KEYS
5877
---------

builtin/repo.c

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515

1616
static const char *const repo_usage[] = {
1717
"git repo info [--format=(keyvalue|nul)] [-z] [<key>...]",
18-
"git repo structure",
18+
"git repo structure [--format=(table|keyvalue|nul)]",
1919
NULL
2020
};
2121

2222
typedef int get_value_fn(struct repository *repo, struct strbuf *buf);
2323

2424
enum output_format {
25+
FORMAT_TABLE,
2526
FORMAT_KEYVALUE,
2627
FORMAT_NUL_TERMINATED,
2728
};
@@ -136,6 +137,8 @@ static int parse_format_cb(const struct option *opt,
136137
*format = FORMAT_NUL_TERMINATED;
137138
else if (!strcmp(arg, "keyvalue"))
138139
*format = FORMAT_KEYVALUE;
140+
else if (!strcmp(arg, "table"))
141+
*format = FORMAT_TABLE;
139142
else
140143
die(_("invalid format '%s'"), arg);
141144

@@ -158,6 +161,8 @@ static int cmd_repo_info(int argc, const char **argv, const char *prefix,
158161
};
159162

160163
argc = parse_options(argc, argv, prefix, options, repo_usage, 0);
164+
if (format != FORMAT_KEYVALUE && format != FORMAT_NUL_TERMINATED)
165+
die(_("unsupported output format"));
161166

162167
return print_fields(argc, argv, repo, format);
163168
}
@@ -330,6 +335,30 @@ static void stats_table_clear(struct stats_table *table)
330335
string_list_clear(&table->rows, 1);
331336
}
332337

338+
static void structure_keyvalue_print(struct repo_structure *stats,
339+
char key_delim, char value_delim)
340+
{
341+
printf("references.branches.count%c%" PRIuMAX "%c", key_delim,
342+
(uintmax_t)stats->refs.branches, value_delim);
343+
printf("references.tags.count%c%" PRIuMAX "%c", key_delim,
344+
(uintmax_t)stats->refs.tags, value_delim);
345+
printf("references.remotes.count%c%" PRIuMAX "%c", key_delim,
346+
(uintmax_t)stats->refs.remotes, value_delim);
347+
printf("references.others.count%c%" PRIuMAX "%c", key_delim,
348+
(uintmax_t)stats->refs.others, value_delim);
349+
350+
printf("objects.commits.count%c%" PRIuMAX "%c", key_delim,
351+
(uintmax_t)stats->objects.commits, value_delim);
352+
printf("objects.trees.count%c%" PRIuMAX "%c", key_delim,
353+
(uintmax_t)stats->objects.trees, value_delim);
354+
printf("objects.blobs.count%c%" PRIuMAX "%c", key_delim,
355+
(uintmax_t)stats->objects.blobs, value_delim);
356+
printf("objects.tags.count%c%" PRIuMAX "%c", key_delim,
357+
(uintmax_t)stats->objects.tags, value_delim);
358+
359+
fflush(stdout);
360+
}
361+
333362
struct count_references_data {
334363
struct ref_stats *stats;
335364
struct rev_info *revs;
@@ -426,9 +455,15 @@ static int cmd_repo_structure(int argc, const char **argv, const char *prefix,
426455
struct stats_table table = {
427456
.rows = STRING_LIST_INIT_DUP,
428457
};
458+
enum output_format format = FORMAT_TABLE;
429459
struct repo_structure stats = { 0 };
430460
struct rev_info revs;
431-
struct option options[] = { 0 };
461+
struct option options[] = {
462+
OPT_CALLBACK_F(0, "format", &format, N_("format"),
463+
N_("output format"),
464+
PARSE_OPT_NONEG, parse_format_cb),
465+
OPT_END()
466+
};
432467

433468
argc = parse_options(argc, argv, prefix, options, repo_usage, 0);
434469
if (argc)
@@ -439,8 +474,20 @@ static int cmd_repo_structure(int argc, const char **argv, const char *prefix,
439474
structure_count_references(&stats.refs, &revs, repo);
440475
structure_count_objects(&stats.objects, &revs);
441476

442-
stats_table_setup_structure(&table, &stats);
443-
stats_table_print_structure(&table);
477+
switch (format) {
478+
case FORMAT_TABLE:
479+
stats_table_setup_structure(&table, &stats);
480+
stats_table_print_structure(&table);
481+
break;
482+
case FORMAT_KEYVALUE:
483+
structure_keyvalue_print(&stats, '=', '\n');
484+
break;
485+
case FORMAT_NUL_TERMINATED:
486+
structure_keyvalue_print(&stats, '\n', '\0');
487+
break;
488+
default:
489+
BUG("invalid output format");
490+
}
444491

445492
stats_table_clear(&table);
446493
release_revisions(&revs);

t/t1901-repo-structure.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,37 @@ test_expect_success 'repository with references and objects' '
7373
)
7474
'
7575

76+
test_expect_success 'keyvalue and nul format' '
77+
test_when_finished "rm -rf repo" &&
78+
git init repo &&
79+
(
80+
cd repo &&
81+
test_commit_bulk 42 &&
82+
git tag -a foo -m bar &&
83+
84+
cat >expect <<-\EOF &&
85+
references.branches.count=1
86+
references.tags.count=1
87+
references.remotes.count=0
88+
references.others.count=0
89+
objects.commits.count=42
90+
objects.trees.count=42
91+
objects.blobs.count=42
92+
objects.tags.count=1
93+
EOF
94+
95+
git repo structure --format=keyvalue >out 2>err &&
96+
97+
test_cmp expect out &&
98+
test_line_count = 0 err &&
99+
100+
# Replace key and value delimiters for nul format.
101+
tr "\n=" "\0\n" <expect >expect_nul &&
102+
git repo structure --format=nul >out 2>err &&
103+
104+
test_cmp expect_nul out &&
105+
test_line_count = 0 err
106+
)
107+
'
108+
76109
test_done

0 commit comments

Comments
 (0)