[GSoC Patch] repo: support category-based prefix querying for info keys
From: K Jayatheerth <hidden>
Date: 2026-07-03 16:47:39
Subsystem:
documentation, the rest · Maintainers:
Jonathan Corbet, Linus Torvalds
Currently, git repo info relies on an all-or-nothing query model where users must either know the exact, fully-qualified key name or use the --all flag to dump the entire repository state. As the number of supported keys expands, dumping all metadata and relying on external filters like grep becomes an inefficient bottleneck for a plumbing command. Enable category-based prefix querying so users can request entire groups of related keys natively Mentored-by: Justin Tobler [off-list ref] Mentored-by: Lucas Seiki Oshiro [off-list ref] Signed-off-by: K Jayatheerth <redacted> --- Hi! This patch adds category-based prefix querying to `git repo info` as part of my GSoC project. A quick note on the implementation: I replaced the `bsearch` with a linear search to find the initial prefix match. I discussed this with my mentors, and we decided to fall back to a linear search because the overhead of a custom binary search (to find the *first* match in a block) wasn't justified given the currently small size of the `repo_info_field` array. Since the array is strictly sorted alphabetically, the loop safely short-circuits via `strncmp` once it steps outside the matching prefix block. Looking forward to feedback! Documentation/git-repo.adoc | 12 ++++++++ builtin/repo.c | 58 +++++++++++++++++++++++-------------- t/t1900-repo-info.sh | 16 ++++++++++ 3 files changed, 64 insertions(+), 22 deletions(-)
diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 42262c1983..3e840d6323 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc@@ -25,6 +25,12 @@ COMMANDS the requested data will be returned based on their keys (see "INFO KEYS" section below). + +If a `<key>` argument matches a category prefix (i.e. a namespace that ends at +a `.` boundary), all keys within that namespace are returned. For example, +`layout` returns both `layout.bare` and `layout.shallow`. The prefix must +align with a namespace boundary; partial prefixes that do not end at a `.` +separator (e.g. `lay`) are treated as unknown keys and will produce an error. ++ The values are returned in the same order in which their respective keys were requested. The `--all` flag requests the values for all the available keys. +
@@ -126,6 +132,12 @@ using the `nul` format: git repo info --format=nul layout.bare layout.shallow ------------ +* Retrieves all keys under the `layout` category prefix: ++ +------------ +git repo info layout +------------ + SEE ALSO -------- linkgit:git-rev-parse[1]
diff --git a/builtin/repo.c b/builtin/repo.c
index 71a5c1c29c..91ea5b5459 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c@@ -90,24 +90,27 @@ static const struct repo_info_field repo_info_field[] = { { "references.format", get_references_format }, }; -static int repo_info_field_cmp(const void *va, const void *vb) +static int is_valid_prefix_match(const char *key, const char *prefix) { - const struct repo_info_field *a = va; - const struct repo_info_field *b = vb; + size_t prefix_len = strlen(prefix); - return strcmp(a->key, b->key); + if (!prefix_len) + return 0; + + if (strncmp(key, prefix, prefix_len)) + return 0; + + return key[prefix_len] == '\0' || prefix[prefix_len - 1] == '.' || key[prefix_len] == '.'; } -static const struct repo_info_field *get_repo_info_field(const char *key) +static size_t find_first_repo_info_field_match(const char *prefix) { - const struct repo_info_field search_key = { key, NULL }; - const struct repo_info_field *found = bsearch(&search_key, - repo_info_field, - ARRAY_SIZE(repo_info_field), - sizeof(*found), - repo_info_field_cmp); - - return found; + for (size_t i = 0; i < ARRAY_SIZE(repo_info_field); i++) { + if (is_valid_prefix_match(repo_info_field[i].key, prefix)) { + return i; + } + } + return SIZE_MAX; } static void print_field(enum output_format format, const char *key,
@@ -135,17 +138,28 @@ static int print_fields(int argc, const char **argv, struct strbuf valbuf = STRBUF_INIT; for (int i = 0; i < argc; i++) { - const char *key = argv[i]; - const struct repo_info_field *field = get_repo_info_field(key); - - if (!field) { - ret = error(_("key '%s' not found"), key); - continue; + const char *prefix = argv[i]; + size_t prefix_len = strlen(prefix); + size_t idx = find_first_repo_info_field_match(prefix); + int found = 0; + + for (; idx < ARRAY_SIZE(repo_info_field); idx++) { + const struct repo_info_field *field = &repo_info_field[idx]; + + if (strncmp(field->key, prefix, prefix_len)) + break; + + if (is_valid_prefix_match(field->key, prefix)) { + strbuf_reset(&valbuf); + field->get_value(repo, &valbuf); + print_field(format, field->key, valbuf.buf); + found = 1; + } } - strbuf_reset(&valbuf); - field->get_value(repo, &valbuf); - print_field(format, key, valbuf.buf); + if (!found) { + ret = error(_("key '%s' not found"), prefix); + } } strbuf_release(&valbuf);
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 39bb77dda0..80ae8f8396 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh@@ -149,6 +149,22 @@ test_expect_success 'git repo info --keys uses lines as its default output forma test_cmp expect actual ' +test_expect_success 'git repo info with category prefix returns all keys in namespace' ' + cat >expect <<-\EOF && + layout.bare=false + layout.shallow=false + EOF + git init prefix-repo && + git -C prefix-repo repo info layout >actual && + test_cmp expect actual +' + +test_expect_success 'git repo info with invalid partial boundary fails' ' + echo "error: key ${SQ}lay${SQ} not found" >expect && + test_must_fail git -C prefix-repo repo info lay 2>actual && + test_cmp expect actual +' + test_expect_success 'git repo info -h shows only repo info usage' ' test_must_fail git repo info -h >actual && test_grep "git repo info" actual &&
--
2.55.0-rc1