From bddea1a22e56b7b2988f795e5e0556c6707a126c Mon Sep 17 00:00:00 2001 From: Eslam reda ragheb Date: Mon, 2 Mar 2026 06:09:20 +0200 Subject: [PATCH 1/6] repo: introduce repo_info context plumbing Introduce a repo_info context and thread it through get_value_fn, field lookup, and value-printing helpers. This prepares repo info for fields that need invocation-specific context in addition to the repository handle. Signed-off-by: Eslam reda ragheb --- builtin/repo.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/builtin/repo.c b/builtin/repo.c index 6a62a6020a5115..e687d833b4ae18 100644 --- a/builtin/repo.c +++ b/builtin/repo.c @@ -23,7 +23,12 @@ static const char *const repo_usage[] = { NULL }; -typedef int get_value_fn(struct repository *repo, struct strbuf *buf); +struct repo_info { + struct repository *repo; + const char *prefix; +}; + +typedef int get_value_fn(struct repo_info *info, struct strbuf *buf); enum output_format { FORMAT_TABLE, @@ -36,27 +41,30 @@ struct field { get_value_fn *get_value; }; -static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf) +static int get_layout_bare(struct repo_info *info UNUSED, struct strbuf *buf) { strbuf_addstr(buf, is_bare_repository() ? "true" : "false"); return 0; } -static int get_layout_shallow(struct repository *repo, struct strbuf *buf) +static int get_layout_shallow(struct repo_info *info, struct strbuf *buf) { + struct repository *repo = info->repo; strbuf_addstr(buf, is_repository_shallow(repo) ? "true" : "false"); return 0; } -static int get_object_format(struct repository *repo, struct strbuf *buf) +static int get_object_format(struct repo_info *info, struct strbuf *buf) { + struct repository *repo = info->repo; strbuf_addstr(buf, repo->hash_algo->name); return 0; } -static int get_references_format(struct repository *repo, struct strbuf *buf) +static int get_references_format(struct repo_info *info, struct strbuf *buf) { + struct repository *repo = info->repo; strbuf_addstr(buf, ref_storage_format_to_name(repo->ref_storage_format)); return 0; @@ -106,7 +114,7 @@ static void print_field(enum output_format format, const char *key, } static int print_fields(int argc, const char **argv, - struct repository *repo, + struct repo_info *info, enum output_format format) { int ret = 0; @@ -124,7 +132,7 @@ static int print_fields(int argc, const char **argv, } strbuf_reset(&valbuf); - get_value(repo, &valbuf); + get_value(info, &valbuf); print_field(format, key, valbuf.buf); } @@ -132,7 +140,7 @@ static int print_fields(int argc, const char **argv, return ret; } -static int print_all_fields(struct repository *repo, +static int print_all_fields(struct repo_info *info, enum output_format format) { struct strbuf valbuf = STRBUF_INIT; @@ -141,7 +149,7 @@ static int print_all_fields(struct repository *repo, const struct field *field = &repo_info_fields[i]; strbuf_reset(&valbuf); - field->get_value(repo, &valbuf); + field->get_value(info, &valbuf); print_field(format, field->key, valbuf.buf); } @@ -195,6 +203,10 @@ static int cmd_repo_info(int argc, const char **argv, const char *prefix, struct repository *repo) { enum output_format format = FORMAT_NEWLINE_TERMINATED; + struct repo_info info = { + .repo = repo, + .prefix = prefix, + }; int all_keys = 0; int show_keys = 0; struct option options[] = { @@ -225,9 +237,9 @@ static int cmd_repo_info(int argc, const char **argv, const char *prefix, die(_("--all and cannot be used together")); if (all_keys) - return print_all_fields(repo, format); + return print_all_fields(&info, format); else - return print_fields(argc, argv, repo, format); + return print_fields(argc, argv, &info, format); } struct ref_stats { From 2369608976c6126bcf40c59974389a84394aab68 Mon Sep 17 00:00:00 2001 From: Eslam reda ragheb Date: Mon, 2 Mar 2026 06:09:39 +0200 Subject: [PATCH 2/6] repo: support category requests in repo info Teach repo info to accept category names (for example, layout) and expand them to matching key.* entries in request order. Explicit keys keep their existing behavior; unknown keys or categories still report clear errors. Signed-off-by: Eslam reda ragheb --- builtin/repo.c | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/builtin/repo.c b/builtin/repo.c index e687d833b4ae18..f614298199da9f 100644 --- a/builtin/repo.c +++ b/builtin/repo.c @@ -96,6 +96,33 @@ static get_value_fn *get_value_fn_for_key(const char *key) return found ? found->get_value : NULL; } +static void print_field(enum output_format format, const char *key, + const char *value); + +static int print_category_fields(const char *category, + struct repo_info *info, + enum output_format format, + struct strbuf *valbuf) +{ + int found = 0; + size_t category_len = strlen(category); + + for (size_t i = 0; i < ARRAY_SIZE(repo_info_fields); i++) { + const struct field *field = &repo_info_fields[i]; + + if (!starts_with(field->key, category) || + field->key[category_len] != '.') + continue; + + strbuf_reset(valbuf); + field->get_value(info, valbuf); + print_field(format, field->key, valbuf->buf); + found = 1; + } + + return found; +} + static void print_field(enum output_format format, const char *key, const char *value) { @@ -126,14 +153,15 @@ static int print_fields(int argc, const char **argv, get_value = get_value_fn_for_key(key); - if (!get_value) { - ret = error(_("key '%s' not found"), key); + if (get_value) { + strbuf_reset(&valbuf); + get_value(info, &valbuf); + print_field(format, key, valbuf.buf); continue; } - strbuf_reset(&valbuf); - get_value(info, &valbuf); - print_field(format, key, valbuf.buf); + if (!print_category_fields(key, info, format, &valbuf)) + ret = error(_("key '%s' not found"), key); } strbuf_release(&valbuf); From 477333a94cdc1c98be8b0dac75a72e2745902bc6 Mon Sep 17 00:00:00 2001 From: Eslam reda ragheb Date: Mon, 23 Feb 2026 14:53:34 +0200 Subject: [PATCH 3/6] repo: add path keys to repo info Add a path category to git repo info with key-value pairs that mirror repository paths users commonly retrieve via rev-parse and git-path lookups. This makes scripting against repo metadata more direct and avoids shelling out to multiple commands for related paths. The new keys are introduced as explicit path.* entries in repo_info_fields and are resolved through dedicated helpers. This keeps lookup behavior predictable and makes future path additions straightforward. Signed-off-by: Eslam reda ragheb --- builtin/repo.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/builtin/repo.c b/builtin/repo.c index f614298199da9f..87c58509296959 100644 --- a/builtin/repo.c +++ b/builtin/repo.c @@ -1,10 +1,12 @@ #define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" +#include "abspath.h" #include "environment.h" #include "hex.h" #include "odb.h" #include "parse-options.h" +#include "path.h" #include "path-walk.h" #include "progress.h" #include "quote.h" @@ -14,6 +16,7 @@ #include "strbuf.h" #include "string-list.h" #include "shallow.h" +#include "submodule.h" #include "utf8.h" static const char *const repo_usage[] = { @@ -41,6 +44,13 @@ struct field { get_value_fn *get_value; }; +static void repo_info_add_path(struct repo_info *info, + struct strbuf *buf, + const char *path) +{ + strbuf_add_absolute_path(buf, path); +} + static int get_layout_bare(struct repo_info *info UNUSED, struct strbuf *buf) { strbuf_addstr(buf, is_bare_repository() ? "true" : "false"); @@ -62,6 +72,87 @@ static int get_object_format(struct repo_info *info, struct strbuf *buf) return 0; } +static int get_path_common_dir(struct repo_info *info, struct strbuf *buf) +{ + repo_info_add_path(info, buf, repo_get_common_dir(info->repo)); + return 0; +} + +static int get_path_config_file(struct repo_info *info, struct strbuf *buf) +{ + struct strbuf path = STRBUF_INIT; + + repo_info_add_path(info, buf, repo_git_path_replace(info->repo, &path, "config")); + strbuf_release(&path); + return 0; +} + +static int get_path_git_dir(struct repo_info *info, struct strbuf *buf) +{ + repo_info_add_path(info, buf, repo_get_git_dir(info->repo)); + return 0; +} + +static int get_path_prefix(struct repo_info *info, struct strbuf *buf) +{ + strbuf_addstr(buf, info->prefix); + return 0; +} + +static int get_path_grafts_file(struct repo_info *info, struct strbuf *buf) +{ + repo_info_add_path(info, buf, repo_get_graft_file(info->repo)); + return 0; +} + +static int get_path_hooks_directory(struct repo_info *info, struct strbuf *buf) +{ + struct strbuf path = STRBUF_INIT; + + repo_info_add_path(info, buf, repo_git_path_replace(info->repo, &path, "hooks")); + strbuf_release(&path); + return 0; +} + +static int get_path_index_file(struct repo_info *info, struct strbuf *buf) +{ + repo_info_add_path(info, buf, repo_get_index_file(info->repo)); + return 0; +} + +static int get_path_objects_directory(struct repo_info *info, struct strbuf *buf) +{ + repo_info_add_path(info, buf, repo_get_object_directory(info->repo)); + return 0; +} + +static int get_path_superproject_working_tree(struct repo_info *info, + struct strbuf *buf) +{ + struct strbuf superproject = STRBUF_INIT; + + if (get_superproject_working_tree(&superproject)) + repo_info_add_path(info, buf, superproject.buf); + + strbuf_release(&superproject); + return 0; +} + +static int get_path_toplevel(struct repo_info *info, struct strbuf *buf) +{ + const char *work_tree = repo_get_work_tree(info->repo); + + if (work_tree) + repo_info_add_path(info, buf, work_tree); + + return 0; +} + +static int get_path_work_tree(struct repo_info *info, struct strbuf *buf) +{ + return get_path_toplevel(info, buf); +} + static int get_references_format(struct repo_info *info, struct strbuf *buf) { struct repository *repo = info->repo; @@ -75,6 +166,17 @@ static const struct field repo_info_fields[] = { { "layout.bare", get_layout_bare }, { "layout.shallow", get_layout_shallow }, { "object.format", get_object_format }, + { "path.common-dir", get_path_common_dir }, + { "path.config-file", get_path_config_file }, + { "path.git-dir", get_path_git_dir }, + { "path.grafts-file", get_path_grafts_file }, + { "path.hooks-directory", get_path_hooks_directory }, + { "path.index-file", get_path_index_file }, + { "path.objects-directory", get_path_objects_directory }, + { "path.prefix", get_path_prefix }, + { "path.superproject-working-tree", get_path_superproject_working_tree }, + { "path.toplevel", get_path_toplevel }, + { "path.working-tree", get_path_work_tree }, { "references.format", get_references_format }, }; @@ -233,7 +335,7 @@ static int cmd_repo_info(int argc, const char **argv, const char *prefix, enum output_format format = FORMAT_NEWLINE_TERMINATED; struct repo_info info = { .repo = repo, - .prefix = prefix, + .prefix = prefix ? prefix : "", }; int all_keys = 0; int show_keys = 0; From 2a7afe4f877d4cad361dbb0d68d86ceab9448edf Mon Sep 17 00:00:00 2001 From: Eslam reda ragheb Date: Mon, 23 Feb 2026 14:53:34 +0200 Subject: [PATCH 4/6] repo: add --path-format for info path output Teach git repo info to accept --path-format=(absolute|relative) so scripts can request stable path style explicitly. This aligns path.* output behavior with existing rev-parse usage patterns and reduces ad-hoc path conversion in callers. The option is wired through repo_info context and used by repo_info_add_path(), so path formatting remains centralized and consistent across all path.* keys. Signed-off-by: Eslam reda ragheb --- builtin/repo.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/builtin/repo.c b/builtin/repo.c index 87c58509296959..bcdb9210cb905e 100644 --- a/builtin/repo.c +++ b/builtin/repo.c @@ -20,15 +20,21 @@ #include "utf8.h" static const char *const repo_usage[] = { - "git repo info [--format=(lines|nul) | -z] [--all | ...]", + "git repo info [--format=(lines|nul) | -z] [--path-format=(absolute|relative)] [--all | ...]", "git repo info --keys [--format=(lines|nul) | -z]", "git repo structure [--format=(table|lines|nul) | -z]", NULL }; +enum path_format { + PATH_FORMAT_ABSOLUTE, + PATH_FORMAT_RELATIVE, +}; + struct repo_info { struct repository *repo; const char *prefix; + enum path_format path_format; }; typedef int get_value_fn(struct repo_info *info, struct strbuf *buf); @@ -48,6 +54,16 @@ static void repo_info_add_path(struct repo_info *info, struct strbuf *buf, const char *path) { + if (info->path_format == PATH_FORMAT_RELATIVE) { + char *cwd = xgetcwd(); + struct strbuf rel_path = STRBUF_INIT; + + strbuf_addstr(buf, relative_path(path, cwd, &rel_path)); + strbuf_release(&rel_path); + free(cwd); + return; + } + strbuf_add_absolute_path(buf, path); } @@ -329,6 +345,21 @@ static int parse_format_cb(const struct option *opt, return 0; } +static int parse_path_format_cb(const struct option *opt, + const char *arg, int unset UNUSED) +{ + enum path_format *path_format = opt->value; + + if (!strcmp(arg, "absolute")) + *path_format = PATH_FORMAT_ABSOLUTE; + else if (!strcmp(arg, "relative")) + *path_format = PATH_FORMAT_RELATIVE; + else + die(_("invalid path format '%s'"), arg); + + return 0; +} + static int cmd_repo_info(int argc, const char **argv, const char *prefix, struct repository *repo) { @@ -336,6 +367,7 @@ static int cmd_repo_info(int argc, const char **argv, const char *prefix, struct repo_info info = { .repo = repo, .prefix = prefix ? prefix : "", + .path_format = PATH_FORMAT_ABSOLUTE, }; int all_keys = 0; int show_keys = 0; @@ -347,6 +379,9 @@ static int cmd_repo_info(int argc, const char **argv, const char *prefix, N_("synonym for --format=nul"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, parse_format_cb), + OPT_CALLBACK_F(0, "path-format", &info.path_format, + N_("format"), N_("path output format"), + PARSE_OPT_NONEG, parse_path_format_cb), OPT_BOOL(0, "all", &all_keys, N_("print all keys/values")), OPT_BOOL(0, "keys", &show_keys, N_("show keys")), OPT_END() From 1e52e7bd7f124481910d455da388c75636b5cb38 Mon Sep 17 00:00:00 2001 From: Eslam reda ragheb Date: Mon, 23 Feb 2026 14:53:36 +0200 Subject: [PATCH 5/6] t1900: cover repo info path keys and path-format Extend t1900 to validate category-key expansion, path.* key behavior, and --path-format handling for git repo info. The tests compare repo info output to equivalent rev-parse values. This ensures behavior remains aligned with existing plumbing semantics. Also keep mixed key/category ordering coverage so callers can rely on deterministic output order when combining explicit keys with category requests. Signed-off-by: Eslam reda ragheb --- t/t1900-repo.sh | 194 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh index a9eb07abe8aaea..6605394d1fc6e7 100755 --- a/t/t1900-repo.sh +++ b/t/t1900-repo.sh @@ -4,6 +4,40 @@ test_description='test git repo-info' . ./test-lib.sh +# git-repo-info keys. It must contain the same keys listed in the const +# repo_info_fields, in lexicographical order. +REPO_INFO_KEYS=' + layout.bare + layout.shallow + object.format + path.common-dir + path.config-file + path.git-dir + path.grafts-file + path.hooks-directory + path.index-file + path.objects-directory + path.prefix + path.superproject-working-tree + path.toplevel + path.working-tree + references.format +' + +REPO_INFO_PATH_KEYS=' + path.common-dir + path.config-file + path.git-dir + path.grafts-file + path.hooks-directory + path.index-file + path.objects-directory + path.prefix + path.superproject-working-tree + path.toplevel + path.working-tree +' + # Test whether a key-value pair is correctly returned # # Usage: test_repo_info