{"thread":{"id":"66010","subject":"[GSoC Patch 0/7] repo: add more path keys to git repo info","startedAt":"2026-07-16T01:22:37Z","lastAt":"2026-09-27T11:47:10Z","messageCount":94,"participants":["K Jayatheerth","Junio C Hamano","Lucas Seiki Oshiro","Justin Tobler","SZEDER Gábor"],"isPatch":true,"patchVersion":1,"patchTotal":7},"messages":[{"id":"548340","messageId":"20260716012138.6714-1-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":null,"subject":"[GSoC Patch 0/7] repo: add more path keys to git repo info","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-16T01:21:31Z","receivedAt":"2026-07-16T01:22:37Z","isPatch":true,"body":"Series adds keys to git repo info.\nKeys output paths of repository components:\n* path.toplevel: repository tree.\n* path.superproject-working-tree: superproject tree from submodules.\n* path.objects: repository objects.\n* path.hooks: repository hooks.\n* path.index: repository index.\n* path.grafts: repository grafts.\n* path.git-prefix: prefix offset.\n\nKeys support suffixes for format.\nCommits contain documentation and tests.\n\nK Jayatheerth (7):\n  repo: add path.toplevel with absolute and relative suffix formatting\n  repo: add path.superproject-working-tree with absolute and relative\n    suffixes\n  repo: add path.objects with absolute and relative suffix formatting\n  repo: add path.hooks with absolute and relative suffix formatting\n  repo: add path.index with absolute and relative suffix formatting\n  repo: add path.grafts with absolute and relative suffix formatting\n  repo: add path.git-prefix path key validation\n\n Documentation/git-repo.adoc |  58 +++++++++++++\n builtin/repo.c              | 167 ++++++++++++++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 108 +++++++++++++++++++++++\n 3 files changed, 333 insertions(+)\n\n-- \n2.55.GIT\n\n"},{"id":"548341","messageId":"20260716012138.6714-2-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260716012138.6714-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch 1/7] repo: add path.toplevel with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-16T01:21:32Z","receivedAt":"2026-07-16T01:23:10Z","isPatch":true,"body":"Scripts frequently need to find the root directory of a repository's\nworking tree. Currently, this requires using `git rev-parse --show-toplevel`\nor inferring it from other path components.\n\nIntroduce `path.toplevel.absolute` and `path.toplevel.relative` keys\nto `git repo info`. This allows scripts to retrieve the top-level\nworking tree path in a predictable, strictly formatted manner without\nrelying on `rev-parse`.\n\nIf requested in a bare repository where no working tree exists, the\ncommand returns an empty string.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 10 ++++++++++\n builtin/repo.c              | 28 ++++++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 30 ++++++++++++++++++++++++++++++\n 3 files changed, 68 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex ed7d80c690..e34abe5fea 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,16 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.toplevel.absolute`::\n+\tThe canonical absolute path to the top-level directory of the\n+\trepository's working tree. Outputs an empty string if the repository\n+\tis bare.\n+\n+`path.toplevel.relative`::\n+\tThe path to the top-level directory of the repository's working\n+\ttree relative to the current working directory. Outputs an empty\n+\tstring if the repository is bare.\n+\n `references.format`::\n \tThe reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 042d6de558..194757eb18 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -121,6 +121,32 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *work_tree = repo_get_work_tree(repo);\n+\n+\tif (!work_tree) {\n+\t\tstrbuf_addstr(buf, \"\");\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *work_tree = repo_get_work_tree(repo);\n+\n+\tif (!work_tree) {\n+\t\tstrbuf_addstr(buf, \"\");\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -137,6 +163,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n+\t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex ae8c22c817..fbb9063ee5 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,4 +213,34 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.toplevel absolute and relative' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tmkdir -p repo/sub &&\n+\t\tcd repo/sub &&\n+\n+\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n+\n+\t\techo \"path.toplevel.absolute=$ROOT\" >expect.abs &&\n+\t\tgit repo info path.toplevel.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.toplevel.relative=../\" >expect.rel &&\n+\t\tgit repo info path.toplevel.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n+test_expect_success 'path.toplevel returns empty in a bare repository' '\n+\ttest_when_finished \"rm -rf bare.git\" &&\n+\tgit init --bare bare.git &&\n+\t(\n+\t\tcd bare.git &&\n+\t\techo \"path.toplevel.absolute=\" >expect &&\n+\t\tgit repo info path.toplevel.absolute >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\n-- \n2.55.GIT\n\n"},{"id":"548342","messageId":"20260716012138.6714-3-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260716012138.6714-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch 2/7] repo: add path.superproject-working-tree with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-16T01:21:33Z","receivedAt":"2026-07-16T01:23:48Z","isPatch":true,"body":"Scripts working in multi-repository setups often need to identify the\ntop-level working tree of a superproject from within a submodule.\nCurrently, this is only exposed via `git rev-parse\n--show-superproject-working-tree`.\n\nIntroduce `path.superproject-working-tree.absolute` and\n`path.superproject-working-tree.relative` keys to `git repo info`.\nThis exposes the core submodule context via a scriptable config-like key\nusing standard format rules.\n\nIf requested when not inside a submodule, the command returns an empty\nstring.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 10 ++++++++++\n builtin/repo.c              | 33 +++++++++++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 34 ++++++++++++++++++++++++++++++++++\n 3 files changed, 77 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex e34abe5fea..03aa57942f 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,16 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.superproject-working-tree.absolute`::\n+\tThe canonical absolute path to the working tree root of the superproject\n+\tif the current repository is an initialized submodule. Outputs an empty\n+\tstring if not in a submodule.\n+\n+`path.superproject-working-tree.relative`::\n+\tThe path to the working tree root of the superproject relative to the\n+\tcurrent working directory if the current repository is an initialized\n+\tsubmodule. Outputs an empty string if not in a submodule.\n+\n `path.toplevel.absolute`::\n \tThe canonical absolute path to the top-level directory of the\n \trepository's working tree. Outputs an empty string if the repository\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 194757eb18..82359473e9 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -18,6 +18,7 @@\n #include \"strbuf.h\"\n #include \"string-list.h\"\n #include \"shallow.h\"\n+#include \"submodule.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"utf8.h\"\n@@ -121,6 +122,36 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\tstruct strbuf superproject = STRBUF_INIT;\n+\n+\tif (!get_superproject_working_tree(&superproject)) {\n+\t\tstrbuf_release(&superproject);\n+\t\tstrbuf_addstr(buf, \"\");\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\tstrbuf_release(&superproject);\n+\treturn 0;\n+}\n+\n+static int get_path_superproject_relative(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\tstruct strbuf superproject = STRBUF_INIT;\n+\n+\tif (!get_superproject_working_tree(&superproject)) {\n+\t\tstrbuf_release(&superproject);\n+\t\tstrbuf_addstr(buf, \"\");\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\tstrbuf_release(&superproject);\n+\treturn 0;\n+}\n+\n static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *work_tree = repo_get_work_tree(repo);\n@@ -163,6 +194,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\n+\t{ \"path.superproject-working-tree.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n \t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n \t{ \"references.format\", get_references_format },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex fbb9063ee5..220b3d4d3d 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,40 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.superproject-working-tree absolute and relative' '\n+\ttest_when_finished \"rm -rf sub super\" &&\n+\tgit init sub &&\n+\ttest_commit -C sub initial &&\n+\tgit init super &&\n+\t(\n+\t\tcd super &&\n+\t\tgit -c protocol.file.allow=always submodule add \"../sub\" sub &&\n+\t\tgit commit -m \"add submodule\" &&\n+\n+\t\tcd sub &&\n+\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n+\n+\t\techo \"path.superproject-working-tree.absolute=$ROOT\" >expect.abs &&\n+\t\tgit repo info path.superproject-working-tree.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.superproject-working-tree.relative=../\" >expect.rel &&\n+\t\tgit repo info path.superproject-working-tree.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n+test_expect_success 'path.superproject-working-tree returns empty when not in a submodule' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\techo \"path.superproject-working-tree.absolute=\" >expect &&\n+\t\tgit repo info path.superproject-working-tree.absolute >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_expect_success 'path.toplevel absolute and relative' '\n \ttest_when_finished \"rm -rf repo\" &&\n \tgit init repo &&\n-- \n2.55.GIT\n\n"},{"id":"548343","messageId":"20260716012138.6714-4-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260716012138.6714-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch 3/7] repo: add path.objects with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-16T01:21:34Z","receivedAt":"2026-07-16T01:24:27Z","isPatch":true,"body":"Tools and deployment hooks frequently query the location of the object\ndatabase directory. Currently, this relies on legacy parsing methods or\nmanually inspecting `git rev-parse --git-path objects`.\n\nIntroduce `path.objects.absolute` and `path.objects.relative` keys to\n`git repo info`. This allows tools to discover the object database\nlocation safely while natively adhering to active `GIT_OBJECT_DIRECTORY`\nenvironment variable overrides.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  9 +++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        |  7 +++++++\n 3 files changed, 40 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 03aa57942f..8429a44b43 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,15 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.objects.absolute`::\n+\tThe canonical absolute path to the repository's object database directory.\n+\tRespects the `GIT_OBJECT_DIRECTORY` environment override.\n+\n+`path.objects.relative`::\n+\tThe path to the repository's object database directory relative to the\n+\tcurrent working directory. Respects the `GIT_OBJECT_DIRECTORY`\n+\tenvironment override.\n+\n `path.superproject-working-tree.absolute`::\n \tThe canonical absolute path to the working tree root of the superproject\n \tif the current repository is an initialized submodule. Outputs an empty\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 82359473e9..d6bdd5bcfa 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *obj_dir = repo_get_object_directory(repo);\n+\n+\tif (!obj_dir)\n+\t\treturn error(_(\"unable to get object directory\"));\n+\n+\tformat_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_objects_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *obj_dir = repo_get_object_directory(repo);\n+\n+\tif (!obj_dir)\n+\t\treturn error(_(\"unable to get object directory\"));\n+\n+\tformat_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n {\n \tstruct strbuf superproject = STRBUF_INIT;\n@@ -194,6 +216,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.objects.absolute\", get_path_objects_absolute },\n+\t{ \"path.objects.relative\", get_path_objects_relative },\n \t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\n \t{ \"path.superproject-working-tree.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 220b3d4d3d..260f4fde43 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,13 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'objects standard' 'objects' '.git/objects'\n+\n+test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \\\n+\t'custom-objects' \\\n+\t'GIT_OBJECT_DIRECTORY=\"$ROOT/custom-objects\" && export GIT_OBJECT_DIRECTORY &&\n+\t mkdir -p \"$ROOT/custom-objects\"'\n+\n test_expect_success 'path.superproject-working-tree absolute and relative' '\n \ttest_when_finished \"rm -rf sub super\" &&\n \tgit init sub &&\n-- \n2.55.GIT\n\n"},{"id":"548344","messageId":"20260716012138.6714-5-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260716012138.6714-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch 4/7] repo: add path.hooks with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-16T01:21:35Z","receivedAt":"2026-07-16T01:25:00Z","isPatch":true,"body":"External tool integrations and validation systems need a stable way to\nidentify where the repository hooks are stored. Currently, this involves\nrelying on `git rev-parse --git-path hooks` or querying `core.hooksPath`\nmanually.\n\nIntroduce `path.hooks.absolute` and `path.hooks.relative` keys to\n`git repo info`. This allows tools to discover the active hooks location\nnatively, ensuring proper resolution regardless of whether Git is using\nthe standard `.git/hooks` structure or a custom `core.hooksPath` setup.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  8 ++++++++\n builtin/repo.c              | 22 ++++++++++++++++++++++\n t/t1900-repo-info.sh        |  6 ++++++\n 3 files changed, 36 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 8429a44b43..7bc1c51310 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,14 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.hooks.absolute`::\n+\tThe canonical absolute path to the repository's hooks directory.\n+\tRespects `core.hooksPath` configuration adjustments.\n+\n+`path.hooks.relative`::\n+\tThe path to the repository's hooks directory relative to the current\n+\tworking directory. Respects `core.hooksPath` configuration adjustments.\n+\n `path.objects.absolute`::\n \tThe canonical absolute path to the repository's object database directory.\n \tRespects the `GIT_OBJECT_DIRECTORY` environment override.\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex d6bdd5bcfa..c921de222d 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,26 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf hooks_path = STRBUF_INIT;\n+\n+\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n+\tformat_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\tstrbuf_release(&hooks_path);\n+\treturn 0;\n+}\n+\n+static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf hooks_path = STRBUF_INIT;\n+\n+\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n+\tformat_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\tstrbuf_release(&hooks_path);\n+\treturn 0;\n+}\n+\n static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *obj_dir = repo_get_object_directory(repo);\n@@ -216,6 +236,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n+\t{ \"path.hooks.relative\", get_path_hooks_relative },\n \t{ \"path.objects.absolute\", get_path_objects_absolute },\n \t{ \"path.objects.relative\", get_path_objects_relative },\n \t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 260f4fde43..cd3f856d04 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'\n+\n+test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n+\t'custom-hooks' \\\n+\t'git config core.hooksPath \"$ROOT/custom-hooks\" && mkdir -p \"$ROOT/custom-hooks\"'\n+\n test_repo_info_path 'objects standard' 'objects' '.git/objects'\n \n test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \\\n-- \n2.55.GIT\n\n"},{"id":"548345","messageId":"20260716012138.6714-6-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260716012138.6714-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch 5/7] repo: add path.index with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-16T01:21:36Z","receivedAt":"2026-07-16T01:25:25Z","isPatch":true,"body":"External script workflows and formatting layers require straightforward\naccess to the location of the index staging file. Currently, tracking\nthis necessitates a legacy call to `git rev-parse --git-path index` or\n`--show-toplevel` logic abstractions.\n\nIntroduce `path.index.absolute` and `path.index.relative` keys to\n`git repo info`. This allows tooling utilities to discover the active\nindex context cleanly while scaling transparently with localized\n`GIT_INDEX_FILE` environment overrides.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  8 ++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        |  6 ++++++\n 3 files changed, 38 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 7bc1c51310..3a837c573e 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -127,6 +127,14 @@ values that they return:\n \tThe path to the repository's hooks directory relative to the current\n \tworking directory. Respects `core.hooksPath` configuration adjustments.\n \n+`path.index.absolute`::\n+\tThe canonical absolute path to the repository's current index file.\n+\tRespects the `GIT_INDEX_FILE` environment override.\n+\n+`path.index.relative`::\n+\tThe path to the repository's current index file relative to the current\n+\tworking directory. Respects the `GIT_INDEX_FILE` environment override.\n+\n `path.objects.absolute`::\n \tThe canonical absolute path to the repository's object database directory.\n \tRespects the `GIT_OBJECT_DIRECTORY` environment override.\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex c921de222d..66bf4c67cc 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -142,6 +142,28 @@ static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *index_file = repo_get_index_file(repo);\n+\n+\tif (!index_file)\n+\t\treturn error(_(\"unable to get index file\"));\n+\n+\tformat_path(buf, index_file, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *index_file = repo_get_index_file(repo);\n+\n+\tif (!index_file)\n+\t\treturn error(_(\"unable to get index file\"));\n+\n+\tformat_path(buf, index_file, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *obj_dir = repo_get_object_directory(repo);\n@@ -238,6 +260,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n \t{ \"path.hooks.relative\", get_path_hooks_relative },\n+\t{ \"path.index.absolute\", get_path_index_absolute },\n+\t{ \"path.index.relative\", get_path_index_relative },\n \t{ \"path.objects.absolute\", get_path_objects_absolute },\n \t{ \"path.objects.relative\", get_path_objects_relative },\n \t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex cd3f856d04..04e6b8553c 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -219,6 +219,12 @@ test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n \t'custom-hooks' \\\n \t'git config core.hooksPath \"$ROOT/custom-hooks\" && mkdir -p \"$ROOT/custom-hooks\"'\n \n+test_repo_info_path 'index standard' 'index' '.git/index'\n+\n+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \\\n+\t'custom-index-file' \\\n+\t'GIT_INDEX_FILE=\"$ROOT/custom-index-file\" && export GIT_INDEX_FILE'\n+\n test_repo_info_path 'objects standard' 'objects' '.git/objects'\n \n test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \\\n-- \n2.55.GIT\n\n"},{"id":"548346","messageId":"20260716012138.6714-7-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260716012138.6714-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch 6/7] repo: add path.grafts with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-16T01:21:37Z","receivedAt":"2026-07-16T01:25:48Z","isPatch":true,"body":"External toolchains managing specialized history rewrites or legacy\nhistory splices require access to the location of the repository grafts\nfile. Currently, this requires a legacy call to `git rev-parse --git-path info/grafts`.\n\nIntroduce `path.grafts.absolute` and `path.grafts.relative` keys to\n`git repo info`. This allows scripting layers to query the active grafts\ncontext cleanly while scaling transparently with active `GIT_GRAFT_FILE`\nenvironment variable overrides.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  8 ++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        |  6 ++++++\n 3 files changed, 38 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 3a837c573e..6c962620ec 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,14 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.grafts.absolute`::\n+\tThe canonical absolute path to the repository grafts file.\n+\tRespects the `GIT_GRAFT_FILE` environment override.\n+\n+`path.grafts.relative`::\n+\tThe path to the repository grafts file relative to the current working\n+\tdirectory. Respects the `GIT_GRAFT_FILE` environment override.\n+\n `path.hooks.absolute`::\n \tThe canonical absolute path to the repository's hooks directory.\n \tRespects `core.hooksPath` configuration adjustments.\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 66bf4c67cc..a97ad71649 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_grafts_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *graft_file = repo_get_graft_file(repo);\n+\n+\tif (!graft_file)\n+\t\treturn error(_(\"unable to get graft file\"));\n+\n+\tformat_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_grafts_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *graft_file = repo_get_graft_file(repo);\n+\n+\tif (!graft_file)\n+\t\treturn error(_(\"unable to get graft file\"));\n+\n+\tformat_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tstruct strbuf hooks_path = STRBUF_INIT;\n@@ -258,6 +280,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.grafts.absolute\", get_path_grafts_absolute },\n+\t{ \"path.grafts.relative\", get_path_grafts_relative },\n \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n \t{ \"path.hooks.relative\", get_path_hooks_relative },\n \t{ \"path.index.absolute\", get_path_index_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 04e6b8553c..6c47989df7 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'grafts standard' 'grafts' '.git/info/grafts'\n+\n+test_repo_info_path 'grafts with GIT_GRAFT_FILE override' 'grafts' \\\n+\t'custom-graft-file' \\\n+\t'GIT_GRAFT_FILE=\"$ROOT/custom-graft-file\" && export GIT_GRAFT_FILE'\n+\n test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'\n \n test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n-- \n2.55.GIT\n\n"},{"id":"548347","messageId":"20260716012138.6714-8-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260716012138.6714-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch 7/7] repo: add path.git-prefix path key validation","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-16T01:21:38Z","receivedAt":"2026-07-16T01:26:14Z","isPatch":true,"body":"Scripts and command-line prompt integrations frequently need to know their\nrelative depth inside a repository working tree layout. Currently, this\nis retrieved using `git rev-parse --show-prefix`.\n\nIntroduce the `path.git-prefix` key to `git repo info`. This mirrors the\nprefix location tracking framework as a standalone key, returning the\nexact relative path offset complete with a trailing slash, or an empty\nstring if run directly at the repository working tree root.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  5 +++++\n builtin/repo.c              | 12 ++++++++++++\n t/t1900-repo-info.sh        | 19 +++++++++++++++++++\n 3 files changed, 36 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 6c962620ec..5ba2ab1612 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -113,6 +113,11 @@ values that they return:\n \tThe path to the Git repository's common directory relative to\n \tthe current working directory.\n \n+`path.git-prefix`::\n+\tThe relative path from the top-level directory of the working tree to\n+\tthe current working directory (including a trailing slash). Outputs an\n+\tempty string if executed at the root of the working tree.\n+\n `path.gitdir.absolute`::\n \tThe canonical absolute path to the Git repository directory (the `.git` directory).\n \ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex a97ad71649..00d5064281 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,3 +1,4 @@\n+#include \"compat/posix.h\"\n #define USE_THE_REPOSITORY_VARIABLE\n \n #include \"builtin.h\"\n@@ -100,6 +101,16 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b\n \treturn 0;\n }\n \n+static int get_path_git_prefix(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\t/*\n+\t * startup_info->prefix is NULL if we are at the working tree root.\n+\t * We add an empty string to ensure the buffer is cleanly initialized.\n+\t */\n+\tstrbuf_addstr(buf, startup_info->prefix ? startup_info->prefix : \"\");\n+\treturn 0;\n+}\n+\n static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *git_dir = repo_get_git_dir(repo);\n@@ -278,6 +289,7 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"object.format\", get_object_format },\n \t{ \"path.commondir.absolute\", get_path_commondir_absolute },\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n+\t{ \"path.git-prefix\", get_path_git_prefix },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n \t{ \"path.grafts.absolute\", get_path_grafts_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 6c47989df7..3e5e42f6d3 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -207,6 +207,25 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.git-prefix at root and in a subdirectory' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\n+\t\techo \"path.git-prefix=\" >expect.root &&\n+\t\tgit repo info path.git-prefix >actual.root &&\n+\t\ttest_cmp expect.root actual.root &&\n+\n+\t\tmkdir -p sub/dir &&\n+\t\tcd sub/dir &&\n+\n+\t\techo \"path.git-prefix=sub/dir/\" >expect.sub &&\n+\t\tgit repo info path.git-prefix >actual.sub &&\n+\t\ttest_cmp expect.sub actual.sub\n+\t)\n+'\n+\n test_repo_info_path 'gitdir standard' 'gitdir' '.git'\n \n test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n-- \n2.55.GIT\n\n"},{"id":"548351","messageId":"xmqqech3k47u.fsf@gitster.g","threadId":"66010","inReplyTo":"20260716012138.6714-8-jayatheerthkulkarni2005@gmail.com","subject":"Re: [GSoC Patch 7/7] repo: add path.git-prefix path key validation","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-07-16T03:23:17Z","receivedAt":"2026-07-16T03:23:20Z","isPatch":true,"body":"K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index a97ad71649..00d5064281 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -1,3 +1,4 @@\n> +#include \"compat/posix.h\"\n>  #define USE_THE_REPOSITORY_VARIABLE\n>  #include \"builtin.h\"\n\nThe first include must be <git-compat-util.h> or common include\nfiles that include <git-compat-util.h> as the first thing, like\n<builtin.h>.\n\nAs the file already includes <builtin.h>, extra inclusion of\n<compat/posix.h> before everything else is an absolute no-no.\n\nBy the way, I do not see any \"validation\" in the patch as the title\nclaims.  Perhaps retitle it to \"repo: add path.git-prefix key\" or\nsomething simpler like that?\n\n"},{"id":"548425","messageId":"CA+rGoLfXGNydaUBz6qjfrHvYZ6T_jCQuoxO_yx1K7jEtqEMpNg@mail.gmail.com","threadId":"66010","inReplyTo":"xmqqech3k47u.fsf@gitster.g","subject":"Re: [GSoC Patch 7/7] repo: add path.git-prefix path key validation","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-16T15:36:36Z","receivedAt":"2026-07-16T15:36:50Z","isPatch":true,"body":"Hey Junio,\n\nOn Thu, Jul 16, 2026 at 8:53 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n>\n> > diff --git a/builtin/repo.c b/builtin/repo.c\n> > index a97ad71649..00d5064281 100644\n> > --- a/builtin/repo.c\n> > +++ b/builtin/repo.c\n> > @@ -1,3 +1,4 @@\n> > +#include \"compat/posix.h\"\n> >  #define USE_THE_REPOSITORY_VARIABLE\n> >  #include \"builtin.h\"\n>\n> The first include must be <git-compat-util.h> or common include\n> files that include <git-compat-util.h> as the first thing, like\n> <builtin.h>.\n>\n> As the file already includes <builtin.h>, extra inclusion of\n> <compat/posix.h> before everything else is an absolute no-no.\n>\n\n\nOh no, that's just my editor adding headers automatically.\nThanks for pointing this out, I will fix it.\n\n> By the way, I do not see any \"validation\" in the patch as the title\n> claims.  Perhaps retitle it to \"repo: add path.git-prefix key\" or\n> something simpler like that?\n>\n\nTrue, I will correct that as well.\n\nRegards,\n- K Jayatheerth\n"},{"id":"548499","messageId":"20260717133015.32040-1-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260716012138.6714-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v2 0/7] repo: add more path keys to git repo info","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-17T13:30:08Z","receivedAt":"2026-07-17T13:31:03Z","isPatch":true,"body":"Series adds keys to git repo info.\nKeys output paths of repository components:\n* path.toplevel: repository tree.\n* path.superproject-working-tree: superproject tree from submodules.\n* path.objects: repository objects.\n* path.hooks: repository hooks.\n* path.index: repository index.\n* path.grafts: repository grafts.\n* path.git-prefix: prefix offset.\n\nKeys support suffixes for format.\nCommits contain documentation and tests.\n\nK Jayatheerth (7):\n  repo: add path.toplevel with absolute and relative suffix formatting\n  repo: add path.superproject-working-tree with absolute and relative\n    suffixes\n  repo: add path.objects with absolute and relative suffix formatting\n  repo: add path.hooks with absolute and relative suffix formatting\n  repo: add path.index with absolute and relative suffix formatting\n  repo: add path.grafts with absolute and relative suffix formatting\n  repo: add path.git-prefix path key\n\n Documentation/git-repo.adoc |  58 +++++++++++++\n builtin/repo.c              | 166 ++++++++++++++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 108 +++++++++++++++++++++++\n 3 files changed, 332 insertions(+)\n\n-- \n2.55.GIT\n"},{"id":"548500","messageId":"20260717133015.32040-2-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260717133015.32040-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v2 1/7] repo: add path.toplevel with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-17T13:30:09Z","receivedAt":"2026-07-17T13:31:40Z","isPatch":true,"body":"Scripts frequently need to find the root directory of a repository's\nworking tree. Currently, this requires using `git rev-parse --show-toplevel`\nor inferring it from other path components.\n\nIntroduce `path.toplevel.absolute` and `path.toplevel.relative` keys\nto `git repo info`. This allows scripts to retrieve the top-level\nworking tree path in a predictable, strictly formatted manner without\nrelying on `rev-parse`.\n\nIf requested in a bare repository where no working tree exists, the\ncommand returns an empty string.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 10 ++++++++++\n builtin/repo.c              | 28 ++++++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 30 ++++++++++++++++++++++++++++++\n 3 files changed, 68 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex ed7d80c690..e34abe5fea 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,16 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.toplevel.absolute`::\n+\tThe canonical absolute path to the top-level directory of the\n+\trepository's working tree. Outputs an empty string if the repository\n+\tis bare.\n+\n+`path.toplevel.relative`::\n+\tThe path to the top-level directory of the repository's working\n+\ttree relative to the current working directory. Outputs an empty\n+\tstring if the repository is bare.\n+\n `references.format`::\n \tThe reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 042d6de558..194757eb18 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -121,6 +121,32 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *work_tree = repo_get_work_tree(repo);\n+\n+\tif (!work_tree) {\n+\t\tstrbuf_addstr(buf, \"\");\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *work_tree = repo_get_work_tree(repo);\n+\n+\tif (!work_tree) {\n+\t\tstrbuf_addstr(buf, \"\");\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -137,6 +163,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n+\t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex ae8c22c817..fbb9063ee5 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,4 +213,34 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.toplevel absolute and relative' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tmkdir -p repo/sub &&\n+\t\tcd repo/sub &&\n+\n+\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n+\n+\t\techo \"path.toplevel.absolute=$ROOT\" >expect.abs &&\n+\t\tgit repo info path.toplevel.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.toplevel.relative=../\" >expect.rel &&\n+\t\tgit repo info path.toplevel.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n+test_expect_success 'path.toplevel returns empty in a bare repository' '\n+\ttest_when_finished \"rm -rf bare.git\" &&\n+\tgit init --bare bare.git &&\n+\t(\n+\t\tcd bare.git &&\n+\t\techo \"path.toplevel.absolute=\" >expect &&\n+\t\tgit repo info path.toplevel.absolute >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\n-- \n2.55.GIT\n\n"},{"id":"548501","messageId":"20260717133015.32040-3-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260717133015.32040-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v2 2/7] repo: add path.superproject-working-tree with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-17T13:30:10Z","receivedAt":"2026-07-17T13:32:37Z","isPatch":true,"body":"Scripts working in multi-repository setups often need to identify the\ntop-level working tree of a superproject from within a submodule.\nCurrently, this is only exposed via `git rev-parse\n--show-superproject-working-tree`.\n\nIntroduce `path.superproject-working-tree.absolute` and\n`path.superproject-working-tree.relative` keys to `git repo info`.\nThis exposes the core submodule context via a scriptable config-like key\nusing standard format rules.\n\nIf requested when not inside a submodule, the command returns an empty\nstring.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 10 ++++++++++\n builtin/repo.c              | 33 +++++++++++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 34 ++++++++++++++++++++++++++++++++++\n 3 files changed, 77 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex e34abe5fea..03aa57942f 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,16 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.superproject-working-tree.absolute`::\n+\tThe canonical absolute path to the working tree root of the superproject\n+\tif the current repository is an initialized submodule. Outputs an empty\n+\tstring if not in a submodule.\n+\n+`path.superproject-working-tree.relative`::\n+\tThe path to the working tree root of the superproject relative to the\n+\tcurrent working directory if the current repository is an initialized\n+\tsubmodule. Outputs an empty string if not in a submodule.\n+\n `path.toplevel.absolute`::\n \tThe canonical absolute path to the top-level directory of the\n \trepository's working tree. Outputs an empty string if the repository\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 194757eb18..82359473e9 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -18,6 +18,7 @@\n #include \"strbuf.h\"\n #include \"string-list.h\"\n #include \"shallow.h\"\n+#include \"submodule.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"utf8.h\"\n@@ -121,6 +122,36 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\tstruct strbuf superproject = STRBUF_INIT;\n+\n+\tif (!get_superproject_working_tree(&superproject)) {\n+\t\tstrbuf_release(&superproject);\n+\t\tstrbuf_addstr(buf, \"\");\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\tstrbuf_release(&superproject);\n+\treturn 0;\n+}\n+\n+static int get_path_superproject_relative(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\tstruct strbuf superproject = STRBUF_INIT;\n+\n+\tif (!get_superproject_working_tree(&superproject)) {\n+\t\tstrbuf_release(&superproject);\n+\t\tstrbuf_addstr(buf, \"\");\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\tstrbuf_release(&superproject);\n+\treturn 0;\n+}\n+\n static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *work_tree = repo_get_work_tree(repo);\n@@ -163,6 +194,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\n+\t{ \"path.superproject-working-tree.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n \t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n \t{ \"references.format\", get_references_format },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex fbb9063ee5..220b3d4d3d 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,40 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.superproject-working-tree absolute and relative' '\n+\ttest_when_finished \"rm -rf sub super\" &&\n+\tgit init sub &&\n+\ttest_commit -C sub initial &&\n+\tgit init super &&\n+\t(\n+\t\tcd super &&\n+\t\tgit -c protocol.file.allow=always submodule add \"../sub\" sub &&\n+\t\tgit commit -m \"add submodule\" &&\n+\n+\t\tcd sub &&\n+\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n+\n+\t\techo \"path.superproject-working-tree.absolute=$ROOT\" >expect.abs &&\n+\t\tgit repo info path.superproject-working-tree.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.superproject-working-tree.relative=../\" >expect.rel &&\n+\t\tgit repo info path.superproject-working-tree.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n+test_expect_success 'path.superproject-working-tree returns empty when not in a submodule' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\techo \"path.superproject-working-tree.absolute=\" >expect &&\n+\t\tgit repo info path.superproject-working-tree.absolute >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_expect_success 'path.toplevel absolute and relative' '\n \ttest_when_finished \"rm -rf repo\" &&\n \tgit init repo &&\n-- \n2.55.GIT\n\n"},{"id":"548502","messageId":"20260717133015.32040-4-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260717133015.32040-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v2 3/7] repo: add path.objects with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-17T13:30:11Z","receivedAt":"2026-07-17T13:33:53Z","isPatch":true,"body":"Tools and deployment hooks frequently query the location of the object\ndatabase directory. Currently, this relies on legacy parsing methods or\nmanually inspecting `git rev-parse --git-path objects`.\n\nIntroduce `path.objects.absolute` and `path.objects.relative` keys to\n`git repo info`. This allows tools to discover the object database\nlocation safely while natively adhering to active `GIT_OBJECT_DIRECTORY`\nenvironment variable overrides.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  9 +++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        |  7 +++++++\n 3 files changed, 40 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 03aa57942f..8429a44b43 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,15 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.objects.absolute`::\n+\tThe canonical absolute path to the repository's object database directory.\n+\tRespects the `GIT_OBJECT_DIRECTORY` environment override.\n+\n+`path.objects.relative`::\n+\tThe path to the repository's object database directory relative to the\n+\tcurrent working directory. Respects the `GIT_OBJECT_DIRECTORY`\n+\tenvironment override.\n+\n `path.superproject-working-tree.absolute`::\n \tThe canonical absolute path to the working tree root of the superproject\n \tif the current repository is an initialized submodule. Outputs an empty\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 82359473e9..d6bdd5bcfa 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *obj_dir = repo_get_object_directory(repo);\n+\n+\tif (!obj_dir)\n+\t\treturn error(_(\"unable to get object directory\"));\n+\n+\tformat_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_objects_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *obj_dir = repo_get_object_directory(repo);\n+\n+\tif (!obj_dir)\n+\t\treturn error(_(\"unable to get object directory\"));\n+\n+\tformat_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n {\n \tstruct strbuf superproject = STRBUF_INIT;\n@@ -194,6 +216,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.objects.absolute\", get_path_objects_absolute },\n+\t{ \"path.objects.relative\", get_path_objects_relative },\n \t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\n \t{ \"path.superproject-working-tree.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 220b3d4d3d..260f4fde43 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,13 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'objects standard' 'objects' '.git/objects'\n+\n+test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \\\n+\t'custom-objects' \\\n+\t'GIT_OBJECT_DIRECTORY=\"$ROOT/custom-objects\" && export GIT_OBJECT_DIRECTORY &&\n+\t mkdir -p \"$ROOT/custom-objects\"'\n+\n test_expect_success 'path.superproject-working-tree absolute and relative' '\n \ttest_when_finished \"rm -rf sub super\" &&\n \tgit init sub &&\n-- \n2.55.GIT\n\n"},{"id":"548503","messageId":"20260717133015.32040-5-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260717133015.32040-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v2 4/7] repo: add path.hooks with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-17T13:30:12Z","receivedAt":"2026-07-17T13:34:35Z","isPatch":true,"body":"External tool integrations and validation systems need a stable way to\nidentify where the repository hooks are stored. Currently, this involves\nrelying on `git rev-parse --git-path hooks` or querying `core.hooksPath`\nmanually.\n\nIntroduce `path.hooks.absolute` and `path.hooks.relative` keys to\n`git repo info`. This allows tools to discover the active hooks location\nnatively, ensuring proper resolution regardless of whether Git is using\nthe standard `.git/hooks` structure or a custom `core.hooksPath` setup.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  8 ++++++++\n builtin/repo.c              | 22 ++++++++++++++++++++++\n t/t1900-repo-info.sh        |  6 ++++++\n 3 files changed, 36 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 8429a44b43..7bc1c51310 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,14 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.hooks.absolute`::\n+\tThe canonical absolute path to the repository's hooks directory.\n+\tRespects `core.hooksPath` configuration adjustments.\n+\n+`path.hooks.relative`::\n+\tThe path to the repository's hooks directory relative to the current\n+\tworking directory. Respects `core.hooksPath` configuration adjustments.\n+\n `path.objects.absolute`::\n \tThe canonical absolute path to the repository's object database directory.\n \tRespects the `GIT_OBJECT_DIRECTORY` environment override.\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex d6bdd5bcfa..c921de222d 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,26 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf hooks_path = STRBUF_INIT;\n+\n+\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n+\tformat_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\tstrbuf_release(&hooks_path);\n+\treturn 0;\n+}\n+\n+static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf hooks_path = STRBUF_INIT;\n+\n+\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n+\tformat_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\tstrbuf_release(&hooks_path);\n+\treturn 0;\n+}\n+\n static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *obj_dir = repo_get_object_directory(repo);\n@@ -216,6 +236,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n+\t{ \"path.hooks.relative\", get_path_hooks_relative },\n \t{ \"path.objects.absolute\", get_path_objects_absolute },\n \t{ \"path.objects.relative\", get_path_objects_relative },\n \t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 260f4fde43..cd3f856d04 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'\n+\n+test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n+\t'custom-hooks' \\\n+\t'git config core.hooksPath \"$ROOT/custom-hooks\" && mkdir -p \"$ROOT/custom-hooks\"'\n+\n test_repo_info_path 'objects standard' 'objects' '.git/objects'\n \n test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \\\n-- \n2.55.GIT\n\n"},{"id":"548504","messageId":"20260717133015.32040-6-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260717133015.32040-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v2 5/7] repo: add path.index with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-17T13:30:13Z","receivedAt":"2026-07-17T13:35:03Z","isPatch":true,"body":"External script workflows and formatting layers require straightforward\naccess to the location of the index staging file. Currently, tracking\nthis necessitates a legacy call to `git rev-parse --git-path index` or\n`--show-toplevel` logic abstractions.\n\nIntroduce `path.index.absolute` and `path.index.relative` keys to\n`git repo info`. This allows tooling utilities to discover the active\nindex context cleanly while scaling transparently with localized\n`GIT_INDEX_FILE` environment overrides.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  8 ++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        |  6 ++++++\n 3 files changed, 38 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 7bc1c51310..3a837c573e 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -127,6 +127,14 @@ values that they return:\n \tThe path to the repository's hooks directory relative to the current\n \tworking directory. Respects `core.hooksPath` configuration adjustments.\n \n+`path.index.absolute`::\n+\tThe canonical absolute path to the repository's current index file.\n+\tRespects the `GIT_INDEX_FILE` environment override.\n+\n+`path.index.relative`::\n+\tThe path to the repository's current index file relative to the current\n+\tworking directory. Respects the `GIT_INDEX_FILE` environment override.\n+\n `path.objects.absolute`::\n \tThe canonical absolute path to the repository's object database directory.\n \tRespects the `GIT_OBJECT_DIRECTORY` environment override.\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex c921de222d..66bf4c67cc 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -142,6 +142,28 @@ static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *index_file = repo_get_index_file(repo);\n+\n+\tif (!index_file)\n+\t\treturn error(_(\"unable to get index file\"));\n+\n+\tformat_path(buf, index_file, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *index_file = repo_get_index_file(repo);\n+\n+\tif (!index_file)\n+\t\treturn error(_(\"unable to get index file\"));\n+\n+\tformat_path(buf, index_file, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *obj_dir = repo_get_object_directory(repo);\n@@ -238,6 +260,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n \t{ \"path.hooks.relative\", get_path_hooks_relative },\n+\t{ \"path.index.absolute\", get_path_index_absolute },\n+\t{ \"path.index.relative\", get_path_index_relative },\n \t{ \"path.objects.absolute\", get_path_objects_absolute },\n \t{ \"path.objects.relative\", get_path_objects_relative },\n \t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex cd3f856d04..04e6b8553c 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -219,6 +219,12 @@ test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n \t'custom-hooks' \\\n \t'git config core.hooksPath \"$ROOT/custom-hooks\" && mkdir -p \"$ROOT/custom-hooks\"'\n \n+test_repo_info_path 'index standard' 'index' '.git/index'\n+\n+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \\\n+\t'custom-index-file' \\\n+\t'GIT_INDEX_FILE=\"$ROOT/custom-index-file\" && export GIT_INDEX_FILE'\n+\n test_repo_info_path 'objects standard' 'objects' '.git/objects'\n \n test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \\\n-- \n2.55.GIT\n\n"},{"id":"548505","messageId":"20260717133015.32040-7-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260717133015.32040-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v2 6/7] repo: add path.grafts with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-17T13:30:14Z","receivedAt":"2026-07-17T13:35:22Z","isPatch":true,"body":"External toolchains managing specialized history rewrites or legacy\nhistory splices require access to the location of the repository grafts\nfile. Currently, this requires a legacy call to `git rev-parse --git-path info/grafts`.\n\nIntroduce `path.grafts.absolute` and `path.grafts.relative` keys to\n`git repo info`. This allows scripting layers to query the active grafts\ncontext cleanly while scaling transparently with active `GIT_GRAFT_FILE`\nenvironment variable overrides.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  8 ++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        |  6 ++++++\n 3 files changed, 38 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 3a837c573e..6c962620ec 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,14 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.grafts.absolute`::\n+\tThe canonical absolute path to the repository grafts file.\n+\tRespects the `GIT_GRAFT_FILE` environment override.\n+\n+`path.grafts.relative`::\n+\tThe path to the repository grafts file relative to the current working\n+\tdirectory. Respects the `GIT_GRAFT_FILE` environment override.\n+\n `path.hooks.absolute`::\n \tThe canonical absolute path to the repository's hooks directory.\n \tRespects `core.hooksPath` configuration adjustments.\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 66bf4c67cc..a97ad71649 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_grafts_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *graft_file = repo_get_graft_file(repo);\n+\n+\tif (!graft_file)\n+\t\treturn error(_(\"unable to get graft file\"));\n+\n+\tformat_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_grafts_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *graft_file = repo_get_graft_file(repo);\n+\n+\tif (!graft_file)\n+\t\treturn error(_(\"unable to get graft file\"));\n+\n+\tformat_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tstruct strbuf hooks_path = STRBUF_INIT;\n@@ -258,6 +280,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.grafts.absolute\", get_path_grafts_absolute },\n+\t{ \"path.grafts.relative\", get_path_grafts_relative },\n \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n \t{ \"path.hooks.relative\", get_path_hooks_relative },\n \t{ \"path.index.absolute\", get_path_index_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 04e6b8553c..6c47989df7 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'grafts standard' 'grafts' '.git/info/grafts'\n+\n+test_repo_info_path 'grafts with GIT_GRAFT_FILE override' 'grafts' \\\n+\t'custom-graft-file' \\\n+\t'GIT_GRAFT_FILE=\"$ROOT/custom-graft-file\" && export GIT_GRAFT_FILE'\n+\n test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'\n \n test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n-- \n2.55.GIT\n\n"},{"id":"548506","messageId":"20260717133015.32040-8-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260717133015.32040-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v2 7/7] repo: add path.git-prefix path key","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-17T13:30:15Z","receivedAt":"2026-07-17T13:35:39Z","isPatch":true,"body":"Scripts and command-line prompt integrations frequently need to know their\nrelative depth inside a repository working tree layout. Currently, this\nis retrieved using `git rev-parse --show-prefix`.\n\nIntroduce the `path.git-prefix` key to `git repo info`. This mirrors the\nprefix location tracking framework as a standalone key, returning the\nexact relative path offset complete with a trailing slash, or an empty\nstring if run directly at the repository working tree root.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  5 +++++\n builtin/repo.c              | 11 +++++++++++\n t/t1900-repo-info.sh        | 19 +++++++++++++++++++\n 3 files changed, 35 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 6c962620ec..5ba2ab1612 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -113,6 +113,11 @@ values that they return:\n \tThe path to the Git repository's common directory relative to\n \tthe current working directory.\n \n+`path.git-prefix`::\n+\tThe relative path from the top-level directory of the working tree to\n+\tthe current working directory (including a trailing slash). Outputs an\n+\tempty string if executed at the root of the working tree.\n+\n `path.gitdir.absolute`::\n \tThe canonical absolute path to the Git repository directory (the `.git` directory).\n \ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex a97ad71649..b93c140c74 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -100,6 +100,16 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b\n \treturn 0;\n }\n \n+static int get_path_git_prefix(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\t/*\n+\t * startup_info->prefix is NULL if we are at the working tree root.\n+\t * We add an empty string to ensure the buffer is cleanly initialized.\n+\t */\n+\tstrbuf_addstr(buf, startup_info->prefix ? startup_info->prefix : \"\");\n+\treturn 0;\n+}\n+\n static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *git_dir = repo_get_git_dir(repo);\n@@ -278,6 +288,7 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"object.format\", get_object_format },\n \t{ \"path.commondir.absolute\", get_path_commondir_absolute },\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n+\t{ \"path.git-prefix\", get_path_git_prefix },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n \t{ \"path.grafts.absolute\", get_path_grafts_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 6c47989df7..3e5e42f6d3 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -207,6 +207,25 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.git-prefix at root and in a subdirectory' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\n+\t\techo \"path.git-prefix=\" >expect.root &&\n+\t\tgit repo info path.git-prefix >actual.root &&\n+\t\ttest_cmp expect.root actual.root &&\n+\n+\t\tmkdir -p sub/dir &&\n+\t\tcd sub/dir &&\n+\n+\t\techo \"path.git-prefix=sub/dir/\" >expect.sub &&\n+\t\tgit repo info path.git-prefix >actual.sub &&\n+\t\ttest_cmp expect.sub actual.sub\n+\t)\n+'\n+\n test_repo_info_path 'gitdir standard' 'gitdir' '.git'\n \n test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n-- \n2.55.GIT\n\n"},{"id":"548641","messageId":"DB49CF15-4980-4213-8463-4C0FE2EC8438@gmail.com","threadId":"66010","inReplyTo":"20260717133015.32040-7-jayatheerthkulkarni2005@gmail.com","subject":"Re: [GSoC Patch v2 6/7] repo: add path.grafts with absolute and relative suffix formatting","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2026-07-20T00:20:22Z","receivedAt":"2026-07-20T00:20:37Z","isPatch":true,"body":"\n> Introduce `path.grafts.absolute` and `path.grafts.relative` keys to\n> `git repo info`. This allows scripting layers to query the active grafts\n> context cleanly while scaling transparently with active `GIT_GRAFT_FILE`\n> environment variable overrides.\n\nI ran `git repo info path.grafts.relative` in a repository with no\n`grafts` file, and it returned `.git/info/grafts`, which obviously\ndoesn't exist.\n\nWouldn't it be better if we check if that file exists before\nreturning this value?\n"},{"id":"548642","messageId":"845D6852-98F5-4168-82CD-90B3B476BCF5@gmail.com","threadId":"66010","inReplyTo":"20260717133015.32040-6-jayatheerthkulkarni2005@gmail.com","subject":"Re: [GSoC Patch v2 5/7] repo: add path.index with absolute and relative suffix formatting","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2026-07-20T00:35:40Z","receivedAt":"2026-07-20T00:35:55Z","isPatch":true,"body":"\n> Introduce `path.index.absolute` and `path.index.relative` keys to\n> `git repo info`. This allows tooling utilities to discover the active\n> index context cleanly while scaling transparently with localized\n> `GIT_INDEX_FILE` environment overrides.\n\nNote that bare repositories doesn't need to have an index file. Maybe\nit would be better if you return an empty value in those cases.\n"},{"id":"548645","messageId":"xmqq33xejomv.fsf@gitster.g","threadId":"66010","inReplyTo":"DB49CF15-4980-4213-8463-4C0FE2EC8438@gmail.com","subject":"Re: [GSoC Patch v2 6/7] repo: add path.grafts with absolute and relative suffix formatting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-07-20T04:01:12Z","receivedAt":"2026-07-20T04:01:15Z","isPatch":true,"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n>> Introduce `path.grafts.absolute` and `path.grafts.relative` keys to\n>> `git repo info`. This allows scripting layers to query the active grafts\n>> context cleanly while scaling transparently with active `GIT_GRAFT_FILE`\n>> environment variable overrides.\n>\n> I ran `git repo info path.grafts.relative` in a repository with no\n> `grafts` file, and it returned `.git/info/grafts`, which obviously\n> doesn't exist.\n>\n> Wouldn't it be better if we check if that file exists before\n> returning this value?\n\nThat is an interesting question, but I think it depends on who is\nquerying and for what purpose.\n\nIf a script is asking where to write the file, then the author wants\nto know where the file is supposed to be, even if no such file\nexists yet.  Since the file format is public, they are free to write\ntheir own tools to manipulate it.\n\nThanks.\n"},{"id":"548710","messageId":"CA+rGoLcr7KTu5S3GN=VsV8dFF0ww0cew+4TvXtiqjGCXtVpMsQ@mail.gmail.com","threadId":"66010","inReplyTo":"xmqq33xejomv.fsf@gitster.g","subject":"Re: [GSoC Patch v2 6/7] repo: add path.grafts with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-21T02:19:18Z","receivedAt":"2026-07-21T02:19:32Z","isPatch":true,"body":"Hey Lucas and Junio,\n\nOn Mon, Jul 20, 2026 at 9:31 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n>\n> >> Introduce `path.grafts.absolute` and `path.grafts.relative` keys to\n> >> `git repo info`. This allows scripting layers to query the active grafts\n> >> context cleanly while scaling transparently with active `GIT_GRAFT_FILE`\n> >> environment variable overrides.\n> >\n> > I ran `git repo info path.grafts.relative` in a repository with no\n> > `grafts` file, and it returned `.git/info/grafts`, which obviously\n> > doesn't exist.\n> >\n> > Wouldn't it be better if we check if that file exists before\n> > returning this value?\n>\n> That is an interesting question, but I think it depends on who is\n> querying and for what purpose.\n>\n> If a script is asking where to write the file, then the author wants\n> to know where the file is supposed to be, even if no such file\n> exists yet.  Since the file format is public, they are free to write\n> their own tools to manipulate it.\n>\n> Thanks.\n\nThat's tough.\nI think I align with Junio here.\n\nI just used rev-parse as my compass to work on this command.\nJust to clarify\nShould I send a v3 changing something?\n\nRegards,\n- K Jayatheerth\n"},{"id":"548903","messageId":"CA+rGoLcgHYk1BgWyNmJT6SGmo6xb_Q4O=LT46FXaC=+tQ_U-qg@mail.gmail.com","threadId":"66010","inReplyTo":"845D6852-98F5-4168-82CD-90B3B476BCF5@gmail.com","subject":"Re: [GSoC Patch v2 5/7] repo: add path.index with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-24T17:49:16Z","receivedAt":"2026-07-24T17:49:31Z","isPatch":true,"body":"Hey Lucas and Junio,\n\nOn Mon, Jul 20, 2026 at 6:05 AM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n>\n>\n> > Introduce `path.index.absolute` and `path.index.relative` keys to\n> > `git repo info`. This allows tooling utilities to discover the active\n> > index context cleanly while scaling transparently with localized\n> > `GIT_INDEX_FILE` environment overrides.\n>\n> Note that bare repositories doesn't need to have an index file. Maybe\n> it would be better if you return an empty value in those cases.\n\nI wanted to follow up on this series.\n\nI am a bit unsure about how to proceed with this specific case,\nespecially after Junio's feedback on the grafts patch.\n\nFor path.grafts, Junio mentioned that we should return the expected\npath even if the file doesn't exist yet,\nso scripts know where to write it. I am wondering if a similar logic\nshould apply to path.index in bare repositories.\n\nWhile bare repos don't have a working tree (and therefore usually no index),\nscripts do sometimes set GIT_INDEX_FILE to build temporary indexes for\ntree manipulation.\n\nShould we strictly return an empty string for bare repos (similar to\nhow we handle path.toplevel),\nor should we return the default <gitdir>/index path in case a script\nwants to know where it would be?\n\nIf this confusion is clear I can send a new version!\n\nThank you,\nRegards\n\n- K Jayatheerth\n"},{"id":"548907","messageId":"xmqqv7a45h3v.fsf@gitster.g","threadId":"66010","inReplyTo":"CA+rGoLcgHYk1BgWyNmJT6SGmo6xb_Q4O=LT46FXaC=+tQ_U-qg@mail.gmail.com","subject":"Re: [GSoC Patch v2 5/7] repo: add path.index with absolute and relative suffix formatting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-07-24T19:21:08Z","receivedAt":"2026-07-24T19:21:11Z","isPatch":true,"body":"K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n\n> While bare repos don't have a working tree (and therefore usually no index),\n> scripts do sometimes set GIT_INDEX_FILE to build temporary indexes for\n> tree manipulation.\n>\n> Should we strictly return an empty string for bare repos (similar to\n> how we handle path.toplevel),\n> or should we return the default <gitdir>/index path in case a script\n> wants to know where it would be?\n\nThe path to the top level can be an empty string if you are already\nat the top level, which is neither an error nor an unusual\ncondition.  I do not understand the contrast you are drawing here.\n\nI expect \"tell me where the index is\" to return <gitdir>/index,\n$GIT_INDEX_FILE, or whatever Git sets during setup.  In other words,\nwhatever repo's '.index_file' member holds when control reaches\nrepo_read_index().\n\nIf a script checks that location and finds no file, that is the\nnatural way to learn there is no index.\n"},{"id":"549003","messageId":"20260726104343.16933-1-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260716012138.6714-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v3 0/7] repo: add more path keys to git repo info","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-26T10:43:36Z","receivedAt":"2026-07-26T10:44:34Z","isPatch":true,"body":"Series adds keys to git repo info.\nKeys output paths of repository components:\n* path.toplevel: repository tree.\n* path.superproject-working-tree: superproject tree from submodules.\n* path.objects: repository objects.\n* path.hooks: repository hooks.\n* path.index: repository index.\n* path.grafts: repository grafts.\n* path.git-prefix: prefix offset.\n\nKeys support suffixes for format.\nCommits contain documentation and tests.\n\nChanges since v2:\n* path.index: no functional change; documented that the returned path\n  reflects the default/configured index location even in a bare\n  repository or when the file doesn't exist yet, and added a test\n  covering path.index inside a bare repository.\n* path.grafts: no functional change; documented that the path is\n  returned even if the grafts file doesn't currently exist on disk.\n* path.git-prefix: no change since v2 (already addressed the stray\n  include and misleading \"validation\" in the subject).\n\nK Jayatheerth (7):\n  repo: add path.toplevel with absolute and relative suffix formatting\n  repo: add path.superproject-working-tree with absolute and relative\n    suffixes\n  repo: add path.objects with absolute and relative suffix formatting\n  repo: add path.hooks with absolute and relative suffix formatting\n  repo: add path.index with absolute and relative suffix formatting\n  repo: add path.grafts with absolute and relative suffix formatting\n  repo: add path.git-prefix path key\n\n Documentation/git-repo.adoc |  64 ++++++++++++++\n builtin/repo.c              | 166 ++++++++++++++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 125 +++++++++++++++++++++++++++\n 3 files changed, 355 insertions(+)\n\nRange-diff against v2:\n1:  297d625ea1 = 1:  297d625ea1 repo: add path.toplevel with absolute and relative suffix formatting\n2:  8a84aaa2a1 = 2:  8a84aaa2a1 repo: add path.superproject-working-tree with absolute and relative suffixes\n3:  159166ce85 = 3:  159166ce85 repo: add path.objects with absolute and relative suffix formatting\n4:  ff5e382043 = 4:  ff5e382043 repo: add path.hooks with absolute and relative suffix formatting\n5:  d906735e3d ! 5:  917d3a5076 repo: add path.index with absolute and relative suffix formatting\n    @@ Documentation/git-repo.adoc: values that they return:\n      \n     +`path.index.absolute`::\n     +\tThe canonical absolute path to the repository's current index file.\n    -+\tRespects the `GIT_INDEX_FILE` environment override.\n    ++\tRespects the `GIT_INDEX_FILE` environment override. The returned path\n    ++\treflects the configured/default index location regardless of whether the\n    ++\trepository is bare or whether the file currently exists.\n     +\n     +`path.index.relative`::\n     +\tThe path to the repository's current index file relative to the current\n     +\tworking directory. Respects the `GIT_INDEX_FILE` environment override.\n    ++\tThe returned path reflects the configured/default index location regardless\n    ++\tof whether the repository is bare or whether the file currently exists.\n     +\n      `path.objects.absolute`::\n      \tThe canonical absolute path to the repository's object database directory.\n    @@ t/t1900-repo-info.sh: test_repo_info_path 'hooks with core.hooksPath override' '\n     +test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \\\n     +\t'custom-index-file' \\\n     +\t'GIT_INDEX_FILE=\"$ROOT/custom-index-file\" && export GIT_INDEX_FILE'\n    ++\n    ++test_expect_success 'path.index in a bare repository returns default index location' '\n    ++\ttest_when_finished \"rm -rf bare.git\" &&\n    ++\tgit init --bare bare.git &&\n    ++\t(\n    ++\t\tcd bare.git &&\n    ++\t\tROOT=\"$(test-tool path-utils real_path .)\" &&\n    ++\n    ++\t\techo \"path.index.absolute=$ROOT/index\" >expect.abs &&\n    ++\t\tgit repo info path.index.absolute >actual.abs &&\n    ++\t\ttest_cmp expect.abs actual.abs &&\n    ++\n    ++\t\techo \"path.index.relative=index\" >expect.rel &&\n    ++\t\tgit repo info path.index.relative >actual.rel &&\n    ++\t\ttest_cmp expect.rel actual.rel\n    ++\t)\n    ++'\n     +\n      test_repo_info_path 'objects standard' 'objects' '.git/objects'\n      \n6:  2d21f9536c ! 6:  d47a5701d5 repo: add path.grafts with absolute and relative suffix formatting\n    @@ Documentation/git-repo.adoc: values that they return:\n      \n     +`path.grafts.absolute`::\n     +\tThe canonical absolute path to the repository grafts file.\n    -+\tRespects the `GIT_GRAFT_FILE` environment override.\n    ++\tRespects the `GIT_GRAFT_FILE` environment override. The path is returned\n    ++\tregardless of whether the file currently exists on disk.\n     +\n     +`path.grafts.relative`::\n     +\tThe path to the repository grafts file relative to the current working\n    -+\tdirectory. Respects the `GIT_GRAFT_FILE` environment override.\n    ++\tdirectory. Respects the `GIT_GRAFT_FILE` environment override. The path\n    ++\tis returned regardless of whether the file currently exists on disk.\n     +\n      `path.hooks.absolute`::\n      \tThe canonical absolute path to the repository's hooks directory.\n7:  1dbe191176 = 7:  fd90b0b3b5 repo: add path.git-prefix path key\n-- \n2.55.GIT\n\n"},{"id":"549004","messageId":"20260726104343.16933-2-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260726104343.16933-1-jayatheerthkulkarni2005@gmail.com","subject":"[PATCH v3 1/7] repo: add path.toplevel with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-26T10:43:37Z","receivedAt":"2026-07-26T10:45:56Z","isPatch":true,"body":"Scripts frequently need to find the root directory of a repository's\nworking tree. Currently, this requires using `git rev-parse --show-toplevel`\nor inferring it from other path components.\n\nIntroduce `path.toplevel.absolute` and `path.toplevel.relative` keys\nto `git repo info`. This allows scripts to retrieve the top-level\nworking tree path in a predictable, strictly formatted manner without\nrelying on `rev-parse`.\n\nIf requested in a bare repository where no working tree exists, the\ncommand returns an empty string.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 10 ++++++++++\n builtin/repo.c              | 28 ++++++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 30 ++++++++++++++++++++++++++++++\n 3 files changed, 68 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex ed7d80c690..e34abe5fea 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,16 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.toplevel.absolute`::\n+\tThe canonical absolute path to the top-level directory of the\n+\trepository's working tree. Outputs an empty string if the repository\n+\tis bare.\n+\n+`path.toplevel.relative`::\n+\tThe path to the top-level directory of the repository's working\n+\ttree relative to the current working directory. Outputs an empty\n+\tstring if the repository is bare.\n+\n `references.format`::\n \tThe reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 042d6de558..194757eb18 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -121,6 +121,32 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *work_tree = repo_get_work_tree(repo);\n+\n+\tif (!work_tree) {\n+\t\tstrbuf_addstr(buf, \"\");\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *work_tree = repo_get_work_tree(repo);\n+\n+\tif (!work_tree) {\n+\t\tstrbuf_addstr(buf, \"\");\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -137,6 +163,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n+\t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex ae8c22c817..fbb9063ee5 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,4 +213,34 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.toplevel absolute and relative' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tmkdir -p repo/sub &&\n+\t\tcd repo/sub &&\n+\n+\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n+\n+\t\techo \"path.toplevel.absolute=$ROOT\" >expect.abs &&\n+\t\tgit repo info path.toplevel.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.toplevel.relative=../\" >expect.rel &&\n+\t\tgit repo info path.toplevel.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n+test_expect_success 'path.toplevel returns empty in a bare repository' '\n+\ttest_when_finished \"rm -rf bare.git\" &&\n+\tgit init --bare bare.git &&\n+\t(\n+\t\tcd bare.git &&\n+\t\techo \"path.toplevel.absolute=\" >expect &&\n+\t\tgit repo info path.toplevel.absolute >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\n-- \n2.55.GIT\n\n"},{"id":"549005","messageId":"20260726104343.16933-3-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260726104343.16933-1-jayatheerthkulkarni2005@gmail.com","subject":"[PATCH v3 2/7] repo: add path.superproject-working-tree with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-26T10:43:38Z","receivedAt":"2026-07-26T10:46:36Z","isPatch":true,"body":"Scripts working in multi-repository setups often need to identify the\ntop-level working tree of a superproject from within a submodule.\nCurrently, this is only exposed via `git rev-parse\n--show-superproject-working-tree`.\n\nIntroduce `path.superproject-working-tree.absolute` and\n`path.superproject-working-tree.relative` keys to `git repo info`.\nThis exposes the core submodule context via a scriptable config-like key\nusing standard format rules.\n\nIf requested when not inside a submodule, the command returns an empty\nstring.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 10 ++++++++++\n builtin/repo.c              | 33 +++++++++++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 34 ++++++++++++++++++++++++++++++++++\n 3 files changed, 77 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex e34abe5fea..03aa57942f 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,16 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.superproject-working-tree.absolute`::\n+\tThe canonical absolute path to the working tree root of the superproject\n+\tif the current repository is an initialized submodule. Outputs an empty\n+\tstring if not in a submodule.\n+\n+`path.superproject-working-tree.relative`::\n+\tThe path to the working tree root of the superproject relative to the\n+\tcurrent working directory if the current repository is an initialized\n+\tsubmodule. Outputs an empty string if not in a submodule.\n+\n `path.toplevel.absolute`::\n \tThe canonical absolute path to the top-level directory of the\n \trepository's working tree. Outputs an empty string if the repository\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 194757eb18..82359473e9 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -18,6 +18,7 @@\n #include \"strbuf.h\"\n #include \"string-list.h\"\n #include \"shallow.h\"\n+#include \"submodule.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"utf8.h\"\n@@ -121,6 +122,36 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\tstruct strbuf superproject = STRBUF_INIT;\n+\n+\tif (!get_superproject_working_tree(&superproject)) {\n+\t\tstrbuf_release(&superproject);\n+\t\tstrbuf_addstr(buf, \"\");\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\tstrbuf_release(&superproject);\n+\treturn 0;\n+}\n+\n+static int get_path_superproject_relative(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\tstruct strbuf superproject = STRBUF_INIT;\n+\n+\tif (!get_superproject_working_tree(&superproject)) {\n+\t\tstrbuf_release(&superproject);\n+\t\tstrbuf_addstr(buf, \"\");\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\tstrbuf_release(&superproject);\n+\treturn 0;\n+}\n+\n static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *work_tree = repo_get_work_tree(repo);\n@@ -163,6 +194,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\n+\t{ \"path.superproject-working-tree.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n \t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n \t{ \"references.format\", get_references_format },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex fbb9063ee5..220b3d4d3d 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,40 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.superproject-working-tree absolute and relative' '\n+\ttest_when_finished \"rm -rf sub super\" &&\n+\tgit init sub &&\n+\ttest_commit -C sub initial &&\n+\tgit init super &&\n+\t(\n+\t\tcd super &&\n+\t\tgit -c protocol.file.allow=always submodule add \"../sub\" sub &&\n+\t\tgit commit -m \"add submodule\" &&\n+\n+\t\tcd sub &&\n+\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n+\n+\t\techo \"path.superproject-working-tree.absolute=$ROOT\" >expect.abs &&\n+\t\tgit repo info path.superproject-working-tree.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.superproject-working-tree.relative=../\" >expect.rel &&\n+\t\tgit repo info path.superproject-working-tree.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n+test_expect_success 'path.superproject-working-tree returns empty when not in a submodule' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\techo \"path.superproject-working-tree.absolute=\" >expect &&\n+\t\tgit repo info path.superproject-working-tree.absolute >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_expect_success 'path.toplevel absolute and relative' '\n \ttest_when_finished \"rm -rf repo\" &&\n \tgit init repo &&\n-- \n2.55.GIT\n\n"},{"id":"549006","messageId":"20260726104343.16933-4-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260726104343.16933-1-jayatheerthkulkarni2005@gmail.com","subject":"[PATCH v3 3/7] repo: add path.objects with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-26T10:43:39Z","receivedAt":"2026-07-26T10:47:00Z","isPatch":true,"body":"Tools and deployment hooks frequently query the location of the object\ndatabase directory. Currently, this relies on legacy parsing methods or\nmanually inspecting `git rev-parse --git-path objects`.\n\nIntroduce `path.objects.absolute` and `path.objects.relative` keys to\n`git repo info`. This allows tools to discover the object database\nlocation safely while natively adhering to active `GIT_OBJECT_DIRECTORY`\nenvironment variable overrides.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  9 +++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        |  7 +++++++\n 3 files changed, 40 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 03aa57942f..8429a44b43 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,15 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.objects.absolute`::\n+\tThe canonical absolute path to the repository's object database directory.\n+\tRespects the `GIT_OBJECT_DIRECTORY` environment override.\n+\n+`path.objects.relative`::\n+\tThe path to the repository's object database directory relative to the\n+\tcurrent working directory. Respects the `GIT_OBJECT_DIRECTORY`\n+\tenvironment override.\n+\n `path.superproject-working-tree.absolute`::\n \tThe canonical absolute path to the working tree root of the superproject\n \tif the current repository is an initialized submodule. Outputs an empty\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 82359473e9..d6bdd5bcfa 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *obj_dir = repo_get_object_directory(repo);\n+\n+\tif (!obj_dir)\n+\t\treturn error(_(\"unable to get object directory\"));\n+\n+\tformat_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_objects_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *obj_dir = repo_get_object_directory(repo);\n+\n+\tif (!obj_dir)\n+\t\treturn error(_(\"unable to get object directory\"));\n+\n+\tformat_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n {\n \tstruct strbuf superproject = STRBUF_INIT;\n@@ -194,6 +216,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.objects.absolute\", get_path_objects_absolute },\n+\t{ \"path.objects.relative\", get_path_objects_relative },\n \t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\n \t{ \"path.superproject-working-tree.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 220b3d4d3d..260f4fde43 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,13 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'objects standard' 'objects' '.git/objects'\n+\n+test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \\\n+\t'custom-objects' \\\n+\t'GIT_OBJECT_DIRECTORY=\"$ROOT/custom-objects\" && export GIT_OBJECT_DIRECTORY &&\n+\t mkdir -p \"$ROOT/custom-objects\"'\n+\n test_expect_success 'path.superproject-working-tree absolute and relative' '\n \ttest_when_finished \"rm -rf sub super\" &&\n \tgit init sub &&\n-- \n2.55.GIT\n\n"},{"id":"549007","messageId":"20260726104343.16933-5-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260726104343.16933-1-jayatheerthkulkarni2005@gmail.com","subject":"[PATCH v3 4/7] repo: add path.hooks with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-26T10:43:40Z","receivedAt":"2026-07-26T10:47:38Z","isPatch":true,"body":"External tool integrations and validation systems need a stable way to\nidentify where the repository hooks are stored. Currently, this involves\nrelying on `git rev-parse --git-path hooks` or querying `core.hooksPath`\nmanually.\n\nIntroduce `path.hooks.absolute` and `path.hooks.relative` keys to\n`git repo info`. This allows tools to discover the active hooks location\nnatively, ensuring proper resolution regardless of whether Git is using\nthe standard `.git/hooks` structure or a custom `core.hooksPath` setup.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  8 ++++++++\n builtin/repo.c              | 22 ++++++++++++++++++++++\n t/t1900-repo-info.sh        |  6 ++++++\n 3 files changed, 36 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 8429a44b43..7bc1c51310 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,14 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.hooks.absolute`::\n+\tThe canonical absolute path to the repository's hooks directory.\n+\tRespects `core.hooksPath` configuration adjustments.\n+\n+`path.hooks.relative`::\n+\tThe path to the repository's hooks directory relative to the current\n+\tworking directory. Respects `core.hooksPath` configuration adjustments.\n+\n `path.objects.absolute`::\n \tThe canonical absolute path to the repository's object database directory.\n \tRespects the `GIT_OBJECT_DIRECTORY` environment override.\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex d6bdd5bcfa..c921de222d 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,26 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf hooks_path = STRBUF_INIT;\n+\n+\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n+\tformat_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\tstrbuf_release(&hooks_path);\n+\treturn 0;\n+}\n+\n+static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf hooks_path = STRBUF_INIT;\n+\n+\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n+\tformat_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\tstrbuf_release(&hooks_path);\n+\treturn 0;\n+}\n+\n static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *obj_dir = repo_get_object_directory(repo);\n@@ -216,6 +236,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n+\t{ \"path.hooks.relative\", get_path_hooks_relative },\n \t{ \"path.objects.absolute\", get_path_objects_absolute },\n \t{ \"path.objects.relative\", get_path_objects_relative },\n \t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 260f4fde43..cd3f856d04 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'\n+\n+test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n+\t'custom-hooks' \\\n+\t'git config core.hooksPath \"$ROOT/custom-hooks\" && mkdir -p \"$ROOT/custom-hooks\"'\n+\n test_repo_info_path 'objects standard' 'objects' '.git/objects'\n \n test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \\\n-- \n2.55.GIT\n\n"},{"id":"549008","messageId":"20260726104343.16933-6-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260726104343.16933-1-jayatheerthkulkarni2005@gmail.com","subject":"[PATCH v3 5/7] repo: add path.index with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-26T10:43:41Z","receivedAt":"2026-07-26T10:48:09Z","isPatch":true,"body":"External script workflows and formatting layers require straightforward\naccess to the location of the index staging file. Currently, tracking\nthis necessitates a legacy call to `git rev-parse --git-path index` or\n`--show-toplevel` logic abstractions.\n\nIntroduce `path.index.absolute` and `path.index.relative` keys to\n`git repo info`. This allows tooling utilities to discover the active\nindex context cleanly while scaling transparently with localized\n`GIT_INDEX_FILE` environment overrides.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 12 ++++++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++\n 3 files changed, 59 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 7bc1c51310..34c4f7d61c 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -127,6 +127,18 @@ values that they return:\n \tThe path to the repository's hooks directory relative to the current\n \tworking directory. Respects `core.hooksPath` configuration adjustments.\n \n+`path.index.absolute`::\n+\tThe canonical absolute path to the repository's current index file.\n+\tRespects the `GIT_INDEX_FILE` environment override. The returned path\n+\treflects the configured/default index location regardless of whether the\n+\trepository is bare or whether the file currently exists.\n+\n+`path.index.relative`::\n+\tThe path to the repository's current index file relative to the current\n+\tworking directory. Respects the `GIT_INDEX_FILE` environment override.\n+\tThe returned path reflects the configured/default index location regardless\n+\tof whether the repository is bare or whether the file currently exists.\n+\n `path.objects.absolute`::\n \tThe canonical absolute path to the repository's object database directory.\n \tRespects the `GIT_OBJECT_DIRECTORY` environment override.\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex c921de222d..66bf4c67cc 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -142,6 +142,28 @@ static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *index_file = repo_get_index_file(repo);\n+\n+\tif (!index_file)\n+\t\treturn error(_(\"unable to get index file\"));\n+\n+\tformat_path(buf, index_file, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *index_file = repo_get_index_file(repo);\n+\n+\tif (!index_file)\n+\t\treturn error(_(\"unable to get index file\"));\n+\n+\tformat_path(buf, index_file, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *obj_dir = repo_get_object_directory(repo);\n@@ -238,6 +260,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n \t{ \"path.hooks.relative\", get_path_hooks_relative },\n+\t{ \"path.index.absolute\", get_path_index_absolute },\n+\t{ \"path.index.relative\", get_path_index_relative },\n \t{ \"path.objects.absolute\", get_path_objects_absolute },\n \t{ \"path.objects.relative\", get_path_objects_relative },\n \t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex cd3f856d04..dee1db2a49 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -219,6 +219,29 @@ test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n \t'custom-hooks' \\\n \t'git config core.hooksPath \"$ROOT/custom-hooks\" && mkdir -p \"$ROOT/custom-hooks\"'\n \n+test_repo_info_path 'index standard' 'index' '.git/index'\n+\n+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \\\n+\t'custom-index-file' \\\n+\t'GIT_INDEX_FILE=\"$ROOT/custom-index-file\" && export GIT_INDEX_FILE'\n+\n+test_expect_success 'path.index in a bare repository returns default index location' '\n+\ttest_when_finished \"rm -rf bare.git\" &&\n+\tgit init --bare bare.git &&\n+\t(\n+\t\tcd bare.git &&\n+\t\tROOT=\"$(test-tool path-utils real_path .)\" &&\n+\n+\t\techo \"path.index.absolute=$ROOT/index\" >expect.abs &&\n+\t\tgit repo info path.index.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.index.relative=index\" >expect.rel &&\n+\t\tgit repo info path.index.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n test_repo_info_path 'objects standard' 'objects' '.git/objects'\n \n test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \\\n-- \n2.55.GIT\n\n"},{"id":"549009","messageId":"20260726104343.16933-7-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260726104343.16933-1-jayatheerthkulkarni2005@gmail.com","subject":"[PATCH v3 6/7] repo: add path.grafts with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-26T10:43:42Z","receivedAt":"2026-07-26T10:48:28Z","isPatch":true,"body":"External toolchains managing specialized history rewrites or legacy\nhistory splices require access to the location of the repository grafts\nfile. Currently, this requires a legacy call to `git rev-parse --git-path info/grafts`.\n\nIntroduce `path.grafts.absolute` and `path.grafts.relative` keys to\n`git repo info`. This allows scripting layers to query the active grafts\ncontext cleanly while scaling transparently with active `GIT_GRAFT_FILE`\nenvironment variable overrides.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 10 ++++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        |  6 ++++++\n 3 files changed, 40 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 34c4f7d61c..def888da91 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,16 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.grafts.absolute`::\n+\tThe canonical absolute path to the repository grafts file.\n+\tRespects the `GIT_GRAFT_FILE` environment override. The path is returned\n+\tregardless of whether the file currently exists on disk.\n+\n+`path.grafts.relative`::\n+\tThe path to the repository grafts file relative to the current working\n+\tdirectory. Respects the `GIT_GRAFT_FILE` environment override. The path\n+\tis returned regardless of whether the file currently exists on disk.\n+\n `path.hooks.absolute`::\n \tThe canonical absolute path to the repository's hooks directory.\n \tRespects `core.hooksPath` configuration adjustments.\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 66bf4c67cc..a97ad71649 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_grafts_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *graft_file = repo_get_graft_file(repo);\n+\n+\tif (!graft_file)\n+\t\treturn error(_(\"unable to get graft file\"));\n+\n+\tformat_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_grafts_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *graft_file = repo_get_graft_file(repo);\n+\n+\tif (!graft_file)\n+\t\treturn error(_(\"unable to get graft file\"));\n+\n+\tformat_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tstruct strbuf hooks_path = STRBUF_INIT;\n@@ -258,6 +280,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.grafts.absolute\", get_path_grafts_absolute },\n+\t{ \"path.grafts.relative\", get_path_grafts_relative },\n \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n \t{ \"path.hooks.relative\", get_path_hooks_relative },\n \t{ \"path.index.absolute\", get_path_index_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex dee1db2a49..15a4ec9b78 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'grafts standard' 'grafts' '.git/info/grafts'\n+\n+test_repo_info_path 'grafts with GIT_GRAFT_FILE override' 'grafts' \\\n+\t'custom-graft-file' \\\n+\t'GIT_GRAFT_FILE=\"$ROOT/custom-graft-file\" && export GIT_GRAFT_FILE'\n+\n test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'\n \n test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n-- \n2.55.GIT\n\n"},{"id":"549010","messageId":"20260726104343.16933-8-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260726104343.16933-1-jayatheerthkulkarni2005@gmail.com","subject":"[PATCH v3 7/7] repo: add path.git-prefix path key","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-26T10:43:43Z","receivedAt":"2026-07-26T10:49:15Z","isPatch":true,"body":"Scripts and command-line prompt integrations frequently need to know their\nrelative depth inside a repository working tree layout. Currently, this\nis retrieved using `git rev-parse --show-prefix`.\n\nIntroduce the `path.git-prefix` key to `git repo info`. This mirrors the\nprefix location tracking framework as a standalone key, returning the\nexact relative path offset complete with a trailing slash, or an empty\nstring if run directly at the repository working tree root.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  5 +++++\n builtin/repo.c              | 11 +++++++++++\n t/t1900-repo-info.sh        | 19 +++++++++++++++++++\n 3 files changed, 35 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex def888da91..98282ea668 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -113,6 +113,11 @@ values that they return:\n \tThe path to the Git repository's common directory relative to\n \tthe current working directory.\n \n+`path.git-prefix`::\n+\tThe relative path from the top-level directory of the working tree to\n+\tthe current working directory (including a trailing slash). Outputs an\n+\tempty string if executed at the root of the working tree.\n+\n `path.gitdir.absolute`::\n \tThe canonical absolute path to the Git repository directory (the `.git` directory).\n \ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex a97ad71649..b93c140c74 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -100,6 +100,16 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b\n \treturn 0;\n }\n \n+static int get_path_git_prefix(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\t/*\n+\t * startup_info->prefix is NULL if we are at the working tree root.\n+\t * We add an empty string to ensure the buffer is cleanly initialized.\n+\t */\n+\tstrbuf_addstr(buf, startup_info->prefix ? startup_info->prefix : \"\");\n+\treturn 0;\n+}\n+\n static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *git_dir = repo_get_git_dir(repo);\n@@ -278,6 +288,7 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"object.format\", get_object_format },\n \t{ \"path.commondir.absolute\", get_path_commondir_absolute },\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n+\t{ \"path.git-prefix\", get_path_git_prefix },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n \t{ \"path.grafts.absolute\", get_path_grafts_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 15a4ec9b78..2879e54668 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -207,6 +207,25 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.git-prefix at root and in a subdirectory' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\n+\t\techo \"path.git-prefix=\" >expect.root &&\n+\t\tgit repo info path.git-prefix >actual.root &&\n+\t\ttest_cmp expect.root actual.root &&\n+\n+\t\tmkdir -p sub/dir &&\n+\t\tcd sub/dir &&\n+\n+\t\techo \"path.git-prefix=sub/dir/\" >expect.sub &&\n+\t\tgit repo info path.git-prefix >actual.sub &&\n+\t\ttest_cmp expect.sub actual.sub\n+\t)\n+'\n+\n test_repo_info_path 'gitdir standard' 'gitdir' '.git'\n \n test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n-- \n2.55.GIT\n\n"},{"id":"549025","messageId":"xmqqse55vhnz.fsf@gitster.g","threadId":"66010","inReplyTo":"20260726104343.16933-1-jayatheerthkulkarni2005@gmail.com","subject":"Re: [GSoC Patch v3 0/7] repo: add more path keys to git repo info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-07-26T16:29:04Z","receivedAt":"2026-07-26T16:29:07Z","isPatch":true,"body":"K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n\n> Series adds keys to git repo info.\n> Keys output paths of repository components:\n> * path.toplevel: repository tree.\n> * path.superproject-working-tree: superproject tree from submodules.\n> * path.objects: repository objects.\n> * path.hooks: repository hooks.\n> * path.index: repository index.\n> * path.grafts: repository grafts.\n> * path.git-prefix: prefix offset.\n\nI guess the issues I had at the design level in the previous\niteration is gone ;-)\n\nI'd love to hear real reviews from others on these patches, but\nat least I didn't spot anything glaringly wrong anymore.\n\nThanks.\n"},{"id":"549027","messageId":"CA+rGoLce-64v7EsQuDbtTd3gx4hsspMo58siHfONWbUBehhNng@mail.gmail.com","threadId":"66010","inReplyTo":"xmqqse55vhnz.fsf@gitster.g","subject":"Re: [GSoC Patch v3 0/7] repo: add more path keys to git repo info","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-26T17:00:43Z","receivedAt":"2026-07-26T17:00:58Z","isPatch":true,"body":"On Sun, Jul 26, 2026 at 9:59 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n>\n> > Series adds keys to git repo info.\n> > Keys output paths of repository components:\n> > * path.toplevel: repository tree.\n> > * path.superproject-working-tree: superproject tree from submodules.\n> > * path.objects: repository objects.\n> > * path.hooks: repository hooks.\n> > * path.index: repository index.\n> > * path.grafts: repository grafts.\n> > * path.git-prefix: prefix offset.\n>\n> I guess the issues I had at the design level in the previous\n> iteration is gone ;-)\n>\n> I'd love to hear real reviews from others on these patches, but\n> at least I didn't spot anything glaringly wrong anymore.\n>\n> Thanks.\n\nSure,\nuntil then I will work on the histogram patches in git repo structure.\n\nThanks for validating!\n\nRegards,\n- K Jayatheerth\n"},{"id":"549060","messageId":"8933F9C6-DF8A-400D-AE1A-CC79CEB26283@gmail.com","threadId":"66010","inReplyTo":"xmqqse55vhnz.fsf@gitster.g","subject":"Re: [GSoC Patch v3 0/7] repo: add more path keys to git repo info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2026-07-27T00:57:07Z","receivedAt":"2026-07-27T00:57:51Z","isPatch":true,"body":"\n> I'd love to hear real reviews from others on these patches, but\n> at least I didn't spot anything glaringly wrong anymore.\n\nIt looks good to me :-)\n\n"},{"id":"549066","messageId":"xmqqpl09ou1u.fsf@gitster.g","threadId":"66010","inReplyTo":"8933F9C6-DF8A-400D-AE1A-CC79CEB26283@gmail.com","subject":"Re: [GSoC Patch v3 0/7] repo: add more path keys to git repo info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-07-27T05:55:41Z","receivedAt":"2026-07-27T05:55:44Z","isPatch":true,"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n>> I'd love to hear real reviews from others on these patches, but\n>> at least I didn't spot anything glaringly wrong anymore.\n>\n> It looks good to me :-)\n\nCan you try to make your review sound a bit more credible to\nreaders?\n\nIt is much harder to give positive reviews than to merely point out\na few breakages in a patch, but there are still things reviewers can\ndo.  Thinking aloud to follow the author's line of thought is a\ntechnique I have found over the years to work well in demonstrating\nthat your Ack is backed by having actually read and understood the\npatch.  There may be other techniques experienced reviewers (by now\nyou know who they are after reading what they wrote on your patches\nand others') use when giving positive reviews.\n\nThanks.\n"},{"id":"549073","messageId":"xmqqh5lkq0qy.fsf@gitster.g","threadId":"66010","inReplyTo":"20260726104343.16933-2-jayatheerthkulkarni2005@gmail.com","subject":"Re: [PATCH v3 1/7] repo: add path.toplevel with absolute and relative suffix formatting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-07-27T08:45:41Z","receivedAt":"2026-07-27T08:45:44Z","isPatch":true,"body":"K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n\n> +static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n> +{\n> +\tconst char *work_tree = repo_get_work_tree(repo);\n> +\n> +\tif (!work_tree) {\n> +\t\tstrbuf_addstr(buf, \"\");\n> +\t\treturn 0;\n> +\t}\n> +\n> +\tformat_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_CANONICAL);\n> +\treturn 0;\n> +}\n\nI can manage to wiggle it in, of course, but I thought we lost the\n'.prefix' member from 'startup_info' in the recent tip of 'master'.\nAs the topic is not targeting 'maint' as a bugfix, perhaps we want\nto use a more up-to-date 'master' as the base of the topic?\n\nThanks.\n"},{"id":"549107","messageId":"CA+rGoLes7q3rGgWL++46M5n+5FSecadAN0+4GKEK2bFPTVx3+w@mail.gmail.com","threadId":"66010","inReplyTo":"xmqqh5lkq0qy.fsf@gitster.g","subject":"Re: [PATCH v3 1/7] repo: add path.toplevel with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-28T01:21:40Z","receivedAt":"2026-07-28T01:21:53Z","isPatch":true,"body":"On Mon, Jul 27, 2026 at 2:15 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n\n> > +     format_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_CANONICAL);\n> > +     return 0;\n> > +}\n>\n> I can manage to wiggle it in, of course, but I thought we lost the\n> '.prefix' member from 'startup_info' in the recent tip of 'master'.\n\nIndeed.\n\n> As the topic is not targeting 'maint' as a bugfix, perhaps we want\n> to use a more up-to-date 'master' as the base of the topic?\n>\n> Thanks.\n\nI see Patrick has changed the prefix member to the repo struct.\nDidn't notice that before!\n\nI will use the latest master branch for v4\n\nRegards,\n- K Jayatheerth\n"},{"id":"549151","messageId":"amjUPEgenletgbp5@denethor","threadId":"66010","inReplyTo":"20260726104343.16933-2-jayatheerthkulkarni2005@gmail.com","subject":"Re: [PATCH v3 1/7] repo: add path.toplevel with absolute and relative suffix formatting","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2026-07-28T16:36:04Z","receivedAt":"2026-07-28T16:36:12Z","isPatch":true,"body":"On 26/07/26 04:13PM, K Jayatheerth wrote:\n> Scripts frequently need to find the root directory of a repository's\n> working tree. Currently, this requires using `git rev-parse --show-toplevel`\n> or inferring it from other path components.\n> \n> Introduce `path.toplevel.absolute` and `path.toplevel.relative` keys\n> to `git repo info`. This allows scripts to retrieve the top-level\n> working tree path in a predictable, strictly formatted manner without\n> relying on `rev-parse`.\n\nOk, this seems like suitable information to also look up under\ngit-repo-info.\n\n> If requested in a bare repository where no working tree exists, the\n> command returns an empty string.\n\nThis matches the existing behavior in git-rev-parse(1). Makes sense.\n\n> Mentored-by: Justin Tobler <jltobler@gmail.com>\n> Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n> ---\n>  Documentation/git-repo.adoc | 10 ++++++++++\n>  builtin/repo.c              | 28 ++++++++++++++++++++++++++++\n>  t/t1900-repo-info.sh        | 30 ++++++++++++++++++++++++++++++\n>  3 files changed, 68 insertions(+)\n> \n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> index ed7d80c690..e34abe5fea 100644\n> --- a/Documentation/git-repo.adoc\n> +++ b/Documentation/git-repo.adoc\n> @@ -119,6 +119,16 @@ values that they return:\n>  `path.gitdir.relative`::\n>  \tThe path to the Git repository directory relative to the current working directory.\n>  \n> +`path.toplevel.absolute`::\n> +\tThe canonical absolute path to the top-level directory of the\n> +\trepository's working tree. Outputs an empty string if the repository\n> +\tis bare.\n> +\n> +`path.toplevel.relative`::\n> +\tThe path to the top-level directory of the repository's working\n> +\ttree relative to the current working directory. Outputs an empty\n> +\tstring if the repository is bare.\n> +\n>  `references.format`::\n>  \tThe reference storage format. The valid values are:\n>  +\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index 042d6de558..194757eb18 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -121,6 +121,32 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n>  \treturn 0;\n>  }\n>  \n> +static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n> +{\n> +\tconst char *work_tree = repo_get_work_tree(repo);\n> +\n> +\tif (!work_tree) {\n> +\t\tstrbuf_addstr(buf, \"\");\n\nThe strbuf here is already NULL-terminated when its initialized. I don't\nthink this should be necessary.\n\n> +\t\treturn 0;\n> +\t}\n> +\n> +\tformat_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_CANONICAL);\n> +\treturn 0;\n> +}\n> +\n> +static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)\n> +{\n> +\tconst char *work_tree = repo_get_work_tree(repo);\n> +\n> +\tif (!work_tree) {\n> +\t\tstrbuf_addstr(buf, \"\");\n\nSame here.\n\n> +\t\treturn 0;\n> +\t}\n> +\n> +\tformat_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_RELATIVE);\n> +\treturn 0;\n> +}\n> +\n>  static int get_references_format(struct repository *repo, struct strbuf *buf)\n>  {\n>  \tstrbuf_addstr(buf,\n> @@ -137,6 +163,8 @@ static const struct repo_info_field repo_info_field[] = {\n>  \t{ \"path.commondir.relative\", get_path_commondir_relative },\n>  \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n>  \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n> +\t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n> +\t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n>  \t{ \"references.format\", get_references_format },\n>  };\n>  \n> diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\n> index ae8c22c817..fbb9063ee5 100755\n> --- a/t/t1900-repo-info.sh\n> +++ b/t/t1900-repo-info.sh\n> @@ -213,4 +213,34 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n>  \t'.git' \\\n>  \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n>  \n> +test_expect_success 'path.toplevel absolute and relative' '\n> +\ttest_when_finished \"rm -rf repo\" &&\n> +\tgit init repo &&\n> +\t(\n> +\t\tmkdir -p repo/sub &&\n> +\t\tcd repo/sub &&\n> +\n> +\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n> +\n> +\t\techo \"path.toplevel.absolute=$ROOT\" >expect.abs &&\n> +\t\tgit repo info path.toplevel.absolute >actual.abs &&\n> +\t\ttest_cmp expect.abs actual.abs &&\n> +\n> +\t\techo \"path.toplevel.relative=../\" >expect.rel &&\n> +\t\tgit repo info path.toplevel.relative >actual.rel &&\n> +\t\ttest_cmp expect.rel actual.rel\n> +\t)\n> +'\n> +\n> +test_expect_success 'path.toplevel returns empty in a bare repository' '\n> +\ttest_when_finished \"rm -rf bare.git\" &&\n> +\tgit init --bare bare.git &&\n> +\t(\n> +\t\tcd bare.git &&\n> +\t\techo \"path.toplevel.absolute=\" >expect &&\n> +\t\tgit repo info path.toplevel.absolute >actual &&\n> +\t\ttest_cmp expect actual\n\nIn this test we are only checking the absolute path. It probably\nwouldn't hurt to also check the relative path too.\n\n-Justin\n"},{"id":"549152","messageId":"amjazXbdRXtH7CoJ@denethor","threadId":"66010","inReplyTo":"20260726104343.16933-3-jayatheerthkulkarni2005@gmail.com","subject":"Re: [PATCH v3 2/7] repo: add path.superproject-working-tree with absolute and relative suffixes","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2026-07-28T16:43:43Z","receivedAt":"2026-07-28T16:43:45Z","isPatch":true,"body":"On 26/07/26 04:13PM, K Jayatheerth wrote:\n> Scripts working in multi-repository setups often need to identify the\n> top-level working tree of a superproject from within a submodule.\n> Currently, this is only exposed via `git rev-parse\n> --show-superproject-working-tree`.\n> \n> Introduce `path.superproject-working-tree.absolute` and\n> `path.superproject-working-tree.relative` keys to `git repo info`.\n> This exposes the core submodule context via a scriptable config-like key\n> using standard format rules.\n\nOk, this also seems like a good fit to include as a key in\ngit-repo-info, but \"superproject-working-tree\" is a bit of a mouthful\nIMO. An alternative could potentially be \"superproject-root\"? Maybe its\nbest to just be consistent with the option name in git-rev-parse(1) and\nkeep it the same though.\n\n> If requested when not inside a submodule, the command returns an empty\n> string.\n> \n> Mentored-by: Justin Tobler <jltobler@gmail.com>\n> Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n> ---\n>  Documentation/git-repo.adoc | 10 ++++++++++\n>  builtin/repo.c              | 33 +++++++++++++++++++++++++++++++++\n>  t/t1900-repo-info.sh        | 34 ++++++++++++++++++++++++++++++++++\n>  3 files changed, 77 insertions(+)\n> \n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> index e34abe5fea..03aa57942f 100644\n> --- a/Documentation/git-repo.adoc\n> +++ b/Documentation/git-repo.adoc\n> @@ -119,6 +119,16 @@ values that they return:\n>  `path.gitdir.relative`::\n>  \tThe path to the Git repository directory relative to the current working directory.\n>  \n> +`path.superproject-working-tree.absolute`::\n> +\tThe canonical absolute path to the working tree root of the superproject\n> +\tif the current repository is an initialized submodule. Outputs an empty\n> +\tstring if not in a submodule.\n> +\n> +`path.superproject-working-tree.relative`::\n> +\tThe path to the working tree root of the superproject relative to the\n> +\tcurrent working directory if the current repository is an initialized\n> +\tsubmodule. Outputs an empty string if not in a submodule.\n> +\n>  `path.toplevel.absolute`::\n>  \tThe canonical absolute path to the top-level directory of the\n>  \trepository's working tree. Outputs an empty string if the repository\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index 194757eb18..82359473e9 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -18,6 +18,7 @@\n>  #include \"strbuf.h\"\n>  #include \"string-list.h\"\n>  #include \"shallow.h\"\n> +#include \"submodule.h\"\n>  #include \"tree.h\"\n>  #include \"tree-walk.h\"\n>  #include \"utf8.h\"\n> @@ -121,6 +122,36 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n>  \treturn 0;\n>  }\n>  \n> +static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n> +{\n> +\tstruct strbuf superproject = STRBUF_INIT;\n> +\n> +\tif (!get_superproject_working_tree(&superproject)) {\n> +\t\tstrbuf_release(&superproject);\n> +\t\tstrbuf_addstr(buf, \"\");\n\nSame comment here as in the previous patch...\n\n> +\t\treturn 0;\n> +\t}\n> +\n> +\tformat_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);\n> +\tstrbuf_release(&superproject);\n> +\treturn 0;\n> +}\n> +\n> +static int get_path_superproject_relative(struct repository *repo UNUSED, struct strbuf *buf)\n> +{\n> +\tstruct strbuf superproject = STRBUF_INIT;\n> +\n> +\tif (!get_superproject_working_tree(&superproject)) {\n> +\t\tstrbuf_release(&superproject);\n> +\t\tstrbuf_addstr(buf, \"\");\n\n...and here as well...\n\n> +\t\treturn 0;\n> +\t}\n> +\n> +\tformat_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);\n> +\tstrbuf_release(&superproject);\n> +\treturn 0;\n> +}\n> +\n>  static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n>  {\n>  \tconst char *work_tree = repo_get_work_tree(repo);\n> @@ -163,6 +194,8 @@ static const struct repo_info_field repo_info_field[] = {\n>  \t{ \"path.commondir.relative\", get_path_commondir_relative },\n>  \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n>  \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n> +\t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\n> +\t{ \"path.superproject-working-tree.relative\", get_path_superproject_relative },\n>  \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n>  \t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n>  \t{ \"references.format\", get_references_format },\n> diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\n> index fbb9063ee5..220b3d4d3d 100755\n> --- a/t/t1900-repo-info.sh\n> +++ b/t/t1900-repo-info.sh\n> @@ -213,6 +213,40 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n>  \t'.git' \\\n>  \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n>  \n> +test_expect_success 'path.superproject-working-tree absolute and relative' '\n> +\ttest_when_finished \"rm -rf sub super\" &&\n> +\tgit init sub &&\n> +\ttest_commit -C sub initial &&\n> +\tgit init super &&\n> +\t(\n> +\t\tcd super &&\n> +\t\tgit -c protocol.file.allow=always submodule add \"../sub\" sub &&\n> +\t\tgit commit -m \"add submodule\" &&\n> +\n> +\t\tcd sub &&\n> +\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n> +\n> +\t\techo \"path.superproject-working-tree.absolute=$ROOT\" >expect.abs &&\n> +\t\tgit repo info path.superproject-working-tree.absolute >actual.abs &&\n> +\t\ttest_cmp expect.abs actual.abs &&\n> +\n> +\t\techo \"path.superproject-working-tree.relative=../\" >expect.rel &&\n> +\t\tgit repo info path.superproject-working-tree.relative >actual.rel &&\n> +\t\ttest_cmp expect.rel actual.rel\n> +\t)\n> +'\n> +\n> +test_expect_success 'path.superproject-working-tree returns empty when not in a submodule' '\n> +\ttest_when_finished \"rm -rf repo\" &&\n> +\tgit init repo &&\n> +\t(\n> +\t\tcd repo &&\n> +\t\techo \"path.superproject-working-tree.absolute=\" >expect &&\n> +\t\tgit repo info path.superproject-working-tree.absolute >actual &&\n> +\t\ttest_cmp expect actual\n\n... and here as well. :)\n\n-Justin\n"},{"id":"549155","messageId":"amjeDPUeoOSN_oYk@denethor","threadId":"66010","inReplyTo":"20260726104343.16933-4-jayatheerthkulkarni2005@gmail.com","subject":"Re: [PATCH v3 3/7] repo: add path.objects with absolute and relative suffix formatting","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2026-07-28T17:12:18Z","receivedAt":"2026-07-28T17:12:25Z","isPatch":true,"body":"On 26/07/26 04:13PM, K Jayatheerth wrote:\n> Tools and deployment hooks frequently query the location of the object\n> database directory. Currently, this relies on legacy parsing methods or\n> manually inspecting `git rev-parse --git-path objects`.\n\n\"Tools and deployment hooks\" seems a bit overly specific. Maybe instead\nwe could just say \"Scripts operating on a repository may need to query\nthe location of the object database directory\"?\n\nAlso, I'm not entirely sure what is meant by \"legacy parsing methods\"\nhere.\n\n> Introduce `path.objects.absolute` and `path.objects.relative` keys to\n> `git repo info`. This allows tools to discover the object database\n> location safely while natively adhering to active `GIT_OBJECT_DIRECTORY`\n> environment variable overrides.\n\nIn the context of pluggable ODBs, this proposed key is a little more\ninteresting because a non-\"files\" ODB source in the future may not even\nhave a filesystem path to an objects directory. When this becomes more\nrelevant, we could just adapt these keys to return an empty string in\nsuch cases, but it does also make me question whether it is information\nthat we should further expose in the first place if it does eventually\nbecomes an internal detail of a specific ODB source.\n\nIt probably doesn't matter too much, but I've CC'd Patrick for his\nthoughts too.\n\n[snip]\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index 82359473e9..d6bdd5bcfa 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n>  \treturn 0;\n>  }\n>  \n> +static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)\n> +{\n> +\tconst char *obj_dir = repo_get_object_directory(repo);\n> +\n> +\tif (!obj_dir)\n> +\t\treturn error(_(\"unable to get object directory\"));\n> +\n> +\tformat_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);\n\nFor the absolute path, do we actually need to provide the prefix? It\nmight make it more clear that its the aboslute path if we just pass \"\"\ninstead?\n\n-Justin\n"},{"id":"549162","messageId":"amjjMwAHSbkQgtPb@denethor","threadId":"66010","inReplyTo":"20260726104343.16933-5-jayatheerthkulkarni2005@gmail.com","subject":"Re: [PATCH v3 4/7] repo: add path.hooks with absolute and relative suffix formatting","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2026-07-28T19:00:29Z","receivedAt":"2026-07-28T19:00:33Z","isPatch":true,"body":"On 26/07/26 04:13PM, K Jayatheerth wrote:\n> External tool integrations and validation systems need a stable way to\n> identify where the repository hooks are stored. Currently, this involves\n> relying on `git rev-parse --git-path hooks` or querying `core.hooksPath`\n> manually.\n\nSimilar to the comment in the previous patch, \"External tool\nintergations and validation systems\" seems overly specific IMO. Also,\n\"need\" is a bit strongly worded as I'm sure its not a requirement for\nevery external script/tool.\n\n> Introduce `path.hooks.absolute` and `path.hooks.relative` keys to\n> `git repo info`. This allows tools to discover the active hooks location\n> natively, ensuring proper resolution regardless of whether Git is using\n> the standard `.git/hooks` structure or a custom `core.hooksPath` setup.\n\nPer hooks path documentation:\n\n  The path can be either absolute or relative. A relative path is taken\n  as relative to the directory where the hooks are run.\n  \n  ...\n\n  You  can also disable all hooks entirely by setting core.hooksPath to\n  /dev/null.\n\nShould we handle this /dev/null case specially? It looks like:\n\n  $ git -c core.hooksPath=/dev/null rev-parse --git-path hooks\n\njust prints '/dev/null'. I do wonder if this makes much sense though in\ncontext of the relative path version of this key. From some quick\ntesting, it appears the git-rev-parse(1) version of this option always\nprints the absolute path if that is what is configured (it appears to\nignore --path-format). Maybe we should just special case /dev/null and\nreturn an empty string? I'm not entirely sure what the best route is\nhere though.\n\n-Justin\n"},{"id":"549218","messageId":"CA+rGoLfJ5Kc_HV7YfJ-y5SpMjb7t7f5ifUKcd=39eTjqzzQF5w@mail.gmail.com","threadId":"66010","inReplyTo":"amjUPEgenletgbp5@denethor","subject":"Re: [PATCH v3 1/7] repo: add path.toplevel with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-07-29T16:36:36Z","receivedAt":"2026-07-29T16:36:50Z","isPatch":true,"body":"Hey Justin,\n\nThanks for the detailed review.\n\nOn Tue, Jul 28, 2026 at 10:06 PM Justin Tobler <jltobler@gmail.com> wrote:\n>\n> On 26/07/26 04:13PM, K Jayatheerth wrote:\n> > Scripts frequently need to find the root directory of a repository's\n> > working tree. Currently, this requires using `git rev-parse --show-toplevel`\n> > or inferring it from other path components.\n> >\n> > Introduce `path.toplevel.absolute` and `path.toplevel.relative` keys\n> > to `git repo info`. This allows scripts to retrieve the top-level\n> > working tree path in a predictable, strictly formatted manner without\n> > relying on `rev-parse`.\n>\n> Ok, this seems like suitable information to also look up under\n> git-repo-info.\n>\n> > If requested in a bare repository where no working tree exists, the\n> > command returns an empty string.\n>\n> This matches the existing behavior in git-rev-parse(1). Makes sense.\n>\n\n\nYes, that is correct.\n\n\n> >\n> > +static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n> > +{\n> > +     const char *work_tree = repo_get_work_tree(repo);\n> > +\n> > +     if (!work_tree) {\n> > +             strbuf_addstr(buf, \"\");\n>\n> The strbuf here is already NULL-terminated when its initialized. I don't\n> think this should be necessary.\n>\n\n\nVery nice point indeed\nI will change that everywhere I have added that.\n\n\n> > +     if (!work_tree) {\n> > +             strbuf_addstr(buf, \"\");\n>\n> Same here.\n>\n\n> > +test_expect_success 'path.toplevel returns empty in a bare repository' '\n> > +     test_when_finished \"rm -rf bare.git\" &&\n> > +     git init --bare bare.git &&\n> > +     (\n> > +             cd bare.git &&\n> > +             echo \"path.toplevel.absolute=\" >expect &&\n> > +             git repo info path.toplevel.absolute >actual &&\n> > +             test_cmp expect actual\n>\n> In this test we are only checking the absolute path. It probably\n> wouldn't hurt to also check the relative path too.\n>\n\n\nGood catch. I'll add relative-path tests in both places.\n\n\n> > Introduce `path.superproject-working-tree.absolute` and\n> > `path.superproject-working-tree.relative` keys to `git repo info`.\n> > This exposes the core submodule context via a scriptable config-like key\n> > using standard format rules.\n>\n> Ok, this also seems like a good fit to include as a key in\n> git-repo-info, but \"superproject-working-tree\" is a bit of a mouthful\n> IMO. An alternative could potentially be \"superproject-root\"? Maybe its\n> best to just be consistent with the option name in git-rev-parse(1) and\n> keep it the same though.\n\n\nshow-superproject-working-tree is what git rev parse uses.\nI am a bit unclear about this.\nSince it is meant to be a scripting command, I don't know if it being\na long name matters much.\nAt the same time, I don't think we can keep the exact\n\"show-superproject-working-tree\" as the key's name.\nAs the \"show\" is indirectly covered by \"path.\" so the \"show\" is just\nredundant if you ask me (I might be wrong here).\nThat is actually why I kept the rest of the name similar and omitted\nthe prefix from the flag name.\n\nIn other words, I am fine with the name being either one.\nThat said, I am more inclined with the \"superproject-root\".\nI think the name explains exactly what it does.\nWhereas \"superproject-working-tree\" was not that clear to me when I\nfirst read it (Might be subjective to me).\n\n\n> > +static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n> > +{\n> > +     struct strbuf superproject = STRBUF_INIT;\n> > +\n> > +     if (!get_superproject_working_tree(&superproject)) {\n> > +             strbuf_release(&superproject);\n> > +             strbuf_addstr(buf, \"\");\n>\n> Same comment here as in the previous patch...\n>\n> > +     struct strbuf superproject = STRBUF_INIT;\n> > +\n> > +     if (!get_superproject_working_tree(&superproject)) {\n> > +             strbuf_release(&superproject);\n> > +             strbuf_addstr(buf, \"\");\n>\n> ...and here as well...\n>\n> > +             return 0;\n> > +     }\n> > +\n> > +     format_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);\n> > +     strbuf_release(&superproject);\n> > +     return 0;\n> > +}\n> > +\n> > +test_expect_success 'path.superproject-working-tree returns empty when not in a submodule' '\n> > +     test_when_finished \"rm -rf repo\" &&\n> > +     git init repo &&\n> > +     (\n> > +             cd repo &&\n> > +             echo \"path.superproject-working-tree.absolute=\" >expect &&\n> > +             git repo info path.superproject-working-tree.absolute >actual &&\n> > +             test_cmp expect actual\n>\n> ... and here as well. :)\n>\n\n\nGood catch. I'll add a relative-path test there as well.\n\n\n> > Tools and deployment hooks frequently query the location of the object\n> > database directory. Currently, this relies on legacy parsing methods or\n> > manually inspecting `git rev-parse --git-path objects`.\n>\n> \"Tools and deployment hooks\" seems a bit overly specific. Maybe instead\n> we could just say \"Scripts operating on a repository may need to query\n> the location of the object database directory\"?\n>\n\n\nI think your version is consistent with other patches as well.\nI wanted to ask if it is not a good practice to write commit messages\nwhich are very specific.\nI also wanted to know what constitutes being overly specific so that I\ncan avoid doing it again.\n\n\n> Also, I'm not entirely sure what is meant by \"legacy parsing methods\"\n> here.\n>\n\n\nSomething scripts have historically done: parse git rev-parse\n--git-dir (or read .git/config by hand)\nand manually append /objects, rather than asking for the object\ndirectory directly\nwhich breaks if GIT_OBJECT_DIRECTORY is set.\nI'll reword the commit message to just say that explicitly instead of\nthe vague \"legacy parsing methods\" phrase.\n\n\n> > Introduce `path.objects.absolute` and `path.objects.relative` keys to\n> > `git repo info`. This allows tools to discover the object database\n> > location safely while natively adhering to active `GIT_OBJECT_DIRECTORY`\n> > environment variable overrides.\n>\n> In the context of pluggable ODBs, this proposed key is a little more\n> interesting because a non-\"files\" ODB source in the future may not even\n> have a filesystem path to an objects directory. When this becomes more\n> relevant, we could just adapt these keys to return an empty string in\n> such cases, but it does also make me question whether it is information\n> that we should further expose in the first place if it does eventually\n> becomes an internal detail of a specific ODB source.\n>\n> It probably doesn't matter too much, but I've CC'd Patrick for his\n> thoughts too.\n>\n\n\nThanks for looping in Patrick!\n\nThat's a fair concern.\nMy thinking is that the \"files\" backend is going to be the reality for\nthe vast majority of\nrepositories for a long time yet, and scripts asking for path.objects\ntoday are almost certainly\nassuming a filesystem-backed ODB anyway (that's the whole reason\nthey'd want the path).\nSo I don't think we're locking ourselves into a bad abstraction by\nadding it now.\n\nIf/when a non-filesystem ODB source becomes real,\nwe'd have two reasonable options:\nreturn an empty string for that key (same pattern we already use for\npath.toplevel/superproject-working-tree when the concept doesn't\napply),\nor deprecate the key outright if it turns out to be meaningless in that world.\nEither way it doesn't block adding it now, worst case we're removing\nor narrowing a key later, not stuck with something actively wrong.\n\n\n\n> > +static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)\n> > +{\n> > +     const char *obj_dir = repo_get_object_directory(repo);\n> > +\n> > +     if (!obj_dir)\n> > +             return error(_(\"unable to get object directory\"));\n> > +\n> > +     format_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);\n>\n> For the absolute path, do we actually need to provide the prefix? It\n> might make it more clear that its the aboslute path if we just pass \"\"\n> instead?\n>\n\n\nThis point was raised in the foundational patch as well.\nIt won't change the output for sure, so I will pass \"\" instead.\n\n\nOn Wed, Jul 29, 2026 at 12:30 AM Justin Tobler <jltobler@gmail.com> wrote:\n>\n> On 26/07/26 04:13PM, K Jayatheerth wrote:\n> > External tool integrations and validation systems need a stable way to\n> > identify where the repository hooks are stored. Currently, this involves\n> > relying on `git rev-parse --git-path hooks` or querying `core.hooksPath`\n> > manually.\n>\n> Similar to the comment in the previous patch, \"External tool\n> intergations and validation systems\" seems overly specific IMO. Also,\n> \"need\" is a bit strongly worded as I'm sure its not a requirement for\n> every external script/tool.\n>\n\n\nGot it, I will try it to be not overly specific as well.\nMaybe something like:\nScripts operating on a repository may want a stable way to identify\nwhere the repository hooks are stored. Currently, this involves\nrelying on `git rev-parse --git-path hooks` or querying\n`core.hooksPath` manually.\n\nIntroduce `path.hooks.absolute` and `path.hooks.relative` keys to\n`git repo info`. This allows scripts to discover the active hooks\nlocation, ensuring proper resolution regardless of whether Git is\nusing the standard `.git/hooks` structure or a custom\n`core.hooksPath` setup.\n\n\n> > Introduce `path.hooks.absolute` and `path.hooks.relative` keys to\n> > `git repo info`. This allows tools to discover the active hooks location\n> > natively, ensuring proper resolution regardless of whether Git is using\n> > the standard `.git/hooks` structure or a custom `core.hooksPath` setup.\n>\n> Per hooks path documentation:\n>\n>   The path can be either absolute or relative. A relative path is taken\n>   as relative to the directory where the hooks are run.\n>\n>   ...\n>\n>   You  can also disable all hooks entirely by setting core.hooksPath to\n>   /dev/null.\n>\n> Should we handle this /dev/null case specially? It looks like:\n>\n>   $ git -c core.hooksPath=/dev/null rev-parse --git-path hooks\n>\n> just prints '/dev/null'. I do wonder if this makes much sense though in\n> context of the relative path version of this key. From some quick\n> testing, it appears the git-rev-parse(1) version of this option always\n> prints the absolute path if that is what is configured (it appears to\n> ignore --path-format). Maybe we should just special case /dev/null and\n> return an empty string? I'm not entirely sure what the best route is\n> here though.\n>\n\n\nAfter thinking about it\nI would lean towards not special-casing it. A few reasons:\n\n/dev/null is just a valid (if unusual) value of core.hooksPath as far\nas Git is concerned, the field is documented to reflect\nwhatever core.hooksPath resolves to, and rev-parse --git-path hooks\nalready treats it as an ordinary path.\nIf we special-case it to an empty string, we'd be the only place in\nGit that treats /dev/null as meaningfully different from any other\nhooksPath value.\n\nEmpty string is already used by other keys in this series\nto mean \"this concept doesn't apply here\" (no working tree, not a submodule).\nUsing empty string for \"hooks are disabled\" would overload that\nmeaning with something different, \"the path exists but you shouldn't\nuse it\"\nand a script can't tell the two apart without also knowing the\nhooksPath convention, which defeats the purpose of adding a structured\nkey in the first place.\n\nAny script that's checking for /dev/null already has to know that\nconvention when\nusing the existing rev-parse interface, so we're not creating new\nsurprises, just carrying forward a pre-existing quirk of\ncore.hooksPath semantics.\n\nFor the relative version:\nI'll let format_path/relative_path do whatever it already does for a\npath that isn't under the prefix\n(I'd guess it falls back to printing the absolute path, similar to how\nrev-parse seems to ignore --path-format for this case).\nThat keeps behavior consistent with the existing tool rather than\nintroducing new special-casing for path.hooks.relative specifically.\n\nI'll add a test covering core.hooksPath=/dev/null to lock in whichever\nbehavior we land on,\nand I can add a doc note under path.hooks.absolute/path.hooks.relative\nmentioning that /dev/null is passed through unchanged if that's the\nvalue in use,\nsimilar to how the existing hooksPath docs describe it.\nLet me know if you still think empty-string is preferable despite the\nabove,  happy to go either way once we agree on the reasoning.\n\nThanks again for the detailed review!\n\nRegards,\n- K Jayatheerth\n"},{"id":"549816","messageId":"20260806101556.162940-1-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260716012138.6714-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC PATCH v4 0/7] repo: add more path keys to git repo info","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-06T10:15:49Z","receivedAt":"2026-08-06T10:20:33Z","isPatch":true,"body":"Series adds keys to git repo info.\n\nKeys output paths of repository components:\n* path.toplevel: repository tree.\n* path.superproject-root: superproject tree from submodules.\n* path.hooks: repository hooks.\n* path.index: repository index.\n* path.grafts: repository grafts.\n* path.git-prefix: prefix offset.\n\nThe patch also removes an unused header.\n\nKeys support suffixes for format.\nCommits contain documentation and tests.\n\nChanges since v3:\n* The prefix member has been shifted from startup_info to repo,\n  therefore we now omit the header and use repo->prefix everywhere.\n  Development of these patches is now based on the latest branch.\n\n* Since strbuf is NUL-terminated, remove all\n  `strbuf_addstr(buf, \"\");` lines from the patches.\n\n* Add relative-path test cases alongside the existing absolute-path\n  test cases wherever we don't use the existing helper.\n\n* Renamed `superproject-working-tree` to `superproject-root` because\n  I feel the latter is a bit clearer. That said, I'm happy with\n  either name, so if you prefer `superproject-working-tree`, I'm\n  perfectly fine reverting it.\n\n* Changed almost all the commit messages to make them not overly\n  specific.\n\n* Dropped `path.objects` from this series. During a sync discussion\n  with my mentors, we concluded that this key should wait, since the\n  object database's on-disk layout is expected to change\n  significantly as pluggable ODB backends are introduced.\n\n* Removed `repo->prefix` from the absolute path functions (the\n  absolute-path variants now pass \"\" instead).\n\n* Added a `/dev/null` test case for `core.hooksPath`, guarded against\n  MINGW since /dev/null doesn't resolve to a real canonicalizable\n  path on Windows.\n\nK Jayatheerth (7):\n  repo: add path.toplevel with absolute and relative suffix formatting\n  repo: add path.superproject-root with absolute and relative suffixes\n  repo: add path.hooks with absolute and relative suffixes\n  repo: add path.index with absolute and relative suffixes\n  repo: add path.grafts with absolute and relative suffixes\n  repo: add path.git-prefix\n  repo: remove unused setup.h include\n\n Documentation/git-repo.adoc |  57 +++++++++++++\n builtin/repo.c              | 137 +++++++++++++++++++++++++++++++-\n t/t1900-repo-info.sh        | 154 +++++++++++++++++++++++++++++++++++-\n 3 files changed, 345 insertions(+), 3 deletions(-)\n\nRange-diff against v3:\n1:  baed121c74 ! 1:  3880485020 repo: add path.toplevel with absolute and relative suffix formatting\n    @@ Commit message\n     \n         Scripts frequently need to find the root directory of a repository's\n         working tree. Currently, this requires using `git rev-parse --show-toplevel`\n    -    or inferring it from other path components.\n    +    or inferring it from other repository information.\n     \n         Introduce `path.toplevel.absolute` and `path.toplevel.relative` keys\n         to `git repo info`. This allows scripts to retrieve the top-level\n    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str\n     +{\n     +\tconst char *work_tree = repo_get_work_tree(repo);\n     +\n    -+\tif (!work_tree) {\n    -+\t\tstrbuf_addstr(buf, \"\");\n    ++\tif (!work_tree)\n     +\t\treturn 0;\n    -+\t}\n     +\n    -+\tformat_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_CANONICAL);\n    ++\tformat_path(buf, work_tree, \"\", PATH_FORMAT_CANONICAL);\n     +\treturn 0;\n     +}\n     +\n    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str\n     +{\n     +\tconst char *work_tree = repo_get_work_tree(repo);\n     +\n    -+\tif (!work_tree) {\n    -+\t\tstrbuf_addstr(buf, \"\");\n    ++\tif (!work_tree)\n     +\t\treturn 0;\n    -+\t}\n     +\n    -+\tformat_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_RELATIVE);\n    ++\tformat_path(buf, work_tree, repo->prefix, PATH_FORMAT_RELATIVE);\n     +\treturn 0;\n     +}\n     +\n    @@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir\n     +\t)\n     +'\n     +\n    -+test_expect_success 'path.toplevel returns empty in a bare repository' '\n    ++test_expect_success 'path.toplevel absolute and relative in a bare repository' '\n     +\ttest_when_finished \"rm -rf bare.git\" &&\n     +\tgit init --bare bare.git &&\n     +\t(\n     +\t\tcd bare.git &&\n    -+\t\techo \"path.toplevel.absolute=\" >expect &&\n    -+\t\tgit repo info path.toplevel.absolute >actual &&\n    -+\t\ttest_cmp expect actual\n    ++\n    ++\t\techo \"path.toplevel.absolute=\" >expect.abs &&\n    ++\t\tgit repo info path.toplevel.absolute >actual.abs &&\n    ++\t\ttest_cmp expect.abs actual.abs &&\n    ++\n    ++\t\techo \"path.toplevel.relative=\" >expect.rel &&\n    ++\t\tgit repo info path.toplevel.relative >actual.rel &&\n    ++\t\ttest_cmp expect.rel actual.rel\n     +\t)\n     +'\n     +\n2:  2f363b6358 ! 2:  1b7b0d286c repo: add path.superproject-working-tree with absolute and relative suffixes\n    @@ Metadata\n     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n     \n      ## Commit message ##\n    -    repo: add path.superproject-working-tree with absolute and relative suffixes\n    +    repo: add path.superproject-root with absolute and relative suffixes\n     \n         Scripts working in multi-repository setups often need to identify the\n         top-level working tree of a superproject from within a submodule.\n         Currently, this is only exposed via `git rev-parse\n         --show-superproject-working-tree`.\n     \n    -    Introduce `path.superproject-working-tree.absolute` and\n    -    `path.superproject-working-tree.relative` keys to `git repo info`.\n    +    Introduce `path.superproject-root.absolute` and\n    +    `path.superproject-root.relative` keys to `git repo info`.\n         This exposes the core submodule context via a scriptable config-like key\n         using standard format rules.\n     \n    @@ Documentation/git-repo.adoc: values that they return:\n      `path.gitdir.relative`::\n      \tThe path to the Git repository directory relative to the current working directory.\n      \n    -+`path.superproject-working-tree.absolute`::\n    ++`path.superproject-root.absolute`::\n     +\tThe canonical absolute path to the working tree root of the superproject\n     +\tif the current repository is an initialized submodule. Outputs an empty\n     +\tstring if not in a submodule.\n     +\n    -+`path.superproject-working-tree.relative`::\n    ++`path.superproject-root.relative`::\n     +\tThe path to the working tree root of the superproject relative to the\n     +\tcurrent working directory if the current repository is an initialized\n     +\tsubmodule. Outputs an empty string if not in a submodule.\n    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str\n     +\n     +\tif (!get_superproject_working_tree(&superproject)) {\n     +\t\tstrbuf_release(&superproject);\n    -+\t\tstrbuf_addstr(buf, \"\");\n     +\t\treturn 0;\n     +\t}\n     +\n    -+\tformat_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);\n    ++\tformat_path(buf, superproject.buf, \"\", PATH_FORMAT_CANONICAL);\n     +\tstrbuf_release(&superproject);\n     +\treturn 0;\n     +}\n     +\n    -+static int get_path_superproject_relative(struct repository *repo UNUSED, struct strbuf *buf)\n    ++static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)\n     +{\n     +\tstruct strbuf superproject = STRBUF_INIT;\n     +\n     +\tif (!get_superproject_working_tree(&superproject)) {\n     +\t\tstrbuf_release(&superproject);\n    -+\t\tstrbuf_addstr(buf, \"\");\n     +\t\treturn 0;\n     +\t}\n     +\n    -+\tformat_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);\n    ++\tformat_path(buf, superproject.buf, repo->prefix, PATH_FORMAT_RELATIVE);\n     +\tstrbuf_release(&superproject);\n     +\treturn 0;\n     +}\n    @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {\n      \t{ \"path.commondir.relative\", get_path_commondir_relative },\n      \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n      \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n    -+\t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\n    -+\t{ \"path.superproject-working-tree.relative\", get_path_superproject_relative },\n    ++\t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n    ++\t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n      \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n      \t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n      \t{ \"references.format\", get_references_format },\n    @@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir\n      \t'.git' \\\n      \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n      \n    -+test_expect_success 'path.superproject-working-tree absolute and relative' '\n    ++test_expect_success 'path.superproject-root absolute and relative' '\n     +\ttest_when_finished \"rm -rf sub super\" &&\n     +\tgit init sub &&\n     +\ttest_commit -C sub initial &&\n    @@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir\n     +\t\tcd sub &&\n     +\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n     +\n    -+\t\techo \"path.superproject-working-tree.absolute=$ROOT\" >expect.abs &&\n    -+\t\tgit repo info path.superproject-working-tree.absolute >actual.abs &&\n    ++\t\techo \"path.superproject-root.absolute=$ROOT\" >expect.abs &&\n    ++\t\tgit repo info path.superproject-root.absolute >actual.abs &&\n     +\t\ttest_cmp expect.abs actual.abs &&\n     +\n    -+\t\techo \"path.superproject-working-tree.relative=../\" >expect.rel &&\n    -+\t\tgit repo info path.superproject-working-tree.relative >actual.rel &&\n    ++\t\techo \"path.superproject-root.relative=../\" >expect.rel &&\n    ++\t\tgit repo info path.superproject-root.relative >actual.rel &&\n     +\t\ttest_cmp expect.rel actual.rel\n     +\t)\n     +'\n     +\n    -+test_expect_success 'path.superproject-working-tree returns empty when not in a submodule' '\n    ++test_expect_success 'path.superproject-root returns empty when not in a submodule' '\n     +\ttest_when_finished \"rm -rf repo\" &&\n     +\tgit init repo &&\n     +\t(\n     +\t\tcd repo &&\n    -+\t\techo \"path.superproject-working-tree.absolute=\" >expect &&\n    -+\t\tgit repo info path.superproject-working-tree.absolute >actual &&\n    -+\t\ttest_cmp expect actual\n    ++\n    ++\t\techo \"path.superproject-root.absolute=\" >expect.abs &&\n    ++\t\tgit repo info path.superproject-root.absolute >actual.abs &&\n    ++\t\ttest_cmp expect.abs actual.abs &&\n    ++\n    ++\t\techo \"path.superproject-root.relative=\" >expect.rel &&\n    ++\t\tgit repo info path.superproject-root.relative >actual.rel &&\n    ++\t\ttest_cmp expect.rel actual.rel\n     +\t)\n     +'\n     +\n4:  e3facc7989 ! 3:  517f621eb0 repo: add path.hooks with absolute and relative suffix formatting\n    @@ Metadata\n     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n     \n      ## Commit message ##\n    -    repo: add path.hooks with absolute and relative suffix formatting\n    +    repo: add path.hooks with absolute and relative suffixes\n     \n    -    External tool integrations and validation systems need a stable way to\n    -    identify where the repository hooks are stored. Currently, this involves\n    -    relying on `git rev-parse --git-path hooks` or querying `core.hooksPath`\n    -    manually.\n    +    Hooks are an integral part of a repository's configuration and are\n    +    commonly used by tooling to automate repository-specific workflows.\n    +    Currently, scripts typically retrieve the hooks directory by invoking\n    +    `git rev-parse --git-path hooks`.\n     \n         Introduce `path.hooks.absolute` and `path.hooks.relative` keys to\n    -    `git repo info`. This allows tools to discover the active hooks location\n    -    natively, ensuring proper resolution regardless of whether Git is using\n    -    the standard `.git/hooks` structure or a custom `core.hooksPath` setup.\n    +    `git repo info`. This exposes the hooks directory as a scriptable\n    +    config-like key using standard format rules, allowing scripts to\n    +    retrieve it through the same interface as other repository path\n    +    information.\n     \n         Mentored-by: Justin Tobler <jltobler@gmail.com>\n         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n    @@ Documentation/git-repo.adoc: values that they return:\n      \n     +`path.hooks.absolute`::\n     +\tThe canonical absolute path to the repository's hooks directory.\n    -+\tRespects `core.hooksPath` configuration adjustments.\n    ++\tRespects the `core.hooksPath` configuration. If `core.hooksPath` is\n    ++\tset to `/dev/null`, that value is returned unchanged.\n     +\n     +`path.hooks.relative`::\n     +\tThe path to the repository's hooks directory relative to the current\n    -+\tworking directory. Respects `core.hooksPath` configuration adjustments.\n    ++\tworking directory. Respects the `core.hooksPath` configuration.\n     +\n    - `path.objects.absolute`::\n    - \tThe canonical absolute path to the repository's object database directory.\n    - \tRespects the `GIT_OBJECT_DIRECTORY` environment override.\n    + `path.superproject-root.absolute`::\n    + \tThe canonical absolute path to the working tree root of the superproject\n    + \tif the current repository is an initialized submodule. Outputs an empty\n     \n      ## builtin/repo.c ##\n     @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str\n     +\tstruct strbuf hooks_path = STRBUF_INIT;\n     +\n     +\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n    -+\tformat_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);\n    ++\tformat_path(buf, hooks_path.buf, \"\", PATH_FORMAT_CANONICAL);\n     +\tstrbuf_release(&hooks_path);\n     +\treturn 0;\n     +}\n    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str\n     +\tstruct strbuf hooks_path = STRBUF_INIT;\n     +\n     +\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n    -+\tformat_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);\n    ++\tformat_path(buf, hooks_path.buf, repo->prefix, PATH_FORMAT_RELATIVE);\n     +\tstrbuf_release(&hooks_path);\n     +\treturn 0;\n     +}\n     +\n    - static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)\n    + static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n      {\n    - \tconst char *obj_dir = repo_get_object_directory(repo);\n    + \tstruct strbuf superproject = STRBUF_INIT;\n     @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {\n      \t{ \"path.commondir.relative\", get_path_commondir_relative },\n      \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n      \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n     +\t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n     +\t{ \"path.hooks.relative\", get_path_hooks_relative },\n    - \t{ \"path.objects.absolute\", get_path_objects_absolute },\n    - \t{ \"path.objects.relative\", get_path_objects_relative },\n    - \t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\n    + \t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n    + \t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n    + \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n     \n      ## t/t1900-repo-info.sh ##\n    +@@ t/t1900-repo-info.sh: test_repo_info_path () {\n    + \t\t\tcd repo/sub &&\n    + \t\t\tROOT=\"$(test-tool path-utils real_path ..)\" && export ROOT &&\n    + \t\t\teval \"$init_command\" &&\n    +-\t\t\techo \"path.$field_name.absolute=$ROOT/$expected_dir\" >expect &&\n    ++\t\t\tcase \"$expected_dir\" in\n    ++\t\t\t/*) EXPECT_ABS=\"$expected_dir\" ;;\n    ++\t\t\t*) EXPECT_ABS=\"$ROOT/$expected_dir\" ;;\n    ++\t\t\tesac &&\n    ++\t\t\techo \"path.$field_name.absolute=$EXPECT_ABS\" >expect &&\n    + \t\t\tgit repo info \"path.$field_name.absolute\" >actual &&\n    + \t\t\ttest_cmp expect actual\n    + \t\t)\n    +@@ t/t1900-repo-info.sh: test_repo_info_path () {\n    + \t\t\tcd repo/sub &&\n    + \t\t\tROOT=\"$(test-tool path-utils real_path ..)\" && export ROOT &&\n    + \t\t\teval \"$init_command\" &&\n    +-\t\t\techo \"path.$field_name.relative=../$expected_dir\" >expect &&\n    ++\t\t\tcase \"$expected_dir\" in\n    ++\t\t\t/*) EXPECT_REL=\"$(test-tool path-utils relative_path \"$expected_dir\" \"$PWD\")\" ;;\n    ++\t\t\t*) EXPECT_REL=\"../$expected_dir\" ;;\n    ++\t\t\tesac &&\n    ++\t\t\techo \"path.$field_name.relative=$EXPECT_REL\" >expect &&\n    + \t\t\tgit repo info \"path.$field_name.relative\" >actual &&\n    + \t\t\ttest_cmp expect actual\n    + \t\t)\n     @@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n      \t'.git' \\\n      \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n      \n    -+test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'\n    ++test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'\n     +\n     +test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n     +\t'custom-hooks' \\\n     +\t'git config core.hooksPath \"$ROOT/custom-hooks\" && mkdir -p \"$ROOT/custom-hooks\"'\n     +\n    - test_repo_info_path 'objects standard' 'objects' '.git/objects'\n    - \n    - test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \\\n    ++# /dev/null is not a real, canonicalizable filesystem path on Windows,\n    ++# so path resolution for core.hooksPath=/dev/null cannot be expected to\n    ++# produce a literal \"/dev/null\" the way it does on POSIX systems.\n    ++if ! test_have_prereq MINGW\n    ++then\n    ++\ttest_repo_info_path 'hooks with core.hooksPath=/dev/null' 'hooks' \\\n    ++\t\t'/dev/null' \\\n    ++\t\t'git config core.hooksPath /dev/null'\n    ++fi\n    ++\n    + test_expect_success 'path.superproject-root absolute and relative' '\n    + \ttest_when_finished \"rm -rf sub super\" &&\n    + \tgit init sub &&\n5:  ab157b8b49 ! 4:  37d28712de repo: add path.index with absolute and relative suffix formatting\n    @@ Metadata\n     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n     \n      ## Commit message ##\n    -    repo: add path.index with absolute and relative suffix formatting\n    +    repo: add path.index with absolute and relative suffixes\n     \n    -    External script workflows and formatting layers require straightforward\n    -    access to the location of the index staging file. Currently, tracking\n    -    this necessitates a legacy call to `git rev-parse --git-path index` or\n    -    `--show-toplevel` logic abstractions.\n    +    The repository index is a fundamental component used by Git and related\n    +    tooling to track the working tree state. Scripts that interact with the\n    +    index currently retrieve its location by invoking\n    +    `git rev-parse --git-path index`.\n     \n         Introduce `path.index.absolute` and `path.index.relative` keys to\n    -    `git repo info`. This allows tooling utilities to discover the active\n    -    index context cleanly while scaling transparently with localized\n    -    `GIT_INDEX_FILE` environment overrides.\n    +    `git repo info`. This exposes the index file location as a scriptable\n    +    config-like key using standard format rules, allowing scripts to\n    +    retrieve it through the same interface as other repository path\n    +    information.\n     \n         Mentored-by: Justin Tobler <jltobler@gmail.com>\n         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n    @@ Commit message\n      ## Documentation/git-repo.adoc ##\n     @@ Documentation/git-repo.adoc: values that they return:\n      \tThe path to the repository's hooks directory relative to the current\n    - \tworking directory. Respects `core.hooksPath` configuration adjustments.\n    + \tworking directory. Respects the `core.hooksPath` configuration.\n      \n     +`path.index.absolute`::\n     +\tThe canonical absolute path to the repository's current index file.\n    -+\tRespects the `GIT_INDEX_FILE` environment override. The returned path\n    -+\treflects the configured/default index location regardless of whether the\n    -+\trepository is bare or whether the file currently exists.\n    ++\tRespects the `GIT_INDEX_FILE` environment override. Returns the\n    ++\tconfigured index path even if the repository is bare or the file does\n    ++\tnot exist.\n     +\n     +`path.index.relative`::\n     +\tThe path to the repository's current index file relative to the current\n     +\tworking directory. Respects the `GIT_INDEX_FILE` environment override.\n    -+\tThe returned path reflects the configured/default index location regardless\n    -+\tof whether the repository is bare or whether the file currently exists.\n    ++\tReturns the configured index path even if the repository is bare or the\n    ++\tfile does not exist.\n     +\n    - `path.objects.absolute`::\n    - \tThe canonical absolute path to the repository's object database directory.\n    - \tRespects the `GIT_OBJECT_DIRECTORY` environment override.\n    + `path.superproject-root.absolute`::\n    + \tThe canonical absolute path to the working tree root of the superproject\n    + \tif the current repository is an initialized submodule. Outputs an empty\n     \n      ## builtin/repo.c ##\n     @@ builtin/repo.c: static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n    @@ builtin/repo.c: static int get_path_hooks_relative(struct repository *repo, stru\n     +\tif (!index_file)\n     +\t\treturn error(_(\"unable to get index file\"));\n     +\n    -+\tformat_path(buf, index_file, startup_info->prefix, PATH_FORMAT_CANONICAL);\n    ++\tformat_path(buf, index_file, \"\", PATH_FORMAT_CANONICAL);\n     +\treturn 0;\n     +}\n     +\n    @@ builtin/repo.c: static int get_path_hooks_relative(struct repository *repo, stru\n     +\tif (!index_file)\n     +\t\treturn error(_(\"unable to get index file\"));\n     +\n    -+\tformat_path(buf, index_file, startup_info->prefix, PATH_FORMAT_RELATIVE);\n    ++\tformat_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);\n     +\treturn 0;\n     +}\n     +\n    - static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)\n    + static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n      {\n    - \tconst char *obj_dir = repo_get_object_directory(repo);\n    + \tstruct strbuf superproject = STRBUF_INIT;\n     @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {\n      \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n      \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n      \t{ \"path.hooks.relative\", get_path_hooks_relative },\n     +\t{ \"path.index.absolute\", get_path_index_absolute },\n     +\t{ \"path.index.relative\", get_path_index_relative },\n    - \t{ \"path.objects.absolute\", get_path_objects_absolute },\n    - \t{ \"path.objects.relative\", get_path_objects_relative },\n    - \t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\n    + \t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n    + \t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n    + \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n     \n      ## t/t1900-repo-info.sh ##\n    -@@ t/t1900-repo-info.sh: test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n    - \t'custom-hooks' \\\n    - \t'git config core.hooksPath \"$ROOT/custom-hooks\" && mkdir -p \"$ROOT/custom-hooks\"'\n    +@@ t/t1900-repo-info.sh: then\n    + \t\t'git config core.hooksPath /dev/null'\n    + fi\n      \n     +test_repo_info_path 'index standard' 'index' '.git/index'\n     +\n    @@ t/t1900-repo-info.sh: test_repo_info_path 'hooks with core.hooksPath override' '\n     +\t)\n     +'\n     +\n    - test_repo_info_path 'objects standard' 'objects' '.git/objects'\n    - \n    - test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \\\n    + test_expect_success 'path.superproject-root absolute and relative' '\n    + \ttest_when_finished \"rm -rf sub super\" &&\n    + \tgit init sub &&\n6:  d80719367b ! 5:  d88340f5a6 repo: add path.grafts with absolute and relative suffix formatting\n    @@ Metadata\n     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n     \n      ## Commit message ##\n    -    repo: add path.grafts with absolute and relative suffix formatting\n    +    repo: add path.grafts with absolute and relative suffixes\n     \n    -    External toolchains managing specialized history rewrites or legacy\n    -    history splices require access to the location of the repository grafts\n    -    file. Currently, this requires a legacy call to `git rev-parse --git-path info/grafts`.\n    +    The repository grafts file specifies alternate parent relationships for\n    +    commits and may be used by repository tooling that needs to inspect or\n    +    manage grafts. Scripts currently retrieve its location by invoking\n    +    `git rev-parse --git-path info/grafts`.\n     \n         Introduce `path.grafts.absolute` and `path.grafts.relative` keys to\n    -    `git repo info`. This allows scripting layers to query the active grafts\n    -    context cleanly while scaling transparently with active `GIT_GRAFT_FILE`\n    -    environment variable overrides.\n    +    `git repo info`. This exposes the grafts file location as a scriptable\n    +    config-like key using standard format rules, allowing scripts to\n    +    retrieve it through the same interface as other repository path\n    +    information.\n     \n         Mentored-by: Justin Tobler <jltobler@gmail.com>\n         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n    @@ Documentation/git-repo.adoc: values that they return:\n      \tThe path to the Git repository directory relative to the current working directory.\n      \n     +`path.grafts.absolute`::\n    -+\tThe canonical absolute path to the repository grafts file.\n    -+\tRespects the `GIT_GRAFT_FILE` environment override. The path is returned\n    -+\tregardless of whether the file currently exists on disk.\n    ++\tThe canonical absolute path to the repository's graft file.\n    ++\tRespects the `GIT_GRAFT_FILE` environment override. The path is\n    ++\treturned regardless of whether the file currently exists on disk.\n     +\n     +`path.grafts.relative`::\n    -+\tThe path to the repository grafts file relative to the current working\n    -+\tdirectory. Respects the `GIT_GRAFT_FILE` environment override. The path\n    -+\tis returned regardless of whether the file currently exists on disk.\n    ++\tThe path to the repository's graft file relative to the current\n    ++\tworking directory. Respects the `GIT_GRAFT_FILE` environment\n    ++\toverride. The path is returned regardless of whether the file\n    ++\tcurrently exists on disk.\n     +\n      `path.hooks.absolute`::\n      \tThe canonical absolute path to the repository's hooks directory.\n    - \tRespects `core.hooksPath` configuration adjustments.\n    + \tRespects the `core.hooksPath` configuration. If `core.hooksPath` is\n     \n      ## builtin/repo.c ##\n     @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str\n     +\tif (!graft_file)\n     +\t\treturn error(_(\"unable to get graft file\"));\n     +\n    -+\tformat_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_CANONICAL);\n    ++\tformat_path(buf, graft_file, \"\", PATH_FORMAT_CANONICAL);\n     +\treturn 0;\n     +}\n     +\n    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str\n     +\tif (!graft_file)\n     +\t\treturn error(_(\"unable to get graft file\"));\n     +\n    -+\tformat_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_RELATIVE);\n    ++\tformat_path(buf, graft_file, repo->prefix, PATH_FORMAT_RELATIVE);\n     +\treturn 0;\n     +}\n     +\n    @@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir\n     +\t'custom-graft-file' \\\n     +\t'GIT_GRAFT_FILE=\"$ROOT/custom-graft-file\" && export GIT_GRAFT_FILE'\n     +\n    - test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'\n    + test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'\n      \n      test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n7:  ac6aee77e8 ! 6:  38b19d8bfd repo: add path.git-prefix path key\n    @@ Metadata\n     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n     \n      ## Commit message ##\n    -    repo: add path.git-prefix path key\n    +    repo: add path.git-prefix\n     \n    -    Scripts and command-line prompt integrations frequently need to know their\n    -    relative depth inside a repository working tree layout. Currently, this\n    -    is retrieved using `git rev-parse --show-prefix`.\n    +    Scripts sometimes need the path from the repository's working tree root\n    +    to the current working directory. While this information can be derived\n    +    through existing Git commands, `git repo info` does not currently expose\n    +    it as a scriptable key.\n     \n    -    Introduce the `path.git-prefix` key to `git repo info`. This mirrors the\n    -    prefix location tracking framework as a standalone key, returning the\n    -    exact relative path offset complete with a trailing slash, or an empty\n    -    string if run directly at the repository working tree root.\n    +    Introduce the `path.git-prefix` key to `git repo info`. The key returns\n    +    the path from the working tree root to the current working directory,\n    +    returning the empty string when invoked from the working tree root.\n     \n         Mentored-by: Justin Tobler <jltobler@gmail.com>\n         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n    @@ Documentation/git-repo.adoc: values that they return:\n      \tthe current working directory.\n      \n     +`path.git-prefix`::\n    -+\tThe relative path from the top-level directory of the working tree to\n    -+\tthe current working directory (including a trailing slash). Outputs an\n    -+\tempty string if executed at the root of the working tree.\n    ++\tThe path from the root of the working tree to the current working\n    ++\tdirectory. Returns the empty string when the current working directory\n    ++\tis the root of the working tree.\n     +\n      `path.gitdir.absolute`::\n      \tThe canonical absolute path to the Git repository directory (the `.git` directory).\n    @@ builtin/repo.c: static int get_path_commondir_relative(struct repository *repo,\n      \treturn 0;\n      }\n      \n    -+static int get_path_git_prefix(struct repository *repo UNUSED, struct strbuf *buf)\n    ++static int get_path_git_prefix(struct repository *repo, struct strbuf *buf)\n     +{\n     +\t/*\n    -+\t * startup_info->prefix is NULL if we are at the working tree root.\n    -+\t * We add an empty string to ensure the buffer is cleanly initialized.\n    ++\t * repo->prefix is NULL when the current working directory is\n    ++\t * the worktree root.\n     +\t */\n    -+\tstrbuf_addstr(buf, startup_info->prefix ? startup_info->prefix : \"\");\n    ++\tstrbuf_addstr(buf, repo->prefix ? repo->prefix : \"\");\n     +\treturn 0;\n     +}\n     +\n    @@ t/t1900-repo-info.sh: test_repo_info_path 'commondir with only GIT_DIR' 'commond\n      \t'.git' \\\n      \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n      \n    -+test_expect_success 'path.git-prefix at root and in a subdirectory' '\n    ++test_expect_success 'path.git-prefix at repository root' '\n     +\ttest_when_finished \"rm -rf repo\" &&\n     +\tgit init repo &&\n     +\t(\n     +\t\tcd repo &&\n    ++\t\techo \"path.git-prefix=\" >expect &&\n    ++\t\tgit repo info path.git-prefix >actual &&\n    ++\t\ttest_cmp expect actual\n    ++\t)\n    ++'\n     +\n    -+\t\techo \"path.git-prefix=\" >expect.root &&\n    -+\t\tgit repo info path.git-prefix >actual.root &&\n    -+\t\ttest_cmp expect.root actual.root &&\n    -+\n    -+\t\tmkdir -p sub/dir &&\n    -+\t\tcd sub/dir &&\n    -+\n    -+\t\techo \"path.git-prefix=sub/dir/\" >expect.sub &&\n    -+\t\tgit repo info path.git-prefix >actual.sub &&\n    -+\t\ttest_cmp expect.sub actual.sub\n    ++test_expect_success 'path.git-prefix in subdirectory' '\n    ++\ttest_when_finished \"rm -rf repo\" &&\n    ++\tgit init repo &&\n    ++\tmkdir -p repo/sub/dir &&\n    ++\t(\n    ++\t\tcd repo/sub/dir &&\n    ++\t\techo \"path.git-prefix=sub/dir/\" >expect &&\n    ++\t\tgit repo info path.git-prefix >actual &&\n    ++\t\ttest_cmp expect actual\n     +\t)\n     +'\n     +\n3:  736c8e0576 ! 7:  f28ee4e169 repo: add path.objects with absolute and relative suffix formatting\n    @@ Metadata\n     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n     \n      ## Commit message ##\n    -    repo: add path.objects with absolute and relative suffix formatting\n    +    repo: remove unused setup.h include\n     \n    -    Tools and deployment hooks frequently query the location of the object\n    -    database directory. Currently, this relies on legacy parsing methods or\n    -    manually inspecting `git rev-parse --git-path objects`.\n    +    The repository prefix is now stored in `struct repository`, so\n    +    builtin/repo.c no longer uses any declarations from setup.h.\n     \n    -    Introduce `path.objects.absolute` and `path.objects.relative` keys to\n    -    `git repo info`. This allows tools to discover the object database\n    -    location safely while natively adhering to active `GIT_OBJECT_DIRECTORY`\n    -    environment variable overrides.\n    +    Remove the now-unused include.\n     \n         Mentored-by: Justin Tobler <jltobler@gmail.com>\n         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n         Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n     \n    - ## Documentation/git-repo.adoc ##\n    -@@ Documentation/git-repo.adoc: values that they return:\n    - `path.gitdir.relative`::\n    - \tThe path to the Git repository directory relative to the current working directory.\n    - \n    -+`path.objects.absolute`::\n    -+\tThe canonical absolute path to the repository's object database directory.\n    -+\tRespects the `GIT_OBJECT_DIRECTORY` environment override.\n    -+\n    -+`path.objects.relative`::\n    -+\tThe path to the repository's object database directory relative to the\n    -+\tcurrent working directory. Respects the `GIT_OBJECT_DIRECTORY`\n    -+\tenvironment override.\n    -+\n    - `path.superproject-working-tree.absolute`::\n    - \tThe canonical absolute path to the working tree root of the superproject\n    - \tif the current repository is an initialized submodule. Outputs an empty\n    -\n      ## builtin/repo.c ##\n    -@@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n    - \treturn 0;\n    - }\n    - \n    -+static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)\n    -+{\n    -+\tconst char *obj_dir = repo_get_object_directory(repo);\n    -+\n    -+\tif (!obj_dir)\n    -+\t\treturn error(_(\"unable to get object directory\"));\n    -+\n    -+\tformat_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);\n    -+\treturn 0;\n    -+}\n    -+\n    -+static int get_path_objects_relative(struct repository *repo, struct strbuf *buf)\n    -+{\n    -+\tconst char *obj_dir = repo_get_object_directory(repo);\n    -+\n    -+\tif (!obj_dir)\n    -+\t\treturn error(_(\"unable to get object directory\"));\n    -+\n    -+\tformat_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);\n    -+\treturn 0;\n    -+}\n    -+\n    - static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n    - {\n    - \tstruct strbuf superproject = STRBUF_INIT;\n    -@@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {\n    - \t{ \"path.commondir.relative\", get_path_commondir_relative },\n    - \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n    - \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n    -+\t{ \"path.objects.absolute\", get_path_objects_absolute },\n    -+\t{ \"path.objects.relative\", get_path_objects_relative },\n    - \t{ \"path.superproject-working-tree.absolute\", get_path_superproject_absolute },\n    - \t{ \"path.superproject-working-tree.relative\", get_path_superproject_relative },\n    - \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n    -\n    - ## t/t1900-repo-info.sh ##\n    -@@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n    - \t'.git' \\\n    - \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n    - \n    -+test_repo_info_path 'objects standard' 'objects' '.git/objects'\n    -+\n    -+test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \\\n    -+\t'custom-objects' \\\n    -+\t'GIT_OBJECT_DIRECTORY=\"$ROOT/custom-objects\" && export GIT_OBJECT_DIRECTORY &&\n    -+\t mkdir -p \"$ROOT/custom-objects\"'\n    -+\n    - test_expect_success 'path.superproject-working-tree absolute and relative' '\n    - \ttest_when_finished \"rm -rf sub super\" &&\n    - \tgit init sub &&\n    +@@\n    + #include \"ref-filter.h\"\n    + #include \"refs.h\"\n    + #include \"revision.h\"\n    +-#include \"setup.h\"\n    + #include \"strbuf.h\"\n    + #include \"string-list.h\"\n    + #include \"shallow.h\"\n-- \n2.55.GIT\n"},{"id":"549817","messageId":"20260806101556.162940-2-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260806101556.162940-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC PATCH v4 1/7] repo: add path.toplevel with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-06T10:15:50Z","receivedAt":"2026-08-06T10:21:00Z","isPatch":true,"body":"Scripts frequently need to find the root directory of a repository's\nworking tree. Currently, this requires using `git rev-parse --show-toplevel`\nor inferring it from other repository information.\n\nIntroduce `path.toplevel.absolute` and `path.toplevel.relative` keys\nto `git repo info`. This allows scripts to retrieve the top-level\nworking tree path in a predictable, strictly formatted manner without\nrelying on `rev-parse`.\n\nIf requested in a bare repository where no working tree exists, the\ncommand returns an empty string.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 10 ++++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 35 +++++++++++++++++++++++++++++++++++\n 3 files changed, 69 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex ed7d80c690..e34abe5fea 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,16 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.toplevel.absolute`::\n+\tThe canonical absolute path to the top-level directory of the\n+\trepository's working tree. Outputs an empty string if the repository\n+\tis bare.\n+\n+`path.toplevel.relative`::\n+\tThe path to the top-level directory of the repository's working\n+\ttree relative to the current working directory. Outputs an empty\n+\tstring if the repository is bare.\n+\n `references.format`::\n \tThe reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 84e012f83f..c31e9cfa70 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -121,6 +121,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *work_tree = repo_get_work_tree(repo);\n+\n+\tif (!work_tree)\n+\t\treturn 0;\n+\n+\tformat_path(buf, work_tree, \"\", PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *work_tree = repo_get_work_tree(repo);\n+\n+\tif (!work_tree)\n+\t\treturn 0;\n+\n+\tformat_path(buf, work_tree, repo->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -137,6 +159,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n+\t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex c85d390f43..9417d1ab65 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,4 +213,39 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.toplevel absolute and relative' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tmkdir -p repo/sub &&\n+\t\tcd repo/sub &&\n+\n+\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n+\n+\t\techo \"path.toplevel.absolute=$ROOT\" >expect.abs &&\n+\t\tgit repo info path.toplevel.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.toplevel.relative=../\" >expect.rel &&\n+\t\tgit repo info path.toplevel.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n+test_expect_success 'path.toplevel absolute and relative in a bare repository' '\n+\ttest_when_finished \"rm -rf bare.git\" &&\n+\tgit init --bare bare.git &&\n+\t(\n+\t\tcd bare.git &&\n+\n+\t\techo \"path.toplevel.absolute=\" >expect.abs &&\n+\t\tgit repo info path.toplevel.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.toplevel.relative=\" >expect.rel &&\n+\t\tgit repo info path.toplevel.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n test_done\n-- \n2.55.GIT\n\n"},{"id":"549818","messageId":"20260806101556.162940-3-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260806101556.162940-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC PATCH v4 2/7] repo: add path.superproject-root with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-06T10:15:51Z","receivedAt":"2026-08-06T10:21:11Z","isPatch":true,"body":"Scripts working in multi-repository setups often need to identify the\ntop-level working tree of a superproject from within a submodule.\nCurrently, this is only exposed via `git rev-parse\n--show-superproject-working-tree`.\n\nIntroduce `path.superproject-root.absolute` and\n`path.superproject-root.relative` keys to `git repo info`.\nThis exposes the core submodule context via a scriptable config-like key\nusing standard format rules.\n\nIf requested when not inside a submodule, the command returns an empty\nstring.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 10 ++++++++++\n builtin/repo.c              | 31 +++++++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 39 +++++++++++++++++++++++++++++++++++++\n 3 files changed, 80 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex e34abe5fea..e524a07f53 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,16 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.superproject-root.absolute`::\n+\tThe canonical absolute path to the working tree root of the superproject\n+\tif the current repository is an initialized submodule. Outputs an empty\n+\tstring if not in a submodule.\n+\n+`path.superproject-root.relative`::\n+\tThe path to the working tree root of the superproject relative to the\n+\tcurrent working directory if the current repository is an initialized\n+\tsubmodule. Outputs an empty string if not in a submodule.\n+\n `path.toplevel.absolute`::\n \tThe canonical absolute path to the top-level directory of the\n \trepository's working tree. Outputs an empty string if the repository\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex c31e9cfa70..47c4fce293 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -18,6 +18,7 @@\n #include \"strbuf.h\"\n #include \"string-list.h\"\n #include \"shallow.h\"\n+#include \"submodule.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"utf8.h\"\n@@ -121,6 +122,34 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\tstruct strbuf superproject = STRBUF_INIT;\n+\n+\tif (!get_superproject_working_tree(&superproject)) {\n+\t\tstrbuf_release(&superproject);\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, superproject.buf, \"\", PATH_FORMAT_CANONICAL);\n+\tstrbuf_release(&superproject);\n+\treturn 0;\n+}\n+\n+static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf superproject = STRBUF_INIT;\n+\n+\tif (!get_superproject_working_tree(&superproject)) {\n+\t\tstrbuf_release(&superproject);\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, superproject.buf, repo->prefix, PATH_FORMAT_RELATIVE);\n+\tstrbuf_release(&superproject);\n+\treturn 0;\n+}\n+\n static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *work_tree = repo_get_work_tree(repo);\n@@ -159,6 +188,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n+\t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n \t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n \t{ \"references.format\", get_references_format },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 9417d1ab65..eec576a1d9 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,45 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.superproject-root absolute and relative' '\n+\ttest_when_finished \"rm -rf sub super\" &&\n+\tgit init sub &&\n+\ttest_commit -C sub initial &&\n+\tgit init super &&\n+\t(\n+\t\tcd super &&\n+\t\tgit -c protocol.file.allow=always submodule add \"../sub\" sub &&\n+\t\tgit commit -m \"add submodule\" &&\n+\n+\t\tcd sub &&\n+\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n+\n+\t\techo \"path.superproject-root.absolute=$ROOT\" >expect.abs &&\n+\t\tgit repo info path.superproject-root.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.superproject-root.relative=../\" >expect.rel &&\n+\t\tgit repo info path.superproject-root.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n+test_expect_success 'path.superproject-root returns empty when not in a submodule' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\n+\t\techo \"path.superproject-root.absolute=\" >expect.abs &&\n+\t\tgit repo info path.superproject-root.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.superproject-root.relative=\" >expect.rel &&\n+\t\tgit repo info path.superproject-root.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n test_expect_success 'path.toplevel absolute and relative' '\n \ttest_when_finished \"rm -rf repo\" &&\n \tgit init repo &&\n-- \n2.55.GIT\n\n"},{"id":"549819","messageId":"20260806101556.162940-4-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260806101556.162940-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC PATCH v4 3/7] repo: add path.hooks with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-06T10:15:52Z","receivedAt":"2026-08-06T10:24:16Z","isPatch":true,"body":"Hooks are an integral part of a repository's configuration and are\ncommonly used by tooling to automate repository-specific workflows.\nCurrently, scripts typically retrieve the hooks directory by invoking\n`git rev-parse --git-path hooks`.\n\nIntroduce `path.hooks.absolute` and `path.hooks.relative` keys to\n`git repo info`. This exposes the hooks directory as a scriptable\nconfig-like key using standard format rules, allowing scripts to\nretrieve it through the same interface as other repository path\ninformation.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  9 +++++++++\n builtin/repo.c              | 22 ++++++++++++++++++++++\n t/t1900-repo-info.sh        | 28 ++++++++++++++++++++++++++--\n 3 files changed, 57 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex e524a07f53..20836cf8f6 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,15 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.hooks.absolute`::\n+\tThe canonical absolute path to the repository's hooks directory.\n+\tRespects the `core.hooksPath` configuration. If `core.hooksPath` is\n+\tset to `/dev/null`, that value is returned unchanged.\n+\n+`path.hooks.relative`::\n+\tThe path to the repository's hooks directory relative to the current\n+\tworking directory. Respects the `core.hooksPath` configuration.\n+\n `path.superproject-root.absolute`::\n \tThe canonical absolute path to the working tree root of the superproject\n \tif the current repository is an initialized submodule. Outputs an empty\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 47c4fce293..d7c451a771 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,26 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf hooks_path = STRBUF_INIT;\n+\n+\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n+\tformat_path(buf, hooks_path.buf, \"\", PATH_FORMAT_CANONICAL);\n+\tstrbuf_release(&hooks_path);\n+\treturn 0;\n+}\n+\n+static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf hooks_path = STRBUF_INIT;\n+\n+\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n+\tformat_path(buf, hooks_path.buf, repo->prefix, PATH_FORMAT_RELATIVE);\n+\tstrbuf_release(&hooks_path);\n+\treturn 0;\n+}\n+\n static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n {\n \tstruct strbuf superproject = STRBUF_INIT;\n@@ -188,6 +208,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n+\t{ \"path.hooks.relative\", get_path_hooks_relative },\n \t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n \t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex eec576a1d9..1da5db4942 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -174,7 +174,11 @@ test_repo_info_path () {\n \t\t\tcd repo/sub &&\n \t\t\tROOT=\"$(test-tool path-utils real_path ..)\" && export ROOT &&\n \t\t\teval \"$init_command\" &&\n-\t\t\techo \"path.$field_name.absolute=$ROOT/$expected_dir\" >expect &&\n+\t\t\tcase \"$expected_dir\" in\n+\t\t\t/*) EXPECT_ABS=\"$expected_dir\" ;;\n+\t\t\t*) EXPECT_ABS=\"$ROOT/$expected_dir\" ;;\n+\t\t\tesac &&\n+\t\t\techo \"path.$field_name.absolute=$EXPECT_ABS\" >expect &&\n \t\t\tgit repo info \"path.$field_name.absolute\" >actual &&\n \t\t\ttest_cmp expect actual\n \t\t)\n@@ -188,7 +192,11 @@ test_repo_info_path () {\n \t\t\tcd repo/sub &&\n \t\t\tROOT=\"$(test-tool path-utils real_path ..)\" && export ROOT &&\n \t\t\teval \"$init_command\" &&\n-\t\t\techo \"path.$field_name.relative=../$expected_dir\" >expect &&\n+\t\t\tcase \"$expected_dir\" in\n+\t\t\t/*) EXPECT_REL=\"$(test-tool path-utils relative_path \"$expected_dir\" \"$PWD\")\" ;;\n+\t\t\t*) EXPECT_REL=\"../$expected_dir\" ;;\n+\t\t\tesac &&\n+\t\t\techo \"path.$field_name.relative=$EXPECT_REL\" >expect &&\n \t\t\tgit repo info \"path.$field_name.relative\" >actual &&\n \t\t\ttest_cmp expect actual\n \t\t)\n@@ -213,6 +221,22 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'\n+\n+test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n+\t'custom-hooks' \\\n+\t'git config core.hooksPath \"$ROOT/custom-hooks\" && mkdir -p \"$ROOT/custom-hooks\"'\n+\n+# /dev/null is not a real, canonicalizable filesystem path on Windows,\n+# so path resolution for core.hooksPath=/dev/null cannot be expected to\n+# produce a literal \"/dev/null\" the way it does on POSIX systems.\n+if ! test_have_prereq MINGW\n+then\n+\ttest_repo_info_path 'hooks with core.hooksPath=/dev/null' 'hooks' \\\n+\t\t'/dev/null' \\\n+\t\t'git config core.hooksPath /dev/null'\n+fi\n+\n test_expect_success 'path.superproject-root absolute and relative' '\n \ttest_when_finished \"rm -rf sub super\" &&\n \tgit init sub &&\n-- \n2.55.GIT\n\n"},{"id":"549820","messageId":"20260806101556.162940-5-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260806101556.162940-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC PATCH v4 4/7] repo: add path.index with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-06T10:15:53Z","receivedAt":"2026-08-06T10:24:53Z","isPatch":true,"body":"The repository index is a fundamental component used by Git and related\ntooling to track the working tree state. Scripts that interact with the\nindex currently retrieve its location by invoking\n`git rev-parse --git-path index`.\n\nIntroduce `path.index.absolute` and `path.index.relative` keys to\n`git repo info`. This exposes the index file location as a scriptable\nconfig-like key using standard format rules, allowing scripts to\nretrieve it through the same interface as other repository path\ninformation.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 12 ++++++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++\n 3 files changed, 59 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 20836cf8f6..08ef47750c 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -128,6 +128,18 @@ values that they return:\n \tThe path to the repository's hooks directory relative to the current\n \tworking directory. Respects the `core.hooksPath` configuration.\n \n+`path.index.absolute`::\n+\tThe canonical absolute path to the repository's current index file.\n+\tRespects the `GIT_INDEX_FILE` environment override. Returns the\n+\tconfigured index path even if the repository is bare or the file does\n+\tnot exist.\n+\n+`path.index.relative`::\n+\tThe path to the repository's current index file relative to the current\n+\tworking directory. Respects the `GIT_INDEX_FILE` environment override.\n+\tReturns the configured index path even if the repository is bare or the\n+\tfile does not exist.\n+\n `path.superproject-root.absolute`::\n \tThe canonical absolute path to the working tree root of the superproject\n \tif the current repository is an initialized submodule. Outputs an empty\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex d7c451a771..2a15327094 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -142,6 +142,28 @@ static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *index_file = repo_get_index_file(repo);\n+\n+\tif (!index_file)\n+\t\treturn error(_(\"unable to get index file\"));\n+\n+\tformat_path(buf, index_file, \"\", PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *index_file = repo_get_index_file(repo);\n+\n+\tif (!index_file)\n+\t\treturn error(_(\"unable to get index file\"));\n+\n+\tformat_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n {\n \tstruct strbuf superproject = STRBUF_INIT;\n@@ -210,6 +232,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n \t{ \"path.hooks.relative\", get_path_hooks_relative },\n+\t{ \"path.index.absolute\", get_path_index_absolute },\n+\t{ \"path.index.relative\", get_path_index_relative },\n \t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n \t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 1da5db4942..431a4842d4 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -237,6 +237,29 @@ then\n \t\t'git config core.hooksPath /dev/null'\n fi\n \n+test_repo_info_path 'index standard' 'index' '.git/index'\n+\n+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \\\n+\t'custom-index-file' \\\n+\t'GIT_INDEX_FILE=\"$ROOT/custom-index-file\" && export GIT_INDEX_FILE'\n+\n+test_expect_success 'path.index in a bare repository returns default index location' '\n+\ttest_when_finished \"rm -rf bare.git\" &&\n+\tgit init --bare bare.git &&\n+\t(\n+\t\tcd bare.git &&\n+\t\tROOT=\"$(test-tool path-utils real_path .)\" &&\n+\n+\t\techo \"path.index.absolute=$ROOT/index\" >expect.abs &&\n+\t\tgit repo info path.index.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.index.relative=index\" >expect.rel &&\n+\t\tgit repo info path.index.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n test_expect_success 'path.superproject-root absolute and relative' '\n \ttest_when_finished \"rm -rf sub super\" &&\n \tgit init sub &&\n-- \n2.55.GIT\n\n"},{"id":"549821","messageId":"20260806101556.162940-6-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260806101556.162940-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC PATCH v4 5/7] repo: add path.grafts with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-06T10:15:54Z","receivedAt":"2026-08-06T10:25:18Z","isPatch":true,"body":"The repository grafts file specifies alternate parent relationships for\ncommits and may be used by repository tooling that needs to inspect or\nmanage grafts. Scripts currently retrieve its location by invoking\n`git rev-parse --git-path info/grafts`.\n\nIntroduce `path.grafts.absolute` and `path.grafts.relative` keys to\n`git repo info`. This exposes the grafts file location as a scriptable\nconfig-like key using standard format rules, allowing scripts to\nretrieve it through the same interface as other repository path\ninformation.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 11 +++++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        |  6 ++++++\n 3 files changed, 41 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 08ef47750c..868ab0ed9f 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,17 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.grafts.absolute`::\n+\tThe canonical absolute path to the repository's graft file.\n+\tRespects the `GIT_GRAFT_FILE` environment override. The path is\n+\treturned regardless of whether the file currently exists on disk.\n+\n+`path.grafts.relative`::\n+\tThe path to the repository's graft file relative to the current\n+\tworking directory. Respects the `GIT_GRAFT_FILE` environment\n+\toverride. The path is returned regardless of whether the file\n+\tcurrently exists on disk.\n+\n `path.hooks.absolute`::\n \tThe canonical absolute path to the repository's hooks directory.\n \tRespects the `core.hooksPath` configuration. If `core.hooksPath` is\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 2a15327094..779240109d 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_grafts_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *graft_file = repo_get_graft_file(repo);\n+\n+\tif (!graft_file)\n+\t\treturn error(_(\"unable to get graft file\"));\n+\n+\tformat_path(buf, graft_file, \"\", PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_grafts_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *graft_file = repo_get_graft_file(repo);\n+\n+\tif (!graft_file)\n+\t\treturn error(_(\"unable to get graft file\"));\n+\n+\tformat_path(buf, graft_file, repo->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tstruct strbuf hooks_path = STRBUF_INIT;\n@@ -230,6 +252,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.grafts.absolute\", get_path_grafts_absolute },\n+\t{ \"path.grafts.relative\", get_path_grafts_relative },\n \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n \t{ \"path.hooks.relative\", get_path_hooks_relative },\n \t{ \"path.index.absolute\", get_path_index_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 431a4842d4..adc4a92487 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -221,6 +221,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'grafts standard' 'grafts' '.git/info/grafts'\n+\n+test_repo_info_path 'grafts with GIT_GRAFT_FILE override' 'grafts' \\\n+\t'custom-graft-file' \\\n+\t'GIT_GRAFT_FILE=\"$ROOT/custom-graft-file\" && export GIT_GRAFT_FILE'\n+\n test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'\n \n test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n-- \n2.55.GIT\n\n"},{"id":"549822","messageId":"20260806101556.162940-7-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260806101556.162940-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC PATCH v4 6/7] repo: add path.git-prefix","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-06T10:15:55Z","receivedAt":"2026-08-06T10:25:43Z","isPatch":true,"body":"Scripts sometimes need the path from the repository's working tree root\nto the current working directory. While this information can be derived\nthrough existing Git commands, `git repo info` does not currently expose\nit as a scriptable key.\n\nIntroduce the `path.git-prefix` key to `git repo info`. The key returns\nthe path from the working tree root to the current working directory,\nreturning the empty string when invoked from the working tree root.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  5 +++++\n builtin/repo.c              | 11 +++++++++++\n t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++\n 3 files changed, 39 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 868ab0ed9f..fb5aceae8f 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -113,6 +113,11 @@ values that they return:\n \tThe path to the Git repository's common directory relative to\n \tthe current working directory.\n \n+`path.git-prefix`::\n+\tThe path from the root of the working tree to the current working\n+\tdirectory. Returns the empty string when the current working directory\n+\tis the root of the working tree.\n+\n `path.gitdir.absolute`::\n \tThe canonical absolute path to the Git repository directory (the `.git` directory).\n \ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 779240109d..c0f99b6869 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -100,6 +100,16 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b\n \treturn 0;\n }\n \n+static int get_path_git_prefix(struct repository *repo, struct strbuf *buf)\n+{\n+\t/*\n+\t * repo->prefix is NULL when the current working directory is\n+\t * the worktree root.\n+\t */\n+\tstrbuf_addstr(buf, repo->prefix ? repo->prefix : \"\");\n+\treturn 0;\n+}\n+\n static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *git_dir = repo_get_git_dir(repo);\n@@ -250,6 +260,7 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"object.format\", get_object_format },\n \t{ \"path.commondir.absolute\", get_path_commondir_absolute },\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n+\t{ \"path.git-prefix\", get_path_git_prefix },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n \t{ \"path.grafts.absolute\", get_path_grafts_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex adc4a92487..b689445b7a 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -215,6 +215,29 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.git-prefix at repository root' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\techo \"path.git-prefix=\" >expect &&\n+\t\tgit repo info path.git-prefix >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'path.git-prefix in subdirectory' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\tmkdir -p repo/sub/dir &&\n+\t(\n+\t\tcd repo/sub/dir &&\n+\t\techo \"path.git-prefix=sub/dir/\" >expect &&\n+\t\tgit repo info path.git-prefix >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_repo_info_path 'gitdir standard' 'gitdir' '.git'\n \n test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n-- \n2.55.GIT\n\n"},{"id":"549823","messageId":"20260806101556.162940-8-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260806101556.162940-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC PATCH v4 7/7] repo: remove unused setup.h include","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-06T10:15:56Z","receivedAt":"2026-08-06T10:26:15Z","isPatch":true,"body":"The repository prefix is now stored in `struct repository`, so\nbuiltin/repo.c no longer uses any declarations from setup.h.\n\nRemove the now-unused include.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n builtin/repo.c | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex c0f99b6869..8e9f6296b8 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -14,7 +14,6 @@\n #include \"ref-filter.h\"\n #include \"refs.h\"\n #include \"revision.h\"\n-#include \"setup.h\"\n #include \"strbuf.h\"\n #include \"string-list.h\"\n #include \"shallow.h\"\n-- \n2.55.GIT\n"},{"id":"550669","messageId":"749F5FAC-5803-4E7A-AEC8-BA653D329EE2@gmail.com","threadId":"66010","inReplyTo":"20260716012138.6714-2-jayatheerthkulkarni2005@gmail.com","subject":"Re: [GSoC Patch 1/7] repo: add path.toplevel with absolute and relative suffix formatting","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2026-08-15T23:06:45Z","receivedAt":"2026-08-15T23:07:00Z","isPatch":true,"body":"\n> \n> +test_expect_success 'path.toplevel absolute and relative' '\n> + test_when_finished \"rm -rf repo\" &&\n> + git init repo &&\n> + (\n> + mkdir -p repo/sub &&\n> + cd repo/sub &&\n> +\n> + ROOT=\"$(test-tool path-utils real_path ..)\" &&\n> +\n> + echo \"path.toplevel.absolute=$ROOT\" >expect.abs &&\n> + git repo info path.toplevel.absolute >actual.abs &&\n> + test_cmp expect.abs actual.abs &&\n> +\n> + echo \"path.toplevel.relative=../\" >expect.rel &&\n> + git repo info path.toplevel.relative >actual.rel &&\n> + test_cmp expect.rel actual.rel\n> + )\n> +'\n\nQuestion: why not use `test_repo_info_path` here?\n"},{"id":"550670","messageId":"6E2B0ADE-2101-47AC-B11B-315897AC2AF9@gmail.com","threadId":"66010","inReplyTo":"20260806101556.162940-7-jayatheerthkulkarni2005@gmail.com","subject":"Re: [GSoC PATCH v4 6/7] repo: add path.git-prefix","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2026-08-16T00:53:49Z","receivedAt":"2026-08-16T00:54:03Z","isPatch":true,"body":"\n> Scripts sometimes need the path from the repository's working tree root\n> to the current working directory. While this information can be derived\n> through existing Git commands, `git repo info` does not currently expose\n> it as a scriptable key.\n\nEven though I understand that this is a relevant info, it seems\nto me that it's a little bit out of place here... This is\na \"current directory information\", not a \"repository information\".\n> \n"},{"id":"550763","messageId":"CA+rGoLf_BPjsxxSz0-DJgGos6iE_7mo=3FMvQsfNpgpFg99VRg@mail.gmail.com","threadId":"66010","inReplyTo":"749F5FAC-5803-4E7A-AEC8-BA653D329EE2@gmail.com","subject":"Re: [GSoC Patch 1/7] repo: add path.toplevel with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-18T16:09:19Z","receivedAt":"2026-08-18T16:09:31Z","isPatch":true,"body":"Hey Lucas,\n\nOn Sun, Aug 16, 2026 at 4:37 AM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n>\n>\n> >\n> > +test_expect_success 'path.toplevel absolute and relative' '\n> > + test_when_finished \"rm -rf repo\" &&\n> > + git init repo &&\n> > + (\n> > + mkdir -p repo/sub &&\n> > + cd repo/sub &&\n> > +\n> > + ROOT=\"$(test-tool path-utils real_path ..)\" &&\n> > +\n> > + echo \"path.toplevel.absolute=$ROOT\" >expect.abs &&\n> > + git repo info path.toplevel.absolute >actual.abs &&\n> > + test_cmp expect.abs actual.abs &&\n> > +\n> > + echo \"path.toplevel.relative=../\" >expect.rel &&\n> > + git repo info path.toplevel.relative >actual.rel &&\n> > + test_cmp expect.rel actual.rel\n> > + )\n> > +'\n>\n> Question: why not use `test_repo_info_path` here?\n\ntest_repo_info_path doesn't quite fit path.toplevel as the helper assumes\nan $expected_dir suffix and constructs $ROOT/$expected_dir and ../$expected_dir,\nwhile toplevel is $ROOT and ../, respectively, therefore I made this\ncase different.\n"},{"id":"550764","messageId":"CA+rGoLfPJb_Pxjm_Bkqd2Ni0BA1vT2LEObwj4EjUaSRRNyRv-Q@mail.gmail.com","threadId":"66010","inReplyTo":"6E2B0ADE-2101-47AC-B11B-315897AC2AF9@gmail.com","subject":"Re: [GSoC PATCH v4 6/7] repo: add path.git-prefix","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-18T16:33:05Z","receivedAt":"2026-08-18T16:33:18Z","isPatch":true,"body":"On Sun, Aug 16, 2026 at 6:24 AM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n>\n>\n> > Scripts sometimes need the path from the repository's working tree root\n> > to the current working directory. While this information can be derived\n> > through existing Git commands, `git repo info` does not currently expose\n> > it as a scriptable key.\n>\n> Even though I understand that this is a relevant info, it seems\n> to me that it's a little bit out of place here... This is\n> a \"current directory information\", not a \"repository information\".\n> >\n\nI agree that path.git-prefix is technically current-directory information\nrather than repository information.\n\nI included it because it is useful path information for scripts\noperating within a repository,\nand it is already exposed by git rev-parse --show-prefix.\nI thought it would fit the purpose of this series, but I can drop it\nif you think it is better kept separate.\n"},{"id":"550767","messageId":"xmqqlda3z773.fsf@gitster.g","threadId":"66010","inReplyTo":"CA+rGoLfPJb_Pxjm_Bkqd2Ni0BA1vT2LEObwj4EjUaSRRNyRv-Q@mail.gmail.com","subject":"Re: [GSoC PATCH v4 6/7] repo: add path.git-prefix","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-18T17:09:36Z","receivedAt":"2026-08-18T17:09:39Z","isPatch":true,"body":"K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n\n> On Sun, Aug 16, 2026 at 6:24 AM Lucas Seiki Oshiro\n> <lucasseikioshiro@gmail.com> wrote:\n>>\n>>\n>> > Scripts sometimes need the path from the repository's working tree root\n>> > to the current working directory. While this information can be derived\n>> > through existing Git commands, `git repo info` does not currently expose\n>> > it as a scriptable key.\n>>\n>> Even though I understand that this is a relevant info, it seems\n>> to me that it's a little bit out of place here... This is\n>> a \"current directory information\", not a \"repository information\".\n>\n> I agree that path.git-prefix is technically current-directory information\n> rather than repository information.\n\nThe same thing can be said about \"git rev-parse --show-cdup\"; you\ncan complain that it is about the current working directory, but in\nreality it is about how to get to the root of the working tree you\nare currently working in, which means that it is very much about\nrepository information, only given as a relative path.  Nobody would\ncomplain that you have 'path.commondir.relative' when you already\noffer its '.absolute' counterpart, saying that '.relative' is about\nyour current working directory and not about the repository, right?\n\nThe same story holds for 'path.git-prefix', I would think.  View it,\nand \"git rev-parse --show-prefix\", as the inverse version of a\n'path.root-of-the-working-tree.relative' (aka 'path.cdup') rather\nthan as being about the \"current directory\".\n\nOh, and if you are offering git-prefix, you should offer cdup as\nwell.\n\nThanks.\n"},{"id":"551022","messageId":"CA+rGoLcFojrG4yfFG2OB+p-qJie==-Xz89Cysjz1mTLw8Xzukw@mail.gmail.com","threadId":"66010","inReplyTo":"xmqqlda3z773.fsf@gitster.g","subject":"Re: [GSoC PATCH v4 6/7] repo: add path.git-prefix","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-21T14:08:07Z","receivedAt":"2026-08-21T14:08:19Z","isPatch":true,"body":"On Tue, Aug 18, 2026 at 10:39 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n>\n> > On Sun, Aug 16, 2026 at 6:24 AM Lucas Seiki Oshiro\n> > <lucasseikioshiro@gmail.com> wrote:\n> >>\n> >>\n> >> > Scripts sometimes need the path from the repository's working tree root\n> >> > to the current working directory. While this information can be derived\n> >> > through existing Git commands, `git repo info` does not currently expose\n> >> > it as a scriptable key.\n> >>\n> >> Even though I understand that this is a relevant info, it seems\n> >> to me that it's a little bit out of place here... This is\n> >> a \"current directory information\", not a \"repository information\".\n> >\n> > I agree that path.git-prefix is technically current-directory information\n> > rather than repository information.\n>\n> The same thing can be said about \"git rev-parse --show-cdup\"; you\n> can complain that it is about the current working directory, but in\n> reality it is about how to get to the root of the working tree you\n> are currently working in, which means that it is very much about\n> repository information, only given as a relative path.  Nobody would\n> complain that you have 'path.commondir.relative' when you already\n> offer its '.absolute' counterpart, saying that '.relative' is about\n> your current working directory and not about the repository, right?\n>\n\nVery true, I agree with you. I believe cdup and git-prefix complement\neach other well, so it makes sense to expose both.\n\n> The same story holds for 'path.git-prefix', I would think.  View it,\n> and \"git rev-parse --show-prefix\", as the inverse version of a\n> 'path.root-of-the-working-tree.relative' (aka 'path.cdup') rather\n> than as being about the \"current directory\".\n>\n> Oh, and if you are offering git-prefix, you should offer cdup as\n> well.\n>\n\nSure, I will add cdup in the next version and send it.\n\nThank you!\n\n- K Jayatheerth\n"},{"id":"551202","messageId":"20260825175818.645579-1-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260716012138.6714-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v5 0/7] add more path keys to git repo info","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-25T17:58:11Z","receivedAt":"2026-08-25T17:58:43Z","isPatch":true,"body":"Series adds keys to git repo info.\n\nKeys output paths of repository components:\n* path.toplevel: repository tree.\n* path.superproject-root: superproject tree from submodules.\n* path.hooks: repository hooks.\n* path.index: repository index.\n* path.grafts: repository grafts.\n* path.git-prefix: prefix offset.\n* path.cdup: relative path to top level from subdirectory.\n\nKeys support suffixes for format.\nCommits contain documentation and tests.\n\nchanges since v4:\n* Add path.cdup in git repo.\n\n* Drop unused header cleanup patch as it will be sent in the next set of cleanup only\n  patches.\n\nK Jayatheerth (7):\n  repo: add path.toplevel with absolute and relative suffix formatting\n  repo: add path.superproject-root with absolute and relative suffixes\n  repo: add path.hooks with absolute and relative suffixes\n  repo: add path.index with absolute and relative suffixes\n  repo: add path.grafts with absolute and relative suffixes\n  repo: add path.git-prefix\n  repo: add path.cdup\n\n Documentation/git-repo.adoc |  62 +++++++++++++\n builtin/repo.c              | 151 ++++++++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 177 +++++++++++++++++++++++++++++++++++-\n 3 files changed, 388 insertions(+), 2 deletions(-)\n\nRange-diff against v4:\n1:  db54cf2177 = 1:  3880485020 repo: add path.toplevel with absolute and relative suffix formatting\n2:  5f5ab2b844 = 2:  1b7b0d286c repo: add path.superproject-root with absolute and relative suffixes\n3:  a8c3526958 = 3:  517f621eb0 repo: add path.hooks with absolute and relative suffixes\n4:  a419389911 = 4:  37d28712de repo: add path.index with absolute and relative suffixes\n5:  a5a28f2e92 = 5:  d88340f5a6 repo: add path.grafts with absolute and relative suffixes\n6:  39775f8d2f = 6:  38b19d8bfd repo: add path.git-prefix\n7:  74dbca6f17 ! 7:  d6ce98e25e repo: remove unused setup.h include\n    @@ Metadata\n     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n     \n      ## Commit message ##\n    -    repo: remove unused setup.h include\n    +    repo: add path.cdup\n     \n    -    The repository prefix is now stored in `struct repository`, so\n    -    builtin/repo.c no longer uses any declarations from setup.h.\n    +    Scripts sometimes need the relative path from the current working\n    +    directory to the repository's working tree root (cdup). While this\n    +    information can be retrieved through `git rev-parse --show-cdup`,\n    +    `git repo info` does not currently expose it as a scriptable key.\n     \n    -    Remove the now-unused include.\n    +    Introduce the `path.cdup` key to `git repo info`. The key returns the\n    +    path from the current working directory to the root of the working tree,\n    +    returning the empty string when invoked from the working tree root.\n     \n         Mentored-by: Justin Tobler <jltobler@gmail.com>\n         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n         Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n     \n    + ## Documentation/git-repo.adoc ##\n    +@@ Documentation/git-repo.adoc: values that they return:\n    + `object.format`::\n    + \tThe object format (hash algorithm) used in the repository.\n    + \n    ++`path.cdup`::\n    ++\tThe path to the root of the working tree relative to the current\n    ++\tworking directory. Returns the empty string when the current\n    ++\tworking directory is the root of the working tree.\n    ++\n    + `path.commondir.absolute`::\n    + \tThe canonical absolute path to the Git repository's common\n    + \tdirectory (the shared `.git` directory containing objects,\n    +\n      ## builtin/repo.c ##\n    -@@\n    - #include \"ref-filter.h\"\n    - #include \"refs.h\"\n    - #include \"revision.h\"\n    --#include \"setup.h\"\n    - #include \"strbuf.h\"\n    - #include \"string-list.h\"\n    - #include \"shallow.h\"\n    +@@ builtin/repo.c: static int get_object_format(struct repository *repo, struct strbuf *buf)\n    + \treturn 0;\n    + }\n    + \n    ++static int get_path_cdup(struct repository *repo, struct strbuf *buf)\n    ++{\n    ++\tconst char *pfx = repo->prefix;\n    ++\n    ++\twhile (pfx) {\n    ++\t\tpfx = strchr(pfx, '/');\n    ++\t\tif (pfx) {\n    ++\t\t\tpfx++;\n    ++\t\t\tstrbuf_addstr(buf, \"../\");\n    ++\t\t}\n    ++\t}\n    ++\treturn 0;\n    ++}\n    ++\n    + static int get_path_commondir_absolute(struct repository *repo, struct strbuf *buf)\n    + {\n    + \tconst char *common_dir = repo_get_common_dir(repo);\n    +@@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {\n    + \t{ \"layout.bare\", get_layout_bare },\n    + \t{ \"layout.shallow\", get_layout_shallow },\n    + \t{ \"object.format\", get_object_format },\n    ++\t{ \"path.cdup\", get_path_cdup },\n    + \t{ \"path.commondir.absolute\", get_path_commondir_absolute },\n    + \t{ \"path.commondir.relative\", get_path_commondir_relative },\n    + \t{ \"path.git-prefix\", get_path_git_prefix },\n    +\n    + ## t/t1900-repo-info.sh ##\n    +@@ t/t1900-repo-info.sh: test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \\\n    + \t'.git' \\\n    + \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n    + \n    ++test_expect_success 'path.cdup at repository root' '\n    ++\ttest_when_finished \"rm -rf repo\" &&\n    ++\tgit init repo &&\n    ++\t(\n    ++\t\tcd repo &&\n    ++\t\techo \"path.cdup=\" >expect &&\n    ++\t\tgit repo info path.cdup >actual &&\n    ++\t\ttest_cmp expect actual\n    ++\t)\n    ++'\n    ++\n    ++test_expect_success 'path.cdup in subdirectory' '\n    ++\ttest_when_finished \"rm -rf repo\" &&\n    ++\tgit init repo &&\n    ++\tmkdir -p repo/sub/dir &&\n    ++\t(\n    ++\t\tcd repo/sub/dir &&\n    ++\t\techo \"path.cdup=../../\" >expect &&\n    ++\t\tgit repo info path.cdup >actual &&\n    ++\t\ttest_cmp expect actual\n    ++\t)\n    ++'\n    ++\n    + test_expect_success 'path.git-prefix at repository root' '\n    + \ttest_when_finished \"rm -rf repo\" &&\n    + \tgit init repo &&\n-- \n2.55.GIT\n"},{"id":"551203","messageId":"20260825175818.645579-2-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260825175818.645579-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v5 1/7] repo: add path.toplevel with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-25T17:58:12Z","receivedAt":"2026-08-25T17:59:17Z","isPatch":true,"body":"Scripts frequently need to find the root directory of a repository's\nworking tree. Currently, this requires using `git rev-parse --show-toplevel`\nor inferring it from other repository information.\n\nIntroduce `path.toplevel.absolute` and `path.toplevel.relative` keys\nto `git repo info`. This allows scripts to retrieve the top-level\nworking tree path in a predictable, strictly formatted manner without\nrelying on `rev-parse`.\n\nIf requested in a bare repository where no working tree exists, the\ncommand returns an empty string.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 10 ++++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 35 +++++++++++++++++++++++++++++++++++\n 3 files changed, 69 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex ed7d80c690..e34abe5fea 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,16 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.toplevel.absolute`::\n+\tThe canonical absolute path to the top-level directory of the\n+\trepository's working tree. Outputs an empty string if the repository\n+\tis bare.\n+\n+`path.toplevel.relative`::\n+\tThe path to the top-level directory of the repository's working\n+\ttree relative to the current working directory. Outputs an empty\n+\tstring if the repository is bare.\n+\n `references.format`::\n \tThe reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 84e012f83f..c31e9cfa70 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -121,6 +121,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *work_tree = repo_get_work_tree(repo);\n+\n+\tif (!work_tree)\n+\t\treturn 0;\n+\n+\tformat_path(buf, work_tree, \"\", PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *work_tree = repo_get_work_tree(repo);\n+\n+\tif (!work_tree)\n+\t\treturn 0;\n+\n+\tformat_path(buf, work_tree, repo->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -137,6 +159,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n+\t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex c85d390f43..9417d1ab65 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,4 +213,39 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.toplevel absolute and relative' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tmkdir -p repo/sub &&\n+\t\tcd repo/sub &&\n+\n+\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n+\n+\t\techo \"path.toplevel.absolute=$ROOT\" >expect.abs &&\n+\t\tgit repo info path.toplevel.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.toplevel.relative=../\" >expect.rel &&\n+\t\tgit repo info path.toplevel.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n+test_expect_success 'path.toplevel absolute and relative in a bare repository' '\n+\ttest_when_finished \"rm -rf bare.git\" &&\n+\tgit init --bare bare.git &&\n+\t(\n+\t\tcd bare.git &&\n+\n+\t\techo \"path.toplevel.absolute=\" >expect.abs &&\n+\t\tgit repo info path.toplevel.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.toplevel.relative=\" >expect.rel &&\n+\t\tgit repo info path.toplevel.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n test_done\n-- \n2.55.GIT\n\n"},{"id":"551204","messageId":"20260825175818.645579-3-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260825175818.645579-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v5 2/7] repo: add path.superproject-root with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-25T17:58:13Z","receivedAt":"2026-08-25T17:59:23Z","isPatch":true,"body":"Scripts working in multi-repository setups often need to identify the\ntop-level working tree of a superproject from within a submodule.\nCurrently, this is only exposed via `git rev-parse\n--show-superproject-working-tree`.\n\nIntroduce `path.superproject-root.absolute` and\n`path.superproject-root.relative` keys to `git repo info`.\nThis exposes the core submodule context via a scriptable config-like key\nusing standard format rules.\n\nIf requested when not inside a submodule, the command returns an empty\nstring.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 10 ++++++++++\n builtin/repo.c              | 31 +++++++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 39 +++++++++++++++++++++++++++++++++++++\n 3 files changed, 80 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex e34abe5fea..e524a07f53 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,16 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.superproject-root.absolute`::\n+\tThe canonical absolute path to the working tree root of the superproject\n+\tif the current repository is an initialized submodule. Outputs an empty\n+\tstring if not in a submodule.\n+\n+`path.superproject-root.relative`::\n+\tThe path to the working tree root of the superproject relative to the\n+\tcurrent working directory if the current repository is an initialized\n+\tsubmodule. Outputs an empty string if not in a submodule.\n+\n `path.toplevel.absolute`::\n \tThe canonical absolute path to the top-level directory of the\n \trepository's working tree. Outputs an empty string if the repository\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex c31e9cfa70..47c4fce293 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -18,6 +18,7 @@\n #include \"strbuf.h\"\n #include \"string-list.h\"\n #include \"shallow.h\"\n+#include \"submodule.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"utf8.h\"\n@@ -121,6 +122,34 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\tstruct strbuf superproject = STRBUF_INIT;\n+\n+\tif (!get_superproject_working_tree(&superproject)) {\n+\t\tstrbuf_release(&superproject);\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, superproject.buf, \"\", PATH_FORMAT_CANONICAL);\n+\tstrbuf_release(&superproject);\n+\treturn 0;\n+}\n+\n+static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf superproject = STRBUF_INIT;\n+\n+\tif (!get_superproject_working_tree(&superproject)) {\n+\t\tstrbuf_release(&superproject);\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, superproject.buf, repo->prefix, PATH_FORMAT_RELATIVE);\n+\tstrbuf_release(&superproject);\n+\treturn 0;\n+}\n+\n static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *work_tree = repo_get_work_tree(repo);\n@@ -159,6 +188,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n+\t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n \t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n \t{ \"references.format\", get_references_format },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 9417d1ab65..eec576a1d9 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,45 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.superproject-root absolute and relative' '\n+\ttest_when_finished \"rm -rf sub super\" &&\n+\tgit init sub &&\n+\ttest_commit -C sub initial &&\n+\tgit init super &&\n+\t(\n+\t\tcd super &&\n+\t\tgit -c protocol.file.allow=always submodule add \"../sub\" sub &&\n+\t\tgit commit -m \"add submodule\" &&\n+\n+\t\tcd sub &&\n+\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n+\n+\t\techo \"path.superproject-root.absolute=$ROOT\" >expect.abs &&\n+\t\tgit repo info path.superproject-root.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.superproject-root.relative=../\" >expect.rel &&\n+\t\tgit repo info path.superproject-root.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n+test_expect_success 'path.superproject-root returns empty when not in a submodule' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\n+\t\techo \"path.superproject-root.absolute=\" >expect.abs &&\n+\t\tgit repo info path.superproject-root.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.superproject-root.relative=\" >expect.rel &&\n+\t\tgit repo info path.superproject-root.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n test_expect_success 'path.toplevel absolute and relative' '\n \ttest_when_finished \"rm -rf repo\" &&\n \tgit init repo &&\n-- \n2.55.GIT\n\n"},{"id":"551205","messageId":"20260825175818.645579-4-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260825175818.645579-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v5 3/7] repo: add path.hooks with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-25T17:58:14Z","receivedAt":"2026-08-25T17:59:36Z","isPatch":true,"body":"Hooks are an integral part of a repository's configuration and are\ncommonly used by tooling to automate repository-specific workflows.\nCurrently, scripts typically retrieve the hooks directory by invoking\n`git rev-parse --git-path hooks`.\n\nIntroduce `path.hooks.absolute` and `path.hooks.relative` keys to\n`git repo info`. This exposes the hooks directory as a scriptable\nconfig-like key using standard format rules, allowing scripts to\nretrieve it through the same interface as other repository path\ninformation.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  9 +++++++++\n builtin/repo.c              | 22 ++++++++++++++++++++++\n t/t1900-repo-info.sh        | 28 ++++++++++++++++++++++++++--\n 3 files changed, 57 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex e524a07f53..20836cf8f6 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,15 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.hooks.absolute`::\n+\tThe canonical absolute path to the repository's hooks directory.\n+\tRespects the `core.hooksPath` configuration. If `core.hooksPath` is\n+\tset to `/dev/null`, that value is returned unchanged.\n+\n+`path.hooks.relative`::\n+\tThe path to the repository's hooks directory relative to the current\n+\tworking directory. Respects the `core.hooksPath` configuration.\n+\n `path.superproject-root.absolute`::\n \tThe canonical absolute path to the working tree root of the superproject\n \tif the current repository is an initialized submodule. Outputs an empty\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 47c4fce293..d7c451a771 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,26 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf hooks_path = STRBUF_INIT;\n+\n+\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n+\tformat_path(buf, hooks_path.buf, \"\", PATH_FORMAT_CANONICAL);\n+\tstrbuf_release(&hooks_path);\n+\treturn 0;\n+}\n+\n+static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf hooks_path = STRBUF_INIT;\n+\n+\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n+\tformat_path(buf, hooks_path.buf, repo->prefix, PATH_FORMAT_RELATIVE);\n+\tstrbuf_release(&hooks_path);\n+\treturn 0;\n+}\n+\n static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n {\n \tstruct strbuf superproject = STRBUF_INIT;\n@@ -188,6 +208,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n+\t{ \"path.hooks.relative\", get_path_hooks_relative },\n \t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n \t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex eec576a1d9..1da5db4942 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -174,7 +174,11 @@ test_repo_info_path () {\n \t\t\tcd repo/sub &&\n \t\t\tROOT=\"$(test-tool path-utils real_path ..)\" && export ROOT &&\n \t\t\teval \"$init_command\" &&\n-\t\t\techo \"path.$field_name.absolute=$ROOT/$expected_dir\" >expect &&\n+\t\t\tcase \"$expected_dir\" in\n+\t\t\t/*) EXPECT_ABS=\"$expected_dir\" ;;\n+\t\t\t*) EXPECT_ABS=\"$ROOT/$expected_dir\" ;;\n+\t\t\tesac &&\n+\t\t\techo \"path.$field_name.absolute=$EXPECT_ABS\" >expect &&\n \t\t\tgit repo info \"path.$field_name.absolute\" >actual &&\n \t\t\ttest_cmp expect actual\n \t\t)\n@@ -188,7 +192,11 @@ test_repo_info_path () {\n \t\t\tcd repo/sub &&\n \t\t\tROOT=\"$(test-tool path-utils real_path ..)\" && export ROOT &&\n \t\t\teval \"$init_command\" &&\n-\t\t\techo \"path.$field_name.relative=../$expected_dir\" >expect &&\n+\t\t\tcase \"$expected_dir\" in\n+\t\t\t/*) EXPECT_REL=\"$(test-tool path-utils relative_path \"$expected_dir\" \"$PWD\")\" ;;\n+\t\t\t*) EXPECT_REL=\"../$expected_dir\" ;;\n+\t\t\tesac &&\n+\t\t\techo \"path.$field_name.relative=$EXPECT_REL\" >expect &&\n \t\t\tgit repo info \"path.$field_name.relative\" >actual &&\n \t\t\ttest_cmp expect actual\n \t\t)\n@@ -213,6 +221,22 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'\n+\n+test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n+\t'custom-hooks' \\\n+\t'git config core.hooksPath \"$ROOT/custom-hooks\" && mkdir -p \"$ROOT/custom-hooks\"'\n+\n+# /dev/null is not a real, canonicalizable filesystem path on Windows,\n+# so path resolution for core.hooksPath=/dev/null cannot be expected to\n+# produce a literal \"/dev/null\" the way it does on POSIX systems.\n+if ! test_have_prereq MINGW\n+then\n+\ttest_repo_info_path 'hooks with core.hooksPath=/dev/null' 'hooks' \\\n+\t\t'/dev/null' \\\n+\t\t'git config core.hooksPath /dev/null'\n+fi\n+\n test_expect_success 'path.superproject-root absolute and relative' '\n \ttest_when_finished \"rm -rf sub super\" &&\n \tgit init sub &&\n-- \n2.55.GIT\n\n"},{"id":"551206","messageId":"20260825175818.645579-5-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260825175818.645579-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v5 4/7] repo: add path.index with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-25T17:58:15Z","receivedAt":"2026-08-25T17:59:50Z","isPatch":true,"body":"The repository index is a fundamental component used by Git and related\ntooling to track the working tree state. Scripts that interact with the\nindex currently retrieve its location by invoking\n`git rev-parse --git-path index`.\n\nIntroduce `path.index.absolute` and `path.index.relative` keys to\n`git repo info`. This exposes the index file location as a scriptable\nconfig-like key using standard format rules, allowing scripts to\nretrieve it through the same interface as other repository path\ninformation.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 12 ++++++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++\n 3 files changed, 59 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 20836cf8f6..08ef47750c 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -128,6 +128,18 @@ values that they return:\n \tThe path to the repository's hooks directory relative to the current\n \tworking directory. Respects the `core.hooksPath` configuration.\n \n+`path.index.absolute`::\n+\tThe canonical absolute path to the repository's current index file.\n+\tRespects the `GIT_INDEX_FILE` environment override. Returns the\n+\tconfigured index path even if the repository is bare or the file does\n+\tnot exist.\n+\n+`path.index.relative`::\n+\tThe path to the repository's current index file relative to the current\n+\tworking directory. Respects the `GIT_INDEX_FILE` environment override.\n+\tReturns the configured index path even if the repository is bare or the\n+\tfile does not exist.\n+\n `path.superproject-root.absolute`::\n \tThe canonical absolute path to the working tree root of the superproject\n \tif the current repository is an initialized submodule. Outputs an empty\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex d7c451a771..2a15327094 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -142,6 +142,28 @@ static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *index_file = repo_get_index_file(repo);\n+\n+\tif (!index_file)\n+\t\treturn error(_(\"unable to get index file\"));\n+\n+\tformat_path(buf, index_file, \"\", PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *index_file = repo_get_index_file(repo);\n+\n+\tif (!index_file)\n+\t\treturn error(_(\"unable to get index file\"));\n+\n+\tformat_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n {\n \tstruct strbuf superproject = STRBUF_INIT;\n@@ -210,6 +232,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n \t{ \"path.hooks.relative\", get_path_hooks_relative },\n+\t{ \"path.index.absolute\", get_path_index_absolute },\n+\t{ \"path.index.relative\", get_path_index_relative },\n \t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n \t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 1da5db4942..431a4842d4 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -237,6 +237,29 @@ then\n \t\t'git config core.hooksPath /dev/null'\n fi\n \n+test_repo_info_path 'index standard' 'index' '.git/index'\n+\n+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \\\n+\t'custom-index-file' \\\n+\t'GIT_INDEX_FILE=\"$ROOT/custom-index-file\" && export GIT_INDEX_FILE'\n+\n+test_expect_success 'path.index in a bare repository returns default index location' '\n+\ttest_when_finished \"rm -rf bare.git\" &&\n+\tgit init --bare bare.git &&\n+\t(\n+\t\tcd bare.git &&\n+\t\tROOT=\"$(test-tool path-utils real_path .)\" &&\n+\n+\t\techo \"path.index.absolute=$ROOT/index\" >expect.abs &&\n+\t\tgit repo info path.index.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.index.relative=index\" >expect.rel &&\n+\t\tgit repo info path.index.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n test_expect_success 'path.superproject-root absolute and relative' '\n \ttest_when_finished \"rm -rf sub super\" &&\n \tgit init sub &&\n-- \n2.55.GIT\n\n"},{"id":"551207","messageId":"20260825175818.645579-6-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260825175818.645579-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v5 5/7] repo: add path.grafts with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-25T17:58:16Z","receivedAt":"2026-08-25T17:59:55Z","isPatch":true,"body":"The repository grafts file specifies alternate parent relationships for\ncommits and may be used by repository tooling that needs to inspect or\nmanage grafts. Scripts currently retrieve its location by invoking\n`git rev-parse --git-path info/grafts`.\n\nIntroduce `path.grafts.absolute` and `path.grafts.relative` keys to\n`git repo info`. This exposes the grafts file location as a scriptable\nconfig-like key using standard format rules, allowing scripts to\nretrieve it through the same interface as other repository path\ninformation.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 11 +++++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        |  6 ++++++\n 3 files changed, 41 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 08ef47750c..868ab0ed9f 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,17 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.grafts.absolute`::\n+\tThe canonical absolute path to the repository's graft file.\n+\tRespects the `GIT_GRAFT_FILE` environment override. The path is\n+\treturned regardless of whether the file currently exists on disk.\n+\n+`path.grafts.relative`::\n+\tThe path to the repository's graft file relative to the current\n+\tworking directory. Respects the `GIT_GRAFT_FILE` environment\n+\toverride. The path is returned regardless of whether the file\n+\tcurrently exists on disk.\n+\n `path.hooks.absolute`::\n \tThe canonical absolute path to the repository's hooks directory.\n \tRespects the `core.hooksPath` configuration. If `core.hooksPath` is\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 2a15327094..779240109d 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_grafts_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *graft_file = repo_get_graft_file(repo);\n+\n+\tif (!graft_file)\n+\t\treturn error(_(\"unable to get graft file\"));\n+\n+\tformat_path(buf, graft_file, \"\", PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_grafts_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *graft_file = repo_get_graft_file(repo);\n+\n+\tif (!graft_file)\n+\t\treturn error(_(\"unable to get graft file\"));\n+\n+\tformat_path(buf, graft_file, repo->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tstruct strbuf hooks_path = STRBUF_INIT;\n@@ -230,6 +252,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.grafts.absolute\", get_path_grafts_absolute },\n+\t{ \"path.grafts.relative\", get_path_grafts_relative },\n \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n \t{ \"path.hooks.relative\", get_path_hooks_relative },\n \t{ \"path.index.absolute\", get_path_index_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 431a4842d4..adc4a92487 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -221,6 +221,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'grafts standard' 'grafts' '.git/info/grafts'\n+\n+test_repo_info_path 'grafts with GIT_GRAFT_FILE override' 'grafts' \\\n+\t'custom-graft-file' \\\n+\t'GIT_GRAFT_FILE=\"$ROOT/custom-graft-file\" && export GIT_GRAFT_FILE'\n+\n test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'\n \n test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n-- \n2.55.GIT\n\n"},{"id":"551208","messageId":"20260825175818.645579-7-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260825175818.645579-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v5 6/7] repo: add path.git-prefix","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-25T17:58:17Z","receivedAt":"2026-08-25T18:00:08Z","isPatch":true,"body":"Scripts sometimes need the path from the repository's working tree root\nto the current working directory. While this information can be derived\nthrough existing Git commands, `git repo info` does not currently expose\nit as a scriptable key.\n\nIntroduce the `path.git-prefix` key to `git repo info`. The key returns\nthe path from the working tree root to the current working directory,\nreturning the empty string when invoked from the working tree root.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  5 +++++\n builtin/repo.c              | 11 +++++++++++\n t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++\n 3 files changed, 39 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 868ab0ed9f..fb5aceae8f 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -113,6 +113,11 @@ values that they return:\n \tThe path to the Git repository's common directory relative to\n \tthe current working directory.\n \n+`path.git-prefix`::\n+\tThe path from the root of the working tree to the current working\n+\tdirectory. Returns the empty string when the current working directory\n+\tis the root of the working tree.\n+\n `path.gitdir.absolute`::\n \tThe canonical absolute path to the Git repository directory (the `.git` directory).\n \ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 779240109d..c0f99b6869 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -100,6 +100,16 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b\n \treturn 0;\n }\n \n+static int get_path_git_prefix(struct repository *repo, struct strbuf *buf)\n+{\n+\t/*\n+\t * repo->prefix is NULL when the current working directory is\n+\t * the worktree root.\n+\t */\n+\tstrbuf_addstr(buf, repo->prefix ? repo->prefix : \"\");\n+\treturn 0;\n+}\n+\n static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *git_dir = repo_get_git_dir(repo);\n@@ -250,6 +260,7 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"object.format\", get_object_format },\n \t{ \"path.commondir.absolute\", get_path_commondir_absolute },\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n+\t{ \"path.git-prefix\", get_path_git_prefix },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n \t{ \"path.grafts.absolute\", get_path_grafts_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex adc4a92487..b689445b7a 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -215,6 +215,29 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.git-prefix at repository root' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\techo \"path.git-prefix=\" >expect &&\n+\t\tgit repo info path.git-prefix >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'path.git-prefix in subdirectory' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\tmkdir -p repo/sub/dir &&\n+\t(\n+\t\tcd repo/sub/dir &&\n+\t\techo \"path.git-prefix=sub/dir/\" >expect &&\n+\t\tgit repo info path.git-prefix >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_repo_info_path 'gitdir standard' 'gitdir' '.git'\n \n test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n-- \n2.55.GIT\n\n"},{"id":"551209","messageId":"20260825175818.645579-8-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260825175818.645579-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v5 7/7] repo: add path.cdup","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-08-25T17:58:18Z","receivedAt":"2026-08-25T18:00:20Z","isPatch":true,"body":"Scripts sometimes need the relative path from the current working\ndirectory to the repository's working tree root (cdup). While this\ninformation can be retrieved through `git rev-parse --show-cdup`,\n`git repo info` does not currently expose it as a scriptable key.\n\nIntroduce the `path.cdup` key to `git repo info`. The key returns the\npath from the current working directory to the root of the working tree,\nreturning the empty string when invoked from the working tree root.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  5 +++++\n builtin/repo.c              | 15 +++++++++++++++\n t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++\n 3 files changed, 43 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex fb5aceae8f..0a30c0d6ab 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -104,6 +104,11 @@ values that they return:\n `object.format`::\n \tThe object format (hash algorithm) used in the repository.\n \n+`path.cdup`::\n+\tThe path to the root of the working tree relative to the current\n+\tworking directory. Returns the empty string when the current\n+\tworking directory is the root of the working tree.\n+\n `path.commondir.absolute`::\n \tThe canonical absolute path to the Git repository's common\n \tdirectory (the shared `.git` directory containing objects,\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex c0f99b6869..2658e8be5c 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -78,6 +78,20 @@ static int get_object_format(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_cdup(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *pfx = repo->prefix;\n+\n+\twhile (pfx) {\n+\t\tpfx = strchr(pfx, '/');\n+\t\tif (pfx) {\n+\t\t\tpfx++;\n+\t\t\tstrbuf_addstr(buf, \"../\");\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n static int get_path_commondir_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *common_dir = repo_get_common_dir(repo);\n@@ -258,6 +272,7 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"layout.bare\", get_layout_bare },\n \t{ \"layout.shallow\", get_layout_shallow },\n \t{ \"object.format\", get_object_format },\n+\t{ \"path.cdup\", get_path_cdup },\n \t{ \"path.commondir.absolute\", get_path_commondir_absolute },\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.git-prefix\", get_path_git_prefix },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex b689445b7a..aa8e59510f 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -215,6 +215,29 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.cdup at repository root' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\techo \"path.cdup=\" >expect &&\n+\t\tgit repo info path.cdup >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'path.cdup in subdirectory' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\tmkdir -p repo/sub/dir &&\n+\t(\n+\t\tcd repo/sub/dir &&\n+\t\techo \"path.cdup=../../\" >expect &&\n+\t\tgit repo info path.cdup >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_expect_success 'path.git-prefix at repository root' '\n \ttest_when_finished \"rm -rf repo\" &&\n \tgit init repo &&\n-- \n2.55.GIT\n\n"},{"id":"551231","messageId":"xmqq7blem1y6.fsf@gitster.g","threadId":"66010","inReplyTo":"20260825175818.645579-8-jayatheerthkulkarni2005@gmail.com","subject":"Re: [GSoC Patch v5 7/7] repo: add path.cdup","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-25T19:31:45Z","receivedAt":"2026-08-25T19:31:48Z","isPatch":true,"body":"K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n\n> +static int get_path_cdup(struct repository *repo, struct strbuf *buf)\n> +{\n> +\tconst char *pfx = repo->prefix;\n> +\n> +\twhile (pfx) {\n> +\t\tpfx = strchr(pfx, '/');\n> +\t\tif (pfx) {\n> +\t\t\tpfx++;\n> +\t\t\tstrbuf_addstr(buf, \"../\");\n> +\t\t}\n> +\t}\n> +\treturn 0;\n> +}\n\nCan we have a small common helper library (e.g., repo-info.c at the\ntop level) shared between this code and builtin/rev-parse.c so\nthat we have only a single implementation of 'cdup'?\n\nThe same comment applies to all the other features that duplicate\nimplementations from rev-parse and elsewhere in the repo-info file.\n"},{"id":"551264","messageId":"ao6UWnHfOK9VkD4n@szeder.dev","threadId":"66010","inReplyTo":"20260825175818.645579-1-jayatheerthkulkarni2005@gmail.com","subject":"Re: [GSoC Patch v5 0/7] add more path keys to git repo info","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2026-08-26T07:23:06Z","receivedAt":"2026-08-26T07:23:19Z","isPatch":true,"body":"Junio,\n\nOn Tue, Aug 25, 2026 at 11:28:11PM +0530, K Jayatheerth wrote:\n>     +@@ builtin/repo.c: static int get_object_format(struct repository *repo, struct strbuf *buf)\n>     + \treturn 0;\n>     + }\n>     + \n>     ++static int get_path_cdup(struct repository *repo, struct strbuf *buf)\n>     ++{\n>     ++\tconst char *pfx = repo->prefix;\n\nThis patch series depends on \"repo->prefix\", but it's queued on top of\n41365c2a9b (The 4th batch for Git 2.56, 2026-07-16), where this field\ndoesn't exist yet, resulting in not buildable commits.\n\nThe series introducing \"repo->prefix\" was merged in 2688b28699 (Merge\nbranch 'ps/setup-split-discovery-and-setup', 2026-07-19).\n\n"},{"id":"551284","messageId":"xmqqtsohj77p.fsf@gitster.g","threadId":"66010","inReplyTo":"ao6UWnHfOK9VkD4n@szeder.dev","subject":"Re: [GSoC Patch v5 0/7] add more path keys to git repo info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-26T14:18:34Z","receivedAt":"2026-08-26T14:18:37Z","isPatch":true,"body":"SZEDER Gábor <szeder.dev@gmail.com> writes:\n\n> This patch series depends on \"repo->prefix\", but it's queued on top of\n> 41365c2a9b (The 4th batch for Git 2.56, 2026-07-16), where this field\n> doesn't exist yet, resulting in not buildable commits.\n\nThanks.  Will queue on top of more recent master.\n"},{"id":"551966","messageId":"CA+rGoLcZ6u+Rbz2PNGiaPbeHB=LSAqxi0r6jYZL3RjG6wimJ3Q@mail.gmail.com","threadId":"66010","inReplyTo":"xmqq7blem1y6.fsf@gitster.g","subject":"Re: [GSoC Patch v5 7/7] repo: add path.cdup","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-04T14:37:06Z","receivedAt":"2026-09-04T14:37:18Z","isPatch":true,"body":"Hey Junio,\n\nOn Wed, Aug 26, 2026 at 1:01 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n>\n> > +static int get_path_cdup(struct repository *repo, struct strbuf *buf)\n> > +{\n> > +     const char *pfx = repo->prefix;\n> > +\n> > +     while (pfx) {\n> > +             pfx = strchr(pfx, '/');\n> > +             if (pfx) {\n> > +                     pfx++;\n> > +                     strbuf_addstr(buf, \"../\");\n> > +             }\n> > +     }\n> > +     return 0;\n> > +}\n>\n> Can we have a small common helper library (e.g., repo-info.c at the\n> top level) shared between this code and builtin/rev-parse.c so\n> that we have only a single implementation of 'cdup'?\n>\n> The same comment applies to all the other features that duplicate\n> implementations from rev-parse and elsewhere in the repo-info file.\n\nApologies for the late response, I had a hectic exam week : )\n\nI started working on this, but I wanted to check how to\nstructure the commits cleanly.\n\nWould it be okay to queue this `path.*` series as-is first, and\nthen address the deduplication in a follow-up refactoring series\nright after (provided there are no other concerns)?\n\nGrouping the new `git repo info` keys with the cleanup of\n`git rev-parse` in a single series would mix feature additions with\nlegacy refactoring. Additionally, since earlier path keys are\nalready merged, moving the shared library to a follow-up series\nkeeps both topics clear.\n\nIn that follow-up set, I can introduce the library alongside porting\nthe first command to use it, and then port the remaining overlapping\ncommands one by one in subsequent commits so each patch remains a\nself-contained, bisectable change.\n\nPlease let me know if this works for you!\n\nRegards,\n- K Jayatheerth\n"},{"id":"552010","messageId":"xmqqh5k43bzo.fsf@gitster.g","threadId":"66010","inReplyTo":"20260825175818.645579-3-jayatheerthkulkarni2005@gmail.com","subject":"Re: [GSoC Patch v5 2/7] repo: add path.superproject-root with absolute and relative suffixes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-09-04T22:09:15Z","receivedAt":"2026-09-04T22:09:18Z","isPatch":true,"body":"K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n\n> +static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n> +{\n> +\tstruct strbuf superproject = STRBUF_INIT;\n> +\n> +\tif (!get_superproject_working_tree(&superproject)) {\n> +\t\tstrbuf_release(&superproject);\n> +\t\treturn 0;\n> +\t}\n> +\n> +\tformat_path(buf, superproject.buf, \"\", PATH_FORMAT_CANONICAL);\n> +\tstrbuf_release(&superproject);\n> +\treturn 0;\n> +}\n> +\n> +static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)\n> +{\n> +\tstruct strbuf superproject = STRBUF_INIT;\n> +\n> +\tif (!get_superproject_working_tree(&superproject)) {\n> +\t\tstrbuf_release(&superproject);\n> +\t\treturn 0;\n> +\t}\n\nHere get_superproject_working_tree() does not care what repository\nwe are working on.  Shouldn't it be updated to take \"repo\" as a\nparameter?\n\nSince it begins like this:\n\n    int get_superproject_working_tree(struct strbuf *buf)\n    {\n            struct child_process cp = CHILD_PROCESS_INIT;\n            struct strbuf sb = STRBUF_INIT;\n            struct strbuf one_up = STRBUF_INIT;\n            char *cwd = xgetcwd();\n            int ret = 0;\n\nI suspect that it based its decision on where you happen to be.\n\nIt means that when I have a checkout of \"git\", with a submodule\n\"sha1collisiondetection\" at its top level already populated, in,\nsay, /var/tmp/x/ directory, the following happens.\n\n    $ cd /var/tmp/x/git\n    $ git repo info path.gitdir.absolute\n    path.gitdir.absolute=/var/tmp/x/git/.git\n    $ git -C sha1collisiondetection repo info path.gitdir.absolute\n    path.gitdir.absolute=/var/tmp/x/git/.git/modules/sha1collisiondetection\n\n    $ D=/var/tmp/x/git/.git/modules/sha1collisiondetection\n    $ git --git-dir=\"$D\" repo info path.superproject-root.absolute\n    path.superproject-root.absolute=\n    $ git -C sha1collisiondetection repo info path.superproject-root.absolute\n    path.superproject-root.absolute=/var/tmp/x/git\n\nThe last two ought to match, but only the latter works correctly.\n\nBefore this series starts reporting path.superproject-root,\nget_superproject_working_tree() needs to be corrected to work on the\nrepository in question (instead of relying on where the process\nhappens to be), no?\n"},{"id":"552025","messageId":"CA+rGoLf-BMh_BNUwi4k7NCLakbxP8vTuLbQKctX2K6MTK5b1yA@mail.gmail.com","threadId":"66010","inReplyTo":"xmqqh5k43bzo.fsf@gitster.g","subject":"Re: [GSoC Patch v5 2/7] repo: add path.superproject-root with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-05T10:01:14Z","receivedAt":"2026-09-05T10:01:26Z","isPatch":true,"body":"On Sat, Sep 5, 2026 at 3:39 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n>\n> > +static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n> > +{\n> > +     struct strbuf superproject = STRBUF_INIT;\n> > +\n> > +     if (!get_superproject_working_tree(&superproject)) {\n> > +             strbuf_release(&superproject);\n> > +             return 0;\n> > +     }\n> > +\n> > +     format_path(buf, superproject.buf, \"\", PATH_FORMAT_CANONICAL);\n> > +     strbuf_release(&superproject);\n> > +     return 0;\n> > +}\n> > +\n> > +static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)\n> > +{\n> > +     struct strbuf superproject = STRBUF_INIT;\n> > +\n> > +     if (!get_superproject_working_tree(&superproject)) {\n> > +             strbuf_release(&superproject);\n> > +             return 0;\n> > +     }\n>\n> Here get_superproject_working_tree() does not care what repository\n> we are working on.  Shouldn't it be updated to take \"repo\" as a\n> parameter?\n>\n> Since it begins like this:\n>\n>     int get_superproject_working_tree(struct strbuf *buf)\n>     {\n>             struct child_process cp = CHILD_PROCESS_INIT;\n>             struct strbuf sb = STRBUF_INIT;\n>             struct strbuf one_up = STRBUF_INIT;\n>             char *cwd = xgetcwd();\n>             int ret = 0;\n>\n> I suspect that it based its decision on where you happen to be.\n>\n> It means that when I have a checkout of \"git\", with a submodule\n> \"sha1collisiondetection\" at its top level already populated, in,\n> say, /var/tmp/x/ directory, the following happens.\n>\n>     $ cd /var/tmp/x/git\n>     $ git repo info path.gitdir.absolute\n>     path.gitdir.absolute=/var/tmp/x/git/.git\n>     $ git -C sha1collisiondetection repo info path.gitdir.absolute\n>     path.gitdir.absolute=/var/tmp/x/git/.git/modules/sha1collisiondetection\n>\n>     $ D=/var/tmp/x/git/.git/modules/sha1collisiondetection\n>     $ git --git-dir=\"$D\" repo info path.superproject-root.absolute\n>     path.superproject-root.absolute=\n>     $ git -C sha1collisiondetection repo info path.superproject-root.absolute\n>     path.superproject-root.absolute=/var/tmp/x/git\n>\n> The last two ought to match, but only the latter works correctly.\n>\n> Before this series starts reporting path.superproject-root,\n> get_superproject_working_tree() needs to be corrected to work on the\n> repository in question (instead of relying on where the process\n> happens to be), no?\n\nYes, precisely.\nI didn't think of it that way.\n\n`get_superproject_working_tree()` currently relies on xgetcwd() rather\nthan inspecting the target repository, which causes explicit repository\ncontexts like --git-dir to break.\n\nI will update `get_superproject_working_tree()` to take repo\nand update its callers accordingly, along with adding test coverage for\n--git-dir in v6.\n\nRegards,\n- K Jayatheerth\n"},{"id":"552554","messageId":"20260911144519.1011780-1-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260716012138.6714-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v6 0/7] add more path keys to git repo info","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-11T14:45:12Z","receivedAt":"2026-09-11T14:46:10Z","isPatch":true,"body":"Series adds keys to git repo info.\n\nKeys output paths of repository components:\n* path.toplevel: repository tree.\n* path.superproject-root: superproject tree from submodules.\n* path.hooks: repository hooks.\n* path.index: repository index.\n* path.grafts: repository grafts.\n* path.git-prefix: prefix offset.\n* path.cdup: relative path to top level from subdirectory.\n\nKeys support suffixes for format.\nCommits contain documentation and tests.\n\nchanges since v5:\n* Refactored get_superproject_working_tree(struct repository *r, ...): Updated\n  function signature and callers (builtin/rev-parse.c, builtin/repo.c)\n  to accept repo, eliminating xgetcwd().\n\nNote on path.cdup duplication:\nAs discussed in v5, path.cdup currently duplicates the prefix-counting\nlogic from rev-parse to keep this feature arc self-contained.\nA follow-up refactoring series will be sent immediately after this\nto introduce a shared repo-info helper library, deduplicating this\nand other overlapping path logic across repo and rev-parse.\n\nK Jayatheerth (7):\n  repo: add path.toplevel with absolute and relative suffix formatting\n  repo: add path.superproject-root with absolute and relative suffixes\n  repo: add path.hooks with absolute and relative suffixes\n  repo: add path.index with absolute and relative suffixes\n  repo: add path.grafts with absolute and relative suffixes\n  repo: add path.git-prefix\n  repo: add path.cdup\n\n Documentation/git-repo.adoc |  62 ++++++++++++\n builtin/repo.c              | 151 ++++++++++++++++++++++++++++\n builtin/rev-parse.c         |   2 +-\n submodule.c                 |  43 ++++----\n submodule.h                 |   2 +-\n t/t1900-repo-info.sh        | 195 +++++++++++++++++++++++++++++++++++-\n 6 files changed, 430 insertions(+), 25 deletions(-)\n\nRange-diff against v5:\n1:  4ce9dcb24e = 1:  3880485020 repo: add path.toplevel with absolute and relative suffix formatting\n2:  c431008d40 ! 2:  e75e17b44e repo: add path.superproject-root with absolute and relative suffixes\n    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str\n      \treturn 0;\n      }\n      \n    -+static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n    ++static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)\n     +{\n     +\tstruct strbuf superproject = STRBUF_INIT;\n     +\n    -+\tif (!get_superproject_working_tree(&superproject)) {\n    ++\tif (!get_superproject_working_tree(repo, &superproject)) {\n     +\t\tstrbuf_release(&superproject);\n     +\t\treturn 0;\n     +\t}\n    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str\n     +{\n     +\tstruct strbuf superproject = STRBUF_INIT;\n     +\n    -+\tif (!get_superproject_working_tree(&superproject)) {\n    ++\tif (!get_superproject_working_tree(repo, &superproject)) {\n     +\t\tstrbuf_release(&superproject);\n     +\t\treturn 0;\n     +\t}\n    @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {\n      \t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n      \t{ \"references.format\", get_references_format },\n     \n    + ## builtin/rev-parse.c ##\n    +@@ builtin/rev-parse.c: int cmd_rev_parse(int argc,\n    + \t\t\t}\n    + \t\t\tif (!strcmp(arg, \"--show-superproject-working-tree\")) {\n    + \t\t\t\tstruct strbuf superproject = STRBUF_INIT;\n    +-\t\t\t\tif (get_superproject_working_tree(&superproject))\n    ++\t\t\t\tif (get_superproject_working_tree(the_repository, &superproject))\n    + \t\t\t\t\tprint_path(superproject.buf, prefix, format, DEFAULT_UNMODIFIED);\n    + \t\t\t\tstrbuf_release(&superproject);\n    + \t\t\t\tcontinue;\n    +\n    + ## submodule.c ##\n    +@@ submodule.c: void absorb_git_dir_into_superproject(const char *path,\n    + \tabsorb_git_dir_into_superproject_recurse(path, super_prefix);\n    + }\n    + \n    +-int get_superproject_working_tree(struct strbuf *buf)\n    ++int get_superproject_working_tree(struct repository *r, struct strbuf *buf)\n    + {\n    + \tstruct child_process cp = CHILD_PROCESS_INIT;\n    + \tstruct strbuf sb = STRBUF_INIT;\n    + \tstruct strbuf one_up = STRBUF_INIT;\n    +-\tchar *cwd = xgetcwd();\n    ++\tstruct strbuf target_wt = STRBUF_INIT;\n    ++\tconst char *worktree;\n    + \tint ret = 0;\n    + \tconst char *subpath;\n    + \tint code;\n    + \tssize_t len;\n    + \n    +-\tif (!is_inside_work_tree(the_repository))\n    +-\t\t/*\n    +-\t\t * FIXME:\n    +-\t\t * We might have a superproject, but it is harder\n    +-\t\t * to determine.\n    +-\t\t */\n    ++\tworktree = repo_get_work_tree(r);\n    ++\tif (!worktree)\n    ++\t\tgoto out;\n    ++\n    ++\tif (!strbuf_realpath(&target_wt, worktree, 0))\n    + \t\tgoto out;\n    + \n    +-\tif (!strbuf_realpath(&one_up, \"../\", 0))\n    ++\tstrbuf_addf(&one_up, \"%s/..\", target_wt.buf);\n    ++\tif (!strbuf_realpath(&one_up, one_up.buf, 0))\n    + \t\tgoto out;\n    + \n    +-\tsubpath = relative_path(cwd, one_up.buf, &sb);\n    ++\tsubpath = relative_path(target_wt.buf, one_up.buf, &sb);\n    + \n    + \tprepare_submodule_repo_env(&cp.env);\n    + \tstrvec_pop(&cp.env);\n    + \n    +-\tstrvec_pushl(&cp.args, \"--literal-pathspecs\", \"-C\", \"..\",\n    ++\tstrvec_pushl(&cp.args, \"--literal-pathspecs\", \"-C\", one_up.buf,\n    + \t\t     \"ls-files\", \"-z\", \"--stage\", \"--full-name\", \"--\",\n    + \t\t     subpath, NULL);\n    + \tstrbuf_reset(&sb);\n    +@@ submodule.c: int get_superproject_working_tree(struct strbuf *buf)\n    + \tcp.git_cmd = 1;\n    + \n    + \tif (start_command(&cp))\n    +-\t\tdie(_(\"could not start ls-files in ..\"));\n    ++\t\tdie(_(\"could not start ls-files in %s\"), one_up.buf);\n    + \n    + \tlen = strbuf_read(&sb, cp.out, PATH_MAX);\n    + \tclose(cp.out);\n    + \n    + \tif (starts_with(sb.buf, \"160000\")) {\n    + \t\tint super_sub_len;\n    +-\t\tint cwd_len = strlen(cwd);\n    ++\t\tint wt_len = target_wt.len;\n    + \t\tchar *super_sub, *super_wt;\n    + \n    + \t\t/*\n    +@@ submodule.c: int get_superproject_working_tree(struct strbuf *buf)\n    + \t\tsuper_sub = strchr(sb.buf, '\\t') + 1;\n    + \t\tsuper_sub_len = strlen(super_sub);\n    + \n    +-\t\tif (super_sub_len > cwd_len ||\n    +-\t\t    strcmp(&cwd[cwd_len - super_sub_len], super_sub))\n    +-\t\t\tBUG(\"returned path string doesn't match cwd?\");\n    ++\t\tif (super_sub_len > wt_len ||\n    ++\t\t    strcmp(&target_wt.buf[wt_len - super_sub_len], super_sub))\n    ++\t\t\tBUG(\"returned path string doesn't match worktree?\");\n    + \n    +-\t\tsuper_wt = xstrdup(cwd);\n    +-\t\tsuper_wt[cwd_len - super_sub_len] = '\\0';\n    ++\t\tsuper_wt = xstrdup(target_wt.buf);\n    ++\t\tsuper_wt[wt_len - super_sub_len] = '\\0';\n    + \n    + \t\tstrbuf_realpath(buf, super_wt, 1);\n    + \t\tret = 1;\n    +@@ submodule.c: int get_superproject_working_tree(struct strbuf *buf)\n    + \tcode = finish_command(&cp);\n    + \n    + \tif (code == 128)\n    +-\t\t/* '../' is not a git repository */\n    ++\t\t/* parent directory is not a git repository */\n    + \t\tret = 0;\n    + \telse if (code == 0 && len == 0)\n    +-\t\t/* There is an unrelated git repository at '../' */\n    ++\t\t/* There is an unrelated git repository at parent directory */\n    + \t\tret = 0;\n    + \telse if (code)\n    + \t\tdie(_(\"ls-tree returned unexpected return code %d\"), code);\n    +@@ submodule.c: int get_superproject_working_tree(struct strbuf *buf)\n    + out:\n    + \tstrbuf_release(&sb);\n    + \tstrbuf_release(&one_up);\n    +-\tfree(cwd);\n    ++\tstrbuf_release(&target_wt);\n    + \treturn ret;\n    + }\n    + \n    +\n    + ## submodule.h ##\n    +@@ submodule.h: void absorb_git_dir_into_superproject(const char *path,\n    +  * project is a submodule of. If this repository is not a submodule of\n    +  * another repository, return 0.\n    +  */\n    +-int get_superproject_working_tree(struct strbuf *buf);\n    ++int get_superproject_working_tree(struct repository *r, struct strbuf *buf);\n    + \n    + #endif\n    +\n      ## t/t1900-repo-info.sh ##\n     @@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n      \t'.git' \\\n    @@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir\n      test_expect_success 'path.toplevel absolute and relative' '\n      \ttest_when_finished \"rm -rf repo\" &&\n      \tgit init repo &&\n    +@@ t/t1900-repo-info.sh: test_expect_success 'path.toplevel absolute and relative in a bare repository' '\n    + \t)\n    + '\n    + \n    ++test_expect_success 'path.superproject-root works with --git-dir' '\n    ++\ttest_when_finished \"rm -rf sub super\" &&\n    ++\tgit init sub &&\n    ++\ttest_commit -C sub initial &&\n    ++\tgit init super &&\n    ++\t(\n    ++\t\tcd super &&\n    ++\t\tgit -c protocol.file.allow=always submodule add \"../sub\" sub &&\n    ++\t\tgit commit -m \"add submodule\" &&\n    ++\n    ++\t\tSUPER_ROOT=\"$(test-tool path-utils real_path .)\" &&\n    ++\t\tMODULE_DIR=\"$SUPER_ROOT/.git/modules/sub\" &&\n    ++\n    ++\t\techo \"path.superproject-root.absolute=$SUPER_ROOT\" >expect &&\n    ++\t\tgit --git-dir=\"$MODULE_DIR\" repo info path.superproject-root.absolute >actual &&\n    ++\t\ttest_cmp expect actual\n    ++\t)\n    ++'\n    + test_done\n3:  8664f8aaae ! 3:  ae8a2592b3 repo: add path.hooks with absolute and relative suffixes\n    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str\n     +\treturn 0;\n     +}\n     +\n    - static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n    + static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)\n      {\n      \tstruct strbuf superproject = STRBUF_INIT;\n     @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {\n4:  438f7e4e16 ! 4:  7f27bf14b9 repo: add path.index with absolute and relative suffixes\n    @@ builtin/repo.c: static int get_path_hooks_relative(struct repository *repo, stru\n     +\treturn 0;\n     +}\n     +\n    - static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)\n    + static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)\n      {\n      \tstruct strbuf superproject = STRBUF_INIT;\n     @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {\n5:  82be68426c = 5:  cc52587980 repo: add path.grafts with absolute and relative suffixes\n6:  346f1654d3 = 6:  77bf284a05 repo: add path.git-prefix\n7:  3ccdd9b5e4 = 7:  e30d98f5da repo: add path.cdup\n-- \n2.55.GIT\n"},{"id":"552555","messageId":"20260911144519.1011780-2-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260911144519.1011780-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v6 1/7] repo: add path.toplevel with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-11T14:45:13Z","receivedAt":"2026-09-11T14:46:15Z","isPatch":true,"body":"Scripts frequently need to find the root directory of a repository's\nworking tree. Currently, this requires using `git rev-parse --show-toplevel`\nor inferring it from other repository information.\n\nIntroduce `path.toplevel.absolute` and `path.toplevel.relative` keys\nto `git repo info`. This allows scripts to retrieve the top-level\nworking tree path in a predictable, strictly formatted manner without\nrelying on `rev-parse`.\n\nIf requested in a bare repository where no working tree exists, the\ncommand returns an empty string.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 10 ++++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 35 +++++++++++++++++++++++++++++++++++\n 3 files changed, 69 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex ed7d80c690..e34abe5fea 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,16 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.toplevel.absolute`::\n+\tThe canonical absolute path to the top-level directory of the\n+\trepository's working tree. Outputs an empty string if the repository\n+\tis bare.\n+\n+`path.toplevel.relative`::\n+\tThe path to the top-level directory of the repository's working\n+\ttree relative to the current working directory. Outputs an empty\n+\tstring if the repository is bare.\n+\n `references.format`::\n \tThe reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 84e012f83f..c31e9cfa70 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -121,6 +121,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *work_tree = repo_get_work_tree(repo);\n+\n+\tif (!work_tree)\n+\t\treturn 0;\n+\n+\tformat_path(buf, work_tree, \"\", PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *work_tree = repo_get_work_tree(repo);\n+\n+\tif (!work_tree)\n+\t\treturn 0;\n+\n+\tformat_path(buf, work_tree, repo->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -137,6 +159,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n+\t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex c85d390f43..9417d1ab65 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,4 +213,39 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.toplevel absolute and relative' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tmkdir -p repo/sub &&\n+\t\tcd repo/sub &&\n+\n+\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n+\n+\t\techo \"path.toplevel.absolute=$ROOT\" >expect.abs &&\n+\t\tgit repo info path.toplevel.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.toplevel.relative=../\" >expect.rel &&\n+\t\tgit repo info path.toplevel.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n+test_expect_success 'path.toplevel absolute and relative in a bare repository' '\n+\ttest_when_finished \"rm -rf bare.git\" &&\n+\tgit init --bare bare.git &&\n+\t(\n+\t\tcd bare.git &&\n+\n+\t\techo \"path.toplevel.absolute=\" >expect.abs &&\n+\t\tgit repo info path.toplevel.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.toplevel.relative=\" >expect.rel &&\n+\t\tgit repo info path.toplevel.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n test_done\n-- \n2.55.GIT\n\n"},{"id":"552556","messageId":"20260911144519.1011780-3-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260911144519.1011780-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v6 2/7] repo: add path.superproject-root with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-11T14:45:14Z","receivedAt":"2026-09-11T14:47:02Z","isPatch":true,"body":"Scripts working in multi-repository setups often need to identify the\ntop-level working tree of a superproject from within a submodule.\nCurrently, this is only exposed via `git rev-parse\n--show-superproject-working-tree`.\n\nIntroduce `path.superproject-root.absolute` and\n`path.superproject-root.relative` keys to `git repo info`.\nThis exposes the core submodule context via a scriptable config-like key\nusing standard format rules.\n\nIf requested when not inside a submodule, the command returns an empty\nstring.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 10 +++++++\n builtin/repo.c              | 31 ++++++++++++++++++++\n builtin/rev-parse.c         |  2 +-\n submodule.c                 | 43 ++++++++++++++--------------\n submodule.h                 |  2 +-\n t/t1900-repo-info.sh        | 57 +++++++++++++++++++++++++++++++++++++\n 6 files changed, 122 insertions(+), 23 deletions(-)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex e34abe5fea..e524a07f53 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,16 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.superproject-root.absolute`::\n+\tThe canonical absolute path to the working tree root of the superproject\n+\tif the current repository is an initialized submodule. Outputs an empty\n+\tstring if not in a submodule.\n+\n+`path.superproject-root.relative`::\n+\tThe path to the working tree root of the superproject relative to the\n+\tcurrent working directory if the current repository is an initialized\n+\tsubmodule. Outputs an empty string if not in a submodule.\n+\n `path.toplevel.absolute`::\n \tThe canonical absolute path to the top-level directory of the\n \trepository's working tree. Outputs an empty string if the repository\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex c31e9cfa70..27ebb7a1c9 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -18,6 +18,7 @@\n #include \"strbuf.h\"\n #include \"string-list.h\"\n #include \"shallow.h\"\n+#include \"submodule.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"utf8.h\"\n@@ -121,6 +122,34 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf superproject = STRBUF_INIT;\n+\n+\tif (!get_superproject_working_tree(repo, &superproject)) {\n+\t\tstrbuf_release(&superproject);\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, superproject.buf, \"\", PATH_FORMAT_CANONICAL);\n+\tstrbuf_release(&superproject);\n+\treturn 0;\n+}\n+\n+static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf superproject = STRBUF_INIT;\n+\n+\tif (!get_superproject_working_tree(repo, &superproject)) {\n+\t\tstrbuf_release(&superproject);\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, superproject.buf, repo->prefix, PATH_FORMAT_RELATIVE);\n+\tstrbuf_release(&superproject);\n+\treturn 0;\n+}\n+\n static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *work_tree = repo_get_work_tree(repo);\n@@ -159,6 +188,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n+\t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n \t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n \t{ \"references.format\", get_references_format },\ndiff --git a/builtin/rev-parse.c b/builtin/rev-parse.c\nindex 43693454d5..e1a6da0076 100644\n--- a/builtin/rev-parse.c\n+++ b/builtin/rev-parse.c\n@@ -997,7 +997,7 @@ int cmd_rev_parse(int argc,\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--show-superproject-working-tree\")) {\n \t\t\t\tstruct strbuf superproject = STRBUF_INIT;\n-\t\t\t\tif (get_superproject_working_tree(&superproject))\n+\t\t\t\tif (get_superproject_working_tree(the_repository, &superproject))\n \t\t\t\t\tprint_path(superproject.buf, prefix, format, DEFAULT_UNMODIFIED);\n \t\t\t\tstrbuf_release(&superproject);\n \t\t\t\tcontinue;\ndiff --git a/submodule.c b/submodule.c\nindex 5c92575888..78856f9840 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -2610,34 +2610,35 @@ void absorb_git_dir_into_superproject(const char *path,\n \tabsorb_git_dir_into_superproject_recurse(path, super_prefix);\n }\n \n-int get_superproject_working_tree(struct strbuf *buf)\n+int get_superproject_working_tree(struct repository *r, struct strbuf *buf)\n {\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf one_up = STRBUF_INIT;\n-\tchar *cwd = xgetcwd();\n+\tstruct strbuf target_wt = STRBUF_INIT;\n+\tconst char *worktree;\n \tint ret = 0;\n \tconst char *subpath;\n \tint code;\n \tssize_t len;\n \n-\tif (!is_inside_work_tree(the_repository))\n-\t\t/*\n-\t\t * FIXME:\n-\t\t * We might have a superproject, but it is harder\n-\t\t * to determine.\n-\t\t */\n+\tworktree = repo_get_work_tree(r);\n+\tif (!worktree)\n+\t\tgoto out;\n+\n+\tif (!strbuf_realpath(&target_wt, worktree, 0))\n \t\tgoto out;\n \n-\tif (!strbuf_realpath(&one_up, \"../\", 0))\n+\tstrbuf_addf(&one_up, \"%s/..\", target_wt.buf);\n+\tif (!strbuf_realpath(&one_up, one_up.buf, 0))\n \t\tgoto out;\n \n-\tsubpath = relative_path(cwd, one_up.buf, &sb);\n+\tsubpath = relative_path(target_wt.buf, one_up.buf, &sb);\n \n \tprepare_submodule_repo_env(&cp.env);\n \tstrvec_pop(&cp.env);\n \n-\tstrvec_pushl(&cp.args, \"--literal-pathspecs\", \"-C\", \"..\",\n+\tstrvec_pushl(&cp.args, \"--literal-pathspecs\", \"-C\", one_up.buf,\n \t\t     \"ls-files\", \"-z\", \"--stage\", \"--full-name\", \"--\",\n \t\t     subpath, NULL);\n \tstrbuf_reset(&sb);\n@@ -2648,14 +2649,14 @@ int get_superproject_working_tree(struct strbuf *buf)\n \tcp.git_cmd = 1;\n \n \tif (start_command(&cp))\n-\t\tdie(_(\"could not start ls-files in ..\"));\n+\t\tdie(_(\"could not start ls-files in %s\"), one_up.buf);\n \n \tlen = strbuf_read(&sb, cp.out, PATH_MAX);\n \tclose(cp.out);\n \n \tif (starts_with(sb.buf, \"160000\")) {\n \t\tint super_sub_len;\n-\t\tint cwd_len = strlen(cwd);\n+\t\tint wt_len = target_wt.len;\n \t\tchar *super_sub, *super_wt;\n \n \t\t/*\n@@ -2666,12 +2667,12 @@ int get_superproject_working_tree(struct strbuf *buf)\n \t\tsuper_sub = strchr(sb.buf, '\\t') + 1;\n \t\tsuper_sub_len = strlen(super_sub);\n \n-\t\tif (super_sub_len > cwd_len ||\n-\t\t    strcmp(&cwd[cwd_len - super_sub_len], super_sub))\n-\t\t\tBUG(\"returned path string doesn't match cwd?\");\n+\t\tif (super_sub_len > wt_len ||\n+\t\t    strcmp(&target_wt.buf[wt_len - super_sub_len], super_sub))\n+\t\t\tBUG(\"returned path string doesn't match worktree?\");\n \n-\t\tsuper_wt = xstrdup(cwd);\n-\t\tsuper_wt[cwd_len - super_sub_len] = '\\0';\n+\t\tsuper_wt = xstrdup(target_wt.buf);\n+\t\tsuper_wt[wt_len - super_sub_len] = '\\0';\n \n \t\tstrbuf_realpath(buf, super_wt, 1);\n \t\tret = 1;\n@@ -2681,10 +2682,10 @@ int get_superproject_working_tree(struct strbuf *buf)\n \tcode = finish_command(&cp);\n \n \tif (code == 128)\n-\t\t/* '../' is not a git repository */\n+\t\t/* parent directory is not a git repository */\n \t\tret = 0;\n \telse if (code == 0 && len == 0)\n-\t\t/* There is an unrelated git repository at '../' */\n+\t\t/* There is an unrelated git repository at parent directory */\n \t\tret = 0;\n \telse if (code)\n \t\tdie(_(\"ls-tree returned unexpected return code %d\"), code);\n@@ -2692,7 +2693,7 @@ int get_superproject_working_tree(struct strbuf *buf)\n out:\n \tstrbuf_release(&sb);\n \tstrbuf_release(&one_up);\n-\tfree(cwd);\n+\tstrbuf_release(&target_wt);\n \treturn ret;\n }\n \ndiff --git a/submodule.h b/submodule.h\nindex b10e16e6c0..1a465a1208 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -170,6 +170,6 @@ void absorb_git_dir_into_superproject(const char *path,\n  * project is a submodule of. If this repository is not a submodule of\n  * another repository, return 0.\n  */\n-int get_superproject_working_tree(struct strbuf *buf);\n+int get_superproject_working_tree(struct repository *r, struct strbuf *buf);\n \n #endif\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 9417d1ab65..2dc0c0fbc5 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,45 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.superproject-root absolute and relative' '\n+\ttest_when_finished \"rm -rf sub super\" &&\n+\tgit init sub &&\n+\ttest_commit -C sub initial &&\n+\tgit init super &&\n+\t(\n+\t\tcd super &&\n+\t\tgit -c protocol.file.allow=always submodule add \"../sub\" sub &&\n+\t\tgit commit -m \"add submodule\" &&\n+\n+\t\tcd sub &&\n+\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n+\n+\t\techo \"path.superproject-root.absolute=$ROOT\" >expect.abs &&\n+\t\tgit repo info path.superproject-root.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.superproject-root.relative=../\" >expect.rel &&\n+\t\tgit repo info path.superproject-root.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n+test_expect_success 'path.superproject-root returns empty when not in a submodule' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\n+\t\techo \"path.superproject-root.absolute=\" >expect.abs &&\n+\t\tgit repo info path.superproject-root.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.superproject-root.relative=\" >expect.rel &&\n+\t\tgit repo info path.superproject-root.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n test_expect_success 'path.toplevel absolute and relative' '\n \ttest_when_finished \"rm -rf repo\" &&\n \tgit init repo &&\n@@ -248,4 +287,22 @@ test_expect_success 'path.toplevel absolute and relative in a bare repository' '\n \t)\n '\n \n+test_expect_success 'path.superproject-root works with --git-dir' '\n+\ttest_when_finished \"rm -rf sub super\" &&\n+\tgit init sub &&\n+\ttest_commit -C sub initial &&\n+\tgit init super &&\n+\t(\n+\t\tcd super &&\n+\t\tgit -c protocol.file.allow=always submodule add \"../sub\" sub &&\n+\t\tgit commit -m \"add submodule\" &&\n+\n+\t\tSUPER_ROOT=\"$(test-tool path-utils real_path .)\" &&\n+\t\tMODULE_DIR=\"$SUPER_ROOT/.git/modules/sub\" &&\n+\n+\t\techo \"path.superproject-root.absolute=$SUPER_ROOT\" >expect &&\n+\t\tgit --git-dir=\"$MODULE_DIR\" repo info path.superproject-root.absolute >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n test_done\n-- \n2.55.GIT\n\n"},{"id":"552557","messageId":"20260911144519.1011780-4-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260911144519.1011780-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v6 3/7] repo: add path.hooks with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-11T14:45:15Z","receivedAt":"2026-09-11T14:47:21Z","isPatch":true,"body":"Hooks are an integral part of a repository's configuration and are\ncommonly used by tooling to automate repository-specific workflows.\nCurrently, scripts typically retrieve the hooks directory by invoking\n`git rev-parse --git-path hooks`.\n\nIntroduce `path.hooks.absolute` and `path.hooks.relative` keys to\n`git repo info`. This exposes the hooks directory as a scriptable\nconfig-like key using standard format rules, allowing scripts to\nretrieve it through the same interface as other repository path\ninformation.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  9 +++++++++\n builtin/repo.c              | 22 ++++++++++++++++++++++\n t/t1900-repo-info.sh        | 28 ++++++++++++++++++++++++++--\n 3 files changed, 57 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex e524a07f53..20836cf8f6 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,15 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.hooks.absolute`::\n+\tThe canonical absolute path to the repository's hooks directory.\n+\tRespects the `core.hooksPath` configuration. If `core.hooksPath` is\n+\tset to `/dev/null`, that value is returned unchanged.\n+\n+`path.hooks.relative`::\n+\tThe path to the repository's hooks directory relative to the current\n+\tworking directory. Respects the `core.hooksPath` configuration.\n+\n `path.superproject-root.absolute`::\n \tThe canonical absolute path to the working tree root of the superproject\n \tif the current repository is an initialized submodule. Outputs an empty\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 27ebb7a1c9..01666dc17c 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,26 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf hooks_path = STRBUF_INIT;\n+\n+\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n+\tformat_path(buf, hooks_path.buf, \"\", PATH_FORMAT_CANONICAL);\n+\tstrbuf_release(&hooks_path);\n+\treturn 0;\n+}\n+\n+static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf hooks_path = STRBUF_INIT;\n+\n+\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n+\tformat_path(buf, hooks_path.buf, repo->prefix, PATH_FORMAT_RELATIVE);\n+\tstrbuf_release(&hooks_path);\n+\treturn 0;\n+}\n+\n static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tstruct strbuf superproject = STRBUF_INIT;\n@@ -188,6 +208,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n+\t{ \"path.hooks.relative\", get_path_hooks_relative },\n \t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n \t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 2dc0c0fbc5..2e7b3867bd 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -174,7 +174,11 @@ test_repo_info_path () {\n \t\t\tcd repo/sub &&\n \t\t\tROOT=\"$(test-tool path-utils real_path ..)\" && export ROOT &&\n \t\t\teval \"$init_command\" &&\n-\t\t\techo \"path.$field_name.absolute=$ROOT/$expected_dir\" >expect &&\n+\t\t\tcase \"$expected_dir\" in\n+\t\t\t/*) EXPECT_ABS=\"$expected_dir\" ;;\n+\t\t\t*) EXPECT_ABS=\"$ROOT/$expected_dir\" ;;\n+\t\t\tesac &&\n+\t\t\techo \"path.$field_name.absolute=$EXPECT_ABS\" >expect &&\n \t\t\tgit repo info \"path.$field_name.absolute\" >actual &&\n \t\t\ttest_cmp expect actual\n \t\t)\n@@ -188,7 +192,11 @@ test_repo_info_path () {\n \t\t\tcd repo/sub &&\n \t\t\tROOT=\"$(test-tool path-utils real_path ..)\" && export ROOT &&\n \t\t\teval \"$init_command\" &&\n-\t\t\techo \"path.$field_name.relative=../$expected_dir\" >expect &&\n+\t\t\tcase \"$expected_dir\" in\n+\t\t\t/*) EXPECT_REL=\"$(test-tool path-utils relative_path \"$expected_dir\" \"$PWD\")\" ;;\n+\t\t\t*) EXPECT_REL=\"../$expected_dir\" ;;\n+\t\t\tesac &&\n+\t\t\techo \"path.$field_name.relative=$EXPECT_REL\" >expect &&\n \t\t\tgit repo info \"path.$field_name.relative\" >actual &&\n \t\t\ttest_cmp expect actual\n \t\t)\n@@ -213,6 +221,22 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'\n+\n+test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n+\t'custom-hooks' \\\n+\t'git config core.hooksPath \"$ROOT/custom-hooks\" && mkdir -p \"$ROOT/custom-hooks\"'\n+\n+# /dev/null is not a real, canonicalizable filesystem path on Windows,\n+# so path resolution for core.hooksPath=/dev/null cannot be expected to\n+# produce a literal \"/dev/null\" the way it does on POSIX systems.\n+if ! test_have_prereq MINGW\n+then\n+\ttest_repo_info_path 'hooks with core.hooksPath=/dev/null' 'hooks' \\\n+\t\t'/dev/null' \\\n+\t\t'git config core.hooksPath /dev/null'\n+fi\n+\n test_expect_success 'path.superproject-root absolute and relative' '\n \ttest_when_finished \"rm -rf sub super\" &&\n \tgit init sub &&\n-- \n2.55.GIT\n\n"},{"id":"552558","messageId":"20260911144519.1011780-5-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260911144519.1011780-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v6 4/7] repo: add path.index with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-11T14:45:16Z","receivedAt":"2026-09-11T14:47:32Z","isPatch":true,"body":"The repository index is a fundamental component used by Git and related\ntooling to track the working tree state. Scripts that interact with the\nindex currently retrieve its location by invoking\n`git rev-parse --git-path index`.\n\nIntroduce `path.index.absolute` and `path.index.relative` keys to\n`git repo info`. This exposes the index file location as a scriptable\nconfig-like key using standard format rules, allowing scripts to\nretrieve it through the same interface as other repository path\ninformation.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 12 ++++++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++\n 3 files changed, 59 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 20836cf8f6..08ef47750c 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -128,6 +128,18 @@ values that they return:\n \tThe path to the repository's hooks directory relative to the current\n \tworking directory. Respects the `core.hooksPath` configuration.\n \n+`path.index.absolute`::\n+\tThe canonical absolute path to the repository's current index file.\n+\tRespects the `GIT_INDEX_FILE` environment override. Returns the\n+\tconfigured index path even if the repository is bare or the file does\n+\tnot exist.\n+\n+`path.index.relative`::\n+\tThe path to the repository's current index file relative to the current\n+\tworking directory. Respects the `GIT_INDEX_FILE` environment override.\n+\tReturns the configured index path even if the repository is bare or the\n+\tfile does not exist.\n+\n `path.superproject-root.absolute`::\n \tThe canonical absolute path to the working tree root of the superproject\n \tif the current repository is an initialized submodule. Outputs an empty\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 01666dc17c..9f1858a127 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -142,6 +142,28 @@ static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *index_file = repo_get_index_file(repo);\n+\n+\tif (!index_file)\n+\t\treturn error(_(\"unable to get index file\"));\n+\n+\tformat_path(buf, index_file, \"\", PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *index_file = repo_get_index_file(repo);\n+\n+\tif (!index_file)\n+\t\treturn error(_(\"unable to get index file\"));\n+\n+\tformat_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tstruct strbuf superproject = STRBUF_INIT;\n@@ -210,6 +232,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n \t{ \"path.hooks.relative\", get_path_hooks_relative },\n+\t{ \"path.index.absolute\", get_path_index_absolute },\n+\t{ \"path.index.relative\", get_path_index_relative },\n \t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n \t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 2e7b3867bd..80e025e040 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -237,6 +237,29 @@ then\n \t\t'git config core.hooksPath /dev/null'\n fi\n \n+test_repo_info_path 'index standard' 'index' '.git/index'\n+\n+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \\\n+\t'custom-index-file' \\\n+\t'GIT_INDEX_FILE=\"$ROOT/custom-index-file\" && export GIT_INDEX_FILE'\n+\n+test_expect_success 'path.index in a bare repository returns default index location' '\n+\ttest_when_finished \"rm -rf bare.git\" &&\n+\tgit init --bare bare.git &&\n+\t(\n+\t\tcd bare.git &&\n+\t\tROOT=\"$(test-tool path-utils real_path .)\" &&\n+\n+\t\techo \"path.index.absolute=$ROOT/index\" >expect.abs &&\n+\t\tgit repo info path.index.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.index.relative=index\" >expect.rel &&\n+\t\tgit repo info path.index.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n test_expect_success 'path.superproject-root absolute and relative' '\n \ttest_when_finished \"rm -rf sub super\" &&\n \tgit init sub &&\n-- \n2.55.GIT\n\n"},{"id":"552559","messageId":"20260911144519.1011780-6-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260911144519.1011780-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v6 5/7] repo: add path.grafts with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-11T14:45:17Z","receivedAt":"2026-09-11T14:47:44Z","isPatch":true,"body":"The repository grafts file specifies alternate parent relationships for\ncommits and may be used by repository tooling that needs to inspect or\nmanage grafts. Scripts currently retrieve its location by invoking\n`git rev-parse --git-path info/grafts`.\n\nIntroduce `path.grafts.absolute` and `path.grafts.relative` keys to\n`git repo info`. This exposes the grafts file location as a scriptable\nconfig-like key using standard format rules, allowing scripts to\nretrieve it through the same interface as other repository path\ninformation.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 11 +++++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        |  6 ++++++\n 3 files changed, 41 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 08ef47750c..868ab0ed9f 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,17 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.grafts.absolute`::\n+\tThe canonical absolute path to the repository's graft file.\n+\tRespects the `GIT_GRAFT_FILE` environment override. The path is\n+\treturned regardless of whether the file currently exists on disk.\n+\n+`path.grafts.relative`::\n+\tThe path to the repository's graft file relative to the current\n+\tworking directory. Respects the `GIT_GRAFT_FILE` environment\n+\toverride. The path is returned regardless of whether the file\n+\tcurrently exists on disk.\n+\n `path.hooks.absolute`::\n \tThe canonical absolute path to the repository's hooks directory.\n \tRespects the `core.hooksPath` configuration. If `core.hooksPath` is\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 9f1858a127..33f97b03af 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_grafts_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *graft_file = repo_get_graft_file(repo);\n+\n+\tif (!graft_file)\n+\t\treturn error(_(\"unable to get graft file\"));\n+\n+\tformat_path(buf, graft_file, \"\", PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_grafts_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *graft_file = repo_get_graft_file(repo);\n+\n+\tif (!graft_file)\n+\t\treturn error(_(\"unable to get graft file\"));\n+\n+\tformat_path(buf, graft_file, repo->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tstruct strbuf hooks_path = STRBUF_INIT;\n@@ -230,6 +252,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.grafts.absolute\", get_path_grafts_absolute },\n+\t{ \"path.grafts.relative\", get_path_grafts_relative },\n \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n \t{ \"path.hooks.relative\", get_path_hooks_relative },\n \t{ \"path.index.absolute\", get_path_index_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 80e025e040..7b0bb0ed42 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -221,6 +221,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'grafts standard' 'grafts' '.git/info/grafts'\n+\n+test_repo_info_path 'grafts with GIT_GRAFT_FILE override' 'grafts' \\\n+\t'custom-graft-file' \\\n+\t'GIT_GRAFT_FILE=\"$ROOT/custom-graft-file\" && export GIT_GRAFT_FILE'\n+\n test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'\n \n test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n-- \n2.55.GIT\n\n"},{"id":"552560","messageId":"20260911144519.1011780-7-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260911144519.1011780-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v6 6/7] repo: add path.git-prefix","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-11T14:45:18Z","receivedAt":"2026-09-11T14:47:52Z","isPatch":true,"body":"Scripts sometimes need the path from the repository's working tree root\nto the current working directory. While this information can be derived\nthrough existing Git commands, `git repo info` does not currently expose\nit as a scriptable key.\n\nIntroduce the `path.git-prefix` key to `git repo info`. The key returns\nthe path from the working tree root to the current working directory,\nreturning the empty string when invoked from the working tree root.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  5 +++++\n builtin/repo.c              | 11 +++++++++++\n t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++\n 3 files changed, 39 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 868ab0ed9f..fb5aceae8f 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -113,6 +113,11 @@ values that they return:\n \tThe path to the Git repository's common directory relative to\n \tthe current working directory.\n \n+`path.git-prefix`::\n+\tThe path from the root of the working tree to the current working\n+\tdirectory. Returns the empty string when the current working directory\n+\tis the root of the working tree.\n+\n `path.gitdir.absolute`::\n \tThe canonical absolute path to the Git repository directory (the `.git` directory).\n \ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 33f97b03af..b5e1567809 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -100,6 +100,16 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b\n \treturn 0;\n }\n \n+static int get_path_git_prefix(struct repository *repo, struct strbuf *buf)\n+{\n+\t/*\n+\t * repo->prefix is NULL when the current working directory is\n+\t * the worktree root.\n+\t */\n+\tstrbuf_addstr(buf, repo->prefix ? repo->prefix : \"\");\n+\treturn 0;\n+}\n+\n static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *git_dir = repo_get_git_dir(repo);\n@@ -250,6 +260,7 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"object.format\", get_object_format },\n \t{ \"path.commondir.absolute\", get_path_commondir_absolute },\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n+\t{ \"path.git-prefix\", get_path_git_prefix },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n \t{ \"path.grafts.absolute\", get_path_grafts_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 7b0bb0ed42..10bbec8139 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -215,6 +215,29 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.git-prefix at repository root' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\techo \"path.git-prefix=\" >expect &&\n+\t\tgit repo info path.git-prefix >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'path.git-prefix in subdirectory' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\tmkdir -p repo/sub/dir &&\n+\t(\n+\t\tcd repo/sub/dir &&\n+\t\techo \"path.git-prefix=sub/dir/\" >expect &&\n+\t\tgit repo info path.git-prefix >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_repo_info_path 'gitdir standard' 'gitdir' '.git'\n \n test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n-- \n2.55.GIT\n\n"},{"id":"552561","messageId":"20260911144519.1011780-8-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260911144519.1011780-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v6 7/7] repo: add path.cdup","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-11T14:45:19Z","receivedAt":"2026-09-11T14:48:11Z","isPatch":true,"body":"Scripts sometimes need the relative path from the current working\ndirectory to the repository's working tree root (cdup). While this\ninformation can be retrieved through `git rev-parse --show-cdup`,\n`git repo info` does not currently expose it as a scriptable key.\n\nIntroduce the `path.cdup` key to `git repo info`. The key returns the\npath from the current working directory to the root of the working tree,\nreturning the empty string when invoked from the working tree root.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  5 +++++\n builtin/repo.c              | 15 +++++++++++++++\n t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++\n 3 files changed, 43 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex fb5aceae8f..0a30c0d6ab 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -104,6 +104,11 @@ values that they return:\n `object.format`::\n \tThe object format (hash algorithm) used in the repository.\n \n+`path.cdup`::\n+\tThe path to the root of the working tree relative to the current\n+\tworking directory. Returns the empty string when the current\n+\tworking directory is the root of the working tree.\n+\n `path.commondir.absolute`::\n \tThe canonical absolute path to the Git repository's common\n \tdirectory (the shared `.git` directory containing objects,\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex b5e1567809..bb129c9fee 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -78,6 +78,20 @@ static int get_object_format(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_cdup(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *pfx = repo->prefix;\n+\n+\twhile (pfx) {\n+\t\tpfx = strchr(pfx, '/');\n+\t\tif (pfx) {\n+\t\t\tpfx++;\n+\t\t\tstrbuf_addstr(buf, \"../\");\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n static int get_path_commondir_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *common_dir = repo_get_common_dir(repo);\n@@ -258,6 +272,7 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"layout.bare\", get_layout_bare },\n \t{ \"layout.shallow\", get_layout_shallow },\n \t{ \"object.format\", get_object_format },\n+\t{ \"path.cdup\", get_path_cdup },\n \t{ \"path.commondir.absolute\", get_path_commondir_absolute },\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.git-prefix\", get_path_git_prefix },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 10bbec8139..5790d70316 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -215,6 +215,29 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.cdup at repository root' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\techo \"path.cdup=\" >expect &&\n+\t\tgit repo info path.cdup >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'path.cdup in subdirectory' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\tmkdir -p repo/sub/dir &&\n+\t(\n+\t\tcd repo/sub/dir &&\n+\t\techo \"path.cdup=../../\" >expect &&\n+\t\tgit repo info path.cdup >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_expect_success 'path.git-prefix at repository root' '\n \ttest_when_finished \"rm -rf repo\" &&\n \tgit init repo &&\n-- \n2.55.GIT\n\n"},{"id":"552570","messageId":"xmqqse3fbudk.fsf@gitster.g","threadId":"66010","inReplyTo":"20260911144519.1011780-5-jayatheerthkulkarni2005@gmail.com","subject":"Re: [GSoC Patch v6 4/7] repo: add path.index with absolute and relative suffixes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-09-11T16:55:19Z","receivedAt":"2026-09-11T16:55:22Z","isPatch":true,"body":"K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n\n> +static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)\n> +{\n> +\tconst char *index_file = repo_get_index_file(repo);\n> +\n> +\tif (!index_file)\n> +\t\treturn error(_(\"unable to get index file\"));\n\nThis is a dead code, as repo_get_index_file() calls BUG(\"\") when it\nfinds that the repo instance is not ready to return this information.\n\nHence I am debating myself if this is a better alternative:\n\n\tconst char *index_file = repo->index_file;\n\n\tif (!index_file)\n\t\treturn error(...);\n\nI dunno.\n\n> +static int get_path_index_relative(struct repository *repo, struct strbuf *buf)\n> +{\n> +\tconst char *index_file = repo_get_index_file(repo);\n> +\n> +\tif (!index_file)\n> +\t\treturn error(_(\"unable to get index file\"));\n> +\n> +\tformat_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);\n> +\treturn 0;\n> +}\n\nDitto.\n"},{"id":"552576","messageId":"xmqqcxujbser.fsf@gitster.g","threadId":"66010","inReplyTo":"20260911144519.1011780-8-jayatheerthkulkarni2005@gmail.com","subject":"Re: [GSoC Patch v6 7/7] repo: add path.cdup","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-09-11T17:37:48Z","receivedAt":"2026-09-11T17:37:50Z","isPatch":true,"body":"K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n\n> Scripts sometimes need the relative path from the current working\n> directory to the repository's working tree root (cdup). While this\n> information can be retrieved through `git rev-parse --show-cdup`,\n> `git repo info` does not currently expose it as a scriptable key.\n>\n> Introduce the `path.cdup` key to `git repo info`. The key returns the\n> path from the current working directory to the root of the working tree,\n> returning the empty string when invoked from the working tree root.\n>\n> Mentored-by: Justin Tobler <jltobler@gmail.com>\n> Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n> ---\n>  Documentation/git-repo.adoc |  5 +++++\n>  builtin/repo.c              | 15 +++++++++++++++\n>  t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++\n>  3 files changed, 43 insertions(+)\n>\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> index fb5aceae8f..0a30c0d6ab 100644\n> --- a/Documentation/git-repo.adoc\n> +++ b/Documentation/git-repo.adoc\n> @@ -104,6 +104,11 @@ values that they return:\n>  `object.format`::\n>  \tThe object format (hash algorithm) used in the repository.\n>  \n> +`path.cdup`::\n> +\tThe path to the root of the working tree relative to the current\n> +\tworking directory. Returns the empty string when the current\n> +\tworking directory is the root of the working tree.\n\nWhat happens when the current working directory is outside the\nworking tree?  For example:\n\n    $ GIT_WORK_TREE=/tmp/x; export GIT_WORK_TREE\n    $ GIT_DIR=$(pwd)/.git; export GIT_DIR\n\nThe current implementation yields an empty string:\n\n    $ git repo info path.cdup\n    path.cdup=\n\nHowever, 'git rev-parse --show-cdup' produces:\n\n    $ git rev-parse --show-cdup\n    /var/tmp/x\n\nWhether this discrepancy matters is unclear.  However, if the goal\nis to consolidate all repository path-related items under the\n'git repo info' umbrella, this difference sticks out.\n"},{"id":"552609","messageId":"xmqqmrtn8mpx.fsf@gitster.g","threadId":"66010","inReplyTo":"20260911144519.1011780-3-jayatheerthkulkarni2005@gmail.com","subject":"Re: [GSoC Patch v6 2/7] repo: add path.superproject-root with absolute and relative suffixes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-09-11T22:08:58Z","receivedAt":"2026-09-11T22:09:00Z","isPatch":true,"body":"K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n\n> Scripts working in multi-repository setups often need to identify the\n> top-level working tree of a superproject from within a submodule.\n> Currently, this is only exposed via `git rev-parse\n> --show-superproject-working-tree`.\n>\n> Introduce `path.superproject-root.absolute` and\n> `path.superproject-root.relative` keys to `git repo info`.\n> This exposes the core submodule context via a scriptable config-like key\n> using standard format rules.\n>\n> If requested when not inside a submodule, the command returns an empty\n> string.\n>\n> Mentored-by: Justin Tobler <jltobler@gmail.com>\n> Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n> ---\n>  Documentation/git-repo.adoc | 10 +++++++\n>  builtin/repo.c              | 31 ++++++++++++++++++++\n>  builtin/rev-parse.c         |  2 +-\n>  submodule.c                 | 43 ++++++++++++++--------------\n>  submodule.h                 |  2 +-\n>  t/t1900-repo-info.sh        | 57 +++++++++++++++++++++++++++++++++++++\n>  6 files changed, 122 insertions(+), 23 deletions(-)\n\nCorrecting get_superproject_working_tree(), which was introduced by\nbf0231c661 (rev-parse: add --show-superproject-working-tree,\n2017-03-08), is a major part of this step.  The old commit added a\nsingle test for very basic use but this step makes not-so-basic use\ncases to also work correctly, right?\n\nIt may be a good idea to split this step into two patches:\n\n - a patch that corrects get_superproject_working_tree(), that adds\n   a new test or two to t1500-rev-parse.sh, next to where bf0231c661\n   (rev-parse: add --show-superproject-working-tree, 2017-03-08)\n   added its test.  The test would fail without the code fix to\n   demonstrate what we improved.\n\n - another patch that uses improved get_superproject_working_tree()\n   to add path.superproject-root.* keys, together with the updates\n   to t1900-repo-info.sh to add tests for the new feature.\n"},{"id":"553083","messageId":"CA+rGoLchSyDz9fcBuaS=M9t1q_Xp3KPsX5m_qKEfAHonQD6uTQ@mail.gmail.com","threadId":"66010","inReplyTo":"xmqqse3fbudk.fsf@gitster.g","subject":"Re: [GSoC Patch v6 4/7] repo: add path.index with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-23T17:25:54Z","receivedAt":"2026-09-23T17:26:06Z","isPatch":true,"body":"> > +     if (!index_file)\n> > +             return error(_(\"unable to get index file\"));\n>\n> This is a dead code, as repo_get_index_file() calls BUG(\"\") when it\n> finds that the repo instance is not ready to return this information.\n>\n> Hence I am debating myself if this is a better alternative:\n>\n>         const char *index_file = repo->index_file;\n>\n>         if (!index_file)\n>                 return error(...);\n>\n> I dunno.\n>\n\nI think I am just gonna remove the if condition from\nget_path_index_relative() and let repo_get_index_file handle the bug\ninstead of rewriting the logic.\n\n> > +static int get_path_index_relative(struct repository *repo, struct strbuf *buf)\n> > +{\n> > +     const char *index_file = repo_get_index_file(repo);\n> > +\n> > +     if (!index_file)\n> > +             return error(_(\"unable to get index file\"));\n> > +\n> > +     format_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);\n> > +     return 0;\n> > +}\n>\n> Ditto.\n\nSame for this.\n"},{"id":"553086","messageId":"CA+rGoLcLvm24XAeN9QOObEC2JbQaBhFmvivJ=-6OZ_t8F0Tw1A@mail.gmail.com","threadId":"66010","inReplyTo":"xmqqcxujbser.fsf@gitster.g","subject":"Re: [GSoC Patch v6 7/7] repo: add path.cdup","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-23T17:37:51Z","receivedAt":"2026-09-23T17:38:05Z","isPatch":true,"body":"On Fri, Sep 11, 2026 at 11:07 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:\n>\n> > Scripts sometimes need the relative path from the current working\n> > directory to the repository's working tree root (cdup). While this\n> > information can be retrieved through `git rev-parse --show-cdup`,\n> > `git repo info` does not currently expose it as a scriptable key.\n> >  [...]\n> > +`path.cdup`::\n> > +     The path to the root of the working tree relative to the current\n> > +     working directory. Returns the empty string when the current\n> > +     working directory is the root of the working tree.\n>\n> What happens when the current working directory is outside the\n> working tree?  For example:\n>\n>     $ GIT_WORK_TREE=/tmp/x; export GIT_WORK_TREE\n>     $ GIT_DIR=$(pwd)/.git; export GIT_DIR\n>\n> The current implementation yields an empty string:\n>\n>     $ git repo info path.cdup\n>     path.cdup=\n>\n> However, 'git rev-parse --show-cdup' produces:\n>\n>     $ git rev-parse --show-cdup\n>     /var/tmp/x\n>\n> Whether this discrepancy matters is unclear.  However, if the goal\n> is to consolidate all repository path-related items under the\n> 'git repo info' umbrella, this difference sticks out.\n\nyeah\nin rev-parse i could find this\n\nif (!is_inside_work_tree(the_repository)) {\nconst char *work_tree =\nrepo_get_work_tree(the_repository);\nif (work_tree)\nprintf(\"%s\\n\", work_tree);\ncontinue;\n}\n\nadding this logic in repo.c should fix it!\n\nThanks\n"},{"id":"553087","messageId":"CA+rGoLcRRZPu8SD-vZw+rEjVzKO02=nMn_x+4ANJX7eh9jgBcw@mail.gmail.com","threadId":"66010","inReplyTo":"xmqqmrtn8mpx.fsf@gitster.g","subject":"Re: [GSoC Patch v6 2/7] repo: add path.superproject-root with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-23T17:40:38Z","receivedAt":"2026-09-23T17:40:51Z","isPatch":true,"body":"On Sat, Sep 12, 2026 at 3:39 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n>\n> Correcting get_superproject_working_tree(), which was introduced by\n> bf0231c661 (rev-parse: add --show-superproject-working-tree,\n> 2017-03-08), is a major part of this step.  The old commit added a\n> single test for very basic use but this step makes not-so-basic use\n> cases to also work correctly, right?\n>\n> It may be a good idea to split this step into two patches:\n>\n>  - a patch that corrects get_superproject_working_tree(), that adds\n>    a new test or two to t1500-rev-parse.sh, next to where bf0231c661\n>    (rev-parse: add --show-superproject-working-tree, 2017-03-08)\n>    added its test.  The test would fail without the code fix to\n>    demonstrate what we improved.\n>\n>  - another patch that uses improved get_superproject_working_tree()\n>    to add path.superproject-root.* keys, together with the updates\n>    to t1900-repo-info.sh to add tests for the new feature.\n\nFair enough. Will do, but I don't think t1500 is a good location tho\nCause the bug affects repo as well\nI will find a good location.\n"},{"id":"553372","messageId":"20260927114420.59724-1-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260716012138.6714-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v7 0/8] add more path keys to git repo info","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-27T11:44:12Z","receivedAt":"2026-09-27T11:44:59Z","isPatch":true,"body":"Series adds keys to git repo info.\n\nKeys output paths of repository components:\n* path.toplevel: repository tree.\n* path.superproject-root: superproject tree from submodules.\n* path.hooks: repository hooks.\n* path.index: repository index.\n* path.grafts: repository grafts.\n* path.git-prefix: prefix offset.\n* path.cdup: relative path to top level from subdirectory.\n\nKeys support suffixes for format.\nCommits contain documentation and tests.\n\nfix a superproject bug where it now takes repo parameter\ninstead of just depending on cwd.\n\nchanges since v6:\n* The superproject bug fix is now a commit of its own.\n* The dead code in index is removed, we simply rely on the helper itself\n  and not write any new conditions.\n* cdup now works even when the worktree is outside the current working directory.\n\n\nK Jayatheerth (8):\n  repo: add path.toplevel with absolute and relative suffix formatting\n  submodule: use repository to find superproject\n  repo: add path.superproject-root with absolute and relative suffixes\n  repo: add path.hooks with absolute and relative suffixes\n  repo: add path.index with absolute and relative suffixes\n  repo: add path.grafts with absolute and relative suffixes\n  repo: add path.git-prefix\n  repo: add path.cdup\n\n Documentation/git-repo.adoc |  62 ++++++++++++\n builtin/repo.c              | 153 ++++++++++++++++++++++++++++\n builtin/rev-parse.c         |   2 +-\n submodule.c                 |  43 ++++----\n submodule.h                 |   2 +-\n t/t1900-repo-info.sh        | 194 +++++++++++++++++++++++++++++++++++-\n t/t7400-submodule-basic.sh  |  19 ++++\n 7 files changed, 450 insertions(+), 25 deletions(-)\n\nRange-diff against v6:\n-:  ---------- > 1:  27b9811163 repo: add path.toplevel with absolute and relative suffix formatting\n1:  d4f3253256 ! 2:  7c90603a1b repo: add path.superproject-root with absolute and relative suffixes\n    @@ Metadata\n     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n     \n      ## Commit message ##\n    -    repo: add path.superproject-root with absolute and relative suffixes\n    +    submodule: use repository to find superproject\n     \n    -    Scripts working in multi-repository setups often need to identify the\n    -    top-level working tree of a superproject from within a submodule.\n    -    Currently, this is only exposed via `git rev-parse\n    -    --show-superproject-working-tree`.\n    +    `get_superproject_working_tree()` currently uses `xgetcwd()` to\n    +    determine the repository whose superproject should be checked.\n     \n    -    Introduce `path.superproject-root.absolute` and\n    -    `path.superproject-root.relative` keys to `git repo info`.\n    -    This exposes the core submodule context via a scriptable config-like key\n    -    using standard format rules.\n    +    This is incorrect when `--git-dir` points to a repository different\n    +    from the one associated with the current working directory. In that\n    +    case, the current working directory may cause the function to return\n    +    the wrong superproject or an empty result.\n     \n    -    If requested when not inside a submodule, the command returns an empty\n    -    string.\n    +    Pass the `repository` to `get_superproject_working_tree()` so that the\n    +    superproject is determined from the repository being inspected rather\n    +    than the current working directory.\n     \n         Mentored-by: Justin Tobler <jltobler@gmail.com>\n         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n         Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n     \n    - ## Documentation/git-repo.adoc ##\n    -@@ Documentation/git-repo.adoc: values that they return:\n    - `path.gitdir.relative`::\n    - \tThe path to the Git repository directory relative to the current working directory.\n    - \n    -+`path.superproject-root.absolute`::\n    -+\tThe canonical absolute path to the working tree root of the superproject\n    -+\tif the current repository is an initialized submodule. Outputs an empty\n    -+\tstring if not in a submodule.\n    -+\n    -+`path.superproject-root.relative`::\n    -+\tThe path to the working tree root of the superproject relative to the\n    -+\tcurrent working directory if the current repository is an initialized\n    -+\tsubmodule. Outputs an empty string if not in a submodule.\n    -+\n    - `path.toplevel.absolute`::\n    - \tThe canonical absolute path to the top-level directory of the\n    - \trepository's working tree. Outputs an empty string if the repository\n    -\n    - ## builtin/repo.c ##\n    -@@\n    - #include \"strbuf.h\"\n    - #include \"string-list.h\"\n    - #include \"shallow.h\"\n    -+#include \"submodule.h\"\n    - #include \"tree.h\"\n    - #include \"tree-walk.h\"\n    - #include \"utf8.h\"\n    -@@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n    - \treturn 0;\n    - }\n    - \n    -+static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)\n    -+{\n    -+\tstruct strbuf superproject = STRBUF_INIT;\n    -+\n    -+\tif (!get_superproject_working_tree(repo, &superproject)) {\n    -+\t\tstrbuf_release(&superproject);\n    -+\t\treturn 0;\n    -+\t}\n    -+\n    -+\tformat_path(buf, superproject.buf, \"\", PATH_FORMAT_CANONICAL);\n    -+\tstrbuf_release(&superproject);\n    -+\treturn 0;\n    -+}\n    -+\n    -+static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)\n    -+{\n    -+\tstruct strbuf superproject = STRBUF_INIT;\n    -+\n    -+\tif (!get_superproject_working_tree(repo, &superproject)) {\n    -+\t\tstrbuf_release(&superproject);\n    -+\t\treturn 0;\n    -+\t}\n    -+\n    -+\tformat_path(buf, superproject.buf, repo->prefix, PATH_FORMAT_RELATIVE);\n    -+\tstrbuf_release(&superproject);\n    -+\treturn 0;\n    -+}\n    -+\n    - static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n    - {\n    - \tconst char *work_tree = repo_get_work_tree(repo);\n    -@@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {\n    - \t{ \"path.commondir.relative\", get_path_commondir_relative },\n    - \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n    - \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n    -+\t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n    -+\t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n    - \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n    - \t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n    - \t{ \"references.format\", get_references_format },\n    -\n      ## builtin/rev-parse.c ##\n     @@ builtin/rev-parse.c: int cmd_rev_parse(int argc,\n      \t\t\t}\n    @@ submodule.h: void absorb_git_dir_into_superproject(const char *path,\n      \n      #endif\n     \n    - ## t/t1900-repo-info.sh ##\n    -@@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n    - \t'.git' \\\n    - \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n    - \n    -+test_expect_success 'path.superproject-root absolute and relative' '\n    -+\ttest_when_finished \"rm -rf sub super\" &&\n    -+\tgit init sub &&\n    -+\ttest_commit -C sub initial &&\n    -+\tgit init super &&\n    -+\t(\n    -+\t\tcd super &&\n    -+\t\tgit -c protocol.file.allow=always submodule add \"../sub\" sub &&\n    -+\t\tgit commit -m \"add submodule\" &&\n    -+\n    -+\t\tcd sub &&\n    -+\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n    -+\n    -+\t\techo \"path.superproject-root.absolute=$ROOT\" >expect.abs &&\n    -+\t\tgit repo info path.superproject-root.absolute >actual.abs &&\n    -+\t\ttest_cmp expect.abs actual.abs &&\n    -+\n    -+\t\techo \"path.superproject-root.relative=../\" >expect.rel &&\n    -+\t\tgit repo info path.superproject-root.relative >actual.rel &&\n    -+\t\ttest_cmp expect.rel actual.rel\n    -+\t)\n    -+'\n    -+\n    -+test_expect_success 'path.superproject-root returns empty when not in a submodule' '\n    -+\ttest_when_finished \"rm -rf repo\" &&\n    -+\tgit init repo &&\n    -+\t(\n    -+\t\tcd repo &&\n    -+\n    -+\t\techo \"path.superproject-root.absolute=\" >expect.abs &&\n    -+\t\tgit repo info path.superproject-root.absolute >actual.abs &&\n    -+\t\ttest_cmp expect.abs actual.abs &&\n    -+\n    -+\t\techo \"path.superproject-root.relative=\" >expect.rel &&\n    -+\t\tgit repo info path.superproject-root.relative >actual.rel &&\n    -+\t\ttest_cmp expect.rel actual.rel\n    -+\t)\n    -+'\n    -+\n    - test_expect_success 'path.toplevel absolute and relative' '\n    - \ttest_when_finished \"rm -rf repo\" &&\n    - \tgit init repo &&\n    -@@ t/t1900-repo-info.sh: test_expect_success 'path.toplevel absolute and relative in a bare repository' '\n    + ## t/t7400-submodule-basic.sh ##\n    +@@ t/t7400-submodule-basic.sh: test_expect_success 'submodule add fails when name is reused' '\n      \t)\n      '\n      \n    @@ t/t1900-repo-info.sh: test_expect_success 'path.toplevel absolute and relative i\n     +\t\ttest_cmp expect actual\n     +\t)\n     +'\n    ++\n      test_done\n3:  1860d19d01 ! 3:  1bfb0c8c1d repo: add path.index with absolute and relative suffixes\n    @@ Metadata\n     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n     \n      ## Commit message ##\n    -    repo: add path.index with absolute and relative suffixes\n    +    repo: add path.superproject-root with absolute and relative suffixes\n     \n    -    The repository index is a fundamental component used by Git and related\n    -    tooling to track the working tree state. Scripts that interact with the\n    -    index currently retrieve its location by invoking\n    -    `git rev-parse --git-path index`.\n    +    Scripts working in multi-repository setups often need to identify the\n    +    top-level working tree of a superproject from within a submodule.\n    +    Currently, this is only exposed via `git rev-parse\n    +    --show-superproject-working-tree`.\n     \n    -    Introduce `path.index.absolute` and `path.index.relative` keys to\n    -    `git repo info`. This exposes the index file location as a scriptable\n    -    config-like key using standard format rules, allowing scripts to\n    -    retrieve it through the same interface as other repository path\n    -    information.\n    +    Introduce `path.superproject-root.absolute` and\n    +    `path.superproject-root.relative` keys to `git repo info`.\n    +    This exposes the core submodule context via a scriptable config-like key\n    +    using standard format rules.\n    +\n    +    If requested when not inside a submodule, the command returns an empty\n    +    string.\n     \n         Mentored-by: Justin Tobler <jltobler@gmail.com>\n         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n    @@ Commit message\n     \n      ## Documentation/git-repo.adoc ##\n     @@ Documentation/git-repo.adoc: values that they return:\n    - \tThe path to the repository's hooks directory relative to the current\n    - \tworking directory. Respects the `core.hooksPath` configuration.\n    + `path.gitdir.relative`::\n    + \tThe path to the Git repository directory relative to the current working directory.\n      \n    -+`path.index.absolute`::\n    -+\tThe canonical absolute path to the repository's current index file.\n    -+\tRespects the `GIT_INDEX_FILE` environment override. Returns the\n    -+\tconfigured index path even if the repository is bare or the file does\n    -+\tnot exist.\n    ++`path.superproject-root.absolute`::\n    ++\tThe canonical absolute path to the working tree root of the superproject\n    ++\tif the current repository is an initialized submodule. Outputs an empty\n    ++\tstring if not in a submodule.\n     +\n    -+`path.index.relative`::\n    -+\tThe path to the repository's current index file relative to the current\n    -+\tworking directory. Respects the `GIT_INDEX_FILE` environment override.\n    -+\tReturns the configured index path even if the repository is bare or the\n    -+\tfile does not exist.\n    ++`path.superproject-root.relative`::\n    ++\tThe path to the working tree root of the superproject relative to the\n    ++\tcurrent working directory if the current repository is an initialized\n    ++\tsubmodule. Outputs an empty string if not in a submodule.\n     +\n    - `path.superproject-root.absolute`::\n    - \tThe canonical absolute path to the working tree root of the superproject\n    - \tif the current repository is an initialized submodule. Outputs an empty\n    + `path.toplevel.absolute`::\n    + \tThe canonical absolute path to the top-level directory of the\n    + \trepository's working tree. Outputs an empty string if the repository\n     \n      ## builtin/repo.c ##\n    -@@ builtin/repo.c: static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n    +@@\n    + #include \"strbuf.h\"\n    + #include \"string-list.h\"\n    + #include \"shallow.h\"\n    ++#include \"submodule.h\"\n    + #include \"tree.h\"\n    + #include \"tree-walk.h\"\n    + #include \"utf8.h\"\n    +@@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n      \treturn 0;\n      }\n      \n    -+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)\n    ++static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)\n     +{\n    -+\tconst char *index_file = repo_get_index_file(repo);\n    ++\tstruct strbuf superproject = STRBUF_INIT;\n     +\n    -+\tif (!index_file)\n    -+\t\treturn error(_(\"unable to get index file\"));\n    ++\tif (!get_superproject_working_tree(repo, &superproject)) {\n    ++\t\tstrbuf_release(&superproject);\n    ++\t\treturn 0;\n    ++\t}\n     +\n    -+\tformat_path(buf, index_file, \"\", PATH_FORMAT_CANONICAL);\n    ++\tformat_path(buf, superproject.buf, \"\", PATH_FORMAT_CANONICAL);\n    ++\tstrbuf_release(&superproject);\n     +\treturn 0;\n     +}\n     +\n    -+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)\n    ++static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)\n     +{\n    -+\tconst char *index_file = repo_get_index_file(repo);\n    ++\tstruct strbuf superproject = STRBUF_INIT;\n     +\n    -+\tif (!index_file)\n    -+\t\treturn error(_(\"unable to get index file\"));\n    ++\tif (!get_superproject_working_tree(repo, &superproject)) {\n    ++\t\tstrbuf_release(&superproject);\n    ++\t\treturn 0;\n    ++\t}\n     +\n    -+\tformat_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);\n    ++\tformat_path(buf, superproject.buf, repo->prefix, PATH_FORMAT_RELATIVE);\n    ++\tstrbuf_release(&superproject);\n     +\treturn 0;\n     +}\n     +\n    - static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)\n    + static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n      {\n    - \tstruct strbuf superproject = STRBUF_INIT;\n    + \tconst char *work_tree = repo_get_work_tree(repo);\n     @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {\n    + \t{ \"path.commondir.relative\", get_path_commondir_relative },\n    + \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n      \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n    - \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n    - \t{ \"path.hooks.relative\", get_path_hooks_relative },\n    -+\t{ \"path.index.absolute\", get_path_index_absolute },\n    -+\t{ \"path.index.relative\", get_path_index_relative },\n    - \t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n    - \t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n    ++\t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n    ++\t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n      \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n    + \t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n    + \t{ \"references.format\", get_references_format },\n     \n      ## t/t1900-repo-info.sh ##\n    -@@ t/t1900-repo-info.sh: then\n    - \t\t'git config core.hooksPath /dev/null'\n    - fi\n    +@@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n    + \t'.git' \\\n    + \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n      \n    -+test_repo_info_path 'index standard' 'index' '.git/index'\n    ++test_expect_success 'path.superproject-root absolute and relative' '\n    ++\ttest_when_finished \"rm -rf sub super\" &&\n    ++\tgit init sub &&\n    ++\ttest_commit -C sub initial &&\n    ++\tgit init super &&\n    ++\t(\n    ++\t\tcd super &&\n    ++\t\tgit -c protocol.file.allow=always submodule add \"../sub\" sub &&\n    ++\t\tgit commit -m \"add submodule\" &&\n    ++\n    ++\t\tcd sub &&\n    ++\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n    ++\n    ++\t\techo \"path.superproject-root.absolute=$ROOT\" >expect.abs &&\n    ++\t\tgit repo info path.superproject-root.absolute >actual.abs &&\n    ++\t\ttest_cmp expect.abs actual.abs &&\n     +\n    -+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \\\n    -+\t'custom-index-file' \\\n    -+\t'GIT_INDEX_FILE=\"$ROOT/custom-index-file\" && export GIT_INDEX_FILE'\n    ++\t\techo \"path.superproject-root.relative=../\" >expect.rel &&\n    ++\t\tgit repo info path.superproject-root.relative >actual.rel &&\n    ++\t\ttest_cmp expect.rel actual.rel\n    ++\t)\n    ++'\n     +\n    -+test_expect_success 'path.index in a bare repository returns default index location' '\n    -+\ttest_when_finished \"rm -rf bare.git\" &&\n    -+\tgit init --bare bare.git &&\n    ++test_expect_success 'path.superproject-root returns empty when not in a submodule' '\n    ++\ttest_when_finished \"rm -rf repo\" &&\n    ++\tgit init repo &&\n     +\t(\n    -+\t\tcd bare.git &&\n    -+\t\tROOT=\"$(test-tool path-utils real_path .)\" &&\n    ++\t\tcd repo &&\n     +\n    -+\t\techo \"path.index.absolute=$ROOT/index\" >expect.abs &&\n    -+\t\tgit repo info path.index.absolute >actual.abs &&\n    ++\t\techo \"path.superproject-root.absolute=\" >expect.abs &&\n    ++\t\tgit repo info path.superproject-root.absolute >actual.abs &&\n     +\t\ttest_cmp expect.abs actual.abs &&\n     +\n    -+\t\techo \"path.index.relative=index\" >expect.rel &&\n    -+\t\tgit repo info path.index.relative >actual.rel &&\n    ++\t\techo \"path.superproject-root.relative=\" >expect.rel &&\n    ++\t\tgit repo info path.superproject-root.relative >actual.rel &&\n     +\t\ttest_cmp expect.rel actual.rel\n     +\t)\n     +'\n     +\n    - test_expect_success 'path.superproject-root absolute and relative' '\n    - \ttest_when_finished \"rm -rf sub super\" &&\n    - \tgit init sub &&\n    + test_expect_success 'path.toplevel absolute and relative' '\n    + \ttest_when_finished \"rm -rf repo\" &&\n    + \tgit init repo &&\n2:  dcbaf1cb96 = 4:  09d2937fcd repo: add path.hooks with absolute and relative suffixes\n-:  ---------- > 5:  d0c186f150 repo: add path.index with absolute and relative suffixes\n4:  ced5b0cb8b = 6:  351b4b7de7 repo: add path.grafts with absolute and relative suffixes\n5:  ac766e49e3 = 7:  ef69d4aa8c repo: add path.git-prefix\n6:  0a0c3dd924 ! 8:  3e61728dba repo: add path.cdup\n    @@ Documentation/git-repo.adoc: values that they return:\n      \tThe object format (hash algorithm) used in the repository.\n      \n     +`path.cdup`::\n    -+\tThe path to the root of the working tree relative to the current\n    -+\tworking directory. Returns the empty string when the current\n    -+\tworking directory is the root of the working tree.\n    ++\tWhen the command is invoked from a subdirectory, show the\n    ++\tpath of the top-level directory relative to the current\n    ++\tdirectory (typically a sequence of \"../\", or an empty string).\n     +\n      `path.commondir.absolute`::\n      \tThe canonical absolute path to the Git repository's common\n    @@ builtin/repo.c: static int get_object_format(struct repository *repo, struct str\n     +{\n     +\tconst char *pfx = repo->prefix;\n     +\n    ++\tif (!is_inside_work_tree(repo)) {\n    ++\t\tconst char *worktree = repo_get_work_tree(repo);\n    ++\n    ++\t\tif (worktree) {\n    ++\t\t\tstrbuf_addstr(buf, worktree);\n    ++\t\t}\n    ++\t}\n    ++\n     +\twhile (pfx) {\n     +\t\tpfx = strchr(pfx, '/');\n     +\t\tif (pfx) {\n    @@ t/t1900-repo-info.sh: test_repo_info_path 'commondir with only GIT_DIR' 'commond\n     +\t\ttest_cmp expect actual\n     +\t)\n     +'\n    ++\n    ++test_expect_success 'path.cdup cwd outside the working tree' '\n    ++\ttest_when_finished \"rm -rf repo\" &&\n    ++\tmkdir -p repo/tmp/x &&\n    ++\tcd repo &&\n    ++\tgit init test &&\n    ++\t(\n    ++\t\techo path.cdup=$(pwd)/tmp/x >./test/expect &&\n    ++\t\tcd test &&\n    ++\t\tGIT_WORK_TREE=../tmp/x &&\n    ++\t\texport GIT_WORK_TREE &&\n    ++\t\tGIT_DIR=$(pwd)/.git &&\n    ++\t\texport GIT_DIR &&\n    ++\t\tgit repo info path.cdup >actual &&\n    ++\t\ttest_cmp expect actual\n    ++\t)\n    ++'\n     +\n      test_expect_success 'path.git-prefix at repository root' '\n      \ttest_when_finished \"rm -rf repo\" &&\n-- \n2.56.0-rc2\n"},{"id":"553373","messageId":"20260927114420.59724-2-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260927114420.59724-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v7 1/8] repo: add path.toplevel with absolute and relative suffix formatting","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-27T11:44:13Z","receivedAt":"2026-09-27T11:45:06Z","isPatch":true,"body":"Scripts frequently need to find the root directory of a repository's\nworking tree. Currently, this requires using `git rev-parse --show-toplevel`\nor inferring it from other repository information.\n\nIntroduce `path.toplevel.absolute` and `path.toplevel.relative` keys\nto `git repo info`. This allows scripts to retrieve the top-level\nworking tree path in a predictable, strictly formatted manner without\nrelying on `rev-parse`.\n\nIf requested in a bare repository where no working tree exists, the\ncommand returns an empty string.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 10 ++++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 35 +++++++++++++++++++++++++++++++++++\n 3 files changed, 69 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex ed7d80c690..e34abe5fea 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,16 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.toplevel.absolute`::\n+\tThe canonical absolute path to the top-level directory of the\n+\trepository's working tree. Outputs an empty string if the repository\n+\tis bare.\n+\n+`path.toplevel.relative`::\n+\tThe path to the top-level directory of the repository's working\n+\ttree relative to the current working directory. Outputs an empty\n+\tstring if the repository is bare.\n+\n `references.format`::\n \tThe reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 84e012f83f..c31e9cfa70 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -121,6 +121,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *work_tree = repo_get_work_tree(repo);\n+\n+\tif (!work_tree)\n+\t\treturn 0;\n+\n+\tformat_path(buf, work_tree, \"\", PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *work_tree = repo_get_work_tree(repo);\n+\n+\tif (!work_tree)\n+\t\treturn 0;\n+\n+\tformat_path(buf, work_tree, repo->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -137,6 +159,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n+\t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex c85d390f43..9417d1ab65 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,4 +213,39 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.toplevel absolute and relative' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tmkdir -p repo/sub &&\n+\t\tcd repo/sub &&\n+\n+\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n+\n+\t\techo \"path.toplevel.absolute=$ROOT\" >expect.abs &&\n+\t\tgit repo info path.toplevel.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.toplevel.relative=../\" >expect.rel &&\n+\t\tgit repo info path.toplevel.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n+test_expect_success 'path.toplevel absolute and relative in a bare repository' '\n+\ttest_when_finished \"rm -rf bare.git\" &&\n+\tgit init --bare bare.git &&\n+\t(\n+\t\tcd bare.git &&\n+\n+\t\techo \"path.toplevel.absolute=\" >expect.abs &&\n+\t\tgit repo info path.toplevel.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.toplevel.relative=\" >expect.rel &&\n+\t\tgit repo info path.toplevel.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n test_done\n-- \n2.56.0-rc2\n\n"},{"id":"553374","messageId":"20260927114420.59724-3-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260927114420.59724-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v7 2/8] submodule: use repository to find superproject","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-27T11:44:14Z","receivedAt":"2026-09-27T11:45:20Z","isPatch":true,"body":"`get_superproject_working_tree()` currently uses `xgetcwd()` to\ndetermine the repository whose superproject should be checked.\n\nThis is incorrect when `--git-dir` points to a repository different\nfrom the one associated with the current working directory. In that\ncase, the current working directory may cause the function to return\nthe wrong superproject or an empty result.\n\nPass the `repository` to `get_superproject_working_tree()` so that the\nsuperproject is determined from the repository being inspected rather\nthan the current working directory.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n builtin/rev-parse.c        |  2 +-\n submodule.c                | 43 +++++++++++++++++++-------------------\n submodule.h                |  2 +-\n t/t7400-submodule-basic.sh | 19 +++++++++++++++++\n 4 files changed, 43 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/rev-parse.c b/builtin/rev-parse.c\nindex 43693454d5..e1a6da0076 100644\n--- a/builtin/rev-parse.c\n+++ b/builtin/rev-parse.c\n@@ -997,7 +997,7 @@ int cmd_rev_parse(int argc,\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--show-superproject-working-tree\")) {\n \t\t\t\tstruct strbuf superproject = STRBUF_INIT;\n-\t\t\t\tif (get_superproject_working_tree(&superproject))\n+\t\t\t\tif (get_superproject_working_tree(the_repository, &superproject))\n \t\t\t\t\tprint_path(superproject.buf, prefix, format, DEFAULT_UNMODIFIED);\n \t\t\t\tstrbuf_release(&superproject);\n \t\t\t\tcontinue;\ndiff --git a/submodule.c b/submodule.c\nindex 6fcb606f7e..4db90ef9b0 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -2610,34 +2610,35 @@ void absorb_git_dir_into_superproject(const char *path,\n \tabsorb_git_dir_into_superproject_recurse(path, super_prefix);\n }\n \n-int get_superproject_working_tree(struct strbuf *buf)\n+int get_superproject_working_tree(struct repository *r, struct strbuf *buf)\n {\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf one_up = STRBUF_INIT;\n-\tchar *cwd = xgetcwd();\n+\tstruct strbuf target_wt = STRBUF_INIT;\n+\tconst char *worktree;\n \tint ret = 0;\n \tconst char *subpath;\n \tint code;\n \tssize_t len;\n \n-\tif (!is_inside_work_tree(the_repository))\n-\t\t/*\n-\t\t * FIXME:\n-\t\t * We might have a superproject, but it is harder\n-\t\t * to determine.\n-\t\t */\n+\tworktree = repo_get_work_tree(r);\n+\tif (!worktree)\n+\t\tgoto out;\n+\n+\tif (!strbuf_realpath(&target_wt, worktree, 0))\n \t\tgoto out;\n \n-\tif (!strbuf_realpath(&one_up, \"../\", 0))\n+\tstrbuf_addf(&one_up, \"%s/..\", target_wt.buf);\n+\tif (!strbuf_realpath(&one_up, one_up.buf, 0))\n \t\tgoto out;\n \n-\tsubpath = relative_path(cwd, one_up.buf, &sb);\n+\tsubpath = relative_path(target_wt.buf, one_up.buf, &sb);\n \n \tprepare_submodule_repo_env(&cp.env);\n \tstrvec_pop(&cp.env);\n \n-\tstrvec_pushl(&cp.args, \"--literal-pathspecs\", \"-C\", \"..\",\n+\tstrvec_pushl(&cp.args, \"--literal-pathspecs\", \"-C\", one_up.buf,\n \t\t     \"ls-files\", \"-z\", \"--stage\", \"--full-name\", \"--\",\n \t\t     subpath, NULL);\n \tstrbuf_reset(&sb);\n@@ -2648,14 +2649,14 @@ int get_superproject_working_tree(struct strbuf *buf)\n \tcp.git_cmd = 1;\n \n \tif (start_command(&cp))\n-\t\tdie(_(\"could not start ls-files in ..\"));\n+\t\tdie(_(\"could not start ls-files in %s\"), one_up.buf);\n \n \tlen = strbuf_read(&sb, cp.out, PATH_MAX);\n \tclose(cp.out);\n \n \tif (starts_with(sb.buf, \"160000\")) {\n \t\tint super_sub_len;\n-\t\tint cwd_len = strlen(cwd);\n+\t\tint wt_len = target_wt.len;\n \t\tchar *super_sub, *super_wt;\n \n \t\t/*\n@@ -2666,12 +2667,12 @@ int get_superproject_working_tree(struct strbuf *buf)\n \t\tsuper_sub = strchr(sb.buf, '\\t') + 1;\n \t\tsuper_sub_len = strlen(super_sub);\n \n-\t\tif (super_sub_len > cwd_len ||\n-\t\t    strcmp(&cwd[cwd_len - super_sub_len], super_sub))\n-\t\t\tBUG(\"returned path string doesn't match cwd?\");\n+\t\tif (super_sub_len > wt_len ||\n+\t\t    strcmp(&target_wt.buf[wt_len - super_sub_len], super_sub))\n+\t\t\tBUG(\"returned path string doesn't match worktree?\");\n \n-\t\tsuper_wt = xstrdup(cwd);\n-\t\tsuper_wt[cwd_len - super_sub_len] = '\\0';\n+\t\tsuper_wt = xstrdup(target_wt.buf);\n+\t\tsuper_wt[wt_len - super_sub_len] = '\\0';\n \n \t\tstrbuf_realpath(buf, super_wt, 1);\n \t\tret = 1;\n@@ -2681,10 +2682,10 @@ int get_superproject_working_tree(struct strbuf *buf)\n \tcode = finish_command(&cp);\n \n \tif (code == 128)\n-\t\t/* '../' is not a git repository */\n+\t\t/* parent directory is not a git repository */\n \t\tret = 0;\n \telse if (code == 0 && len == 0)\n-\t\t/* There is an unrelated git repository at '../' */\n+\t\t/* There is an unrelated git repository at parent directory */\n \t\tret = 0;\n \telse if (code)\n \t\tdie(_(\"ls-tree returned unexpected return code %d\"), code);\n@@ -2692,7 +2693,7 @@ int get_superproject_working_tree(struct strbuf *buf)\n out:\n \tstrbuf_release(&sb);\n \tstrbuf_release(&one_up);\n-\tfree(cwd);\n+\tstrbuf_release(&target_wt);\n \treturn ret;\n }\n \ndiff --git a/submodule.h b/submodule.h\nindex b10e16e6c0..1a465a1208 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -170,6 +170,6 @@ void absorb_git_dir_into_superproject(const char *path,\n  * project is a submodule of. If this repository is not a submodule of\n  * another repository, return 0.\n  */\n-int get_superproject_working_tree(struct strbuf *buf);\n+int get_superproject_working_tree(struct repository *r, struct strbuf *buf);\n \n #endif\ndiff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh\nindex eefdecb0bd..5c3d9f2829 100755\n--- a/t/t7400-submodule-basic.sh\n+++ b/t/t7400-submodule-basic.sh\n@@ -1549,4 +1549,23 @@ test_expect_success 'submodule add fails when name is reused' '\n \t)\n '\n \n+test_expect_success 'path.superproject-root works with --git-dir' '\n+\ttest_when_finished \"rm -rf sub super\" &&\n+\tgit init sub &&\n+\ttest_commit -C sub initial &&\n+\tgit init super &&\n+\t(\n+\t\tcd super &&\n+\t\tgit -c protocol.file.allow=always submodule add \"../sub\" sub &&\n+\t\tgit commit -m \"add submodule\" &&\n+\n+\t\tSUPER_ROOT=\"$(test-tool path-utils real_path .)\" &&\n+\t\tMODULE_DIR=\"$SUPER_ROOT/.git/modules/sub\" &&\n+\n+\t\techo \"path.superproject-root.absolute=$SUPER_ROOT\" >expect &&\n+\t\tgit --git-dir=\"$MODULE_DIR\" repo info path.superproject-root.absolute >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\n-- \n2.56.0-rc2\n\n"},{"id":"553375","messageId":"20260927114420.59724-4-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260927114420.59724-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v7 3/8] repo: add path.superproject-root with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-27T11:44:15Z","receivedAt":"2026-09-27T11:45:28Z","isPatch":true,"body":"Scripts working in multi-repository setups often need to identify the\ntop-level working tree of a superproject from within a submodule.\nCurrently, this is only exposed via `git rev-parse\n--show-superproject-working-tree`.\n\nIntroduce `path.superproject-root.absolute` and\n`path.superproject-root.relative` keys to `git repo info`.\nThis exposes the core submodule context via a scriptable config-like key\nusing standard format rules.\n\nIf requested when not inside a submodule, the command returns an empty\nstring.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 10 ++++++++++\n builtin/repo.c              | 31 +++++++++++++++++++++++++++++\n t/t1900-repo-info.sh        | 39 +++++++++++++++++++++++++++++++++++++\n 3 files changed, 80 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex e34abe5fea..e524a07f53 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,16 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.superproject-root.absolute`::\n+\tThe canonical absolute path to the working tree root of the superproject\n+\tif the current repository is an initialized submodule. Outputs an empty\n+\tstring if not in a submodule.\n+\n+`path.superproject-root.relative`::\n+\tThe path to the working tree root of the superproject relative to the\n+\tcurrent working directory if the current repository is an initialized\n+\tsubmodule. Outputs an empty string if not in a submodule.\n+\n `path.toplevel.absolute`::\n \tThe canonical absolute path to the top-level directory of the\n \trepository's working tree. Outputs an empty string if the repository\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex c31e9cfa70..27ebb7a1c9 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -18,6 +18,7 @@\n #include \"strbuf.h\"\n #include \"string-list.h\"\n #include \"shallow.h\"\n+#include \"submodule.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"utf8.h\"\n@@ -121,6 +122,34 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf superproject = STRBUF_INIT;\n+\n+\tif (!get_superproject_working_tree(repo, &superproject)) {\n+\t\tstrbuf_release(&superproject);\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, superproject.buf, \"\", PATH_FORMAT_CANONICAL);\n+\tstrbuf_release(&superproject);\n+\treturn 0;\n+}\n+\n+static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf superproject = STRBUF_INIT;\n+\n+\tif (!get_superproject_working_tree(repo, &superproject)) {\n+\t\tstrbuf_release(&superproject);\n+\t\treturn 0;\n+\t}\n+\n+\tformat_path(buf, superproject.buf, repo->prefix, PATH_FORMAT_RELATIVE);\n+\tstrbuf_release(&superproject);\n+\treturn 0;\n+}\n+\n static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *work_tree = repo_get_work_tree(repo);\n@@ -159,6 +188,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n+\t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\n \t{ \"path.toplevel.relative\", get_path_toplevel_relative },\n \t{ \"references.format\", get_references_format },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 9417d1ab65..eec576a1d9 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -213,6 +213,45 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.superproject-root absolute and relative' '\n+\ttest_when_finished \"rm -rf sub super\" &&\n+\tgit init sub &&\n+\ttest_commit -C sub initial &&\n+\tgit init super &&\n+\t(\n+\t\tcd super &&\n+\t\tgit -c protocol.file.allow=always submodule add \"../sub\" sub &&\n+\t\tgit commit -m \"add submodule\" &&\n+\n+\t\tcd sub &&\n+\t\tROOT=\"$(test-tool path-utils real_path ..)\" &&\n+\n+\t\techo \"path.superproject-root.absolute=$ROOT\" >expect.abs &&\n+\t\tgit repo info path.superproject-root.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.superproject-root.relative=../\" >expect.rel &&\n+\t\tgit repo info path.superproject-root.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n+test_expect_success 'path.superproject-root returns empty when not in a submodule' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\n+\t\techo \"path.superproject-root.absolute=\" >expect.abs &&\n+\t\tgit repo info path.superproject-root.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.superproject-root.relative=\" >expect.rel &&\n+\t\tgit repo info path.superproject-root.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n test_expect_success 'path.toplevel absolute and relative' '\n \ttest_when_finished \"rm -rf repo\" &&\n \tgit init repo &&\n-- \n2.56.0-rc2\n\n"},{"id":"553376","messageId":"20260927114420.59724-5-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260927114420.59724-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v7 4/8] repo: add path.hooks with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-27T11:44:16Z","receivedAt":"2026-09-27T11:45:44Z","isPatch":true,"body":"Hooks are an integral part of a repository's configuration and are\ncommonly used by tooling to automate repository-specific workflows.\nCurrently, scripts typically retrieve the hooks directory by invoking\n`git rev-parse --git-path hooks`.\n\nIntroduce `path.hooks.absolute` and `path.hooks.relative` keys to\n`git repo info`. This exposes the hooks directory as a scriptable\nconfig-like key using standard format rules, allowing scripts to\nretrieve it through the same interface as other repository path\ninformation.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  9 +++++++++\n builtin/repo.c              | 22 ++++++++++++++++++++++\n t/t1900-repo-info.sh        | 28 ++++++++++++++++++++++++++--\n 3 files changed, 57 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex e524a07f53..20836cf8f6 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,15 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.hooks.absolute`::\n+\tThe canonical absolute path to the repository's hooks directory.\n+\tRespects the `core.hooksPath` configuration. If `core.hooksPath` is\n+\tset to `/dev/null`, that value is returned unchanged.\n+\n+`path.hooks.relative`::\n+\tThe path to the repository's hooks directory relative to the current\n+\tworking directory. Respects the `core.hooksPath` configuration.\n+\n `path.superproject-root.absolute`::\n \tThe canonical absolute path to the working tree root of the superproject\n \tif the current repository is an initialized submodule. Outputs an empty\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 27ebb7a1c9..01666dc17c 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,26 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf hooks_path = STRBUF_INIT;\n+\n+\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n+\tformat_path(buf, hooks_path.buf, \"\", PATH_FORMAT_CANONICAL);\n+\tstrbuf_release(&hooks_path);\n+\treturn 0;\n+}\n+\n+static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tstruct strbuf hooks_path = STRBUF_INIT;\n+\n+\trepo_git_path_replace(repo, &hooks_path, \"hooks\");\n+\tformat_path(buf, hooks_path.buf, repo->prefix, PATH_FORMAT_RELATIVE);\n+\tstrbuf_release(&hooks_path);\n+\treturn 0;\n+}\n+\n static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tstruct strbuf superproject = STRBUF_INIT;\n@@ -188,6 +208,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n+\t{ \"path.hooks.relative\", get_path_hooks_relative },\n \t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n \t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex eec576a1d9..1da5db4942 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -174,7 +174,11 @@ test_repo_info_path () {\n \t\t\tcd repo/sub &&\n \t\t\tROOT=\"$(test-tool path-utils real_path ..)\" && export ROOT &&\n \t\t\teval \"$init_command\" &&\n-\t\t\techo \"path.$field_name.absolute=$ROOT/$expected_dir\" >expect &&\n+\t\t\tcase \"$expected_dir\" in\n+\t\t\t/*) EXPECT_ABS=\"$expected_dir\" ;;\n+\t\t\t*) EXPECT_ABS=\"$ROOT/$expected_dir\" ;;\n+\t\t\tesac &&\n+\t\t\techo \"path.$field_name.absolute=$EXPECT_ABS\" >expect &&\n \t\t\tgit repo info \"path.$field_name.absolute\" >actual &&\n \t\t\ttest_cmp expect actual\n \t\t)\n@@ -188,7 +192,11 @@ test_repo_info_path () {\n \t\t\tcd repo/sub &&\n \t\t\tROOT=\"$(test-tool path-utils real_path ..)\" && export ROOT &&\n \t\t\teval \"$init_command\" &&\n-\t\t\techo \"path.$field_name.relative=../$expected_dir\" >expect &&\n+\t\t\tcase \"$expected_dir\" in\n+\t\t\t/*) EXPECT_REL=\"$(test-tool path-utils relative_path \"$expected_dir\" \"$PWD\")\" ;;\n+\t\t\t*) EXPECT_REL=\"../$expected_dir\" ;;\n+\t\t\tesac &&\n+\t\t\techo \"path.$field_name.relative=$EXPECT_REL\" >expect &&\n \t\t\tgit repo info \"path.$field_name.relative\" >actual &&\n \t\t\ttest_cmp expect actual\n \t\t)\n@@ -213,6 +221,22 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'\n+\n+test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n+\t'custom-hooks' \\\n+\t'git config core.hooksPath \"$ROOT/custom-hooks\" && mkdir -p \"$ROOT/custom-hooks\"'\n+\n+# /dev/null is not a real, canonicalizable filesystem path on Windows,\n+# so path resolution for core.hooksPath=/dev/null cannot be expected to\n+# produce a literal \"/dev/null\" the way it does on POSIX systems.\n+if ! test_have_prereq MINGW\n+then\n+\ttest_repo_info_path 'hooks with core.hooksPath=/dev/null' 'hooks' \\\n+\t\t'/dev/null' \\\n+\t\t'git config core.hooksPath /dev/null'\n+fi\n+\n test_expect_success 'path.superproject-root absolute and relative' '\n \ttest_when_finished \"rm -rf sub super\" &&\n \tgit init sub &&\n-- \n2.56.0-rc2\n\n"},{"id":"553377","messageId":"20260927114420.59724-6-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260927114420.59724-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v7 5/8] repo: add path.index with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-27T11:44:17Z","receivedAt":"2026-09-27T11:46:02Z","isPatch":true,"body":"The repository index is a fundamental component used by Git and related\ntooling to track the working tree state. Scripts that interact with the\nindex currently retrieve its location by invoking\n`git rev-parse --git-path index`.\n\nIntroduce `path.index.absolute` and `path.index.relative` keys to\n`git repo info`. This exposes the index file location as a scriptable\nconfig-like key using standard format rules, allowing scripts to\nretrieve it through the same interface as other repository path\ninformation.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 12 ++++++++++++\n builtin/repo.c              | 18 ++++++++++++++++++\n t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++\n 3 files changed, 53 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 20836cf8f6..08ef47750c 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -128,6 +128,18 @@ values that they return:\n \tThe path to the repository's hooks directory relative to the current\n \tworking directory. Respects the `core.hooksPath` configuration.\n \n+`path.index.absolute`::\n+\tThe canonical absolute path to the repository's current index file.\n+\tRespects the `GIT_INDEX_FILE` environment override. Returns the\n+\tconfigured index path even if the repository is bare or the file does\n+\tnot exist.\n+\n+`path.index.relative`::\n+\tThe path to the repository's current index file relative to the current\n+\tworking directory. Respects the `GIT_INDEX_FILE` environment override.\n+\tReturns the configured index path even if the repository is bare or the\n+\tfile does not exist.\n+\n `path.superproject-root.absolute`::\n \tThe canonical absolute path to the working tree root of the superproject\n \tif the current repository is an initialized submodule. Outputs an empty\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 01666dc17c..a4ea2022eb 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -142,6 +142,22 @@ static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *index_file = repo_get_index_file(repo);\n+\n+\tformat_path(buf, index_file, \"\", PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *index_file = repo_get_index_file(repo);\n+\n+\tformat_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tstruct strbuf superproject = STRBUF_INIT;\n@@ -210,6 +226,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n \t{ \"path.hooks.relative\", get_path_hooks_relative },\n+\t{ \"path.index.absolute\", get_path_index_absolute },\n+\t{ \"path.index.relative\", get_path_index_relative },\n \t{ \"path.superproject-root.absolute\", get_path_superproject_absolute },\n \t{ \"path.superproject-root.relative\", get_path_superproject_relative },\n \t{ \"path.toplevel.absolute\", get_path_toplevel_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 1da5db4942..431a4842d4 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -237,6 +237,29 @@ then\n \t\t'git config core.hooksPath /dev/null'\n fi\n \n+test_repo_info_path 'index standard' 'index' '.git/index'\n+\n+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \\\n+\t'custom-index-file' \\\n+\t'GIT_INDEX_FILE=\"$ROOT/custom-index-file\" && export GIT_INDEX_FILE'\n+\n+test_expect_success 'path.index in a bare repository returns default index location' '\n+\ttest_when_finished \"rm -rf bare.git\" &&\n+\tgit init --bare bare.git &&\n+\t(\n+\t\tcd bare.git &&\n+\t\tROOT=\"$(test-tool path-utils real_path .)\" &&\n+\n+\t\techo \"path.index.absolute=$ROOT/index\" >expect.abs &&\n+\t\tgit repo info path.index.absolute >actual.abs &&\n+\t\ttest_cmp expect.abs actual.abs &&\n+\n+\t\techo \"path.index.relative=index\" >expect.rel &&\n+\t\tgit repo info path.index.relative >actual.rel &&\n+\t\ttest_cmp expect.rel actual.rel\n+\t)\n+'\n+\n test_expect_success 'path.superproject-root absolute and relative' '\n \ttest_when_finished \"rm -rf sub super\" &&\n \tgit init sub &&\n-- \n2.56.0-rc2\n\n"},{"id":"553378","messageId":"20260927114420.59724-7-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260927114420.59724-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v7 6/8] repo: add path.grafts with absolute and relative suffixes","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-27T11:44:18Z","receivedAt":"2026-09-27T11:46:20Z","isPatch":true,"body":"The repository grafts file specifies alternate parent relationships for\ncommits and may be used by repository tooling that needs to inspect or\nmanage grafts. Scripts currently retrieve its location by invoking\n`git rev-parse --git-path info/grafts`.\n\nIntroduce `path.grafts.absolute` and `path.grafts.relative` keys to\n`git repo info`. This exposes the grafts file location as a scriptable\nconfig-like key using standard format rules, allowing scripts to\nretrieve it through the same interface as other repository path\ninformation.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc | 11 +++++++++++\n builtin/repo.c              | 24 ++++++++++++++++++++++++\n t/t1900-repo-info.sh        |  6 ++++++\n 3 files changed, 41 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 08ef47750c..868ab0ed9f 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -119,6 +119,17 @@ values that they return:\n `path.gitdir.relative`::\n \tThe path to the Git repository directory relative to the current working directory.\n \n+`path.grafts.absolute`::\n+\tThe canonical absolute path to the repository's graft file.\n+\tRespects the `GIT_GRAFT_FILE` environment override. The path is\n+\treturned regardless of whether the file currently exists on disk.\n+\n+`path.grafts.relative`::\n+\tThe path to the repository's graft file relative to the current\n+\tworking directory. Respects the `GIT_GRAFT_FILE` environment\n+\toverride. The path is returned regardless of whether the file\n+\tcurrently exists on disk.\n+\n `path.hooks.absolute`::\n \tThe canonical absolute path to the repository's hooks directory.\n \tRespects the `core.hooksPath` configuration. If `core.hooksPath` is\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex a4ea2022eb..8144b74361 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_grafts_absolute(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *graft_file = repo_get_graft_file(repo);\n+\n+\tif (!graft_file)\n+\t\treturn error(_(\"unable to get graft file\"));\n+\n+\tformat_path(buf, graft_file, \"\", PATH_FORMAT_CANONICAL);\n+\treturn 0;\n+}\n+\n+static int get_path_grafts_relative(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *graft_file = repo_get_graft_file(repo);\n+\n+\tif (!graft_file)\n+\t\treturn error(_(\"unable to get graft file\"));\n+\n+\tformat_path(buf, graft_file, repo->prefix, PATH_FORMAT_RELATIVE);\n+\treturn 0;\n+}\n+\n static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tstruct strbuf hooks_path = STRBUF_INIT;\n@@ -224,6 +246,8 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n+\t{ \"path.grafts.absolute\", get_path_grafts_absolute },\n+\t{ \"path.grafts.relative\", get_path_grafts_relative },\n \t{ \"path.hooks.absolute\", get_path_hooks_absolute },\n \t{ \"path.hooks.relative\", get_path_hooks_relative },\n \t{ \"path.index.absolute\", get_path_index_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 431a4842d4..adc4a92487 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -221,6 +221,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_repo_info_path 'grafts standard' 'grafts' '.git/info/grafts'\n+\n+test_repo_info_path 'grafts with GIT_GRAFT_FILE override' 'grafts' \\\n+\t'custom-graft-file' \\\n+\t'GIT_GRAFT_FILE=\"$ROOT/custom-graft-file\" && export GIT_GRAFT_FILE'\n+\n test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'\n \n test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \\\n-- \n2.56.0-rc2\n\n"},{"id":"553379","messageId":"20260927114420.59724-8-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260927114420.59724-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v7 7/8] repo: add path.git-prefix","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-27T11:44:19Z","receivedAt":"2026-09-27T11:46:42Z","isPatch":true,"body":"Scripts sometimes need the path from the repository's working tree root\nto the current working directory. While this information can be derived\nthrough existing Git commands, `git repo info` does not currently expose\nit as a scriptable key.\n\nIntroduce the `path.git-prefix` key to `git repo info`. The key returns\nthe path from the working tree root to the current working directory,\nreturning the empty string when invoked from the working tree root.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  5 +++++\n builtin/repo.c              | 11 +++++++++++\n t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++\n 3 files changed, 39 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 868ab0ed9f..fb5aceae8f 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -113,6 +113,11 @@ values that they return:\n \tThe path to the Git repository's common directory relative to\n \tthe current working directory.\n \n+`path.git-prefix`::\n+\tThe path from the root of the working tree to the current working\n+\tdirectory. Returns the empty string when the current working directory\n+\tis the root of the working tree.\n+\n `path.gitdir.absolute`::\n \tThe canonical absolute path to the Git repository directory (the `.git` directory).\n \ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 8144b74361..ce78cdc44e 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -100,6 +100,16 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b\n \treturn 0;\n }\n \n+static int get_path_git_prefix(struct repository *repo, struct strbuf *buf)\n+{\n+\t/*\n+\t * repo->prefix is NULL when the current working directory is\n+\t * the worktree root.\n+\t */\n+\tstrbuf_addstr(buf, repo->prefix ? repo->prefix : \"\");\n+\treturn 0;\n+}\n+\n static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *git_dir = repo_get_git_dir(repo);\n@@ -244,6 +254,7 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"object.format\", get_object_format },\n \t{ \"path.commondir.absolute\", get_path_commondir_absolute },\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n+\t{ \"path.git-prefix\", get_path_git_prefix },\n \t{ \"path.gitdir.absolute\", get_path_gitdir_absolute },\n \t{ \"path.gitdir.relative\", get_path_gitdir_relative },\n \t{ \"path.grafts.absolute\", get_path_grafts_absolute },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex adc4a92487..b689445b7a 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -215,6 +215,29 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.git-prefix at repository root' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\techo \"path.git-prefix=\" >expect &&\n+\t\tgit repo info path.git-prefix >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'path.git-prefix in subdirectory' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\tmkdir -p repo/sub/dir &&\n+\t(\n+\t\tcd repo/sub/dir &&\n+\t\techo \"path.git-prefix=sub/dir/\" >expect &&\n+\t\tgit repo info path.git-prefix >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_repo_info_path 'gitdir standard' 'gitdir' '.git'\n \n test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \\\n-- \n2.56.0-rc2\n\n"},{"id":"553380","messageId":"20260927114420.59724-9-jayatheerthkulkarni2005@gmail.com","threadId":"66010","inReplyTo":"20260927114420.59724-1-jayatheerthkulkarni2005@gmail.com","subject":"[GSoC Patch v7 8/8] repo: add path.cdup","fromName":"K Jayatheerth","fromEmail":"jayatheerthkulkarni2005@gmail.com","sentAt":"2026-09-27T11:44:20Z","receivedAt":"2026-09-27T11:47:10Z","isPatch":true,"body":"Scripts sometimes need the relative path from the current working\ndirectory to the repository's working tree root (cdup). While this\ninformation can be retrieved through `git rev-parse --show-cdup`,\n`git repo info` does not currently expose it as a scriptable key.\n\nIntroduce the `path.cdup` key to `git repo info`. The key returns the\npath from the current working directory to the root of the working tree,\nreturning the empty string when invoked from the working tree root.\n\nMentored-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\nSigned-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>\n---\n Documentation/git-repo.adoc |  5 +++++\n builtin/repo.c              | 23 +++++++++++++++++++++\n t/t1900-repo-info.sh        | 40 +++++++++++++++++++++++++++++++++++++\n 3 files changed, 68 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex fb5aceae8f..7e14aac244 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -104,6 +104,11 @@ values that they return:\n `object.format`::\n \tThe object format (hash algorithm) used in the repository.\n \n+`path.cdup`::\n+\tWhen the command is invoked from a subdirectory, show the\n+\tpath of the top-level directory relative to the current\n+\tdirectory (typically a sequence of \"../\", or an empty string).\n+\n `path.commondir.absolute`::\n \tThe canonical absolute path to the Git repository's common\n \tdirectory (the shared `.git` directory containing objects,\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex ce78cdc44e..d4083abedc 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -78,6 +78,28 @@ static int get_object_format(struct repository *repo, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_path_cdup(struct repository *repo, struct strbuf *buf)\n+{\n+\tconst char *pfx = repo->prefix;\n+\n+\tif (!is_inside_work_tree(repo)) {\n+\t\tconst char *worktree = repo_get_work_tree(repo);\n+\n+\t\tif (worktree) {\n+\t\t\tstrbuf_addstr(buf, worktree);\n+\t\t}\n+\t}\n+\n+\twhile (pfx) {\n+\t\tpfx = strchr(pfx, '/');\n+\t\tif (pfx) {\n+\t\t\tpfx++;\n+\t\t\tstrbuf_addstr(buf, \"../\");\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n static int get_path_commondir_absolute(struct repository *repo, struct strbuf *buf)\n {\n \tconst char *common_dir = repo_get_common_dir(repo);\n@@ -252,6 +274,7 @@ static const struct repo_info_field repo_info_field[] = {\n \t{ \"layout.bare\", get_layout_bare },\n \t{ \"layout.shallow\", get_layout_shallow },\n \t{ \"object.format\", get_object_format },\n+\t{ \"path.cdup\", get_path_cdup },\n \t{ \"path.commondir.absolute\", get_path_commondir_absolute },\n \t{ \"path.commondir.relative\", get_path_commondir_relative },\n \t{ \"path.git-prefix\", get_path_git_prefix },\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex b689445b7a..c600074c24 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -215,6 +215,46 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \\\n \t'.git' \\\n \t'GIT_DIR=\"../.git\" && export GIT_DIR'\n \n+test_expect_success 'path.cdup at repository root' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\techo \"path.cdup=\" >expect &&\n+\t\tgit repo info path.cdup >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'path.cdup in subdirectory' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\tmkdir -p repo/sub/dir &&\n+\t(\n+\t\tcd repo/sub/dir &&\n+\t\techo \"path.cdup=../../\" >expect &&\n+\t\tgit repo info path.cdup >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'path.cdup cwd outside the working tree' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tmkdir -p repo/tmp/x &&\n+\tcd repo &&\n+\tgit init test &&\n+\t(\n+\t\techo path.cdup=$(pwd)/tmp/x >./test/expect &&\n+\t\tcd test &&\n+\t\tGIT_WORK_TREE=../tmp/x &&\n+\t\texport GIT_WORK_TREE &&\n+\t\tGIT_DIR=$(pwd)/.git &&\n+\t\texport GIT_DIR &&\n+\t\tgit repo info path.cdup >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_expect_success 'path.git-prefix at repository root' '\n \ttest_when_finished \"rm -rf repo\" &&\n \tgit init repo &&\n-- \n2.56.0-rc2\n\n"}]}