Volume XXII, number 279Tuesday, October 6, 2026Latest message 1 hour ago

The Git List

News and archive of git@vger.kernel.org, since April 2005

patch, 7 partsrepo: add more path keys to git repo info

94 messages between Jul 16, 2026 and Sep 27, 2026, from K Jayatheerth, Junio C Hamano, Lucas Seiki Oshiro, Justin Tobler, SZEDER Gábor.

Plain Markdown or JSON for tools and agents. Diffs are folded; open one to read it.

K JayatheerthJul 16, 2026, 01:21 UTC on lore
Series adds keys to git repo info.
Keys output paths of repository components:
* path.toplevel: repository tree.
* path.superproject-working-tree: superproject tree from submodules.
* path.objects: repository objects.
* path.hooks: repository hooks.
* path.index: repository index.
* path.grafts: repository grafts.
* path.git-prefix: prefix offset.

Keys support suffixes for format. Commits contain documentation and tests.

K Jayatheerth (7):
  repo: add path.toplevel with absolute and relative suffix formatting
  repo: add path.superproject-working-tree with absolute and relative
    suffixes
  repo: add path.objects with absolute and relative suffix formatting
  repo: add path.hooks with absolute and relative suffix formatting
  repo: add path.index with absolute and relative suffix formatting
  repo: add path.grafts with absolute and relative suffix formatting
  repo: add path.git-prefix path key validation
 Documentation/git-repo.adoc |  58 +++++++++++++
 builtin/repo.c              | 167 ++++++++++++++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 108 +++++++++++++++++++++++
 3 files changed, 333 insertions(+)
-- 
2.55.GIT
K JayatheerthJul 16, 2026, 01:21 UTC in reply to K Jayatheerth on lore

[GSoC Patch 1/7] repo: add path.toplevel with absolute and relative suffix formatting

Scripts frequently need to find the root directory of a repository's working tree. Currently, this requires using `git rev-parse --show-toplevel` or inferring it from other path components.

Introduce `path.toplevel.absolute` and `path.toplevel.relative` keys to `git repo info`. This allows scripts to retrieve the top-level working tree path in a predictable, strictly formatted manner without relying on `rev-parse`.

If requested in a bare repository where no working tree exists, the command returns an empty string.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 10 ++++++++++
 builtin/repo.c              | 28 ++++++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 30 ++++++++++++++++++++++++++++++
 3 files changed, 68 insertions(+)
Show changes to 3 files +68 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index ed7d80c690..e34abe5fea 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,16 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.toplevel.absolute`::
+	The canonical absolute path to the top-level directory of the
+	repository's working tree. Outputs an empty string if the repository
+	is bare.
+
+`path.toplevel.relative`::
+	The path to the top-level directory of the repository's working
+	tree relative to the current working directory. Outputs an empty
+	string if the repository is bare.
+
 `references.format`::
 	The reference storage format. The valid values are:
 +
diff --git a/builtin/repo.c b/builtin/repo.c
index 042d6de558..194757eb18 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -121,6 +121,32 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *work_tree = repo_get_work_tree(repo);
+
+	if (!work_tree) {
+		strbuf_addstr(buf, "");
+		return 0;
+	}
+
+	format_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *work_tree = repo_get_work_tree(repo);
+
+	if (!work_tree) {
+		strbuf_addstr(buf, "");
+		return 0;
+	}
+
+	format_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_references_format(struct repository *repo, struct strbuf *buf)
 {
 	strbuf_addstr(buf,
@@ -137,6 +163,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.toplevel.absolute", get_path_toplevel_absolute },
+	{ "path.toplevel.relative", get_path_toplevel_relative },
 	{ "references.format", get_references_format },
 };
 
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index ae8c22c817..fbb9063ee5 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,4 +213,34 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.toplevel absolute and relative' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		mkdir -p repo/sub &&
+		cd repo/sub &&
+
+		ROOT="$(test-tool path-utils real_path ..)" &&
+
+		echo "path.toplevel.absolute=$ROOT" >expect.abs &&
+		git repo info path.toplevel.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.toplevel.relative=../" >expect.rel &&
+		git repo info path.toplevel.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
+test_expect_success 'path.toplevel returns empty in a bare repository' '
+	test_when_finished "rm -rf bare.git" &&
+	git init --bare bare.git &&
+	(
+		cd bare.git &&
+		echo "path.toplevel.absolute=" >expect &&
+		git repo info path.toplevel.absolute >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done
-- 
2.55.GIT
K JayatheerthJul 16, 2026, 01:21 UTC in reply to K Jayatheerth on lore

[GSoC Patch 2/7] repo: add path.superproject-working-tree with absolute and relative suffixes

Scripts working in multi-repository setups often need to identify the top-level working tree of a superproject from within a submodule. Currently, this is only exposed via `git rev-parse --show-superproject-working-tree`.

Introduce `path.superproject-working-tree.absolute` and `path.superproject-working-tree.relative` keys to `git repo info`. This exposes the core submodule context via a scriptable config-like key using standard format rules.

If requested when not inside a submodule, the command returns an empty string.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 10 ++++++++++
 builtin/repo.c              | 33 +++++++++++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 34 ++++++++++++++++++++++++++++++++++
 3 files changed, 77 insertions(+)
Show changes to 3 files +77 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index e34abe5fea..03aa57942f 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,16 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.superproject-working-tree.absolute`::
+	The canonical absolute path to the working tree root of the superproject
+	if the current repository is an initialized submodule. Outputs an empty
+	string if not in a submodule.
+
+`path.superproject-working-tree.relative`::
+	The path to the working tree root of the superproject relative to the
+	current working directory if the current repository is an initialized
+	submodule. Outputs an empty string if not in a submodule.
+
 `path.toplevel.absolute`::
 	The canonical absolute path to the top-level directory of the
 	repository's working tree. Outputs an empty string if the repository
diff --git a/builtin/repo.c b/builtin/repo.c
index 194757eb18..82359473e9 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -18,6 +18,7 @@
 #include "strbuf.h"
 #include "string-list.h"
 #include "shallow.h"
+#include "submodule.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "utf8.h"
@@ -121,6 +122,36 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
+{
+	struct strbuf superproject = STRBUF_INIT;
+
+	if (!get_superproject_working_tree(&superproject)) {
+		strbuf_release(&superproject);
+		strbuf_addstr(buf, "");
+		return 0;
+	}
+
+	format_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	strbuf_release(&superproject);
+	return 0;
+}
+
+static int get_path_superproject_relative(struct repository *repo UNUSED, struct strbuf *buf)
+{
+	struct strbuf superproject = STRBUF_INIT;
+
+	if (!get_superproject_working_tree(&superproject)) {
+		strbuf_release(&superproject);
+		strbuf_addstr(buf, "");
+		return 0;
+	}
+
+	format_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	strbuf_release(&superproject);
+	return 0;
+}
+
 static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *work_tree = repo_get_work_tree(repo);
@@ -163,6 +194,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
+	{ "path.superproject-working-tree.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
 	{ "path.toplevel.relative", get_path_toplevel_relative },
 	{ "references.format", get_references_format },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index fbb9063ee5..220b3d4d3d 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,40 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.superproject-working-tree absolute and relative' '
+	test_when_finished "rm -rf sub super" &&
+	git init sub &&
+	test_commit -C sub initial &&
+	git init super &&
+	(
+		cd super &&
+		git -c protocol.file.allow=always submodule add "../sub" sub &&
+		git commit -m "add submodule" &&
+
+		cd sub &&
+		ROOT="$(test-tool path-utils real_path ..)" &&
+
+		echo "path.superproject-working-tree.absolute=$ROOT" >expect.abs &&
+		git repo info path.superproject-working-tree.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.superproject-working-tree.relative=../" >expect.rel &&
+		git repo info path.superproject-working-tree.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
+test_expect_success 'path.superproject-working-tree returns empty when not in a submodule' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		echo "path.superproject-working-tree.absolute=" >expect &&
+		git repo info path.superproject-working-tree.absolute >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_expect_success 'path.toplevel absolute and relative' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
-- 
2.55.GIT
K JayatheerthJul 16, 2026, 01:21 UTC in reply to K Jayatheerth on lore

[GSoC Patch 3/7] repo: add path.objects with absolute and relative suffix formatting

Tools and deployment hooks frequently query the location of the object database directory. Currently, this relies on legacy parsing methods or manually inspecting `git rev-parse --git-path objects`.

Introduce `path.objects.absolute` and `path.objects.relative` keys to `git repo info`. This allows tools to discover the object database location safely while natively adhering to active `GIT_OBJECT_DIRECTORY` environment variable overrides.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  9 +++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        |  7 +++++++
 3 files changed, 40 insertions(+)
Show changes to 3 files +40 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 03aa57942f..8429a44b43 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,15 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.objects.absolute`::
+	The canonical absolute path to the repository's object database directory.
+	Respects the `GIT_OBJECT_DIRECTORY` environment override.
+
+`path.objects.relative`::
+	The path to the repository's object database directory relative to the
+	current working directory. Respects the `GIT_OBJECT_DIRECTORY`
+	environment override.
+
 `path.superproject-working-tree.absolute`::
 	The canonical absolute path to the working tree root of the superproject
 	if the current repository is an initialized submodule. Outputs an empty
diff --git a/builtin/repo.c b/builtin/repo.c
index 82359473e9..d6bdd5bcfa 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *obj_dir = repo_get_object_directory(repo);
+
+	if (!obj_dir)
+		return error(_("unable to get object directory"));
+
+	format_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_objects_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *obj_dir = repo_get_object_directory(repo);
+
+	if (!obj_dir)
+		return error(_("unable to get object directory"));
+
+	format_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
 {
 	struct strbuf superproject = STRBUF_INIT;
@@ -194,6 +216,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.objects.absolute", get_path_objects_absolute },
+	{ "path.objects.relative", get_path_objects_relative },
 	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
 	{ "path.superproject-working-tree.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 220b3d4d3d..260f4fde43 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,13 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'objects standard' 'objects' '.git/objects'
+
+test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \
+	'custom-objects' \
+	'GIT_OBJECT_DIRECTORY="$ROOT/custom-objects" && export GIT_OBJECT_DIRECTORY &&
+	 mkdir -p "$ROOT/custom-objects"'
+
 test_expect_success 'path.superproject-working-tree absolute and relative' '
 	test_when_finished "rm -rf sub super" &&
 	git init sub &&
-- 
2.55.GIT
K JayatheerthJul 16, 2026, 01:21 UTC in reply to K Jayatheerth on lore

[GSoC Patch 4/7] repo: add path.hooks with absolute and relative suffix formatting

External tool integrations and validation systems need a stable way to identify where the repository hooks are stored. Currently, this involves relying on `git rev-parse --git-path hooks` or querying `core.hooksPath` manually.

Introduce `path.hooks.absolute` and `path.hooks.relative` keys to `git repo info`. This allows tools to discover the active hooks location natively, ensuring proper resolution regardless of whether Git is using the standard `.git/hooks` structure or a custom `core.hooksPath` setup.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  8 ++++++++
 builtin/repo.c              | 22 ++++++++++++++++++++++
 t/t1900-repo-info.sh        |  6 ++++++
 3 files changed, 36 insertions(+)
Show changes to 3 files +36 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 8429a44b43..7bc1c51310 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,14 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.hooks.absolute`::
+	The canonical absolute path to the repository's hooks directory.
+	Respects `core.hooksPath` configuration adjustments.
+
+`path.hooks.relative`::
+	The path to the repository's hooks directory relative to the current
+	working directory. Respects `core.hooksPath` configuration adjustments.
+
 `path.objects.absolute`::
 	The canonical absolute path to the repository's object database directory.
 	Respects the `GIT_OBJECT_DIRECTORY` environment override.
diff --git a/builtin/repo.c b/builtin/repo.c
index d6bdd5bcfa..c921de222d 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,26 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf hooks_path = STRBUF_INIT;
+
+	repo_git_path_replace(repo, &hooks_path, "hooks");
+	format_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	strbuf_release(&hooks_path);
+	return 0;
+}
+
+static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf hooks_path = STRBUF_INIT;
+
+	repo_git_path_replace(repo, &hooks_path, "hooks");
+	format_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	strbuf_release(&hooks_path);
+	return 0;
+}
+
 static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *obj_dir = repo_get_object_directory(repo);
@@ -216,6 +236,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.hooks.absolute", get_path_hooks_absolute },
+	{ "path.hooks.relative", get_path_hooks_relative },
 	{ "path.objects.absolute", get_path_objects_absolute },
 	{ "path.objects.relative", get_path_objects_relative },
 	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 260f4fde43..cd3f856d04 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'
+
+test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
+	'custom-hooks' \
+	'git config core.hooksPath "$ROOT/custom-hooks" && mkdir -p "$ROOT/custom-hooks"'
+
 test_repo_info_path 'objects standard' 'objects' '.git/objects'
 
 test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \
-- 
2.55.GIT
K JayatheerthJul 16, 2026, 01:21 UTC in reply to K Jayatheerth on lore

[GSoC Patch 5/7] repo: add path.index with absolute and relative suffix formatting

External script workflows and formatting layers require straightforward access to the location of the index staging file. Currently, tracking this necessitates a legacy call to `git rev-parse --git-path index` or `--show-toplevel` logic abstractions.

Introduce `path.index.absolute` and `path.index.relative` keys to `git repo info`. This allows tooling utilities to discover the active index context cleanly while scaling transparently with localized `GIT_INDEX_FILE` environment overrides.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  8 ++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        |  6 ++++++
 3 files changed, 38 insertions(+)
Show changes to 3 files +38 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 7bc1c51310..3a837c573e 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -127,6 +127,14 @@ values that they return:
 	The path to the repository's hooks directory relative to the current
 	working directory. Respects `core.hooksPath` configuration adjustments.
 
+`path.index.absolute`::
+	The canonical absolute path to the repository's current index file.
+	Respects the `GIT_INDEX_FILE` environment override.
+
+`path.index.relative`::
+	The path to the repository's current index file relative to the current
+	working directory. Respects the `GIT_INDEX_FILE` environment override.
+
 `path.objects.absolute`::
 	The canonical absolute path to the repository's object database directory.
 	Respects the `GIT_OBJECT_DIRECTORY` environment override.
diff --git a/builtin/repo.c b/builtin/repo.c
index c921de222d..66bf4c67cc 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -142,6 +142,28 @@ static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *index_file = repo_get_index_file(repo);
+
+	if (!index_file)
+		return error(_("unable to get index file"));
+
+	format_path(buf, index_file, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *index_file = repo_get_index_file(repo);
+
+	if (!index_file)
+		return error(_("unable to get index file"));
+
+	format_path(buf, index_file, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *obj_dir = repo_get_object_directory(repo);
@@ -238,6 +260,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.gitdir.relative", get_path_gitdir_relative },
 	{ "path.hooks.absolute", get_path_hooks_absolute },
 	{ "path.hooks.relative", get_path_hooks_relative },
+	{ "path.index.absolute", get_path_index_absolute },
+	{ "path.index.relative", get_path_index_relative },
 	{ "path.objects.absolute", get_path_objects_absolute },
 	{ "path.objects.relative", get_path_objects_relative },
 	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index cd3f856d04..04e6b8553c 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -219,6 +219,12 @@ test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
 	'custom-hooks' \
 	'git config core.hooksPath "$ROOT/custom-hooks" && mkdir -p "$ROOT/custom-hooks"'
 
+test_repo_info_path 'index standard' 'index' '.git/index'
+
+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \
+	'custom-index-file' \
+	'GIT_INDEX_FILE="$ROOT/custom-index-file" && export GIT_INDEX_FILE'
+
 test_repo_info_path 'objects standard' 'objects' '.git/objects'
 
 test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \
-- 
2.55.GIT
K JayatheerthJul 16, 2026, 01:21 UTC in reply to K Jayatheerth on lore

[GSoC Patch 6/7] repo: add path.grafts with absolute and relative suffix formatting

External toolchains managing specialized history rewrites or legacy history splices require access to the location of the repository grafts file. Currently, this requires a legacy call to `git rev-parse --git-path info/grafts`.

Introduce `path.grafts.absolute` and `path.grafts.relative` keys to `git repo info`. This allows scripting layers to query the active grafts context cleanly while scaling transparently with active `GIT_GRAFT_FILE` environment variable overrides.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  8 ++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        |  6 ++++++
 3 files changed, 38 insertions(+)
Show changes to 3 files +38 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 3a837c573e..6c962620ec 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,14 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.grafts.absolute`::
+	The canonical absolute path to the repository grafts file.
+	Respects the `GIT_GRAFT_FILE` environment override.
+
+`path.grafts.relative`::
+	The path to the repository grafts file relative to the current working
+	directory. Respects the `GIT_GRAFT_FILE` environment override.
+
 `path.hooks.absolute`::
 	The canonical absolute path to the repository's hooks directory.
 	Respects `core.hooksPath` configuration adjustments.
diff --git a/builtin/repo.c b/builtin/repo.c
index 66bf4c67cc..a97ad71649 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_grafts_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *graft_file = repo_get_graft_file(repo);
+
+	if (!graft_file)
+		return error(_("unable to get graft file"));
+
+	format_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_grafts_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *graft_file = repo_get_graft_file(repo);
+
+	if (!graft_file)
+		return error(_("unable to get graft file"));
+
+	format_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)
 {
 	struct strbuf hooks_path = STRBUF_INIT;
@@ -258,6 +280,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.grafts.absolute", get_path_grafts_absolute },
+	{ "path.grafts.relative", get_path_grafts_relative },
 	{ "path.hooks.absolute", get_path_hooks_absolute },
 	{ "path.hooks.relative", get_path_hooks_relative },
 	{ "path.index.absolute", get_path_index_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 04e6b8553c..6c47989df7 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'grafts standard' 'grafts' '.git/info/grafts'
+
+test_repo_info_path 'grafts with GIT_GRAFT_FILE override' 'grafts' \
+	'custom-graft-file' \
+	'GIT_GRAFT_FILE="$ROOT/custom-graft-file" && export GIT_GRAFT_FILE'
+
 test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'
 
 test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
-- 
2.55.GIT
K JayatheerthJul 16, 2026, 01:21 UTC in reply to K Jayatheerth on lore

[GSoC Patch 7/7] repo: add path.git-prefix path key validation

Scripts and command-line prompt integrations frequently need to know their relative depth inside a repository working tree layout. Currently, this is retrieved using `git rev-parse --show-prefix`.

Introduce the `path.git-prefix` key to `git repo info`. This mirrors the prefix location tracking framework as a standalone key, returning the exact relative path offset complete with a trailing slash, or an empty string if run directly at the repository working tree root.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  5 +++++
 builtin/repo.c              | 12 ++++++++++++
 t/t1900-repo-info.sh        | 19 +++++++++++++++++++
 3 files changed, 36 insertions(+)
Show changes to 3 files +36 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 6c962620ec..5ba2ab1612 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -113,6 +113,11 @@ values that they return:
 	The path to the Git repository's common directory relative to
 	the current working directory.
 
+`path.git-prefix`::
+	The relative path from the top-level directory of the working tree to
+	the current working directory (including a trailing slash). Outputs an
+	empty string if executed at the root of the working tree.
+
 `path.gitdir.absolute`::
 	The canonical absolute path to the Git repository directory (the `.git` directory).
 
diff --git a/builtin/repo.c b/builtin/repo.c
index a97ad71649..00d5064281 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -1,3 +1,4 @@
+#include "compat/posix.h"
 #define USE_THE_REPOSITORY_VARIABLE
 
 #include "builtin.h"
@@ -100,6 +101,16 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b
 	return 0;
 }
 
+static int get_path_git_prefix(struct repository *repo UNUSED, struct strbuf *buf)
+{
+	/*
+	 * startup_info->prefix is NULL if we are at the working tree root.
+	 * We add an empty string to ensure the buffer is cleanly initialized.
+	 */
+	strbuf_addstr(buf, startup_info->prefix ? startup_info->prefix : "");
+	return 0;
+}
+
 static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *git_dir = repo_get_git_dir(repo);
@@ -278,6 +289,7 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "object.format", get_object_format },
 	{ "path.commondir.absolute", get_path_commondir_absolute },
 	{ "path.commondir.relative", get_path_commondir_relative },
+	{ "path.git-prefix", get_path_git_prefix },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
 	{ "path.grafts.absolute", get_path_grafts_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 6c47989df7..3e5e42f6d3 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -207,6 +207,25 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.git-prefix at root and in a subdirectory' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		echo "path.git-prefix=" >expect.root &&
+		git repo info path.git-prefix >actual.root &&
+		test_cmp expect.root actual.root &&
+
+		mkdir -p sub/dir &&
+		cd sub/dir &&
+
+		echo "path.git-prefix=sub/dir/" >expect.sub &&
+		git repo info path.git-prefix >actual.sub &&
+		test_cmp expect.sub actual.sub
+	)
+'
+
 test_repo_info_path 'gitdir standard' 'gitdir' '.git'
 
 test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
-- 
2.55.GIT
Junio C HamanoJul 16, 2026, 03:23 UTC in reply to K Jayatheerth on lore

Re: [GSoC Patch 7/7] repo: add path.git-prefix path key validation

K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
Show 8 quoted lines
> diff --git a/builtin/repo.c b/builtin/repo.c
> index a97ad71649..00d5064281 100644
> --- a/builtin/repo.c
> +++ b/builtin/repo.c
> @@ -1,3 +1,4 @@
> +#include "compat/posix.h"
>  #define USE_THE_REPOSITORY_VARIABLE
>  #include "builtin.h"

The first include must be <git-compat-util.h> or common include files that include <git-compat-util.h> as the first thing, like <builtin.h>.

As the file already includes <builtin.h>, extra inclusion of <compat/posix.h> before everything else is an absolute no-no.

By the way, I do not see any "validation" in the patch as the title claims. Perhaps retitle it to "repo: add path.git-prefix key" or something simpler like that?

K JayatheerthJul 16, 2026, 15:36 UTC in reply to Junio C Hamano on lore

Re: [GSoC Patch 7/7] repo: add path.git-prefix path key validation

Hey Junio,
On Thu, Jul 16, 2026 at 8:53 AM Junio C Hamano <gitster@pobox.com> wrote:
Show 19 quoted lines
>
> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
>
> > diff --git a/builtin/repo.c b/builtin/repo.c
> > index a97ad71649..00d5064281 100644
> > --- a/builtin/repo.c
> > +++ b/builtin/repo.c
> > @@ -1,3 +1,4 @@
> > +#include "compat/posix.h"
> >  #define USE_THE_REPOSITORY_VARIABLE
> >  #include "builtin.h"
>
> The first include must be <git-compat-util.h> or common include
> files that include <git-compat-util.h> as the first thing, like
> <builtin.h>.
>
> As the file already includes <builtin.h>, extra inclusion of
> <compat/posix.h> before everything else is an absolute no-no.
>

Oh no, that's just my editor adding headers automatically. Thanks for pointing this out, I will fix it.

> By the way, I do not see any "validation" in the patch as the title
> claims.  Perhaps retitle it to "repo: add path.git-prefix key" or
> something simpler like that?
>
True, I will correct that as well.
Regards,
- K Jayatheerth
K JayatheerthJul 17, 2026, 13:30 UTC in reply to K Jayatheerth on lore

[GSoC Patch v2 0/7] repo: add more path keys to git repo info

Series adds keys to git repo info.
Keys output paths of repository components:
* path.toplevel: repository tree.
* path.superproject-working-tree: superproject tree from submodules.
* path.objects: repository objects.
* path.hooks: repository hooks.
* path.index: repository index.
* path.grafts: repository grafts.
* path.git-prefix: prefix offset.

Keys support suffixes for format. Commits contain documentation and tests.

K Jayatheerth (7):
  repo: add path.toplevel with absolute and relative suffix formatting
  repo: add path.superproject-working-tree with absolute and relative
    suffixes
  repo: add path.objects with absolute and relative suffix formatting
  repo: add path.hooks with absolute and relative suffix formatting
  repo: add path.index with absolute and relative suffix formatting
  repo: add path.grafts with absolute and relative suffix formatting
  repo: add path.git-prefix path key
 Documentation/git-repo.adoc |  58 +++++++++++++
 builtin/repo.c              | 166 ++++++++++++++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 108 +++++++++++++++++++++++
 3 files changed, 332 insertions(+)
-- 
2.55.GIT
K JayatheerthJul 17, 2026, 13:30 UTC in reply to K Jayatheerth on lore

[GSoC Patch v2 1/7] repo: add path.toplevel with absolute and relative suffix formatting

Scripts frequently need to find the root directory of a repository's working tree. Currently, this requires using `git rev-parse --show-toplevel` or inferring it from other path components.

Introduce `path.toplevel.absolute` and `path.toplevel.relative` keys to `git repo info`. This allows scripts to retrieve the top-level working tree path in a predictable, strictly formatted manner without relying on `rev-parse`.

If requested in a bare repository where no working tree exists, the command returns an empty string.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 10 ++++++++++
 builtin/repo.c              | 28 ++++++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 30 ++++++++++++++++++++++++++++++
 3 files changed, 68 insertions(+)
Show changes to 3 files +68 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index ed7d80c690..e34abe5fea 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,16 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.toplevel.absolute`::
+	The canonical absolute path to the top-level directory of the
+	repository's working tree. Outputs an empty string if the repository
+	is bare.
+
+`path.toplevel.relative`::
+	The path to the top-level directory of the repository's working
+	tree relative to the current working directory. Outputs an empty
+	string if the repository is bare.
+
 `references.format`::
 	The reference storage format. The valid values are:
 +
diff --git a/builtin/repo.c b/builtin/repo.c
index 042d6de558..194757eb18 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -121,6 +121,32 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *work_tree = repo_get_work_tree(repo);
+
+	if (!work_tree) {
+		strbuf_addstr(buf, "");
+		return 0;
+	}
+
+	format_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *work_tree = repo_get_work_tree(repo);
+
+	if (!work_tree) {
+		strbuf_addstr(buf, "");
+		return 0;
+	}
+
+	format_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_references_format(struct repository *repo, struct strbuf *buf)
 {
 	strbuf_addstr(buf,
@@ -137,6 +163,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.toplevel.absolute", get_path_toplevel_absolute },
+	{ "path.toplevel.relative", get_path_toplevel_relative },
 	{ "references.format", get_references_format },
 };
 
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index ae8c22c817..fbb9063ee5 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,4 +213,34 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.toplevel absolute and relative' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		mkdir -p repo/sub &&
+		cd repo/sub &&
+
+		ROOT="$(test-tool path-utils real_path ..)" &&
+
+		echo "path.toplevel.absolute=$ROOT" >expect.abs &&
+		git repo info path.toplevel.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.toplevel.relative=../" >expect.rel &&
+		git repo info path.toplevel.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
+test_expect_success 'path.toplevel returns empty in a bare repository' '
+	test_when_finished "rm -rf bare.git" &&
+	git init --bare bare.git &&
+	(
+		cd bare.git &&
+		echo "path.toplevel.absolute=" >expect &&
+		git repo info path.toplevel.absolute >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done
-- 
2.55.GIT
K JayatheerthJul 17, 2026, 13:30 UTC in reply to K Jayatheerth on lore

[GSoC Patch v2 2/7] repo: add path.superproject-working-tree with absolute and relative suffixes

Scripts working in multi-repository setups often need to identify the top-level working tree of a superproject from within a submodule. Currently, this is only exposed via `git rev-parse --show-superproject-working-tree`.

Introduce `path.superproject-working-tree.absolute` and `path.superproject-working-tree.relative` keys to `git repo info`. This exposes the core submodule context via a scriptable config-like key using standard format rules.

If requested when not inside a submodule, the command returns an empty string.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 10 ++++++++++
 builtin/repo.c              | 33 +++++++++++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 34 ++++++++++++++++++++++++++++++++++
 3 files changed, 77 insertions(+)
Show changes to 3 files +77 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index e34abe5fea..03aa57942f 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,16 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.superproject-working-tree.absolute`::
+	The canonical absolute path to the working tree root of the superproject
+	if the current repository is an initialized submodule. Outputs an empty
+	string if not in a submodule.
+
+`path.superproject-working-tree.relative`::
+	The path to the working tree root of the superproject relative to the
+	current working directory if the current repository is an initialized
+	submodule. Outputs an empty string if not in a submodule.
+
 `path.toplevel.absolute`::
 	The canonical absolute path to the top-level directory of the
 	repository's working tree. Outputs an empty string if the repository
diff --git a/builtin/repo.c b/builtin/repo.c
index 194757eb18..82359473e9 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -18,6 +18,7 @@
 #include "strbuf.h"
 #include "string-list.h"
 #include "shallow.h"
+#include "submodule.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "utf8.h"
@@ -121,6 +122,36 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
+{
+	struct strbuf superproject = STRBUF_INIT;
+
+	if (!get_superproject_working_tree(&superproject)) {
+		strbuf_release(&superproject);
+		strbuf_addstr(buf, "");
+		return 0;
+	}
+
+	format_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	strbuf_release(&superproject);
+	return 0;
+}
+
+static int get_path_superproject_relative(struct repository *repo UNUSED, struct strbuf *buf)
+{
+	struct strbuf superproject = STRBUF_INIT;
+
+	if (!get_superproject_working_tree(&superproject)) {
+		strbuf_release(&superproject);
+		strbuf_addstr(buf, "");
+		return 0;
+	}
+
+	format_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	strbuf_release(&superproject);
+	return 0;
+}
+
 static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *work_tree = repo_get_work_tree(repo);
@@ -163,6 +194,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
+	{ "path.superproject-working-tree.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
 	{ "path.toplevel.relative", get_path_toplevel_relative },
 	{ "references.format", get_references_format },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index fbb9063ee5..220b3d4d3d 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,40 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.superproject-working-tree absolute and relative' '
+	test_when_finished "rm -rf sub super" &&
+	git init sub &&
+	test_commit -C sub initial &&
+	git init super &&
+	(
+		cd super &&
+		git -c protocol.file.allow=always submodule add "../sub" sub &&
+		git commit -m "add submodule" &&
+
+		cd sub &&
+		ROOT="$(test-tool path-utils real_path ..)" &&
+
+		echo "path.superproject-working-tree.absolute=$ROOT" >expect.abs &&
+		git repo info path.superproject-working-tree.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.superproject-working-tree.relative=../" >expect.rel &&
+		git repo info path.superproject-working-tree.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
+test_expect_success 'path.superproject-working-tree returns empty when not in a submodule' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		echo "path.superproject-working-tree.absolute=" >expect &&
+		git repo info path.superproject-working-tree.absolute >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_expect_success 'path.toplevel absolute and relative' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
-- 
2.55.GIT
K JayatheerthJul 17, 2026, 13:30 UTC in reply to K Jayatheerth on lore

[GSoC Patch v2 3/7] repo: add path.objects with absolute and relative suffix formatting

Tools and deployment hooks frequently query the location of the object database directory. Currently, this relies on legacy parsing methods or manually inspecting `git rev-parse --git-path objects`.

Introduce `path.objects.absolute` and `path.objects.relative` keys to `git repo info`. This allows tools to discover the object database location safely while natively adhering to active `GIT_OBJECT_DIRECTORY` environment variable overrides.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  9 +++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        |  7 +++++++
 3 files changed, 40 insertions(+)
Show changes to 3 files +40 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 03aa57942f..8429a44b43 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,15 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.objects.absolute`::
+	The canonical absolute path to the repository's object database directory.
+	Respects the `GIT_OBJECT_DIRECTORY` environment override.
+
+`path.objects.relative`::
+	The path to the repository's object database directory relative to the
+	current working directory. Respects the `GIT_OBJECT_DIRECTORY`
+	environment override.
+
 `path.superproject-working-tree.absolute`::
 	The canonical absolute path to the working tree root of the superproject
 	if the current repository is an initialized submodule. Outputs an empty
diff --git a/builtin/repo.c b/builtin/repo.c
index 82359473e9..d6bdd5bcfa 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *obj_dir = repo_get_object_directory(repo);
+
+	if (!obj_dir)
+		return error(_("unable to get object directory"));
+
+	format_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_objects_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *obj_dir = repo_get_object_directory(repo);
+
+	if (!obj_dir)
+		return error(_("unable to get object directory"));
+
+	format_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
 {
 	struct strbuf superproject = STRBUF_INIT;
@@ -194,6 +216,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.objects.absolute", get_path_objects_absolute },
+	{ "path.objects.relative", get_path_objects_relative },
 	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
 	{ "path.superproject-working-tree.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 220b3d4d3d..260f4fde43 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,13 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'objects standard' 'objects' '.git/objects'
+
+test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \
+	'custom-objects' \
+	'GIT_OBJECT_DIRECTORY="$ROOT/custom-objects" && export GIT_OBJECT_DIRECTORY &&
+	 mkdir -p "$ROOT/custom-objects"'
+
 test_expect_success 'path.superproject-working-tree absolute and relative' '
 	test_when_finished "rm -rf sub super" &&
 	git init sub &&
-- 
2.55.GIT
K JayatheerthJul 17, 2026, 13:30 UTC in reply to K Jayatheerth on lore

[GSoC Patch v2 4/7] repo: add path.hooks with absolute and relative suffix formatting

External tool integrations and validation systems need a stable way to identify where the repository hooks are stored. Currently, this involves relying on `git rev-parse --git-path hooks` or querying `core.hooksPath` manually.

Introduce `path.hooks.absolute` and `path.hooks.relative` keys to `git repo info`. This allows tools to discover the active hooks location natively, ensuring proper resolution regardless of whether Git is using the standard `.git/hooks` structure or a custom `core.hooksPath` setup.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  8 ++++++++
 builtin/repo.c              | 22 ++++++++++++++++++++++
 t/t1900-repo-info.sh        |  6 ++++++
 3 files changed, 36 insertions(+)
Show changes to 3 files +36 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 8429a44b43..7bc1c51310 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,14 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.hooks.absolute`::
+	The canonical absolute path to the repository's hooks directory.
+	Respects `core.hooksPath` configuration adjustments.
+
+`path.hooks.relative`::
+	The path to the repository's hooks directory relative to the current
+	working directory. Respects `core.hooksPath` configuration adjustments.
+
 `path.objects.absolute`::
 	The canonical absolute path to the repository's object database directory.
 	Respects the `GIT_OBJECT_DIRECTORY` environment override.
diff --git a/builtin/repo.c b/builtin/repo.c
index d6bdd5bcfa..c921de222d 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,26 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf hooks_path = STRBUF_INIT;
+
+	repo_git_path_replace(repo, &hooks_path, "hooks");
+	format_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	strbuf_release(&hooks_path);
+	return 0;
+}
+
+static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf hooks_path = STRBUF_INIT;
+
+	repo_git_path_replace(repo, &hooks_path, "hooks");
+	format_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	strbuf_release(&hooks_path);
+	return 0;
+}
+
 static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *obj_dir = repo_get_object_directory(repo);
@@ -216,6 +236,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.hooks.absolute", get_path_hooks_absolute },
+	{ "path.hooks.relative", get_path_hooks_relative },
 	{ "path.objects.absolute", get_path_objects_absolute },
 	{ "path.objects.relative", get_path_objects_relative },
 	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 260f4fde43..cd3f856d04 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'
+
+test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
+	'custom-hooks' \
+	'git config core.hooksPath "$ROOT/custom-hooks" && mkdir -p "$ROOT/custom-hooks"'
+
 test_repo_info_path 'objects standard' 'objects' '.git/objects'
 
 test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \
-- 
2.55.GIT
K JayatheerthJul 17, 2026, 13:30 UTC in reply to K Jayatheerth on lore

[GSoC Patch v2 5/7] repo: add path.index with absolute and relative suffix formatting

External script workflows and formatting layers require straightforward access to the location of the index staging file. Currently, tracking this necessitates a legacy call to `git rev-parse --git-path index` or `--show-toplevel` logic abstractions.

Introduce `path.index.absolute` and `path.index.relative` keys to `git repo info`. This allows tooling utilities to discover the active index context cleanly while scaling transparently with localized `GIT_INDEX_FILE` environment overrides.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  8 ++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        |  6 ++++++
 3 files changed, 38 insertions(+)
Show changes to 3 files +38 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 7bc1c51310..3a837c573e 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -127,6 +127,14 @@ values that they return:
 	The path to the repository's hooks directory relative to the current
 	working directory. Respects `core.hooksPath` configuration adjustments.
 
+`path.index.absolute`::
+	The canonical absolute path to the repository's current index file.
+	Respects the `GIT_INDEX_FILE` environment override.
+
+`path.index.relative`::
+	The path to the repository's current index file relative to the current
+	working directory. Respects the `GIT_INDEX_FILE` environment override.
+
 `path.objects.absolute`::
 	The canonical absolute path to the repository's object database directory.
 	Respects the `GIT_OBJECT_DIRECTORY` environment override.
diff --git a/builtin/repo.c b/builtin/repo.c
index c921de222d..66bf4c67cc 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -142,6 +142,28 @@ static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *index_file = repo_get_index_file(repo);
+
+	if (!index_file)
+		return error(_("unable to get index file"));
+
+	format_path(buf, index_file, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *index_file = repo_get_index_file(repo);
+
+	if (!index_file)
+		return error(_("unable to get index file"));
+
+	format_path(buf, index_file, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *obj_dir = repo_get_object_directory(repo);
@@ -238,6 +260,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.gitdir.relative", get_path_gitdir_relative },
 	{ "path.hooks.absolute", get_path_hooks_absolute },
 	{ "path.hooks.relative", get_path_hooks_relative },
+	{ "path.index.absolute", get_path_index_absolute },
+	{ "path.index.relative", get_path_index_relative },
 	{ "path.objects.absolute", get_path_objects_absolute },
 	{ "path.objects.relative", get_path_objects_relative },
 	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index cd3f856d04..04e6b8553c 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -219,6 +219,12 @@ test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
 	'custom-hooks' \
 	'git config core.hooksPath "$ROOT/custom-hooks" && mkdir -p "$ROOT/custom-hooks"'
 
+test_repo_info_path 'index standard' 'index' '.git/index'
+
+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \
+	'custom-index-file' \
+	'GIT_INDEX_FILE="$ROOT/custom-index-file" && export GIT_INDEX_FILE'
+
 test_repo_info_path 'objects standard' 'objects' '.git/objects'
 
 test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \
-- 
2.55.GIT
K JayatheerthJul 17, 2026, 13:30 UTC in reply to K Jayatheerth on lore

[GSoC Patch v2 6/7] repo: add path.grafts with absolute and relative suffix formatting

External toolchains managing specialized history rewrites or legacy history splices require access to the location of the repository grafts file. Currently, this requires a legacy call to `git rev-parse --git-path info/grafts`.

Introduce `path.grafts.absolute` and `path.grafts.relative` keys to `git repo info`. This allows scripting layers to query the active grafts context cleanly while scaling transparently with active `GIT_GRAFT_FILE` environment variable overrides.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  8 ++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        |  6 ++++++
 3 files changed, 38 insertions(+)
Show changes to 3 files +38 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 3a837c573e..6c962620ec 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,14 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.grafts.absolute`::
+	The canonical absolute path to the repository grafts file.
+	Respects the `GIT_GRAFT_FILE` environment override.
+
+`path.grafts.relative`::
+	The path to the repository grafts file relative to the current working
+	directory. Respects the `GIT_GRAFT_FILE` environment override.
+
 `path.hooks.absolute`::
 	The canonical absolute path to the repository's hooks directory.
 	Respects `core.hooksPath` configuration adjustments.
diff --git a/builtin/repo.c b/builtin/repo.c
index 66bf4c67cc..a97ad71649 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_grafts_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *graft_file = repo_get_graft_file(repo);
+
+	if (!graft_file)
+		return error(_("unable to get graft file"));
+
+	format_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_grafts_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *graft_file = repo_get_graft_file(repo);
+
+	if (!graft_file)
+		return error(_("unable to get graft file"));
+
+	format_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)
 {
 	struct strbuf hooks_path = STRBUF_INIT;
@@ -258,6 +280,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.grafts.absolute", get_path_grafts_absolute },
+	{ "path.grafts.relative", get_path_grafts_relative },
 	{ "path.hooks.absolute", get_path_hooks_absolute },
 	{ "path.hooks.relative", get_path_hooks_relative },
 	{ "path.index.absolute", get_path_index_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 04e6b8553c..6c47989df7 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'grafts standard' 'grafts' '.git/info/grafts'
+
+test_repo_info_path 'grafts with GIT_GRAFT_FILE override' 'grafts' \
+	'custom-graft-file' \
+	'GIT_GRAFT_FILE="$ROOT/custom-graft-file" && export GIT_GRAFT_FILE'
+
 test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'
 
 test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
-- 
2.55.GIT
K JayatheerthJul 17, 2026, 13:30 UTC in reply to K Jayatheerth on lore

[GSoC Patch v2 7/7] repo: add path.git-prefix path key

Scripts and command-line prompt integrations frequently need to know their relative depth inside a repository working tree layout. Currently, this is retrieved using `git rev-parse --show-prefix`.

Introduce the `path.git-prefix` key to `git repo info`. This mirrors the prefix location tracking framework as a standalone key, returning the exact relative path offset complete with a trailing slash, or an empty string if run directly at the repository working tree root.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  5 +++++
 builtin/repo.c              | 11 +++++++++++
 t/t1900-repo-info.sh        | 19 +++++++++++++++++++
 3 files changed, 35 insertions(+)
Show changes to 3 files +35 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 6c962620ec..5ba2ab1612 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -113,6 +113,11 @@ values that they return:
 	The path to the Git repository's common directory relative to
 	the current working directory.
 
+`path.git-prefix`::
+	The relative path from the top-level directory of the working tree to
+	the current working directory (including a trailing slash). Outputs an
+	empty string if executed at the root of the working tree.
+
 `path.gitdir.absolute`::
 	The canonical absolute path to the Git repository directory (the `.git` directory).
 
diff --git a/builtin/repo.c b/builtin/repo.c
index a97ad71649..b93c140c74 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -100,6 +100,16 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b
 	return 0;
 }
 
+static int get_path_git_prefix(struct repository *repo UNUSED, struct strbuf *buf)
+{
+	/*
+	 * startup_info->prefix is NULL if we are at the working tree root.
+	 * We add an empty string to ensure the buffer is cleanly initialized.
+	 */
+	strbuf_addstr(buf, startup_info->prefix ? startup_info->prefix : "");
+	return 0;
+}
+
 static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *git_dir = repo_get_git_dir(repo);
@@ -278,6 +288,7 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "object.format", get_object_format },
 	{ "path.commondir.absolute", get_path_commondir_absolute },
 	{ "path.commondir.relative", get_path_commondir_relative },
+	{ "path.git-prefix", get_path_git_prefix },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
 	{ "path.grafts.absolute", get_path_grafts_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 6c47989df7..3e5e42f6d3 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -207,6 +207,25 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.git-prefix at root and in a subdirectory' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		echo "path.git-prefix=" >expect.root &&
+		git repo info path.git-prefix >actual.root &&
+		test_cmp expect.root actual.root &&
+
+		mkdir -p sub/dir &&
+		cd sub/dir &&
+
+		echo "path.git-prefix=sub/dir/" >expect.sub &&
+		git repo info path.git-prefix >actual.sub &&
+		test_cmp expect.sub actual.sub
+	)
+'
+
 test_repo_info_path 'gitdir standard' 'gitdir' '.git'
 
 test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
-- 
2.55.GIT
Lucas Seiki OshiroJul 20, 2026, 00:20 UTC in reply to K Jayatheerth on lore

Re: [GSoC Patch v2 6/7] repo: add path.grafts with absolute and relative suffix formatting

> Introduce `path.grafts.absolute` and `path.grafts.relative` keys to
> `git repo info`. This allows scripting layers to query the active grafts
> context cleanly while scaling transparently with active `GIT_GRAFT_FILE`
> environment variable overrides.

I ran `git repo info path.grafts.relative` in a repository with no `grafts` file, and it returned `.git/info/grafts`, which obviously doesn't exist.

Wouldn't it be better if we check if that file exists before returning this value?

Lucas Seiki OshiroJul 20, 2026, 00:35 UTC in reply to K Jayatheerth on lore

Re: [GSoC Patch v2 5/7] repo: add path.index with absolute and relative suffix formatting

> Introduce `path.index.absolute` and `path.index.relative` keys to
> `git repo info`. This allows tooling utilities to discover the active
> index context cleanly while scaling transparently with localized
> `GIT_INDEX_FILE` environment overrides.

Note that bare repositories doesn't need to have an index file. Maybe it would be better if you return an empty value in those cases.

Junio C HamanoJul 20, 2026, 04:01 UTC in reply to Lucas Seiki Oshiro on lore

Re: [GSoC Patch v2 6/7] repo: add path.grafts with absolute and relative suffix formatting

Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:
Show 11 quoted lines
>> Introduce `path.grafts.absolute` and `path.grafts.relative` keys to
>> `git repo info`. This allows scripting layers to query the active grafts
>> context cleanly while scaling transparently with active `GIT_GRAFT_FILE`
>> environment variable overrides.
>
> I ran `git repo info path.grafts.relative` in a repository with no
> `grafts` file, and it returned `.git/info/grafts`, which obviously
> doesn't exist.
>
> Wouldn't it be better if we check if that file exists before
> returning this value?

That is an interesting question, but I think it depends on who is querying and for what purpose.

If a script is asking where to write the file, then the author wants to know where the file is supposed to be, even if no such file exists yet. Since the file format is public, they are free to write their own tools to manipulate it.

Thanks.
K JayatheerthJul 21, 2026, 02:19 UTC in reply to Junio C Hamano on lore

Re: [GSoC Patch v2 6/7] repo: add path.grafts with absolute and relative suffix formatting

Hey Lucas and Junio,
On Mon, Jul 20, 2026 at 9:31 AM Junio C Hamano <gitster@pobox.com> wrote:
Show 24 quoted lines
>
> Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:
>
> >> Introduce `path.grafts.absolute` and `path.grafts.relative` keys to
> >> `git repo info`. This allows scripting layers to query the active grafts
> >> context cleanly while scaling transparently with active `GIT_GRAFT_FILE`
> >> environment variable overrides.
> >
> > I ran `git repo info path.grafts.relative` in a repository with no
> > `grafts` file, and it returned `.git/info/grafts`, which obviously
> > doesn't exist.
> >
> > Wouldn't it be better if we check if that file exists before
> > returning this value?
>
> That is an interesting question, but I think it depends on who is
> querying and for what purpose.
>
> If a script is asking where to write the file, then the author wants
> to know where the file is supposed to be, even if no such file
> exists yet.  Since the file format is public, they are free to write
> their own tools to manipulate it.
>
> Thanks.

That's tough. I think I align with Junio here.

I just used rev-parse as my compass to work on this command. Just to clarify Should I send a v3 changing something?

Regards,
- K Jayatheerth
K JayatheerthJul 24, 2026, 17:49 UTC in reply to Lucas Seiki Oshiro on lore

Re: [GSoC Patch v2 5/7] repo: add path.index with absolute and relative suffix formatting

Hey Lucas and Junio,

On Mon, Jul 20, 2026 at 6:05 AM Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> wrote:

Show 9 quoted lines
>
>
> > Introduce `path.index.absolute` and `path.index.relative` keys to
> > `git repo info`. This allows tooling utilities to discover the active
> > index context cleanly while scaling transparently with localized
> > `GIT_INDEX_FILE` environment overrides.
>
> Note that bare repositories doesn't need to have an index file. Maybe
> it would be better if you return an empty value in those cases.
I wanted to follow up on this series.

I am a bit unsure about how to proceed with this specific case, especially after Junio's feedback on the grafts patch.

For path.grafts, Junio mentioned that we should return the expected path even if the file doesn't exist yet, so scripts know where to write it. I am wondering if a similar logic should apply to path.index in bare repositories.

While bare repos don't have a working tree (and therefore usually no index), scripts do sometimes set GIT_INDEX_FILE to build temporary indexes for tree manipulation.

Should we strictly return an empty string for bare repos (similar to how we handle path.toplevel), or should we return the default <gitdir>/index path in case a script wants to know where it would be?

If this confusion is clear I can send a new version!

Thank you, Regards

- K Jayatheerth
Junio C HamanoJul 24, 2026, 19:21 UTC in reply to K Jayatheerth on lore

Re: [GSoC Patch v2 5/7] repo: add path.index with absolute and relative suffix formatting

K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
Show 8 quoted lines
> While bare repos don't have a working tree (and therefore usually no index),
> scripts do sometimes set GIT_INDEX_FILE to build temporary indexes for
> tree manipulation.
>
> Should we strictly return an empty string for bare repos (similar to
> how we handle path.toplevel),
> or should we return the default <gitdir>/index path in case a script
> wants to know where it would be?

The path to the top level can be an empty string if you are already at the top level, which is neither an error nor an unusual condition. I do not understand the contrast you are drawing here.

I expect "tell me where the index is" to return <gitdir>/index, $GIT_INDEX_FILE, or whatever Git sets during setup. In other words, whatever repo's '.index_file' member holds when control reaches repo_read_index().

If a script checks that location and finds no file, that is the natural way to learn there is no index.

K JayatheerthJul 26, 2026, 10:43 UTC in reply to K Jayatheerth on lore

[GSoC Patch v3 0/7] repo: add more path keys to git repo info

Series adds keys to git repo info.
Keys output paths of repository components:
* path.toplevel: repository tree.
* path.superproject-working-tree: superproject tree from submodules.
* path.objects: repository objects.
* path.hooks: repository hooks.
* path.index: repository index.
* path.grafts: repository grafts.
* path.git-prefix: prefix offset.

Keys support suffixes for format. Commits contain documentation and tests.

Changes since v2:
* path.index: no functional change; documented that the returned path
  reflects the default/configured index location even in a bare
  repository or when the file doesn't exist yet, and added a test
  covering path.index inside a bare repository.
* path.grafts: no functional change; documented that the path is
  returned even if the grafts file doesn't currently exist on disk.
* path.git-prefix: no change since v2 (already addressed the stray
  include and misleading "validation" in the subject).
K Jayatheerth (7):
  repo: add path.toplevel with absolute and relative suffix formatting
  repo: add path.superproject-working-tree with absolute and relative
    suffixes
  repo: add path.objects with absolute and relative suffix formatting
  repo: add path.hooks with absolute and relative suffix formatting
  repo: add path.index with absolute and relative suffix formatting
  repo: add path.grafts with absolute and relative suffix formatting
  repo: add path.git-prefix path key
 Documentation/git-repo.adoc |  64 ++++++++++++++
 builtin/repo.c              | 166 ++++++++++++++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 125 +++++++++++++++++++++++++++
 3 files changed, 355 insertions(+)
Range-diff against v2:
1:  297d625ea1 = 1:  297d625ea1 repo: add path.toplevel with absolute and relative suffix formatting
2:  8a84aaa2a1 = 2:  8a84aaa2a1 repo: add path.superproject-working-tree with absolute and relative suffixes
3:  159166ce85 = 3:  159166ce85 repo: add path.objects with absolute and relative suffix formatting
4:  ff5e382043 = 4:  ff5e382043 repo: add path.hooks with absolute and relative suffix formatting
5:  d906735e3d ! 5:  917d3a5076 repo: add path.index with absolute and relative suffix formatting
    @@ Documentation/git-repo.adoc: values that they return:
      
     +`path.index.absolute`::
     +	The canonical absolute path to the repository's current index file.
    -+	Respects the `GIT_INDEX_FILE` environment override.
    ++	Respects the `GIT_INDEX_FILE` environment override. The returned path
    ++	reflects the configured/default index location regardless of whether the
    ++	repository is bare or whether the file currently exists.
     +
     +`path.index.relative`::
     +	The path to the repository's current index file relative to the current
     +	working directory. Respects the `GIT_INDEX_FILE` environment override.
    ++	The returned path reflects the configured/default index location regardless
    ++	of whether the repository is bare or whether the file currently exists.
     +
      `path.objects.absolute`::
      	The canonical absolute path to the repository's object database directory.
    @@ t/t1900-repo-info.sh: test_repo_info_path 'hooks with core.hooksPath override' '
     +test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \
     +	'custom-index-file' \
     +	'GIT_INDEX_FILE="$ROOT/custom-index-file" && export GIT_INDEX_FILE'
    ++
    ++test_expect_success 'path.index in a bare repository returns default index location' '
    ++	test_when_finished "rm -rf bare.git" &&
    ++	git init --bare bare.git &&
    ++	(
    ++		cd bare.git &&
    ++		ROOT="$(test-tool path-utils real_path .)" &&
    ++
    ++		echo "path.index.absolute=$ROOT/index" >expect.abs &&
    ++		git repo info path.index.absolute >actual.abs &&
    ++		test_cmp expect.abs actual.abs &&
    ++
    ++		echo "path.index.relative=index" >expect.rel &&
    ++		git repo info path.index.relative >actual.rel &&
    ++		test_cmp expect.rel actual.rel
    ++	)
    ++'
     +
      test_repo_info_path 'objects standard' 'objects' '.git/objects'
      
6:  2d21f9536c ! 6:  d47a5701d5 repo: add path.grafts with absolute and relative suffix formatting
    @@ Documentation/git-repo.adoc: values that they return:
      
     +`path.grafts.absolute`::
     +	The canonical absolute path to the repository grafts file.
    -+	Respects the `GIT_GRAFT_FILE` environment override.
    ++	Respects the `GIT_GRAFT_FILE` environment override. The path is returned
    ++	regardless of whether the file currently exists on disk.
     +
     +`path.grafts.relative`::
     +	The path to the repository grafts file relative to the current working
    -+	directory. Respects the `GIT_GRAFT_FILE` environment override.
    ++	directory. Respects the `GIT_GRAFT_FILE` environment override. The path
    ++	is returned regardless of whether the file currently exists on disk.
     +
      `path.hooks.absolute`::
      	The canonical absolute path to the repository's hooks directory.
7:  1dbe191176 = 7:  fd90b0b3b5 repo: add path.git-prefix path key
-- 
2.55.GIT
K JayatheerthJul 26, 2026, 10:43 UTC in reply to K Jayatheerth on lore

[PATCH v3 1/7] repo: add path.toplevel with absolute and relative suffix formatting

Scripts frequently need to find the root directory of a repository's working tree. Currently, this requires using `git rev-parse --show-toplevel` or inferring it from other path components.

Introduce `path.toplevel.absolute` and `path.toplevel.relative` keys to `git repo info`. This allows scripts to retrieve the top-level working tree path in a predictable, strictly formatted manner without relying on `rev-parse`.

If requested in a bare repository where no working tree exists, the command returns an empty string.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 10 ++++++++++
 builtin/repo.c              | 28 ++++++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 30 ++++++++++++++++++++++++++++++
 3 files changed, 68 insertions(+)
Show changes to 3 files +68 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index ed7d80c690..e34abe5fea 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,16 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.toplevel.absolute`::
+	The canonical absolute path to the top-level directory of the
+	repository's working tree. Outputs an empty string if the repository
+	is bare.
+
+`path.toplevel.relative`::
+	The path to the top-level directory of the repository's working
+	tree relative to the current working directory. Outputs an empty
+	string if the repository is bare.
+
 `references.format`::
 	The reference storage format. The valid values are:
 +
diff --git a/builtin/repo.c b/builtin/repo.c
index 042d6de558..194757eb18 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -121,6 +121,32 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *work_tree = repo_get_work_tree(repo);
+
+	if (!work_tree) {
+		strbuf_addstr(buf, "");
+		return 0;
+	}
+
+	format_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *work_tree = repo_get_work_tree(repo);
+
+	if (!work_tree) {
+		strbuf_addstr(buf, "");
+		return 0;
+	}
+
+	format_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_references_format(struct repository *repo, struct strbuf *buf)
 {
 	strbuf_addstr(buf,
@@ -137,6 +163,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.toplevel.absolute", get_path_toplevel_absolute },
+	{ "path.toplevel.relative", get_path_toplevel_relative },
 	{ "references.format", get_references_format },
 };
 
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index ae8c22c817..fbb9063ee5 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,4 +213,34 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.toplevel absolute and relative' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		mkdir -p repo/sub &&
+		cd repo/sub &&
+
+		ROOT="$(test-tool path-utils real_path ..)" &&
+
+		echo "path.toplevel.absolute=$ROOT" >expect.abs &&
+		git repo info path.toplevel.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.toplevel.relative=../" >expect.rel &&
+		git repo info path.toplevel.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
+test_expect_success 'path.toplevel returns empty in a bare repository' '
+	test_when_finished "rm -rf bare.git" &&
+	git init --bare bare.git &&
+	(
+		cd bare.git &&
+		echo "path.toplevel.absolute=" >expect &&
+		git repo info path.toplevel.absolute >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done
-- 
2.55.GIT
K JayatheerthJul 26, 2026, 10:43 UTC in reply to K Jayatheerth on lore

[PATCH v3 2/7] repo: add path.superproject-working-tree with absolute and relative suffixes

Scripts working in multi-repository setups often need to identify the top-level working tree of a superproject from within a submodule. Currently, this is only exposed via `git rev-parse --show-superproject-working-tree`.

Introduce `path.superproject-working-tree.absolute` and `path.superproject-working-tree.relative` keys to `git repo info`. This exposes the core submodule context via a scriptable config-like key using standard format rules.

If requested when not inside a submodule, the command returns an empty string.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 10 ++++++++++
 builtin/repo.c              | 33 +++++++++++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 34 ++++++++++++++++++++++++++++++++++
 3 files changed, 77 insertions(+)
Show changes to 3 files +77 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index e34abe5fea..03aa57942f 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,16 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.superproject-working-tree.absolute`::
+	The canonical absolute path to the working tree root of the superproject
+	if the current repository is an initialized submodule. Outputs an empty
+	string if not in a submodule.
+
+`path.superproject-working-tree.relative`::
+	The path to the working tree root of the superproject relative to the
+	current working directory if the current repository is an initialized
+	submodule. Outputs an empty string if not in a submodule.
+
 `path.toplevel.absolute`::
 	The canonical absolute path to the top-level directory of the
 	repository's working tree. Outputs an empty string if the repository
diff --git a/builtin/repo.c b/builtin/repo.c
index 194757eb18..82359473e9 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -18,6 +18,7 @@
 #include "strbuf.h"
 #include "string-list.h"
 #include "shallow.h"
+#include "submodule.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "utf8.h"
@@ -121,6 +122,36 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
+{
+	struct strbuf superproject = STRBUF_INIT;
+
+	if (!get_superproject_working_tree(&superproject)) {
+		strbuf_release(&superproject);
+		strbuf_addstr(buf, "");
+		return 0;
+	}
+
+	format_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	strbuf_release(&superproject);
+	return 0;
+}
+
+static int get_path_superproject_relative(struct repository *repo UNUSED, struct strbuf *buf)
+{
+	struct strbuf superproject = STRBUF_INIT;
+
+	if (!get_superproject_working_tree(&superproject)) {
+		strbuf_release(&superproject);
+		strbuf_addstr(buf, "");
+		return 0;
+	}
+
+	format_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	strbuf_release(&superproject);
+	return 0;
+}
+
 static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *work_tree = repo_get_work_tree(repo);
@@ -163,6 +194,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
+	{ "path.superproject-working-tree.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
 	{ "path.toplevel.relative", get_path_toplevel_relative },
 	{ "references.format", get_references_format },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index fbb9063ee5..220b3d4d3d 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,40 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.superproject-working-tree absolute and relative' '
+	test_when_finished "rm -rf sub super" &&
+	git init sub &&
+	test_commit -C sub initial &&
+	git init super &&
+	(
+		cd super &&
+		git -c protocol.file.allow=always submodule add "../sub" sub &&
+		git commit -m "add submodule" &&
+
+		cd sub &&
+		ROOT="$(test-tool path-utils real_path ..)" &&
+
+		echo "path.superproject-working-tree.absolute=$ROOT" >expect.abs &&
+		git repo info path.superproject-working-tree.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.superproject-working-tree.relative=../" >expect.rel &&
+		git repo info path.superproject-working-tree.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
+test_expect_success 'path.superproject-working-tree returns empty when not in a submodule' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		echo "path.superproject-working-tree.absolute=" >expect &&
+		git repo info path.superproject-working-tree.absolute >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_expect_success 'path.toplevel absolute and relative' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
-- 
2.55.GIT
K JayatheerthJul 26, 2026, 10:43 UTC in reply to K Jayatheerth on lore

[PATCH v3 3/7] repo: add path.objects with absolute and relative suffix formatting

Tools and deployment hooks frequently query the location of the object database directory. Currently, this relies on legacy parsing methods or manually inspecting `git rev-parse --git-path objects`.

Introduce `path.objects.absolute` and `path.objects.relative` keys to `git repo info`. This allows tools to discover the object database location safely while natively adhering to active `GIT_OBJECT_DIRECTORY` environment variable overrides.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  9 +++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        |  7 +++++++
 3 files changed, 40 insertions(+)
Show changes to 3 files +40 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 03aa57942f..8429a44b43 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,15 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.objects.absolute`::
+	The canonical absolute path to the repository's object database directory.
+	Respects the `GIT_OBJECT_DIRECTORY` environment override.
+
+`path.objects.relative`::
+	The path to the repository's object database directory relative to the
+	current working directory. Respects the `GIT_OBJECT_DIRECTORY`
+	environment override.
+
 `path.superproject-working-tree.absolute`::
 	The canonical absolute path to the working tree root of the superproject
 	if the current repository is an initialized submodule. Outputs an empty
diff --git a/builtin/repo.c b/builtin/repo.c
index 82359473e9..d6bdd5bcfa 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *obj_dir = repo_get_object_directory(repo);
+
+	if (!obj_dir)
+		return error(_("unable to get object directory"));
+
+	format_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_objects_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *obj_dir = repo_get_object_directory(repo);
+
+	if (!obj_dir)
+		return error(_("unable to get object directory"));
+
+	format_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
 {
 	struct strbuf superproject = STRBUF_INIT;
@@ -194,6 +216,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.objects.absolute", get_path_objects_absolute },
+	{ "path.objects.relative", get_path_objects_relative },
 	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
 	{ "path.superproject-working-tree.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 220b3d4d3d..260f4fde43 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,13 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'objects standard' 'objects' '.git/objects'
+
+test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \
+	'custom-objects' \
+	'GIT_OBJECT_DIRECTORY="$ROOT/custom-objects" && export GIT_OBJECT_DIRECTORY &&
+	 mkdir -p "$ROOT/custom-objects"'
+
 test_expect_success 'path.superproject-working-tree absolute and relative' '
 	test_when_finished "rm -rf sub super" &&
 	git init sub &&
-- 
2.55.GIT
K JayatheerthJul 26, 2026, 10:43 UTC in reply to K Jayatheerth on lore

[PATCH v3 4/7] repo: add path.hooks with absolute and relative suffix formatting

External tool integrations and validation systems need a stable way to identify where the repository hooks are stored. Currently, this involves relying on `git rev-parse --git-path hooks` or querying `core.hooksPath` manually.

Introduce `path.hooks.absolute` and `path.hooks.relative` keys to `git repo info`. This allows tools to discover the active hooks location natively, ensuring proper resolution regardless of whether Git is using the standard `.git/hooks` structure or a custom `core.hooksPath` setup.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  8 ++++++++
 builtin/repo.c              | 22 ++++++++++++++++++++++
 t/t1900-repo-info.sh        |  6 ++++++
 3 files changed, 36 insertions(+)
Show changes to 3 files +36 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 8429a44b43..7bc1c51310 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,14 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.hooks.absolute`::
+	The canonical absolute path to the repository's hooks directory.
+	Respects `core.hooksPath` configuration adjustments.
+
+`path.hooks.relative`::
+	The path to the repository's hooks directory relative to the current
+	working directory. Respects `core.hooksPath` configuration adjustments.
+
 `path.objects.absolute`::
 	The canonical absolute path to the repository's object database directory.
 	Respects the `GIT_OBJECT_DIRECTORY` environment override.
diff --git a/builtin/repo.c b/builtin/repo.c
index d6bdd5bcfa..c921de222d 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,26 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf hooks_path = STRBUF_INIT;
+
+	repo_git_path_replace(repo, &hooks_path, "hooks");
+	format_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	strbuf_release(&hooks_path);
+	return 0;
+}
+
+static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf hooks_path = STRBUF_INIT;
+
+	repo_git_path_replace(repo, &hooks_path, "hooks");
+	format_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	strbuf_release(&hooks_path);
+	return 0;
+}
+
 static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *obj_dir = repo_get_object_directory(repo);
@@ -216,6 +236,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.hooks.absolute", get_path_hooks_absolute },
+	{ "path.hooks.relative", get_path_hooks_relative },
 	{ "path.objects.absolute", get_path_objects_absolute },
 	{ "path.objects.relative", get_path_objects_relative },
 	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 260f4fde43..cd3f856d04 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'
+
+test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
+	'custom-hooks' \
+	'git config core.hooksPath "$ROOT/custom-hooks" && mkdir -p "$ROOT/custom-hooks"'
+
 test_repo_info_path 'objects standard' 'objects' '.git/objects'
 
 test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \
-- 
2.55.GIT
K JayatheerthJul 26, 2026, 10:43 UTC in reply to K Jayatheerth on lore

[PATCH v3 5/7] repo: add path.index with absolute and relative suffix formatting

External script workflows and formatting layers require straightforward access to the location of the index staging file. Currently, tracking this necessitates a legacy call to `git rev-parse --git-path index` or `--show-toplevel` logic abstractions.

Introduce `path.index.absolute` and `path.index.relative` keys to `git repo info`. This allows tooling utilities to discover the active index context cleanly while scaling transparently with localized `GIT_INDEX_FILE` environment overrides.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 12 ++++++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++
 3 files changed, 59 insertions(+)
Show changes to 3 files +59 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 7bc1c51310..34c4f7d61c 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -127,6 +127,18 @@ values that they return:
 	The path to the repository's hooks directory relative to the current
 	working directory. Respects `core.hooksPath` configuration adjustments.
 
+`path.index.absolute`::
+	The canonical absolute path to the repository's current index file.
+	Respects the `GIT_INDEX_FILE` environment override. The returned path
+	reflects the configured/default index location regardless of whether the
+	repository is bare or whether the file currently exists.
+
+`path.index.relative`::
+	The path to the repository's current index file relative to the current
+	working directory. Respects the `GIT_INDEX_FILE` environment override.
+	The returned path reflects the configured/default index location regardless
+	of whether the repository is bare or whether the file currently exists.
+
 `path.objects.absolute`::
 	The canonical absolute path to the repository's object database directory.
 	Respects the `GIT_OBJECT_DIRECTORY` environment override.
diff --git a/builtin/repo.c b/builtin/repo.c
index c921de222d..66bf4c67cc 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -142,6 +142,28 @@ static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *index_file = repo_get_index_file(repo);
+
+	if (!index_file)
+		return error(_("unable to get index file"));
+
+	format_path(buf, index_file, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *index_file = repo_get_index_file(repo);
+
+	if (!index_file)
+		return error(_("unable to get index file"));
+
+	format_path(buf, index_file, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *obj_dir = repo_get_object_directory(repo);
@@ -238,6 +260,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.gitdir.relative", get_path_gitdir_relative },
 	{ "path.hooks.absolute", get_path_hooks_absolute },
 	{ "path.hooks.relative", get_path_hooks_relative },
+	{ "path.index.absolute", get_path_index_absolute },
+	{ "path.index.relative", get_path_index_relative },
 	{ "path.objects.absolute", get_path_objects_absolute },
 	{ "path.objects.relative", get_path_objects_relative },
 	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index cd3f856d04..dee1db2a49 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -219,6 +219,29 @@ test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
 	'custom-hooks' \
 	'git config core.hooksPath "$ROOT/custom-hooks" && mkdir -p "$ROOT/custom-hooks"'
 
+test_repo_info_path 'index standard' 'index' '.git/index'
+
+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \
+	'custom-index-file' \
+	'GIT_INDEX_FILE="$ROOT/custom-index-file" && export GIT_INDEX_FILE'
+
+test_expect_success 'path.index in a bare repository returns default index location' '
+	test_when_finished "rm -rf bare.git" &&
+	git init --bare bare.git &&
+	(
+		cd bare.git &&
+		ROOT="$(test-tool path-utils real_path .)" &&
+
+		echo "path.index.absolute=$ROOT/index" >expect.abs &&
+		git repo info path.index.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.index.relative=index" >expect.rel &&
+		git repo info path.index.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
 test_repo_info_path 'objects standard' 'objects' '.git/objects'
 
 test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \
-- 
2.55.GIT
K JayatheerthJul 26, 2026, 10:43 UTC in reply to K Jayatheerth on lore

[PATCH v3 6/7] repo: add path.grafts with absolute and relative suffix formatting

External toolchains managing specialized history rewrites or legacy history splices require access to the location of the repository grafts file. Currently, this requires a legacy call to `git rev-parse --git-path info/grafts`.

Introduce `path.grafts.absolute` and `path.grafts.relative` keys to `git repo info`. This allows scripting layers to query the active grafts context cleanly while scaling transparently with active `GIT_GRAFT_FILE` environment variable overrides.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 10 ++++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        |  6 ++++++
 3 files changed, 40 insertions(+)
Show changes to 3 files +40 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 34c4f7d61c..def888da91 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,16 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.grafts.absolute`::
+	The canonical absolute path to the repository grafts file.
+	Respects the `GIT_GRAFT_FILE` environment override. The path is returned
+	regardless of whether the file currently exists on disk.
+
+`path.grafts.relative`::
+	The path to the repository grafts file relative to the current working
+	directory. Respects the `GIT_GRAFT_FILE` environment override. The path
+	is returned regardless of whether the file currently exists on disk.
+
 `path.hooks.absolute`::
 	The canonical absolute path to the repository's hooks directory.
 	Respects `core.hooksPath` configuration adjustments.
diff --git a/builtin/repo.c b/builtin/repo.c
index 66bf4c67cc..a97ad71649 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_grafts_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *graft_file = repo_get_graft_file(repo);
+
+	if (!graft_file)
+		return error(_("unable to get graft file"));
+
+	format_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_grafts_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *graft_file = repo_get_graft_file(repo);
+
+	if (!graft_file)
+		return error(_("unable to get graft file"));
+
+	format_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)
 {
 	struct strbuf hooks_path = STRBUF_INIT;
@@ -258,6 +280,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.grafts.absolute", get_path_grafts_absolute },
+	{ "path.grafts.relative", get_path_grafts_relative },
 	{ "path.hooks.absolute", get_path_hooks_absolute },
 	{ "path.hooks.relative", get_path_hooks_relative },
 	{ "path.index.absolute", get_path_index_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index dee1db2a49..15a4ec9b78 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'grafts standard' 'grafts' '.git/info/grafts'
+
+test_repo_info_path 'grafts with GIT_GRAFT_FILE override' 'grafts' \
+	'custom-graft-file' \
+	'GIT_GRAFT_FILE="$ROOT/custom-graft-file" && export GIT_GRAFT_FILE'
+
 test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'
 
 test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
-- 
2.55.GIT
K JayatheerthJul 26, 2026, 10:43 UTC in reply to K Jayatheerth on lore

[PATCH v3 7/7] repo: add path.git-prefix path key

Scripts and command-line prompt integrations frequently need to know their relative depth inside a repository working tree layout. Currently, this is retrieved using `git rev-parse --show-prefix`.

Introduce the `path.git-prefix` key to `git repo info`. This mirrors the prefix location tracking framework as a standalone key, returning the exact relative path offset complete with a trailing slash, or an empty string if run directly at the repository working tree root.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  5 +++++
 builtin/repo.c              | 11 +++++++++++
 t/t1900-repo-info.sh        | 19 +++++++++++++++++++
 3 files changed, 35 insertions(+)
Show changes to 3 files +35 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index def888da91..98282ea668 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -113,6 +113,11 @@ values that they return:
 	The path to the Git repository's common directory relative to
 	the current working directory.
 
+`path.git-prefix`::
+	The relative path from the top-level directory of the working tree to
+	the current working directory (including a trailing slash). Outputs an
+	empty string if executed at the root of the working tree.
+
 `path.gitdir.absolute`::
 	The canonical absolute path to the Git repository directory (the `.git` directory).
 
diff --git a/builtin/repo.c b/builtin/repo.c
index a97ad71649..b93c140c74 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -100,6 +100,16 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b
 	return 0;
 }
 
+static int get_path_git_prefix(struct repository *repo UNUSED, struct strbuf *buf)
+{
+	/*
+	 * startup_info->prefix is NULL if we are at the working tree root.
+	 * We add an empty string to ensure the buffer is cleanly initialized.
+	 */
+	strbuf_addstr(buf, startup_info->prefix ? startup_info->prefix : "");
+	return 0;
+}
+
 static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *git_dir = repo_get_git_dir(repo);
@@ -278,6 +288,7 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "object.format", get_object_format },
 	{ "path.commondir.absolute", get_path_commondir_absolute },
 	{ "path.commondir.relative", get_path_commondir_relative },
+	{ "path.git-prefix", get_path_git_prefix },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
 	{ "path.grafts.absolute", get_path_grafts_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 15a4ec9b78..2879e54668 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -207,6 +207,25 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.git-prefix at root and in a subdirectory' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		echo "path.git-prefix=" >expect.root &&
+		git repo info path.git-prefix >actual.root &&
+		test_cmp expect.root actual.root &&
+
+		mkdir -p sub/dir &&
+		cd sub/dir &&
+
+		echo "path.git-prefix=sub/dir/" >expect.sub &&
+		git repo info path.git-prefix >actual.sub &&
+		test_cmp expect.sub actual.sub
+	)
+'
+
 test_repo_info_path 'gitdir standard' 'gitdir' '.git'
 
 test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
-- 
2.55.GIT
Junio C HamanoJul 26, 2026, 16:29 UTC in reply to K Jayatheerth on lore

Re: [GSoC Patch v3 0/7] repo: add more path keys to git repo info

K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
Show 9 quoted lines
> Series adds keys to git repo info.
> Keys output paths of repository components:
> * path.toplevel: repository tree.
> * path.superproject-working-tree: superproject tree from submodules.
> * path.objects: repository objects.
> * path.hooks: repository hooks.
> * path.index: repository index.
> * path.grafts: repository grafts.
> * path.git-prefix: prefix offset.

I guess the issues I had at the design level in the previous iteration is gone ;-)

I'd love to hear real reviews from others on these patches, but at least I didn't spot anything glaringly wrong anymore.

Thanks.
K JayatheerthJul 26, 2026, 17:00 UTC in reply to Junio C Hamano on lore

Re: [GSoC Patch v3 0/7] repo: add more path keys to git repo info

On Sun, Jul 26, 2026 at 9:59 PM Junio C Hamano <gitster@pobox.com> wrote:
Show 20 quoted lines
>
> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
>
> > Series adds keys to git repo info.
> > Keys output paths of repository components:
> > * path.toplevel: repository tree.
> > * path.superproject-working-tree: superproject tree from submodules.
> > * path.objects: repository objects.
> > * path.hooks: repository hooks.
> > * path.index: repository index.
> > * path.grafts: repository grafts.
> > * path.git-prefix: prefix offset.
>
> I guess the issues I had at the design level in the previous
> iteration is gone ;-)
>
> I'd love to hear real reviews from others on these patches, but
> at least I didn't spot anything glaringly wrong anymore.
>
> Thanks.

Sure, until then I will work on the histogram patches in git repo structure.

Thanks for validating!
Regards,
- K Jayatheerth
Lucas Seiki OshiroJul 27, 2026, 00:57 UTC in reply to Junio C Hamano on lore

Re: [GSoC Patch v3 0/7] repo: add more path keys to git repo info

> I'd love to hear real reviews from others on these patches, but
> at least I didn't spot anything glaringly wrong anymore.
It looks good to me :-)
Junio C HamanoJul 27, 2026, 05:55 UTC in reply to Lucas Seiki Oshiro on lore

Re: [GSoC Patch v3 0/7] repo: add more path keys to git repo info

Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:
>> I'd love to hear real reviews from others on these patches, but
>> at least I didn't spot anything glaringly wrong anymore.
>
> It looks good to me :-)

Can you try to make your review sound a bit more credible to readers?

It is much harder to give positive reviews than to merely point out a few breakages in a patch, but there are still things reviewers can do. Thinking aloud to follow the author's line of thought is a technique I have found over the years to work well in demonstrating that your Ack is backed by having actually read and understood the patch. There may be other techniques experienced reviewers (by now you know who they are after reading what they wrote on your patches and others') use when giving positive reviews.

Thanks.
Junio C HamanoJul 27, 2026, 08:45 UTC in reply to K Jayatheerth on lore

Re: [PATCH v3 1/7] repo: add path.toplevel with absolute and relative suffix formatting

K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
Show 12 quoted lines
> +static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
> +{
> +	const char *work_tree = repo_get_work_tree(repo);
> +
> +	if (!work_tree) {
> +		strbuf_addstr(buf, "");
> +		return 0;
> +	}
> +
> +	format_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_CANONICAL);
> +	return 0;
> +}

I can manage to wiggle it in, of course, but I thought we lost the '.prefix' member from 'startup_info' in the recent tip of 'master'. As the topic is not targeting 'maint' as a bugfix, perhaps we want to use a more up-to-date 'master' as the base of the topic?

Thanks.
K JayatheerthJul 28, 2026, 01:21 UTC in reply to Junio C Hamano on lore

Re: [PATCH v3 1/7] repo: add path.toplevel with absolute and relative suffix formatting

On Mon, Jul 27, 2026 at 2:15 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
Show 6 quoted lines
> > +     format_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_CANONICAL);
> > +     return 0;
> > +}
>
> I can manage to wiggle it in, of course, but I thought we lost the
> '.prefix' member from 'startup_info' in the recent tip of 'master'.
Indeed.
> As the topic is not targeting 'maint' as a bugfix, perhaps we want
> to use a more up-to-date 'master' as the base of the topic?
>
> Thanks.

I see Patrick has changed the prefix member to the repo struct. Didn't notice that before!

I will use the latest master branch for v4
Regards,
- K Jayatheerth
Justin ToblerJul 28, 2026, 16:36 UTC in reply to K Jayatheerth on lore

Re: [PATCH v3 1/7] repo: add path.toplevel with absolute and relative suffix formatting

On 26/07/26 04:13PM, K Jayatheerth wrote:
Show 8 quoted lines
> Scripts frequently need to find the root directory of a repository's
> working tree. Currently, this requires using `git rev-parse --show-toplevel`
> or inferring it from other path components.
> 
> Introduce `path.toplevel.absolute` and `path.toplevel.relative` keys
> to `git repo info`. This allows scripts to retrieve the top-level
> working tree path in a predictable, strictly formatted manner without
> relying on `rev-parse`.

Ok, this seems like suitable information to also look up under git-repo-info.

> If requested in a bare repository where no working tree exists, the
> command returns an empty string.
This matches the existing behavior in git-rev-parse(1). Makes sense.
Show 44 quoted lines
> Mentored-by: Justin Tobler <jltobler@gmail.com>
> Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
> Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
> ---
>  Documentation/git-repo.adoc | 10 ++++++++++
>  builtin/repo.c              | 28 ++++++++++++++++++++++++++++
>  t/t1900-repo-info.sh        | 30 ++++++++++++++++++++++++++++++
>  3 files changed, 68 insertions(+)
> 
> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
> index ed7d80c690..e34abe5fea 100644
> --- a/Documentation/git-repo.adoc
> +++ b/Documentation/git-repo.adoc
> @@ -119,6 +119,16 @@ values that they return:
>  `path.gitdir.relative`::
>  	The path to the Git repository directory relative to the current working directory.
>  
> +`path.toplevel.absolute`::
> +	The canonical absolute path to the top-level directory of the
> +	repository's working tree. Outputs an empty string if the repository
> +	is bare.
> +
> +`path.toplevel.relative`::
> +	The path to the top-level directory of the repository's working
> +	tree relative to the current working directory. Outputs an empty
> +	string if the repository is bare.
> +
>  `references.format`::
>  	The reference storage format. The valid values are:
>  +
> diff --git a/builtin/repo.c b/builtin/repo.c
> index 042d6de558..194757eb18 100644
> --- a/builtin/repo.c
> +++ b/builtin/repo.c
> @@ -121,6 +121,32 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
>  	return 0;
>  }
>  
> +static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
> +{
> +	const char *work_tree = repo_get_work_tree(repo);
> +
> +	if (!work_tree) {
> +		strbuf_addstr(buf, "");

The strbuf here is already NULL-terminated when its initialized. I don't think this should be necessary.

Show 13 quoted lines
> +		return 0;
> +	}
> +
> +	format_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_CANONICAL);
> +	return 0;
> +}
> +
> +static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)
> +{
> +	const char *work_tree = repo_get_work_tree(repo);
> +
> +	if (!work_tree) {
> +		strbuf_addstr(buf, "");
Same here.
Show 54 quoted lines
> +		return 0;
> +	}
> +
> +	format_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_RELATIVE);
> +	return 0;
> +}
> +
>  static int get_references_format(struct repository *repo, struct strbuf *buf)
>  {
>  	strbuf_addstr(buf,
> @@ -137,6 +163,8 @@ static const struct repo_info_field repo_info_field[] = {
>  	{ "path.commondir.relative", get_path_commondir_relative },
>  	{ "path.gitdir.absolute", get_path_gitdir_absolute },
>  	{ "path.gitdir.relative", get_path_gitdir_relative },
> +	{ "path.toplevel.absolute", get_path_toplevel_absolute },
> +	{ "path.toplevel.relative", get_path_toplevel_relative },
>  	{ "references.format", get_references_format },
>  };
>  
> diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
> index ae8c22c817..fbb9063ee5 100755
> --- a/t/t1900-repo-info.sh
> +++ b/t/t1900-repo-info.sh
> @@ -213,4 +213,34 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
>  	'.git' \
>  	'GIT_DIR="../.git" && export GIT_DIR'
>  
> +test_expect_success 'path.toplevel absolute and relative' '
> +	test_when_finished "rm -rf repo" &&
> +	git init repo &&
> +	(
> +		mkdir -p repo/sub &&
> +		cd repo/sub &&
> +
> +		ROOT="$(test-tool path-utils real_path ..)" &&
> +
> +		echo "path.toplevel.absolute=$ROOT" >expect.abs &&
> +		git repo info path.toplevel.absolute >actual.abs &&
> +		test_cmp expect.abs actual.abs &&
> +
> +		echo "path.toplevel.relative=../" >expect.rel &&
> +		git repo info path.toplevel.relative >actual.rel &&
> +		test_cmp expect.rel actual.rel
> +	)
> +'
> +
> +test_expect_success 'path.toplevel returns empty in a bare repository' '
> +	test_when_finished "rm -rf bare.git" &&
> +	git init --bare bare.git &&
> +	(
> +		cd bare.git &&
> +		echo "path.toplevel.absolute=" >expect &&
> +		git repo info path.toplevel.absolute >actual &&
> +		test_cmp expect actual

In this test we are only checking the absolute path. It probably wouldn't hurt to also check the relative path too.

-Justin
Justin ToblerJul 28, 2026, 16:43 UTC in reply to K Jayatheerth on lore

Re: [PATCH v3 2/7] repo: add path.superproject-working-tree with absolute and relative suffixes

On 26/07/26 04:13PM, K Jayatheerth wrote:
Show 9 quoted lines
> Scripts working in multi-repository setups often need to identify the
> top-level working tree of a superproject from within a submodule.
> Currently, this is only exposed via `git rev-parse
> --show-superproject-working-tree`.
> 
> Introduce `path.superproject-working-tree.absolute` and
> `path.superproject-working-tree.relative` keys to `git repo info`.
> This exposes the core submodule context via a scriptable config-like key
> using standard format rules.

Ok, this also seems like a good fit to include as a key in git-repo-info, but "superproject-working-tree" is a bit of a mouthful IMO. An alternative could potentially be "superproject-root"? Maybe its best to just be consistent with the option name in git-rev-parse(1) and keep it the same though.

Show 56 quoted lines
> If requested when not inside a submodule, the command returns an empty
> string.
> 
> Mentored-by: Justin Tobler <jltobler@gmail.com>
> Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
> Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
> ---
>  Documentation/git-repo.adoc | 10 ++++++++++
>  builtin/repo.c              | 33 +++++++++++++++++++++++++++++++++
>  t/t1900-repo-info.sh        | 34 ++++++++++++++++++++++++++++++++++
>  3 files changed, 77 insertions(+)
> 
> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
> index e34abe5fea..03aa57942f 100644
> --- a/Documentation/git-repo.adoc
> +++ b/Documentation/git-repo.adoc
> @@ -119,6 +119,16 @@ values that they return:
>  `path.gitdir.relative`::
>  	The path to the Git repository directory relative to the current working directory.
>  
> +`path.superproject-working-tree.absolute`::
> +	The canonical absolute path to the working tree root of the superproject
> +	if the current repository is an initialized submodule. Outputs an empty
> +	string if not in a submodule.
> +
> +`path.superproject-working-tree.relative`::
> +	The path to the working tree root of the superproject relative to the
> +	current working directory if the current repository is an initialized
> +	submodule. Outputs an empty string if not in a submodule.
> +
>  `path.toplevel.absolute`::
>  	The canonical absolute path to the top-level directory of the
>  	repository's working tree. Outputs an empty string if the repository
> diff --git a/builtin/repo.c b/builtin/repo.c
> index 194757eb18..82359473e9 100644
> --- a/builtin/repo.c
> +++ b/builtin/repo.c
> @@ -18,6 +18,7 @@
>  #include "strbuf.h"
>  #include "string-list.h"
>  #include "shallow.h"
> +#include "submodule.h"
>  #include "tree.h"
>  #include "tree-walk.h"
>  #include "utf8.h"
> @@ -121,6 +122,36 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
>  	return 0;
>  }
>  
> +static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
> +{
> +	struct strbuf superproject = STRBUF_INIT;
> +
> +	if (!get_superproject_working_tree(&superproject)) {
> +		strbuf_release(&superproject);
> +		strbuf_addstr(buf, "");
Same comment here as in the previous patch...
Show 15 quoted lines
> +		return 0;
> +	}
> +
> +	format_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);
> +	strbuf_release(&superproject);
> +	return 0;
> +}
> +
> +static int get_path_superproject_relative(struct repository *repo UNUSED, struct strbuf *buf)
> +{
> +	struct strbuf superproject = STRBUF_INIT;
> +
> +	if (!get_superproject_working_tree(&superproject)) {
> +		strbuf_release(&superproject);
> +		strbuf_addstr(buf, "");
...and here as well...
Show 59 quoted lines
> +		return 0;
> +	}
> +
> +	format_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);
> +	strbuf_release(&superproject);
> +	return 0;
> +}
> +
>  static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
>  {
>  	const char *work_tree = repo_get_work_tree(repo);
> @@ -163,6 +194,8 @@ static const struct repo_info_field repo_info_field[] = {
>  	{ "path.commondir.relative", get_path_commondir_relative },
>  	{ "path.gitdir.absolute", get_path_gitdir_absolute },
>  	{ "path.gitdir.relative", get_path_gitdir_relative },
> +	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
> +	{ "path.superproject-working-tree.relative", get_path_superproject_relative },
>  	{ "path.toplevel.absolute", get_path_toplevel_absolute },
>  	{ "path.toplevel.relative", get_path_toplevel_relative },
>  	{ "references.format", get_references_format },
> diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
> index fbb9063ee5..220b3d4d3d 100755
> --- a/t/t1900-repo-info.sh
> +++ b/t/t1900-repo-info.sh
> @@ -213,6 +213,40 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
>  	'.git' \
>  	'GIT_DIR="../.git" && export GIT_DIR'
>  
> +test_expect_success 'path.superproject-working-tree absolute and relative' '
> +	test_when_finished "rm -rf sub super" &&
> +	git init sub &&
> +	test_commit -C sub initial &&
> +	git init super &&
> +	(
> +		cd super &&
> +		git -c protocol.file.allow=always submodule add "../sub" sub &&
> +		git commit -m "add submodule" &&
> +
> +		cd sub &&
> +		ROOT="$(test-tool path-utils real_path ..)" &&
> +
> +		echo "path.superproject-working-tree.absolute=$ROOT" >expect.abs &&
> +		git repo info path.superproject-working-tree.absolute >actual.abs &&
> +		test_cmp expect.abs actual.abs &&
> +
> +		echo "path.superproject-working-tree.relative=../" >expect.rel &&
> +		git repo info path.superproject-working-tree.relative >actual.rel &&
> +		test_cmp expect.rel actual.rel
> +	)
> +'
> +
> +test_expect_success 'path.superproject-working-tree returns empty when not in a submodule' '
> +	test_when_finished "rm -rf repo" &&
> +	git init repo &&
> +	(
> +		cd repo &&
> +		echo "path.superproject-working-tree.absolute=" >expect &&
> +		git repo info path.superproject-working-tree.absolute >actual &&
> +		test_cmp expect actual
... and here as well. :)
-Justin
Justin ToblerJul 28, 2026, 17:12 UTC in reply to K Jayatheerth on lore

Re: [PATCH v3 3/7] repo: add path.objects with absolute and relative suffix formatting

On 26/07/26 04:13PM, K Jayatheerth wrote:
> Tools and deployment hooks frequently query the location of the object
> database directory. Currently, this relies on legacy parsing methods or
> manually inspecting `git rev-parse --git-path objects`.

"Tools and deployment hooks" seems a bit overly specific. Maybe instead we could just say "Scripts operating on a repository may need to query the location of the object database directory"?

Also, I'm not entirely sure what is meant by "legacy parsing methods" here.

> Introduce `path.objects.absolute` and `path.objects.relative` keys to
> `git repo info`. This allows tools to discover the object database
> location safely while natively adhering to active `GIT_OBJECT_DIRECTORY`
> environment variable overrides.

In the context of pluggable ODBs, this proposed key is a little more interesting because a non-"files" ODB source in the future may not even have a filesystem path to an objects directory. When this becomes more relevant, we could just adapt these keys to return an empty string in such cases, but it does also make me question whether it is information that we should further expose in the first place if it does eventually becomes an internal detail of a specific ODB source.

It probably doesn't matter too much, but I've CC'd Patrick for his thoughts too.

[snip]
Show 16 quoted lines
> diff --git a/builtin/repo.c b/builtin/repo.c
> index 82359473e9..d6bdd5bcfa 100644
> --- a/builtin/repo.c
> +++ b/builtin/repo.c
> @@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
>  	return 0;
>  }
>  
> +static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)
> +{
> +	const char *obj_dir = repo_get_object_directory(repo);
> +
> +	if (!obj_dir)
> +		return error(_("unable to get object directory"));
> +
> +	format_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);

For the absolute path, do we actually need to provide the prefix? It might make it more clear that its the aboslute path if we just pass "" instead?

-Justin
Justin ToblerJul 28, 2026, 19:00 UTC in reply to K Jayatheerth on lore

Re: [PATCH v3 4/7] repo: add path.hooks with absolute and relative suffix formatting

On 26/07/26 04:13PM, K Jayatheerth wrote:
> External tool integrations and validation systems need a stable way to
> identify where the repository hooks are stored. Currently, this involves
> relying on `git rev-parse --git-path hooks` or querying `core.hooksPath`
> manually.

Similar to the comment in the previous patch, "External tool intergations and validation systems" seems overly specific IMO. Also, "need" is a bit strongly worded as I'm sure its not a requirement for every external script/tool.

> Introduce `path.hooks.absolute` and `path.hooks.relative` keys to
> `git repo info`. This allows tools to discover the active hooks location
> natively, ensuring proper resolution regardless of whether Git is using
> the standard `.git/hooks` structure or a custom `core.hooksPath` setup.
Per hooks path documentation:
  The path can be either absolute or relative. A relative path is taken
  as relative to the directory where the hooks are run.
  
  ...
  You  can also disable all hooks entirely by setting core.hooksPath to
  /dev/null.
Should we handle this /dev/null case specially? It looks like:
  $ git -c core.hooksPath=/dev/null rev-parse --git-path hooks

just prints '/dev/null'. I do wonder if this makes much sense though in context of the relative path version of this key. From some quick testing, it appears the git-rev-parse(1) version of this option always prints the absolute path if that is what is configured (it appears to ignore --path-format). Maybe we should just special case /dev/null and return an empty string? I'm not entirely sure what the best route is here though.

-Justin
K JayatheerthJul 29, 2026, 16:36 UTC in reply to Justin Tobler on lore

Re: [PATCH v3 1/7] repo: add path.toplevel with absolute and relative suffix formatting

Hey Justin,
Thanks for the detailed review.
On Tue, Jul 28, 2026 at 10:06 PM Justin Tobler <jltobler@gmail.com> wrote:
Show 19 quoted lines
>
> On 26/07/26 04:13PM, K Jayatheerth wrote:
> > Scripts frequently need to find the root directory of a repository's
> > working tree. Currently, this requires using `git rev-parse --show-toplevel`
> > or inferring it from other path components.
> >
> > Introduce `path.toplevel.absolute` and `path.toplevel.relative` keys
> > to `git repo info`. This allows scripts to retrieve the top-level
> > working tree path in a predictable, strictly formatted manner without
> > relying on `rev-parse`.
>
> Ok, this seems like suitable information to also look up under
> git-repo-info.
>
> > If requested in a bare repository where no working tree exists, the
> > command returns an empty string.
>
> This matches the existing behavior in git-rev-parse(1). Makes sense.
>
Yes, that is correct.
Show 11 quoted lines
> >
> > +static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
> > +{
> > +     const char *work_tree = repo_get_work_tree(repo);
> > +
> > +     if (!work_tree) {
> > +             strbuf_addstr(buf, "");
>
> The strbuf here is already NULL-terminated when its initialized. I don't
> think this should be necessary.
>

Very nice point indeed I will change that everywhere I have added that.

Show 5 quoted lines
> > +     if (!work_tree) {
> > +             strbuf_addstr(buf, "");
>
> Same here.
>
Show 12 quoted lines
> > +test_expect_success 'path.toplevel returns empty in a bare repository' '
> > +     test_when_finished "rm -rf bare.git" &&
> > +     git init --bare bare.git &&
> > +     (
> > +             cd bare.git &&
> > +             echo "path.toplevel.absolute=" >expect &&
> > +             git repo info path.toplevel.absolute >actual &&
> > +             test_cmp expect actual
>
> In this test we are only checking the absolute path. It probably
> wouldn't hurt to also check the relative path too.
>
Good catch. I'll add relative-path tests in both places.
Show 10 quoted lines
> > Introduce `path.superproject-working-tree.absolute` and
> > `path.superproject-working-tree.relative` keys to `git repo info`.
> > This exposes the core submodule context via a scriptable config-like key
> > using standard format rules.
>
> Ok, this also seems like a good fit to include as a key in
> git-repo-info, but "superproject-working-tree" is a bit of a mouthful
> IMO. An alternative could potentially be "superproject-root"? Maybe its
> best to just be consistent with the option name in git-rev-parse(1) and
> keep it the same though.

show-superproject-working-tree is what git rev parse uses. I am a bit unclear about this. Since it is meant to be a scripting command, I don't know if it being a long name matters much. At the same time, I don't think we can keep the exact "show-superproject-working-tree" as the key's name. As the "show" is indirectly covered by "path." so the "show" is just redundant if you ask me (I might be wrong here). That is actually why I kept the rest of the name similar and omitted the prefix from the flag name.

In other words, I am fine with the name being either one. That said, I am more inclined with the "superproject-root". I think the name explains exactly what it does. Whereas "superproject-working-tree" was not that clear to me when I first read it (Might be subjective to me).

Show 37 quoted lines
> > +static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
> > +{
> > +     struct strbuf superproject = STRBUF_INIT;
> > +
> > +     if (!get_superproject_working_tree(&superproject)) {
> > +             strbuf_release(&superproject);
> > +             strbuf_addstr(buf, "");
>
> Same comment here as in the previous patch...
>
> > +     struct strbuf superproject = STRBUF_INIT;
> > +
> > +     if (!get_superproject_working_tree(&superproject)) {
> > +             strbuf_release(&superproject);
> > +             strbuf_addstr(buf, "");
>
> ...and here as well...
>
> > +             return 0;
> > +     }
> > +
> > +     format_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);
> > +     strbuf_release(&superproject);
> > +     return 0;
> > +}
> > +
> > +test_expect_success 'path.superproject-working-tree returns empty when not in a submodule' '
> > +     test_when_finished "rm -rf repo" &&
> > +     git init repo &&
> > +     (
> > +             cd repo &&
> > +             echo "path.superproject-working-tree.absolute=" >expect &&
> > +             git repo info path.superproject-working-tree.absolute >actual &&
> > +             test_cmp expect actual
>
> ... and here as well. :)
>
Good catch. I'll add a relative-path test there as well.
Show 8 quoted lines
> > Tools and deployment hooks frequently query the location of the object
> > database directory. Currently, this relies on legacy parsing methods or
> > manually inspecting `git rev-parse --git-path objects`.
>
> "Tools and deployment hooks" seems a bit overly specific. Maybe instead
> we could just say "Scripts operating on a repository may need to query
> the location of the object database directory"?
>

I think your version is consistent with other patches as well. I wanted to ask if it is not a good practice to write commit messages which are very specific. I also wanted to know what constitutes being overly specific so that I can avoid doing it again.

> Also, I'm not entirely sure what is meant by "legacy parsing methods"
> here.
>

Something scripts have historically done: parse git rev-parse --git-dir (or read .git/config by hand) and manually append /objects, rather than asking for the object directory directly which breaks if GIT_OBJECT_DIRECTORY is set. I'll reword the commit message to just say that explicitly instead of the vague "legacy parsing methods" phrase.

Show 16 quoted lines
> > Introduce `path.objects.absolute` and `path.objects.relative` keys to
> > `git repo info`. This allows tools to discover the object database
> > location safely while natively adhering to active `GIT_OBJECT_DIRECTORY`
> > environment variable overrides.
>
> In the context of pluggable ODBs, this proposed key is a little more
> interesting because a non-"files" ODB source in the future may not even
> have a filesystem path to an objects directory. When this becomes more
> relevant, we could just adapt these keys to return an empty string in
> such cases, but it does also make me question whether it is information
> that we should further expose in the first place if it does eventually
> becomes an internal detail of a specific ODB source.
>
> It probably doesn't matter too much, but I've CC'd Patrick for his
> thoughts too.
>
Thanks for looping in Patrick!

That's a fair concern. My thinking is that the "files" backend is going to be the reality for the vast majority of repositories for a long time yet, and scripts asking for path.objects today are almost certainly assuming a filesystem-backed ODB anyway (that's the whole reason they'd want the path). So I don't think we're locking ourselves into a bad abstraction by adding it now.

If/when a non-filesystem ODB source becomes real, we'd have two reasonable options: return an empty string for that key (same pattern we already use for path.toplevel/superproject-working-tree when the concept doesn't apply), or deprecate the key outright if it turns out to be meaningless in that world. Either way it doesn't block adding it now, worst case we're removing or narrowing a key later, not stuck with something actively wrong.

Show 13 quoted lines
> > +static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)
> > +{
> > +     const char *obj_dir = repo_get_object_directory(repo);
> > +
> > +     if (!obj_dir)
> > +             return error(_("unable to get object directory"));
> > +
> > +     format_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);
>
> For the absolute path, do we actually need to provide the prefix? It
> might make it more clear that its the aboslute path if we just pass ""
> instead?
>

This point was raised in the foundational patch as well. It won't change the output for sure, so I will pass "" instead.

On Wed, Jul 29, 2026 at 12:30 AM Justin Tobler <jltobler@gmail.com> wrote:
Show 12 quoted lines
>
> On 26/07/26 04:13PM, K Jayatheerth wrote:
> > External tool integrations and validation systems need a stable way to
> > identify where the repository hooks are stored. Currently, this involves
> > relying on `git rev-parse --git-path hooks` or querying `core.hooksPath`
> > manually.
>
> Similar to the comment in the previous patch, "External tool
> intergations and validation systems" seems overly specific IMO. Also,
> "need" is a bit strongly worded as I'm sure its not a requirement for
> every external script/tool.
>

Got it, I will try it to be not overly specific as well. Maybe something like: Scripts operating on a repository may want a stable way to identify where the repository hooks are stored. Currently, this involves relying on `git rev-parse --git-path hooks` or querying `core.hooksPath` manually.

Introduce `path.hooks.absolute` and `path.hooks.relative` keys to `git repo info`. This allows scripts to discover the active hooks location, ensuring proper resolution regardless of whether Git is using the standard `.git/hooks` structure or a custom `core.hooksPath` setup.

Show 27 quoted lines
> > Introduce `path.hooks.absolute` and `path.hooks.relative` keys to
> > `git repo info`. This allows tools to discover the active hooks location
> > natively, ensuring proper resolution regardless of whether Git is using
> > the standard `.git/hooks` structure or a custom `core.hooksPath` setup.
>
> Per hooks path documentation:
>
>   The path can be either absolute or relative. A relative path is taken
>   as relative to the directory where the hooks are run.
>
>   ...
>
>   You  can also disable all hooks entirely by setting core.hooksPath to
>   /dev/null.
>
> Should we handle this /dev/null case specially? It looks like:
>
>   $ git -c core.hooksPath=/dev/null rev-parse --git-path hooks
>
> just prints '/dev/null'. I do wonder if this makes much sense though in
> context of the relative path version of this key. From some quick
> testing, it appears the git-rev-parse(1) version of this option always
> prints the absolute path if that is what is configured (it appears to
> ignore --path-format). Maybe we should just special case /dev/null and
> return an empty string? I'm not entirely sure what the best route is
> here though.
>

After thinking about it I would lean towards not special-casing it. A few reasons:

/dev/null is just a valid (if unusual) value of core.hooksPath as far as Git is concerned, the field is documented to reflect whatever core.hooksPath resolves to, and rev-parse --git-path hooks already treats it as an ordinary path. If we special-case it to an empty string, we'd be the only place in Git that treats /dev/null as meaningfully different from any other hooksPath value.

Empty string is already used by other keys in this series to mean "this concept doesn't apply here" (no working tree, not a submodule). Using empty string for "hooks are disabled" would overload that meaning with something different, "the path exists but you shouldn't use it" and a script can't tell the two apart without also knowing the hooksPath convention, which defeats the purpose of adding a structured key in the first place.

Any script that's checking for /dev/null already has to know that convention when using the existing rev-parse interface, so we're not creating new surprises, just carrying forward a pre-existing quirk of core.hooksPath semantics.

For the relative version: I'll let format_path/relative_path do whatever it already does for a path that isn't under the prefix (I'd guess it falls back to printing the absolute path, similar to how rev-parse seems to ignore --path-format for this case). That keeps behavior consistent with the existing tool rather than introducing new special-casing for path.hooks.relative specifically.

I'll add a test covering core.hooksPath=/dev/null to lock in whichever behavior we land on, and I can add a doc note under path.hooks.absolute/path.hooks.relative mentioning that /dev/null is passed through unchanged if that's the value in use, similar to how the existing hooksPath docs describe it. Let me know if you still think empty-string is preferable despite the above, happy to go either way once we agree on the reasoning.

Thanks again for the detailed review!
Regards,
- K Jayatheerth
K JayatheerthAug 6, 2026, 10:15 UTC in reply to K Jayatheerth on lore

[GSoC PATCH v4 0/7] repo: add more path keys to git repo info

Series adds keys to git repo info.
Keys output paths of repository components:
* path.toplevel: repository tree.
* path.superproject-root: superproject tree from submodules.
* path.hooks: repository hooks.
* path.index: repository index.
* path.grafts: repository grafts.
* path.git-prefix: prefix offset.
The patch also removes an unused header.

Keys support suffixes for format. Commits contain documentation and tests.

Changes since v3:
* The prefix member has been shifted from startup_info to repo,
  therefore we now omit the header and use repo->prefix everywhere.
  Development of these patches is now based on the latest branch.
* Since strbuf is NUL-terminated, remove all
  `strbuf_addstr(buf, "");` lines from the patches.
* Add relative-path test cases alongside the existing absolute-path
  test cases wherever we don't use the existing helper.
* Renamed `superproject-working-tree` to `superproject-root` because
  I feel the latter is a bit clearer. That said, I'm happy with
  either name, so if you prefer `superproject-working-tree`, I'm
  perfectly fine reverting it.
* Changed almost all the commit messages to make them not overly
  specific.
* Dropped `path.objects` from this series. During a sync discussion
  with my mentors, we concluded that this key should wait, since the
  object database's on-disk layout is expected to change
  significantly as pluggable ODB backends are introduced.
* Removed `repo->prefix` from the absolute path functions (the
  absolute-path variants now pass "" instead).
* Added a `/dev/null` test case for `core.hooksPath`, guarded against
  MINGW since /dev/null doesn't resolve to a real canonicalizable
  path on Windows.
K Jayatheerth (7):
  repo: add path.toplevel with absolute and relative suffix formatting
  repo: add path.superproject-root with absolute and relative suffixes
  repo: add path.hooks with absolute and relative suffixes
  repo: add path.index with absolute and relative suffixes
  repo: add path.grafts with absolute and relative suffixes
  repo: add path.git-prefix
  repo: remove unused setup.h include
 Documentation/git-repo.adoc |  57 +++++++++++++
 builtin/repo.c              | 137 +++++++++++++++++++++++++++++++-
 t/t1900-repo-info.sh        | 154 +++++++++++++++++++++++++++++++++++-
 3 files changed, 345 insertions(+), 3 deletions(-)
Range-diff against v3:
1:  baed121c74 ! 1:  3880485020 repo: add path.toplevel with absolute and relative suffix formatting
    @@ Commit message
     
         Scripts frequently need to find the root directory of a repository's
         working tree. Currently, this requires using `git rev-parse --show-toplevel`
    -    or inferring it from other path components.
    +    or inferring it from other repository information.
     
         Introduce `path.toplevel.absolute` and `path.toplevel.relative` keys
         to `git repo info`. This allows scripts to retrieve the top-level
    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str
     +{
     +	const char *work_tree = repo_get_work_tree(repo);
     +
    -+	if (!work_tree) {
    -+		strbuf_addstr(buf, "");
    ++	if (!work_tree)
     +		return 0;
    -+	}
     +
    -+	format_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_CANONICAL);
    ++	format_path(buf, work_tree, "", PATH_FORMAT_CANONICAL);
     +	return 0;
     +}
     +
    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str
     +{
     +	const char *work_tree = repo_get_work_tree(repo);
     +
    -+	if (!work_tree) {
    -+		strbuf_addstr(buf, "");
    ++	if (!work_tree)
     +		return 0;
    -+	}
     +
    -+	format_path(buf, work_tree, startup_info->prefix, PATH_FORMAT_RELATIVE);
    ++	format_path(buf, work_tree, repo->prefix, PATH_FORMAT_RELATIVE);
     +	return 0;
     +}
     +
    @@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir
     +	)
     +'
     +
    -+test_expect_success 'path.toplevel returns empty in a bare repository' '
    ++test_expect_success 'path.toplevel absolute and relative in a bare repository' '
     +	test_when_finished "rm -rf bare.git" &&
     +	git init --bare bare.git &&
     +	(
     +		cd bare.git &&
    -+		echo "path.toplevel.absolute=" >expect &&
    -+		git repo info path.toplevel.absolute >actual &&
    -+		test_cmp expect actual
    ++
    ++		echo "path.toplevel.absolute=" >expect.abs &&
    ++		git repo info path.toplevel.absolute >actual.abs &&
    ++		test_cmp expect.abs actual.abs &&
    ++
    ++		echo "path.toplevel.relative=" >expect.rel &&
    ++		git repo info path.toplevel.relative >actual.rel &&
    ++		test_cmp expect.rel actual.rel
     +	)
     +'
     +
2:  2f363b6358 ! 2:  1b7b0d286c repo: add path.superproject-working-tree with absolute and relative suffixes
    @@ Metadata
     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
     
      ## Commit message ##
    -    repo: add path.superproject-working-tree with absolute and relative suffixes
    +    repo: add path.superproject-root with absolute and relative suffixes
     
         Scripts working in multi-repository setups often need to identify the
         top-level working tree of a superproject from within a submodule.
         Currently, this is only exposed via `git rev-parse
         --show-superproject-working-tree`.
     
    -    Introduce `path.superproject-working-tree.absolute` and
    -    `path.superproject-working-tree.relative` keys to `git repo info`.
    +    Introduce `path.superproject-root.absolute` and
    +    `path.superproject-root.relative` keys to `git repo info`.
         This exposes the core submodule context via a scriptable config-like key
         using standard format rules.
     
    @@ Documentation/git-repo.adoc: values that they return:
      `path.gitdir.relative`::
      	The path to the Git repository directory relative to the current working directory.
      
    -+`path.superproject-working-tree.absolute`::
    ++`path.superproject-root.absolute`::
     +	The canonical absolute path to the working tree root of the superproject
     +	if the current repository is an initialized submodule. Outputs an empty
     +	string if not in a submodule.
     +
    -+`path.superproject-working-tree.relative`::
    ++`path.superproject-root.relative`::
     +	The path to the working tree root of the superproject relative to the
     +	current working directory if the current repository is an initialized
     +	submodule. Outputs an empty string if not in a submodule.
    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str
     +
     +	if (!get_superproject_working_tree(&superproject)) {
     +		strbuf_release(&superproject);
    -+		strbuf_addstr(buf, "");
     +		return 0;
     +	}
     +
    -+	format_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);
    ++	format_path(buf, superproject.buf, "", PATH_FORMAT_CANONICAL);
     +	strbuf_release(&superproject);
     +	return 0;
     +}
     +
    -+static int get_path_superproject_relative(struct repository *repo UNUSED, struct strbuf *buf)
    ++static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)
     +{
     +	struct strbuf superproject = STRBUF_INIT;
     +
     +	if (!get_superproject_working_tree(&superproject)) {
     +		strbuf_release(&superproject);
    -+		strbuf_addstr(buf, "");
     +		return 0;
     +	}
     +
    -+	format_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);
    ++	format_path(buf, superproject.buf, repo->prefix, PATH_FORMAT_RELATIVE);
     +	strbuf_release(&superproject);
     +	return 0;
     +}
    @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {
      	{ "path.commondir.relative", get_path_commondir_relative },
      	{ "path.gitdir.absolute", get_path_gitdir_absolute },
      	{ "path.gitdir.relative", get_path_gitdir_relative },
    -+	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
    -+	{ "path.superproject-working-tree.relative", get_path_superproject_relative },
    ++	{ "path.superproject-root.absolute", get_path_superproject_absolute },
    ++	{ "path.superproject-root.relative", get_path_superproject_relative },
      	{ "path.toplevel.absolute", get_path_toplevel_absolute },
      	{ "path.toplevel.relative", get_path_toplevel_relative },
      	{ "references.format", get_references_format },
    @@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir
      	'.git' \
      	'GIT_DIR="../.git" && export GIT_DIR'
      
    -+test_expect_success 'path.superproject-working-tree absolute and relative' '
    ++test_expect_success 'path.superproject-root absolute and relative' '
     +	test_when_finished "rm -rf sub super" &&
     +	git init sub &&
     +	test_commit -C sub initial &&
    @@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir
     +		cd sub &&
     +		ROOT="$(test-tool path-utils real_path ..)" &&
     +
    -+		echo "path.superproject-working-tree.absolute=$ROOT" >expect.abs &&
    -+		git repo info path.superproject-working-tree.absolute >actual.abs &&
    ++		echo "path.superproject-root.absolute=$ROOT" >expect.abs &&
    ++		git repo info path.superproject-root.absolute >actual.abs &&
     +		test_cmp expect.abs actual.abs &&
     +
    -+		echo "path.superproject-working-tree.relative=../" >expect.rel &&
    -+		git repo info path.superproject-working-tree.relative >actual.rel &&
    ++		echo "path.superproject-root.relative=../" >expect.rel &&
    ++		git repo info path.superproject-root.relative >actual.rel &&
     +		test_cmp expect.rel actual.rel
     +	)
     +'
     +
    -+test_expect_success 'path.superproject-working-tree returns empty when not in a submodule' '
    ++test_expect_success 'path.superproject-root returns empty when not in a submodule' '
     +	test_when_finished "rm -rf repo" &&
     +	git init repo &&
     +	(
     +		cd repo &&
    -+		echo "path.superproject-working-tree.absolute=" >expect &&
    -+		git repo info path.superproject-working-tree.absolute >actual &&
    -+		test_cmp expect actual
    ++
    ++		echo "path.superproject-root.absolute=" >expect.abs &&
    ++		git repo info path.superproject-root.absolute >actual.abs &&
    ++		test_cmp expect.abs actual.abs &&
    ++
    ++		echo "path.superproject-root.relative=" >expect.rel &&
    ++		git repo info path.superproject-root.relative >actual.rel &&
    ++		test_cmp expect.rel actual.rel
     +	)
     +'
     +
4:  e3facc7989 ! 3:  517f621eb0 repo: add path.hooks with absolute and relative suffix formatting
    @@ Metadata
     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
     
      ## Commit message ##
    -    repo: add path.hooks with absolute and relative suffix formatting
    +    repo: add path.hooks with absolute and relative suffixes
     
    -    External tool integrations and validation systems need a stable way to
    -    identify where the repository hooks are stored. Currently, this involves
    -    relying on `git rev-parse --git-path hooks` or querying `core.hooksPath`
    -    manually.
    +    Hooks are an integral part of a repository's configuration and are
    +    commonly used by tooling to automate repository-specific workflows.
    +    Currently, scripts typically retrieve the hooks directory by invoking
    +    `git rev-parse --git-path hooks`.
     
         Introduce `path.hooks.absolute` and `path.hooks.relative` keys to
    -    `git repo info`. This allows tools to discover the active hooks location
    -    natively, ensuring proper resolution regardless of whether Git is using
    -    the standard `.git/hooks` structure or a custom `core.hooksPath` setup.
    +    `git repo info`. This exposes the hooks directory as a scriptable
    +    config-like key using standard format rules, allowing scripts to
    +    retrieve it through the same interface as other repository path
    +    information.
     
         Mentored-by: Justin Tobler <jltobler@gmail.com>
         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
    @@ Documentation/git-repo.adoc: values that they return:
      
     +`path.hooks.absolute`::
     +	The canonical absolute path to the repository's hooks directory.
    -+	Respects `core.hooksPath` configuration adjustments.
    ++	Respects the `core.hooksPath` configuration. If `core.hooksPath` is
    ++	set to `/dev/null`, that value is returned unchanged.
     +
     +`path.hooks.relative`::
     +	The path to the repository's hooks directory relative to the current
    -+	working directory. Respects `core.hooksPath` configuration adjustments.
    ++	working directory. Respects the `core.hooksPath` configuration.
     +
    - `path.objects.absolute`::
    - 	The canonical absolute path to the repository's object database directory.
    - 	Respects the `GIT_OBJECT_DIRECTORY` environment override.
    + `path.superproject-root.absolute`::
    + 	The canonical absolute path to the working tree root of the superproject
    + 	if the current repository is an initialized submodule. Outputs an empty
     
      ## builtin/repo.c ##
     @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str
     +	struct strbuf hooks_path = STRBUF_INIT;
     +
     +	repo_git_path_replace(repo, &hooks_path, "hooks");
    -+	format_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);
    ++	format_path(buf, hooks_path.buf, "", PATH_FORMAT_CANONICAL);
     +	strbuf_release(&hooks_path);
     +	return 0;
     +}
    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str
     +	struct strbuf hooks_path = STRBUF_INIT;
     +
     +	repo_git_path_replace(repo, &hooks_path, "hooks");
    -+	format_path(buf, hooks_path.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);
    ++	format_path(buf, hooks_path.buf, repo->prefix, PATH_FORMAT_RELATIVE);
     +	strbuf_release(&hooks_path);
     +	return 0;
     +}
     +
    - static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)
    + static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
      {
    - 	const char *obj_dir = repo_get_object_directory(repo);
    + 	struct strbuf superproject = STRBUF_INIT;
     @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {
      	{ "path.commondir.relative", get_path_commondir_relative },
      	{ "path.gitdir.absolute", get_path_gitdir_absolute },
      	{ "path.gitdir.relative", get_path_gitdir_relative },
     +	{ "path.hooks.absolute", get_path_hooks_absolute },
     +	{ "path.hooks.relative", get_path_hooks_relative },
    - 	{ "path.objects.absolute", get_path_objects_absolute },
    - 	{ "path.objects.relative", get_path_objects_relative },
    - 	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
    + 	{ "path.superproject-root.absolute", get_path_superproject_absolute },
    + 	{ "path.superproject-root.relative", get_path_superproject_relative },
    + 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
     
      ## t/t1900-repo-info.sh ##
    +@@ t/t1900-repo-info.sh: test_repo_info_path () {
    + 			cd repo/sub &&
    + 			ROOT="$(test-tool path-utils real_path ..)" && export ROOT &&
    + 			eval "$init_command" &&
    +-			echo "path.$field_name.absolute=$ROOT/$expected_dir" >expect &&
    ++			case "$expected_dir" in
    ++			/*) EXPECT_ABS="$expected_dir" ;;
    ++			*) EXPECT_ABS="$ROOT/$expected_dir" ;;
    ++			esac &&
    ++			echo "path.$field_name.absolute=$EXPECT_ABS" >expect &&
    + 			git repo info "path.$field_name.absolute" >actual &&
    + 			test_cmp expect actual
    + 		)
    +@@ t/t1900-repo-info.sh: test_repo_info_path () {
    + 			cd repo/sub &&
    + 			ROOT="$(test-tool path-utils real_path ..)" && export ROOT &&
    + 			eval "$init_command" &&
    +-			echo "path.$field_name.relative=../$expected_dir" >expect &&
    ++			case "$expected_dir" in
    ++			/*) EXPECT_REL="$(test-tool path-utils relative_path "$expected_dir" "$PWD")" ;;
    ++			*) EXPECT_REL="../$expected_dir" ;;
    ++			esac &&
    ++			echo "path.$field_name.relative=$EXPECT_REL" >expect &&
    + 			git repo info "path.$field_name.relative" >actual &&
    + 			test_cmp expect actual
    + 		)
     @@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
      	'.git' \
      	'GIT_DIR="../.git" && export GIT_DIR'
      
    -+test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'
    ++test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'
     +
     +test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
     +	'custom-hooks' \
     +	'git config core.hooksPath "$ROOT/custom-hooks" && mkdir -p "$ROOT/custom-hooks"'
     +
    - test_repo_info_path 'objects standard' 'objects' '.git/objects'
    - 
    - test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \
    ++# /dev/null is not a real, canonicalizable filesystem path on Windows,
    ++# so path resolution for core.hooksPath=/dev/null cannot be expected to
    ++# produce a literal "/dev/null" the way it does on POSIX systems.
    ++if ! test_have_prereq MINGW
    ++then
    ++	test_repo_info_path 'hooks with core.hooksPath=/dev/null' 'hooks' \
    ++		'/dev/null' \
    ++		'git config core.hooksPath /dev/null'
    ++fi
    ++
    + test_expect_success 'path.superproject-root absolute and relative' '
    + 	test_when_finished "rm -rf sub super" &&
    + 	git init sub &&
5:  ab157b8b49 ! 4:  37d28712de repo: add path.index with absolute and relative suffix formatting
    @@ Metadata
     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
     
      ## Commit message ##
    -    repo: add path.index with absolute and relative suffix formatting
    +    repo: add path.index with absolute and relative suffixes
     
    -    External script workflows and formatting layers require straightforward
    -    access to the location of the index staging file. Currently, tracking
    -    this necessitates a legacy call to `git rev-parse --git-path index` or
    -    `--show-toplevel` logic abstractions.
    +    The repository index is a fundamental component used by Git and related
    +    tooling to track the working tree state. Scripts that interact with the
    +    index currently retrieve its location by invoking
    +    `git rev-parse --git-path index`.
     
         Introduce `path.index.absolute` and `path.index.relative` keys to
    -    `git repo info`. This allows tooling utilities to discover the active
    -    index context cleanly while scaling transparently with localized
    -    `GIT_INDEX_FILE` environment overrides.
    +    `git repo info`. This exposes the index file location as a scriptable
    +    config-like key using standard format rules, allowing scripts to
    +    retrieve it through the same interface as other repository path
    +    information.
     
         Mentored-by: Justin Tobler <jltobler@gmail.com>
         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
    @@ Commit message
      ## Documentation/git-repo.adoc ##
     @@ Documentation/git-repo.adoc: values that they return:
      	The path to the repository's hooks directory relative to the current
    - 	working directory. Respects `core.hooksPath` configuration adjustments.
    + 	working directory. Respects the `core.hooksPath` configuration.
      
     +`path.index.absolute`::
     +	The canonical absolute path to the repository's current index file.
    -+	Respects the `GIT_INDEX_FILE` environment override. The returned path
    -+	reflects the configured/default index location regardless of whether the
    -+	repository is bare or whether the file currently exists.
    ++	Respects the `GIT_INDEX_FILE` environment override. Returns the
    ++	configured index path even if the repository is bare or the file does
    ++	not exist.
     +
     +`path.index.relative`::
     +	The path to the repository's current index file relative to the current
     +	working directory. Respects the `GIT_INDEX_FILE` environment override.
    -+	The returned path reflects the configured/default index location regardless
    -+	of whether the repository is bare or whether the file currently exists.
    ++	Returns the configured index path even if the repository is bare or the
    ++	file does not exist.
     +
    - `path.objects.absolute`::
    - 	The canonical absolute path to the repository's object database directory.
    - 	Respects the `GIT_OBJECT_DIRECTORY` environment override.
    + `path.superproject-root.absolute`::
    + 	The canonical absolute path to the working tree root of the superproject
    + 	if the current repository is an initialized submodule. Outputs an empty
     
      ## builtin/repo.c ##
     @@ builtin/repo.c: static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
    @@ builtin/repo.c: static int get_path_hooks_relative(struct repository *repo, stru
     +	if (!index_file)
     +		return error(_("unable to get index file"));
     +
    -+	format_path(buf, index_file, startup_info->prefix, PATH_FORMAT_CANONICAL);
    ++	format_path(buf, index_file, "", PATH_FORMAT_CANONICAL);
     +	return 0;
     +}
     +
    @@ builtin/repo.c: static int get_path_hooks_relative(struct repository *repo, stru
     +	if (!index_file)
     +		return error(_("unable to get index file"));
     +
    -+	format_path(buf, index_file, startup_info->prefix, PATH_FORMAT_RELATIVE);
    ++	format_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);
     +	return 0;
     +}
     +
    - static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)
    + static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
      {
    - 	const char *obj_dir = repo_get_object_directory(repo);
    + 	struct strbuf superproject = STRBUF_INIT;
     @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {
      	{ "path.gitdir.relative", get_path_gitdir_relative },
      	{ "path.hooks.absolute", get_path_hooks_absolute },
      	{ "path.hooks.relative", get_path_hooks_relative },
     +	{ "path.index.absolute", get_path_index_absolute },
     +	{ "path.index.relative", get_path_index_relative },
    - 	{ "path.objects.absolute", get_path_objects_absolute },
    - 	{ "path.objects.relative", get_path_objects_relative },
    - 	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
    + 	{ "path.superproject-root.absolute", get_path_superproject_absolute },
    + 	{ "path.superproject-root.relative", get_path_superproject_relative },
    + 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
     
      ## t/t1900-repo-info.sh ##
    -@@ t/t1900-repo-info.sh: test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
    - 	'custom-hooks' \
    - 	'git config core.hooksPath "$ROOT/custom-hooks" && mkdir -p "$ROOT/custom-hooks"'
    +@@ t/t1900-repo-info.sh: then
    + 		'git config core.hooksPath /dev/null'
    + fi
      
     +test_repo_info_path 'index standard' 'index' '.git/index'
     +
    @@ t/t1900-repo-info.sh: test_repo_info_path 'hooks with core.hooksPath override' '
     +	)
     +'
     +
    - test_repo_info_path 'objects standard' 'objects' '.git/objects'
    - 
    - test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \
    + test_expect_success 'path.superproject-root absolute and relative' '
    + 	test_when_finished "rm -rf sub super" &&
    + 	git init sub &&
6:  d80719367b ! 5:  d88340f5a6 repo: add path.grafts with absolute and relative suffix formatting
    @@ Metadata
     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
     
      ## Commit message ##
    -    repo: add path.grafts with absolute and relative suffix formatting
    +    repo: add path.grafts with absolute and relative suffixes
     
    -    External toolchains managing specialized history rewrites or legacy
    -    history splices require access to the location of the repository grafts
    -    file. Currently, this requires a legacy call to `git rev-parse --git-path info/grafts`.
    +    The repository grafts file specifies alternate parent relationships for
    +    commits and may be used by repository tooling that needs to inspect or
    +    manage grafts. Scripts currently retrieve its location by invoking
    +    `git rev-parse --git-path info/grafts`.
     
         Introduce `path.grafts.absolute` and `path.grafts.relative` keys to
    -    `git repo info`. This allows scripting layers to query the active grafts
    -    context cleanly while scaling transparently with active `GIT_GRAFT_FILE`
    -    environment variable overrides.
    +    `git repo info`. This exposes the grafts file location as a scriptable
    +    config-like key using standard format rules, allowing scripts to
    +    retrieve it through the same interface as other repository path
    +    information.
     
         Mentored-by: Justin Tobler <jltobler@gmail.com>
         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
    @@ Documentation/git-repo.adoc: values that they return:
      	The path to the Git repository directory relative to the current working directory.
      
     +`path.grafts.absolute`::
    -+	The canonical absolute path to the repository grafts file.
    -+	Respects the `GIT_GRAFT_FILE` environment override. The path is returned
    -+	regardless of whether the file currently exists on disk.
    ++	The canonical absolute path to the repository's graft file.
    ++	Respects the `GIT_GRAFT_FILE` environment override. The path is
    ++	returned regardless of whether the file currently exists on disk.
     +
     +`path.grafts.relative`::
    -+	The path to the repository grafts file relative to the current working
    -+	directory. Respects the `GIT_GRAFT_FILE` environment override. The path
    -+	is returned regardless of whether the file currently exists on disk.
    ++	The path to the repository's graft file relative to the current
    ++	working directory. Respects the `GIT_GRAFT_FILE` environment
    ++	override. The path is returned regardless of whether the file
    ++	currently exists on disk.
     +
      `path.hooks.absolute`::
      	The canonical absolute path to the repository's hooks directory.
    - 	Respects `core.hooksPath` configuration adjustments.
    + 	Respects the `core.hooksPath` configuration. If `core.hooksPath` is
     
      ## builtin/repo.c ##
     @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str
     +	if (!graft_file)
     +		return error(_("unable to get graft file"));
     +
    -+	format_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_CANONICAL);
    ++	format_path(buf, graft_file, "", PATH_FORMAT_CANONICAL);
     +	return 0;
     +}
     +
    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str
     +	if (!graft_file)
     +		return error(_("unable to get graft file"));
     +
    -+	format_path(buf, graft_file, startup_info->prefix, PATH_FORMAT_RELATIVE);
    ++	format_path(buf, graft_file, repo->prefix, PATH_FORMAT_RELATIVE);
     +	return 0;
     +}
     +
    @@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir
     +	'custom-graft-file' \
     +	'GIT_GRAFT_FILE="$ROOT/custom-graft-file" && export GIT_GRAFT_FILE'
     +
    - test_repo_info_path 'hooks standard fallback' 'hooks' '.git/hooks'
    + test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'
      
      test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
7:  ac6aee77e8 ! 6:  38b19d8bfd repo: add path.git-prefix path key
    @@ Metadata
     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
     
      ## Commit message ##
    -    repo: add path.git-prefix path key
    +    repo: add path.git-prefix
     
    -    Scripts and command-line prompt integrations frequently need to know their
    -    relative depth inside a repository working tree layout. Currently, this
    -    is retrieved using `git rev-parse --show-prefix`.
    +    Scripts sometimes need the path from the repository's working tree root
    +    to the current working directory. While this information can be derived
    +    through existing Git commands, `git repo info` does not currently expose
    +    it as a scriptable key.
     
    -    Introduce the `path.git-prefix` key to `git repo info`. This mirrors the
    -    prefix location tracking framework as a standalone key, returning the
    -    exact relative path offset complete with a trailing slash, or an empty
    -    string if run directly at the repository working tree root.
    +    Introduce the `path.git-prefix` key to `git repo info`. The key returns
    +    the path from the working tree root to the current working directory,
    +    returning the empty string when invoked from the working tree root.
     
         Mentored-by: Justin Tobler <jltobler@gmail.com>
         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
    @@ Documentation/git-repo.adoc: values that they return:
      	the current working directory.
      
     +`path.git-prefix`::
    -+	The relative path from the top-level directory of the working tree to
    -+	the current working directory (including a trailing slash). Outputs an
    -+	empty string if executed at the root of the working tree.
    ++	The path from the root of the working tree to the current working
    ++	directory. Returns the empty string when the current working directory
    ++	is the root of the working tree.
     +
      `path.gitdir.absolute`::
      	The canonical absolute path to the Git repository directory (the `.git` directory).
    @@ builtin/repo.c: static int get_path_commondir_relative(struct repository *repo,
      	return 0;
      }
      
    -+static int get_path_git_prefix(struct repository *repo UNUSED, struct strbuf *buf)
    ++static int get_path_git_prefix(struct repository *repo, struct strbuf *buf)
     +{
     +	/*
    -+	 * startup_info->prefix is NULL if we are at the working tree root.
    -+	 * We add an empty string to ensure the buffer is cleanly initialized.
    ++	 * repo->prefix is NULL when the current working directory is
    ++	 * the worktree root.
     +	 */
    -+	strbuf_addstr(buf, startup_info->prefix ? startup_info->prefix : "");
    ++	strbuf_addstr(buf, repo->prefix ? repo->prefix : "");
     +	return 0;
     +}
     +
    @@ t/t1900-repo-info.sh: test_repo_info_path 'commondir with only GIT_DIR' 'commond
      	'.git' \
      	'GIT_DIR="../.git" && export GIT_DIR'
      
    -+test_expect_success 'path.git-prefix at root and in a subdirectory' '
    ++test_expect_success 'path.git-prefix at repository root' '
     +	test_when_finished "rm -rf repo" &&
     +	git init repo &&
     +	(
     +		cd repo &&
    ++		echo "path.git-prefix=" >expect &&
    ++		git repo info path.git-prefix >actual &&
    ++		test_cmp expect actual
    ++	)
    ++'
     +
    -+		echo "path.git-prefix=" >expect.root &&
    -+		git repo info path.git-prefix >actual.root &&
    -+		test_cmp expect.root actual.root &&
    -+
    -+		mkdir -p sub/dir &&
    -+		cd sub/dir &&
    -+
    -+		echo "path.git-prefix=sub/dir/" >expect.sub &&
    -+		git repo info path.git-prefix >actual.sub &&
    -+		test_cmp expect.sub actual.sub
    ++test_expect_success 'path.git-prefix in subdirectory' '
    ++	test_when_finished "rm -rf repo" &&
    ++	git init repo &&
    ++	mkdir -p repo/sub/dir &&
    ++	(
    ++		cd repo/sub/dir &&
    ++		echo "path.git-prefix=sub/dir/" >expect &&
    ++		git repo info path.git-prefix >actual &&
    ++		test_cmp expect actual
     +	)
     +'
     +
3:  736c8e0576 ! 7:  f28ee4e169 repo: add path.objects with absolute and relative suffix formatting
    @@ Metadata
     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
     
      ## Commit message ##
    -    repo: add path.objects with absolute and relative suffix formatting
    +    repo: remove unused setup.h include
     
    -    Tools and deployment hooks frequently query the location of the object
    -    database directory. Currently, this relies on legacy parsing methods or
    -    manually inspecting `git rev-parse --git-path objects`.
    +    The repository prefix is now stored in `struct repository`, so
    +    builtin/repo.c no longer uses any declarations from setup.h.
     
    -    Introduce `path.objects.absolute` and `path.objects.relative` keys to
    -    `git repo info`. This allows tools to discover the object database
    -    location safely while natively adhering to active `GIT_OBJECT_DIRECTORY`
    -    environment variable overrides.
    +    Remove the now-unused include.
     
         Mentored-by: Justin Tobler <jltobler@gmail.com>
         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
         Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
     
    - ## Documentation/git-repo.adoc ##
    -@@ Documentation/git-repo.adoc: values that they return:
    - `path.gitdir.relative`::
    - 	The path to the Git repository directory relative to the current working directory.
    - 
    -+`path.objects.absolute`::
    -+	The canonical absolute path to the repository's object database directory.
    -+	Respects the `GIT_OBJECT_DIRECTORY` environment override.
    -+
    -+`path.objects.relative`::
    -+	The path to the repository's object database directory relative to the
    -+	current working directory. Respects the `GIT_OBJECT_DIRECTORY`
    -+	environment override.
    -+
    - `path.superproject-working-tree.absolute`::
    - 	The canonical absolute path to the working tree root of the superproject
    - 	if the current repository is an initialized submodule. Outputs an empty
    -
      ## builtin/repo.c ##
    -@@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
    - 	return 0;
    - }
    - 
    -+static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)
    -+{
    -+	const char *obj_dir = repo_get_object_directory(repo);
    -+
    -+	if (!obj_dir)
    -+		return error(_("unable to get object directory"));
    -+
    -+	format_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);
    -+	return 0;
    -+}
    -+
    -+static int get_path_objects_relative(struct repository *repo, struct strbuf *buf)
    -+{
    -+	const char *obj_dir = repo_get_object_directory(repo);
    -+
    -+	if (!obj_dir)
    -+		return error(_("unable to get object directory"));
    -+
    -+	format_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);
    -+	return 0;
    -+}
    -+
    - static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
    - {
    - 	struct strbuf superproject = STRBUF_INIT;
    -@@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {
    - 	{ "path.commondir.relative", get_path_commondir_relative },
    - 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
    - 	{ "path.gitdir.relative", get_path_gitdir_relative },
    -+	{ "path.objects.absolute", get_path_objects_absolute },
    -+	{ "path.objects.relative", get_path_objects_relative },
    - 	{ "path.superproject-working-tree.absolute", get_path_superproject_absolute },
    - 	{ "path.superproject-working-tree.relative", get_path_superproject_relative },
    - 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
    -
    - ## t/t1900-repo-info.sh ##
    -@@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
    - 	'.git' \
    - 	'GIT_DIR="../.git" && export GIT_DIR'
    - 
    -+test_repo_info_path 'objects standard' 'objects' '.git/objects'
    -+
    -+test_repo_info_path 'objects with GIT_OBJECT_DIRECTORY override' 'objects' \
    -+	'custom-objects' \
    -+	'GIT_OBJECT_DIRECTORY="$ROOT/custom-objects" && export GIT_OBJECT_DIRECTORY &&
    -+	 mkdir -p "$ROOT/custom-objects"'
    -+
    - test_expect_success 'path.superproject-working-tree absolute and relative' '
    - 	test_when_finished "rm -rf sub super" &&
    - 	git init sub &&
    +@@
    + #include "ref-filter.h"
    + #include "refs.h"
    + #include "revision.h"
    +-#include "setup.h"
    + #include "strbuf.h"
    + #include "string-list.h"
    + #include "shallow.h"
-- 
2.55.GIT
K JayatheerthAug 6, 2026, 10:15 UTC in reply to K Jayatheerth on lore

[GSoC PATCH v4 1/7] repo: add path.toplevel with absolute and relative suffix formatting

Scripts frequently need to find the root directory of a repository's working tree. Currently, this requires using `git rev-parse --show-toplevel` or inferring it from other repository information.

Introduce `path.toplevel.absolute` and `path.toplevel.relative` keys to `git repo info`. This allows scripts to retrieve the top-level working tree path in a predictable, strictly formatted manner without relying on `rev-parse`.

If requested in a bare repository where no working tree exists, the command returns an empty string.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 10 ++++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 35 +++++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+)
Show changes to 3 files +69 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index ed7d80c690..e34abe5fea 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,16 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.toplevel.absolute`::
+	The canonical absolute path to the top-level directory of the
+	repository's working tree. Outputs an empty string if the repository
+	is bare.
+
+`path.toplevel.relative`::
+	The path to the top-level directory of the repository's working
+	tree relative to the current working directory. Outputs an empty
+	string if the repository is bare.
+
 `references.format`::
 	The reference storage format. The valid values are:
 +
diff --git a/builtin/repo.c b/builtin/repo.c
index 84e012f83f..c31e9cfa70 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -121,6 +121,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *work_tree = repo_get_work_tree(repo);
+
+	if (!work_tree)
+		return 0;
+
+	format_path(buf, work_tree, "", PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *work_tree = repo_get_work_tree(repo);
+
+	if (!work_tree)
+		return 0;
+
+	format_path(buf, work_tree, repo->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_references_format(struct repository *repo, struct strbuf *buf)
 {
 	strbuf_addstr(buf,
@@ -137,6 +159,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.toplevel.absolute", get_path_toplevel_absolute },
+	{ "path.toplevel.relative", get_path_toplevel_relative },
 	{ "references.format", get_references_format },
 };
 
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index c85d390f43..9417d1ab65 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,4 +213,39 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.toplevel absolute and relative' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		mkdir -p repo/sub &&
+		cd repo/sub &&
+
+		ROOT="$(test-tool path-utils real_path ..)" &&
+
+		echo "path.toplevel.absolute=$ROOT" >expect.abs &&
+		git repo info path.toplevel.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.toplevel.relative=../" >expect.rel &&
+		git repo info path.toplevel.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
+test_expect_success 'path.toplevel absolute and relative in a bare repository' '
+	test_when_finished "rm -rf bare.git" &&
+	git init --bare bare.git &&
+	(
+		cd bare.git &&
+
+		echo "path.toplevel.absolute=" >expect.abs &&
+		git repo info path.toplevel.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.toplevel.relative=" >expect.rel &&
+		git repo info path.toplevel.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
 test_done
-- 
2.55.GIT
K JayatheerthAug 6, 2026, 10:15 UTC in reply to K Jayatheerth on lore

[GSoC PATCH v4 2/7] repo: add path.superproject-root with absolute and relative suffixes

Scripts working in multi-repository setups often need to identify the top-level working tree of a superproject from within a submodule. Currently, this is only exposed via `git rev-parse --show-superproject-working-tree`.

Introduce `path.superproject-root.absolute` and `path.superproject-root.relative` keys to `git repo info`. This exposes the core submodule context via a scriptable config-like key using standard format rules.

If requested when not inside a submodule, the command returns an empty string.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 10 ++++++++++
 builtin/repo.c              | 31 +++++++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 39 +++++++++++++++++++++++++++++++++++++
 3 files changed, 80 insertions(+)
Show changes to 3 files +80 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index e34abe5fea..e524a07f53 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,16 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.superproject-root.absolute`::
+	The canonical absolute path to the working tree root of the superproject
+	if the current repository is an initialized submodule. Outputs an empty
+	string if not in a submodule.
+
+`path.superproject-root.relative`::
+	The path to the working tree root of the superproject relative to the
+	current working directory if the current repository is an initialized
+	submodule. Outputs an empty string if not in a submodule.
+
 `path.toplevel.absolute`::
 	The canonical absolute path to the top-level directory of the
 	repository's working tree. Outputs an empty string if the repository
diff --git a/builtin/repo.c b/builtin/repo.c
index c31e9cfa70..47c4fce293 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -18,6 +18,7 @@
 #include "strbuf.h"
 #include "string-list.h"
 #include "shallow.h"
+#include "submodule.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "utf8.h"
@@ -121,6 +122,34 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
+{
+	struct strbuf superproject = STRBUF_INIT;
+
+	if (!get_superproject_working_tree(&superproject)) {
+		strbuf_release(&superproject);
+		return 0;
+	}
+
+	format_path(buf, superproject.buf, "", PATH_FORMAT_CANONICAL);
+	strbuf_release(&superproject);
+	return 0;
+}
+
+static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf superproject = STRBUF_INIT;
+
+	if (!get_superproject_working_tree(&superproject)) {
+		strbuf_release(&superproject);
+		return 0;
+	}
+
+	format_path(buf, superproject.buf, repo->prefix, PATH_FORMAT_RELATIVE);
+	strbuf_release(&superproject);
+	return 0;
+}
+
 static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *work_tree = repo_get_work_tree(repo);
@@ -159,6 +188,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.superproject-root.absolute", get_path_superproject_absolute },
+	{ "path.superproject-root.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
 	{ "path.toplevel.relative", get_path_toplevel_relative },
 	{ "references.format", get_references_format },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 9417d1ab65..eec576a1d9 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,45 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.superproject-root absolute and relative' '
+	test_when_finished "rm -rf sub super" &&
+	git init sub &&
+	test_commit -C sub initial &&
+	git init super &&
+	(
+		cd super &&
+		git -c protocol.file.allow=always submodule add "../sub" sub &&
+		git commit -m "add submodule" &&
+
+		cd sub &&
+		ROOT="$(test-tool path-utils real_path ..)" &&
+
+		echo "path.superproject-root.absolute=$ROOT" >expect.abs &&
+		git repo info path.superproject-root.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.superproject-root.relative=../" >expect.rel &&
+		git repo info path.superproject-root.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
+test_expect_success 'path.superproject-root returns empty when not in a submodule' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		echo "path.superproject-root.absolute=" >expect.abs &&
+		git repo info path.superproject-root.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.superproject-root.relative=" >expect.rel &&
+		git repo info path.superproject-root.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
 test_expect_success 'path.toplevel absolute and relative' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
-- 
2.55.GIT
K JayatheerthAug 6, 2026, 10:15 UTC in reply to K Jayatheerth on lore

[GSoC PATCH v4 3/7] repo: add path.hooks with absolute and relative suffixes

Hooks are an integral part of a repository's configuration and are commonly used by tooling to automate repository-specific workflows. Currently, scripts typically retrieve the hooks directory by invoking `git rev-parse --git-path hooks`.

Introduce `path.hooks.absolute` and `path.hooks.relative` keys to `git repo info`. This exposes the hooks directory as a scriptable config-like key using standard format rules, allowing scripts to retrieve it through the same interface as other repository path information.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  9 +++++++++
 builtin/repo.c              | 22 ++++++++++++++++++++++
 t/t1900-repo-info.sh        | 28 ++++++++++++++++++++++++++--
 3 files changed, 57 insertions(+), 2 deletions(-)
Show changes to 3 files +57 −2

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index e524a07f53..20836cf8f6 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,15 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.hooks.absolute`::
+	The canonical absolute path to the repository's hooks directory.
+	Respects the `core.hooksPath` configuration. If `core.hooksPath` is
+	set to `/dev/null`, that value is returned unchanged.
+
+`path.hooks.relative`::
+	The path to the repository's hooks directory relative to the current
+	working directory. Respects the `core.hooksPath` configuration.
+
 `path.superproject-root.absolute`::
 	The canonical absolute path to the working tree root of the superproject
 	if the current repository is an initialized submodule. Outputs an empty
diff --git a/builtin/repo.c b/builtin/repo.c
index 47c4fce293..d7c451a771 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,26 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf hooks_path = STRBUF_INIT;
+
+	repo_git_path_replace(repo, &hooks_path, "hooks");
+	format_path(buf, hooks_path.buf, "", PATH_FORMAT_CANONICAL);
+	strbuf_release(&hooks_path);
+	return 0;
+}
+
+static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf hooks_path = STRBUF_INIT;
+
+	repo_git_path_replace(repo, &hooks_path, "hooks");
+	format_path(buf, hooks_path.buf, repo->prefix, PATH_FORMAT_RELATIVE);
+	strbuf_release(&hooks_path);
+	return 0;
+}
+
 static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
 {
 	struct strbuf superproject = STRBUF_INIT;
@@ -188,6 +208,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.hooks.absolute", get_path_hooks_absolute },
+	{ "path.hooks.relative", get_path_hooks_relative },
 	{ "path.superproject-root.absolute", get_path_superproject_absolute },
 	{ "path.superproject-root.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index eec576a1d9..1da5db4942 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -174,7 +174,11 @@ test_repo_info_path () {
 			cd repo/sub &&
 			ROOT="$(test-tool path-utils real_path ..)" && export ROOT &&
 			eval "$init_command" &&
-			echo "path.$field_name.absolute=$ROOT/$expected_dir" >expect &&
+			case "$expected_dir" in
+			/*) EXPECT_ABS="$expected_dir" ;;
+			*) EXPECT_ABS="$ROOT/$expected_dir" ;;
+			esac &&
+			echo "path.$field_name.absolute=$EXPECT_ABS" >expect &&
 			git repo info "path.$field_name.absolute" >actual &&
 			test_cmp expect actual
 		)
@@ -188,7 +192,11 @@ test_repo_info_path () {
 			cd repo/sub &&
 			ROOT="$(test-tool path-utils real_path ..)" && export ROOT &&
 			eval "$init_command" &&
-			echo "path.$field_name.relative=../$expected_dir" >expect &&
+			case "$expected_dir" in
+			/*) EXPECT_REL="$(test-tool path-utils relative_path "$expected_dir" "$PWD")" ;;
+			*) EXPECT_REL="../$expected_dir" ;;
+			esac &&
+			echo "path.$field_name.relative=$EXPECT_REL" >expect &&
 			git repo info "path.$field_name.relative" >actual &&
 			test_cmp expect actual
 		)
@@ -213,6 +221,22 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'
+
+test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
+	'custom-hooks' \
+	'git config core.hooksPath "$ROOT/custom-hooks" && mkdir -p "$ROOT/custom-hooks"'
+
+# /dev/null is not a real, canonicalizable filesystem path on Windows,
+# so path resolution for core.hooksPath=/dev/null cannot be expected to
+# produce a literal "/dev/null" the way it does on POSIX systems.
+if ! test_have_prereq MINGW
+then
+	test_repo_info_path 'hooks with core.hooksPath=/dev/null' 'hooks' \
+		'/dev/null' \
+		'git config core.hooksPath /dev/null'
+fi
+
 test_expect_success 'path.superproject-root absolute and relative' '
 	test_when_finished "rm -rf sub super" &&
 	git init sub &&
-- 
2.55.GIT
K JayatheerthAug 6, 2026, 10:15 UTC in reply to K Jayatheerth on lore

[GSoC PATCH v4 4/7] repo: add path.index with absolute and relative suffixes

The repository index is a fundamental component used by Git and related tooling to track the working tree state. Scripts that interact with the index currently retrieve its location by invoking `git rev-parse --git-path index`.

Introduce `path.index.absolute` and `path.index.relative` keys to `git repo info`. This exposes the index file location as a scriptable config-like key using standard format rules, allowing scripts to retrieve it through the same interface as other repository path information.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 12 ++++++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++
 3 files changed, 59 insertions(+)
Show changes to 3 files +59 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 20836cf8f6..08ef47750c 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -128,6 +128,18 @@ values that they return:
 	The path to the repository's hooks directory relative to the current
 	working directory. Respects the `core.hooksPath` configuration.
 
+`path.index.absolute`::
+	The canonical absolute path to the repository's current index file.
+	Respects the `GIT_INDEX_FILE` environment override. Returns the
+	configured index path even if the repository is bare or the file does
+	not exist.
+
+`path.index.relative`::
+	The path to the repository's current index file relative to the current
+	working directory. Respects the `GIT_INDEX_FILE` environment override.
+	Returns the configured index path even if the repository is bare or the
+	file does not exist.
+
 `path.superproject-root.absolute`::
 	The canonical absolute path to the working tree root of the superproject
 	if the current repository is an initialized submodule. Outputs an empty
diff --git a/builtin/repo.c b/builtin/repo.c
index d7c451a771..2a15327094 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -142,6 +142,28 @@ static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *index_file = repo_get_index_file(repo);
+
+	if (!index_file)
+		return error(_("unable to get index file"));
+
+	format_path(buf, index_file, "", PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *index_file = repo_get_index_file(repo);
+
+	if (!index_file)
+		return error(_("unable to get index file"));
+
+	format_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
 {
 	struct strbuf superproject = STRBUF_INIT;
@@ -210,6 +232,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.gitdir.relative", get_path_gitdir_relative },
 	{ "path.hooks.absolute", get_path_hooks_absolute },
 	{ "path.hooks.relative", get_path_hooks_relative },
+	{ "path.index.absolute", get_path_index_absolute },
+	{ "path.index.relative", get_path_index_relative },
 	{ "path.superproject-root.absolute", get_path_superproject_absolute },
 	{ "path.superproject-root.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 1da5db4942..431a4842d4 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -237,6 +237,29 @@ then
 		'git config core.hooksPath /dev/null'
 fi
 
+test_repo_info_path 'index standard' 'index' '.git/index'
+
+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \
+	'custom-index-file' \
+	'GIT_INDEX_FILE="$ROOT/custom-index-file" && export GIT_INDEX_FILE'
+
+test_expect_success 'path.index in a bare repository returns default index location' '
+	test_when_finished "rm -rf bare.git" &&
+	git init --bare bare.git &&
+	(
+		cd bare.git &&
+		ROOT="$(test-tool path-utils real_path .)" &&
+
+		echo "path.index.absolute=$ROOT/index" >expect.abs &&
+		git repo info path.index.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.index.relative=index" >expect.rel &&
+		git repo info path.index.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
 test_expect_success 'path.superproject-root absolute and relative' '
 	test_when_finished "rm -rf sub super" &&
 	git init sub &&
-- 
2.55.GIT
K JayatheerthAug 6, 2026, 10:15 UTC in reply to K Jayatheerth on lore

[GSoC PATCH v4 5/7] repo: add path.grafts with absolute and relative suffixes

The repository grafts file specifies alternate parent relationships for commits and may be used by repository tooling that needs to inspect or manage grafts. Scripts currently retrieve its location by invoking `git rev-parse --git-path info/grafts`.

Introduce `path.grafts.absolute` and `path.grafts.relative` keys to `git repo info`. This exposes the grafts file location as a scriptable config-like key using standard format rules, allowing scripts to retrieve it through the same interface as other repository path information.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 11 +++++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        |  6 ++++++
 3 files changed, 41 insertions(+)
Show changes to 3 files +41 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 08ef47750c..868ab0ed9f 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,17 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.grafts.absolute`::
+	The canonical absolute path to the repository's graft file.
+	Respects the `GIT_GRAFT_FILE` environment override. The path is
+	returned regardless of whether the file currently exists on disk.
+
+`path.grafts.relative`::
+	The path to the repository's graft file relative to the current
+	working directory. Respects the `GIT_GRAFT_FILE` environment
+	override. The path is returned regardless of whether the file
+	currently exists on disk.
+
 `path.hooks.absolute`::
 	The canonical absolute path to the repository's hooks directory.
 	Respects the `core.hooksPath` configuration. If `core.hooksPath` is
diff --git a/builtin/repo.c b/builtin/repo.c
index 2a15327094..779240109d 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_grafts_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *graft_file = repo_get_graft_file(repo);
+
+	if (!graft_file)
+		return error(_("unable to get graft file"));
+
+	format_path(buf, graft_file, "", PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_grafts_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *graft_file = repo_get_graft_file(repo);
+
+	if (!graft_file)
+		return error(_("unable to get graft file"));
+
+	format_path(buf, graft_file, repo->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)
 {
 	struct strbuf hooks_path = STRBUF_INIT;
@@ -230,6 +252,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.grafts.absolute", get_path_grafts_absolute },
+	{ "path.grafts.relative", get_path_grafts_relative },
 	{ "path.hooks.absolute", get_path_hooks_absolute },
 	{ "path.hooks.relative", get_path_hooks_relative },
 	{ "path.index.absolute", get_path_index_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 431a4842d4..adc4a92487 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -221,6 +221,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'grafts standard' 'grafts' '.git/info/grafts'
+
+test_repo_info_path 'grafts with GIT_GRAFT_FILE override' 'grafts' \
+	'custom-graft-file' \
+	'GIT_GRAFT_FILE="$ROOT/custom-graft-file" && export GIT_GRAFT_FILE'
+
 test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'
 
 test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
-- 
2.55.GIT
K JayatheerthAug 6, 2026, 10:15 UTC in reply to K Jayatheerth on lore

[GSoC PATCH v4 6/7] repo: add path.git-prefix

Scripts sometimes need the path from the repository's working tree root to the current working directory. While this information can be derived through existing Git commands, `git repo info` does not currently expose it as a scriptable key.

Introduce the `path.git-prefix` key to `git repo info`. The key returns the path from the working tree root to the current working directory, returning the empty string when invoked from the working tree root.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  5 +++++
 builtin/repo.c              | 11 +++++++++++
 t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++
 3 files changed, 39 insertions(+)
Show changes to 3 files +39 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 868ab0ed9f..fb5aceae8f 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -113,6 +113,11 @@ values that they return:
 	The path to the Git repository's common directory relative to
 	the current working directory.
 
+`path.git-prefix`::
+	The path from the root of the working tree to the current working
+	directory. Returns the empty string when the current working directory
+	is the root of the working tree.
+
 `path.gitdir.absolute`::
 	The canonical absolute path to the Git repository directory (the `.git` directory).
 
diff --git a/builtin/repo.c b/builtin/repo.c
index 779240109d..c0f99b6869 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -100,6 +100,16 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b
 	return 0;
 }
 
+static int get_path_git_prefix(struct repository *repo, struct strbuf *buf)
+{
+	/*
+	 * repo->prefix is NULL when the current working directory is
+	 * the worktree root.
+	 */
+	strbuf_addstr(buf, repo->prefix ? repo->prefix : "");
+	return 0;
+}
+
 static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *git_dir = repo_get_git_dir(repo);
@@ -250,6 +260,7 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "object.format", get_object_format },
 	{ "path.commondir.absolute", get_path_commondir_absolute },
 	{ "path.commondir.relative", get_path_commondir_relative },
+	{ "path.git-prefix", get_path_git_prefix },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
 	{ "path.grafts.absolute", get_path_grafts_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index adc4a92487..b689445b7a 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -215,6 +215,29 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.git-prefix at repository root' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		echo "path.git-prefix=" >expect &&
+		git repo info path.git-prefix >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'path.git-prefix in subdirectory' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	mkdir -p repo/sub/dir &&
+	(
+		cd repo/sub/dir &&
+		echo "path.git-prefix=sub/dir/" >expect &&
+		git repo info path.git-prefix >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_repo_info_path 'gitdir standard' 'gitdir' '.git'
 
 test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
-- 
2.55.GIT
K JayatheerthAug 6, 2026, 10:15 UTC in reply to K Jayatheerth on lore

[GSoC PATCH v4 7/7] repo: remove unused setup.h include

The repository prefix is now stored in `struct repository`, so builtin/repo.c no longer uses any declarations from setup.h.

Remove the now-unused include.
Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 builtin/repo.c | 1 -
 1 file changed, 1 deletion(-)
Show changes to builtin/repo.c +0 −1
diff --git a/builtin/repo.c b/builtin/repo.c
index c0f99b6869..8e9f6296b8 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -14,7 +14,6 @@
 #include "ref-filter.h"
 #include "refs.h"
 #include "revision.h"
-#include "setup.h"
 #include "strbuf.h"
 #include "string-list.h"
 #include "shallow.h"
-- 
2.55.GIT
Lucas Seiki OshiroAug 15, 2026, 23:06 UTC in reply to K Jayatheerth on lore

Re: [GSoC Patch 1/7] repo: add path.toplevel with absolute and relative suffix formatting

Show 19 quoted lines
> 
> +test_expect_success 'path.toplevel absolute and relative' '
> + test_when_finished "rm -rf repo" &&
> + git init repo &&
> + (
> + mkdir -p repo/sub &&
> + cd repo/sub &&
> +
> + ROOT="$(test-tool path-utils real_path ..)" &&
> +
> + echo "path.toplevel.absolute=$ROOT" >expect.abs &&
> + git repo info path.toplevel.absolute >actual.abs &&
> + test_cmp expect.abs actual.abs &&
> +
> + echo "path.toplevel.relative=../" >expect.rel &&
> + git repo info path.toplevel.relative >actual.rel &&
> + test_cmp expect.rel actual.rel
> + )
> +'
Question: why not use `test_repo_info_path` here?
Lucas Seiki OshiroAug 16, 2026, 00:53 UTC in reply to K Jayatheerth on lore

Re: [GSoC PATCH v4 6/7] repo: add path.git-prefix

> Scripts sometimes need the path from the repository's working tree root
> to the current working directory. While this information can be derived
> through existing Git commands, `git repo info` does not currently expose
> it as a scriptable key.

Even though I understand that this is a relevant info, it seems to me that it's a little bit out of place here... This is a "current directory information", not a "repository information".

> 
K JayatheerthAug 18, 2026, 16:09 UTC in reply to Lucas Seiki Oshiro on lore

Re: [GSoC Patch 1/7] repo: add path.toplevel with absolute and relative suffix formatting

Hey Lucas,

On Sun, Aug 16, 2026 at 4:37 AM Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> wrote:

Show 23 quoted lines
>
>
> >
> > +test_expect_success 'path.toplevel absolute and relative' '
> > + test_when_finished "rm -rf repo" &&
> > + git init repo &&
> > + (
> > + mkdir -p repo/sub &&
> > + cd repo/sub &&
> > +
> > + ROOT="$(test-tool path-utils real_path ..)" &&
> > +
> > + echo "path.toplevel.absolute=$ROOT" >expect.abs &&
> > + git repo info path.toplevel.absolute >actual.abs &&
> > + test_cmp expect.abs actual.abs &&
> > +
> > + echo "path.toplevel.relative=../" >expect.rel &&
> > + git repo info path.toplevel.relative >actual.rel &&
> > + test_cmp expect.rel actual.rel
> > + )
> > +'
>
> Question: why not use `test_repo_info_path` here?

test_repo_info_path doesn't quite fit path.toplevel as the helper assumes an $expected_dir suffix and constructs $ROOT/$expected_dir and ../$expected_dir, while toplevel is $ROOT and ../, respectively, therefore I made this case different.

K JayatheerthAug 18, 2026, 16:33 UTC in reply to Lucas Seiki Oshiro on lore

Re: [GSoC PATCH v4 6/7] repo: add path.git-prefix

On Sun, Aug 16, 2026 at 6:24 AM Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> wrote:

Show 11 quoted lines
>
>
> > Scripts sometimes need the path from the repository's working tree root
> > to the current working directory. While this information can be derived
> > through existing Git commands, `git repo info` does not currently expose
> > it as a scriptable key.
>
> Even though I understand that this is a relevant info, it seems
> to me that it's a little bit out of place here... This is
> a "current directory information", not a "repository information".
> >

I agree that path.git-prefix is technically current-directory information rather than repository information.

I included it because it is useful path information for scripts operating within a repository, and it is already exposed by git rev-parse --show-prefix. I thought it would fit the purpose of this series, but I can drop it if you think it is better kept separate.

Junio C HamanoAug 18, 2026, 17:09 UTC in reply to K Jayatheerth on lore

Re: [GSoC PATCH v4 6/7] repo: add path.git-prefix

K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
Show 15 quoted lines
> On Sun, Aug 16, 2026 at 6:24 AM Lucas Seiki Oshiro
> <lucasseikioshiro@gmail.com> wrote:
>>
>>
>> > Scripts sometimes need the path from the repository's working tree root
>> > to the current working directory. While this information can be derived
>> > through existing Git commands, `git repo info` does not currently expose
>> > it as a scriptable key.
>>
>> Even though I understand that this is a relevant info, it seems
>> to me that it's a little bit out of place here... This is
>> a "current directory information", not a "repository information".
>
> I agree that path.git-prefix is technically current-directory information
> rather than repository information.

The same thing can be said about "git rev-parse --show-cdup"; you can complain that it is about the current working directory, but in reality it is about how to get to the root of the working tree you are currently working in, which means that it is very much about repository information, only given as a relative path. Nobody would complain that you have 'path.commondir.relative' when you already offer its '.absolute' counterpart, saying that '.relative' is about your current working directory and not about the repository, right?

The same story holds for 'path.git-prefix', I would think. View it, and "git rev-parse --show-prefix", as the inverse version of a 'path.root-of-the-working-tree.relative' (aka 'path.cdup') rather than as being about the "current directory".

Oh, and if you are offering git-prefix, you should offer cdup as well.

Thanks.
K JayatheerthAug 21, 2026, 14:08 UTC in reply to Junio C Hamano on lore

Re: [GSoC PATCH v4 6/7] repo: add path.git-prefix

On Tue, Aug 18, 2026 at 10:39 PM Junio C Hamano <gitster@pobox.com> wrote:
Show 28 quoted lines
>
> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
>
> > On Sun, Aug 16, 2026 at 6:24 AM Lucas Seiki Oshiro
> > <lucasseikioshiro@gmail.com> wrote:
> >>
> >>
> >> > Scripts sometimes need the path from the repository's working tree root
> >> > to the current working directory. While this information can be derived
> >> > through existing Git commands, `git repo info` does not currently expose
> >> > it as a scriptable key.
> >>
> >> Even though I understand that this is a relevant info, it seems
> >> to me that it's a little bit out of place here... This is
> >> a "current directory information", not a "repository information".
> >
> > I agree that path.git-prefix is technically current-directory information
> > rather than repository information.
>
> The same thing can be said about "git rev-parse --show-cdup"; you
> can complain that it is about the current working directory, but in
> reality it is about how to get to the root of the working tree you
> are currently working in, which means that it is very much about
> repository information, only given as a relative path.  Nobody would
> complain that you have 'path.commondir.relative' when you already
> offer its '.absolute' counterpart, saying that '.relative' is about
> your current working directory and not about the repository, right?
>

Very true, I agree with you. I believe cdup and git-prefix complement each other well, so it makes sense to expose both.

Show 8 quoted lines
> The same story holds for 'path.git-prefix', I would think.  View it,
> and "git rev-parse --show-prefix", as the inverse version of a
> 'path.root-of-the-working-tree.relative' (aka 'path.cdup') rather
> than as being about the "current directory".
>
> Oh, and if you are offering git-prefix, you should offer cdup as
> well.
>
Sure, I will add cdup in the next version and send it.
Thank you!
- K Jayatheerth
K JayatheerthAug 25, 2026, 17:58 UTC in reply to K Jayatheerth on lore

[GSoC Patch v5 0/7] add more path keys to git repo info

Series adds keys to git repo info.
Keys output paths of repository components:
* path.toplevel: repository tree.
* path.superproject-root: superproject tree from submodules.
* path.hooks: repository hooks.
* path.index: repository index.
* path.grafts: repository grafts.
* path.git-prefix: prefix offset.
* path.cdup: relative path to top level from subdirectory.

Keys support suffixes for format. Commits contain documentation and tests.

changes since v4:
* Add path.cdup in git repo.
* Drop unused header cleanup patch as it will be sent in the next set of cleanup only
  patches.
K Jayatheerth (7):
  repo: add path.toplevel with absolute and relative suffix formatting
  repo: add path.superproject-root with absolute and relative suffixes
  repo: add path.hooks with absolute and relative suffixes
  repo: add path.index with absolute and relative suffixes
  repo: add path.grafts with absolute and relative suffixes
  repo: add path.git-prefix
  repo: add path.cdup
 Documentation/git-repo.adoc |  62 +++++++++++++
 builtin/repo.c              | 151 ++++++++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 177 +++++++++++++++++++++++++++++++++++-
 3 files changed, 388 insertions(+), 2 deletions(-)
Range-diff against v4:
1:  db54cf2177 = 1:  3880485020 repo: add path.toplevel with absolute and relative suffix formatting
2:  5f5ab2b844 = 2:  1b7b0d286c repo: add path.superproject-root with absolute and relative suffixes
3:  a8c3526958 = 3:  517f621eb0 repo: add path.hooks with absolute and relative suffixes
4:  a419389911 = 4:  37d28712de repo: add path.index with absolute and relative suffixes
5:  a5a28f2e92 = 5:  d88340f5a6 repo: add path.grafts with absolute and relative suffixes
6:  39775f8d2f = 6:  38b19d8bfd repo: add path.git-prefix
7:  74dbca6f17 ! 7:  d6ce98e25e repo: remove unused setup.h include
    @@ Metadata
     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
     
      ## Commit message ##
    -    repo: remove unused setup.h include
    +    repo: add path.cdup
     
    -    The repository prefix is now stored in `struct repository`, so
    -    builtin/repo.c no longer uses any declarations from setup.h.
    +    Scripts sometimes need the relative path from the current working
    +    directory to the repository's working tree root (cdup). While this
    +    information can be retrieved through `git rev-parse --show-cdup`,
    +    `git repo info` does not currently expose it as a scriptable key.
     
    -    Remove the now-unused include.
    +    Introduce the `path.cdup` key to `git repo info`. The key returns the
    +    path from the current working directory to the root of the working tree,
    +    returning the empty string when invoked from the working tree root.
     
         Mentored-by: Justin Tobler <jltobler@gmail.com>
         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
         Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
     
    + ## Documentation/git-repo.adoc ##
    +@@ Documentation/git-repo.adoc: values that they return:
    + `object.format`::
    + 	The object format (hash algorithm) used in the repository.
    + 
    ++`path.cdup`::
    ++	The path to the root of the working tree relative to the current
    ++	working directory. Returns the empty string when the current
    ++	working directory is the root of the working tree.
    ++
    + `path.commondir.absolute`::
    + 	The canonical absolute path to the Git repository's common
    + 	directory (the shared `.git` directory containing objects,
    +
      ## builtin/repo.c ##
    -@@
    - #include "ref-filter.h"
    - #include "refs.h"
    - #include "revision.h"
    --#include "setup.h"
    - #include "strbuf.h"
    - #include "string-list.h"
    - #include "shallow.h"
    +@@ builtin/repo.c: static int get_object_format(struct repository *repo, struct strbuf *buf)
    + 	return 0;
    + }
    + 
    ++static int get_path_cdup(struct repository *repo, struct strbuf *buf)
    ++{
    ++	const char *pfx = repo->prefix;
    ++
    ++	while (pfx) {
    ++		pfx = strchr(pfx, '/');
    ++		if (pfx) {
    ++			pfx++;
    ++			strbuf_addstr(buf, "../");
    ++		}
    ++	}
    ++	return 0;
    ++}
    ++
    + static int get_path_commondir_absolute(struct repository *repo, struct strbuf *buf)
    + {
    + 	const char *common_dir = repo_get_common_dir(repo);
    +@@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {
    + 	{ "layout.bare", get_layout_bare },
    + 	{ "layout.shallow", get_layout_shallow },
    + 	{ "object.format", get_object_format },
    ++	{ "path.cdup", get_path_cdup },
    + 	{ "path.commondir.absolute", get_path_commondir_absolute },
    + 	{ "path.commondir.relative", get_path_commondir_relative },
    + 	{ "path.git-prefix", get_path_git_prefix },
    +
    + ## t/t1900-repo-info.sh ##
    +@@ t/t1900-repo-info.sh: test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
    + 	'.git' \
    + 	'GIT_DIR="../.git" && export GIT_DIR'
    + 
    ++test_expect_success 'path.cdup at repository root' '
    ++	test_when_finished "rm -rf repo" &&
    ++	git init repo &&
    ++	(
    ++		cd repo &&
    ++		echo "path.cdup=" >expect &&
    ++		git repo info path.cdup >actual &&
    ++		test_cmp expect actual
    ++	)
    ++'
    ++
    ++test_expect_success 'path.cdup in subdirectory' '
    ++	test_when_finished "rm -rf repo" &&
    ++	git init repo &&
    ++	mkdir -p repo/sub/dir &&
    ++	(
    ++		cd repo/sub/dir &&
    ++		echo "path.cdup=../../" >expect &&
    ++		git repo info path.cdup >actual &&
    ++		test_cmp expect actual
    ++	)
    ++'
    ++
    + test_expect_success 'path.git-prefix at repository root' '
    + 	test_when_finished "rm -rf repo" &&
    + 	git init repo &&
-- 
2.55.GIT
K JayatheerthAug 25, 2026, 17:58 UTC in reply to K Jayatheerth on lore

[GSoC Patch v5 1/7] repo: add path.toplevel with absolute and relative suffix formatting

Scripts frequently need to find the root directory of a repository's working tree. Currently, this requires using `git rev-parse --show-toplevel` or inferring it from other repository information.

Introduce `path.toplevel.absolute` and `path.toplevel.relative` keys to `git repo info`. This allows scripts to retrieve the top-level working tree path in a predictable, strictly formatted manner without relying on `rev-parse`.

If requested in a bare repository where no working tree exists, the command returns an empty string.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 10 ++++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 35 +++++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+)
Show changes to 3 files +69 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index ed7d80c690..e34abe5fea 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,16 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.toplevel.absolute`::
+	The canonical absolute path to the top-level directory of the
+	repository's working tree. Outputs an empty string if the repository
+	is bare.
+
+`path.toplevel.relative`::
+	The path to the top-level directory of the repository's working
+	tree relative to the current working directory. Outputs an empty
+	string if the repository is bare.
+
 `references.format`::
 	The reference storage format. The valid values are:
 +
diff --git a/builtin/repo.c b/builtin/repo.c
index 84e012f83f..c31e9cfa70 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -121,6 +121,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *work_tree = repo_get_work_tree(repo);
+
+	if (!work_tree)
+		return 0;
+
+	format_path(buf, work_tree, "", PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *work_tree = repo_get_work_tree(repo);
+
+	if (!work_tree)
+		return 0;
+
+	format_path(buf, work_tree, repo->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_references_format(struct repository *repo, struct strbuf *buf)
 {
 	strbuf_addstr(buf,
@@ -137,6 +159,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.toplevel.absolute", get_path_toplevel_absolute },
+	{ "path.toplevel.relative", get_path_toplevel_relative },
 	{ "references.format", get_references_format },
 };
 
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index c85d390f43..9417d1ab65 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,4 +213,39 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.toplevel absolute and relative' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		mkdir -p repo/sub &&
+		cd repo/sub &&
+
+		ROOT="$(test-tool path-utils real_path ..)" &&
+
+		echo "path.toplevel.absolute=$ROOT" >expect.abs &&
+		git repo info path.toplevel.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.toplevel.relative=../" >expect.rel &&
+		git repo info path.toplevel.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
+test_expect_success 'path.toplevel absolute and relative in a bare repository' '
+	test_when_finished "rm -rf bare.git" &&
+	git init --bare bare.git &&
+	(
+		cd bare.git &&
+
+		echo "path.toplevel.absolute=" >expect.abs &&
+		git repo info path.toplevel.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.toplevel.relative=" >expect.rel &&
+		git repo info path.toplevel.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
 test_done
-- 
2.55.GIT
K JayatheerthAug 25, 2026, 17:58 UTC in reply to K Jayatheerth on lore

[GSoC Patch v5 2/7] repo: add path.superproject-root with absolute and relative suffixes

Scripts working in multi-repository setups often need to identify the top-level working tree of a superproject from within a submodule. Currently, this is only exposed via `git rev-parse --show-superproject-working-tree`.

Introduce `path.superproject-root.absolute` and `path.superproject-root.relative` keys to `git repo info`. This exposes the core submodule context via a scriptable config-like key using standard format rules.

If requested when not inside a submodule, the command returns an empty string.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 10 ++++++++++
 builtin/repo.c              | 31 +++++++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 39 +++++++++++++++++++++++++++++++++++++
 3 files changed, 80 insertions(+)
Show changes to 3 files +80 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index e34abe5fea..e524a07f53 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,16 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.superproject-root.absolute`::
+	The canonical absolute path to the working tree root of the superproject
+	if the current repository is an initialized submodule. Outputs an empty
+	string if not in a submodule.
+
+`path.superproject-root.relative`::
+	The path to the working tree root of the superproject relative to the
+	current working directory if the current repository is an initialized
+	submodule. Outputs an empty string if not in a submodule.
+
 `path.toplevel.absolute`::
 	The canonical absolute path to the top-level directory of the
 	repository's working tree. Outputs an empty string if the repository
diff --git a/builtin/repo.c b/builtin/repo.c
index c31e9cfa70..47c4fce293 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -18,6 +18,7 @@
 #include "strbuf.h"
 #include "string-list.h"
 #include "shallow.h"
+#include "submodule.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "utf8.h"
@@ -121,6 +122,34 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
+{
+	struct strbuf superproject = STRBUF_INIT;
+
+	if (!get_superproject_working_tree(&superproject)) {
+		strbuf_release(&superproject);
+		return 0;
+	}
+
+	format_path(buf, superproject.buf, "", PATH_FORMAT_CANONICAL);
+	strbuf_release(&superproject);
+	return 0;
+}
+
+static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf superproject = STRBUF_INIT;
+
+	if (!get_superproject_working_tree(&superproject)) {
+		strbuf_release(&superproject);
+		return 0;
+	}
+
+	format_path(buf, superproject.buf, repo->prefix, PATH_FORMAT_RELATIVE);
+	strbuf_release(&superproject);
+	return 0;
+}
+
 static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *work_tree = repo_get_work_tree(repo);
@@ -159,6 +188,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.superproject-root.absolute", get_path_superproject_absolute },
+	{ "path.superproject-root.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
 	{ "path.toplevel.relative", get_path_toplevel_relative },
 	{ "references.format", get_references_format },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 9417d1ab65..eec576a1d9 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,45 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.superproject-root absolute and relative' '
+	test_when_finished "rm -rf sub super" &&
+	git init sub &&
+	test_commit -C sub initial &&
+	git init super &&
+	(
+		cd super &&
+		git -c protocol.file.allow=always submodule add "../sub" sub &&
+		git commit -m "add submodule" &&
+
+		cd sub &&
+		ROOT="$(test-tool path-utils real_path ..)" &&
+
+		echo "path.superproject-root.absolute=$ROOT" >expect.abs &&
+		git repo info path.superproject-root.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.superproject-root.relative=../" >expect.rel &&
+		git repo info path.superproject-root.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
+test_expect_success 'path.superproject-root returns empty when not in a submodule' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		echo "path.superproject-root.absolute=" >expect.abs &&
+		git repo info path.superproject-root.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.superproject-root.relative=" >expect.rel &&
+		git repo info path.superproject-root.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
 test_expect_success 'path.toplevel absolute and relative' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
-- 
2.55.GIT
K JayatheerthAug 25, 2026, 17:58 UTC in reply to K Jayatheerth on lore

[GSoC Patch v5 3/7] repo: add path.hooks with absolute and relative suffixes

Hooks are an integral part of a repository's configuration and are commonly used by tooling to automate repository-specific workflows. Currently, scripts typically retrieve the hooks directory by invoking `git rev-parse --git-path hooks`.

Introduce `path.hooks.absolute` and `path.hooks.relative` keys to `git repo info`. This exposes the hooks directory as a scriptable config-like key using standard format rules, allowing scripts to retrieve it through the same interface as other repository path information.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  9 +++++++++
 builtin/repo.c              | 22 ++++++++++++++++++++++
 t/t1900-repo-info.sh        | 28 ++++++++++++++++++++++++++--
 3 files changed, 57 insertions(+), 2 deletions(-)
Show changes to 3 files +57 −2

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index e524a07f53..20836cf8f6 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,15 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.hooks.absolute`::
+	The canonical absolute path to the repository's hooks directory.
+	Respects the `core.hooksPath` configuration. If `core.hooksPath` is
+	set to `/dev/null`, that value is returned unchanged.
+
+`path.hooks.relative`::
+	The path to the repository's hooks directory relative to the current
+	working directory. Respects the `core.hooksPath` configuration.
+
 `path.superproject-root.absolute`::
 	The canonical absolute path to the working tree root of the superproject
 	if the current repository is an initialized submodule. Outputs an empty
diff --git a/builtin/repo.c b/builtin/repo.c
index 47c4fce293..d7c451a771 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,26 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf hooks_path = STRBUF_INIT;
+
+	repo_git_path_replace(repo, &hooks_path, "hooks");
+	format_path(buf, hooks_path.buf, "", PATH_FORMAT_CANONICAL);
+	strbuf_release(&hooks_path);
+	return 0;
+}
+
+static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf hooks_path = STRBUF_INIT;
+
+	repo_git_path_replace(repo, &hooks_path, "hooks");
+	format_path(buf, hooks_path.buf, repo->prefix, PATH_FORMAT_RELATIVE);
+	strbuf_release(&hooks_path);
+	return 0;
+}
+
 static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
 {
 	struct strbuf superproject = STRBUF_INIT;
@@ -188,6 +208,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.hooks.absolute", get_path_hooks_absolute },
+	{ "path.hooks.relative", get_path_hooks_relative },
 	{ "path.superproject-root.absolute", get_path_superproject_absolute },
 	{ "path.superproject-root.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index eec576a1d9..1da5db4942 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -174,7 +174,11 @@ test_repo_info_path () {
 			cd repo/sub &&
 			ROOT="$(test-tool path-utils real_path ..)" && export ROOT &&
 			eval "$init_command" &&
-			echo "path.$field_name.absolute=$ROOT/$expected_dir" >expect &&
+			case "$expected_dir" in
+			/*) EXPECT_ABS="$expected_dir" ;;
+			*) EXPECT_ABS="$ROOT/$expected_dir" ;;
+			esac &&
+			echo "path.$field_name.absolute=$EXPECT_ABS" >expect &&
 			git repo info "path.$field_name.absolute" >actual &&
 			test_cmp expect actual
 		)
@@ -188,7 +192,11 @@ test_repo_info_path () {
 			cd repo/sub &&
 			ROOT="$(test-tool path-utils real_path ..)" && export ROOT &&
 			eval "$init_command" &&
-			echo "path.$field_name.relative=../$expected_dir" >expect &&
+			case "$expected_dir" in
+			/*) EXPECT_REL="$(test-tool path-utils relative_path "$expected_dir" "$PWD")" ;;
+			*) EXPECT_REL="../$expected_dir" ;;
+			esac &&
+			echo "path.$field_name.relative=$EXPECT_REL" >expect &&
 			git repo info "path.$field_name.relative" >actual &&
 			test_cmp expect actual
 		)
@@ -213,6 +221,22 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'
+
+test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
+	'custom-hooks' \
+	'git config core.hooksPath "$ROOT/custom-hooks" && mkdir -p "$ROOT/custom-hooks"'
+
+# /dev/null is not a real, canonicalizable filesystem path on Windows,
+# so path resolution for core.hooksPath=/dev/null cannot be expected to
+# produce a literal "/dev/null" the way it does on POSIX systems.
+if ! test_have_prereq MINGW
+then
+	test_repo_info_path 'hooks with core.hooksPath=/dev/null' 'hooks' \
+		'/dev/null' \
+		'git config core.hooksPath /dev/null'
+fi
+
 test_expect_success 'path.superproject-root absolute and relative' '
 	test_when_finished "rm -rf sub super" &&
 	git init sub &&
-- 
2.55.GIT
K JayatheerthAug 25, 2026, 17:58 UTC in reply to K Jayatheerth on lore

[GSoC Patch v5 4/7] repo: add path.index with absolute and relative suffixes

The repository index is a fundamental component used by Git and related tooling to track the working tree state. Scripts that interact with the index currently retrieve its location by invoking `git rev-parse --git-path index`.

Introduce `path.index.absolute` and `path.index.relative` keys to `git repo info`. This exposes the index file location as a scriptable config-like key using standard format rules, allowing scripts to retrieve it through the same interface as other repository path information.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 12 ++++++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++
 3 files changed, 59 insertions(+)
Show changes to 3 files +59 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 20836cf8f6..08ef47750c 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -128,6 +128,18 @@ values that they return:
 	The path to the repository's hooks directory relative to the current
 	working directory. Respects the `core.hooksPath` configuration.
 
+`path.index.absolute`::
+	The canonical absolute path to the repository's current index file.
+	Respects the `GIT_INDEX_FILE` environment override. Returns the
+	configured index path even if the repository is bare or the file does
+	not exist.
+
+`path.index.relative`::
+	The path to the repository's current index file relative to the current
+	working directory. Respects the `GIT_INDEX_FILE` environment override.
+	Returns the configured index path even if the repository is bare or the
+	file does not exist.
+
 `path.superproject-root.absolute`::
 	The canonical absolute path to the working tree root of the superproject
 	if the current repository is an initialized submodule. Outputs an empty
diff --git a/builtin/repo.c b/builtin/repo.c
index d7c451a771..2a15327094 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -142,6 +142,28 @@ static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *index_file = repo_get_index_file(repo);
+
+	if (!index_file)
+		return error(_("unable to get index file"));
+
+	format_path(buf, index_file, "", PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *index_file = repo_get_index_file(repo);
+
+	if (!index_file)
+		return error(_("unable to get index file"));
+
+	format_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
 {
 	struct strbuf superproject = STRBUF_INIT;
@@ -210,6 +232,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.gitdir.relative", get_path_gitdir_relative },
 	{ "path.hooks.absolute", get_path_hooks_absolute },
 	{ "path.hooks.relative", get_path_hooks_relative },
+	{ "path.index.absolute", get_path_index_absolute },
+	{ "path.index.relative", get_path_index_relative },
 	{ "path.superproject-root.absolute", get_path_superproject_absolute },
 	{ "path.superproject-root.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 1da5db4942..431a4842d4 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -237,6 +237,29 @@ then
 		'git config core.hooksPath /dev/null'
 fi
 
+test_repo_info_path 'index standard' 'index' '.git/index'
+
+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \
+	'custom-index-file' \
+	'GIT_INDEX_FILE="$ROOT/custom-index-file" && export GIT_INDEX_FILE'
+
+test_expect_success 'path.index in a bare repository returns default index location' '
+	test_when_finished "rm -rf bare.git" &&
+	git init --bare bare.git &&
+	(
+		cd bare.git &&
+		ROOT="$(test-tool path-utils real_path .)" &&
+
+		echo "path.index.absolute=$ROOT/index" >expect.abs &&
+		git repo info path.index.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.index.relative=index" >expect.rel &&
+		git repo info path.index.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
 test_expect_success 'path.superproject-root absolute and relative' '
 	test_when_finished "rm -rf sub super" &&
 	git init sub &&
-- 
2.55.GIT
K JayatheerthAug 25, 2026, 17:58 UTC in reply to K Jayatheerth on lore

[GSoC Patch v5 5/7] repo: add path.grafts with absolute and relative suffixes

The repository grafts file specifies alternate parent relationships for commits and may be used by repository tooling that needs to inspect or manage grafts. Scripts currently retrieve its location by invoking `git rev-parse --git-path info/grafts`.

Introduce `path.grafts.absolute` and `path.grafts.relative` keys to `git repo info`. This exposes the grafts file location as a scriptable config-like key using standard format rules, allowing scripts to retrieve it through the same interface as other repository path information.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 11 +++++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        |  6 ++++++
 3 files changed, 41 insertions(+)
Show changes to 3 files +41 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 08ef47750c..868ab0ed9f 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,17 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.grafts.absolute`::
+	The canonical absolute path to the repository's graft file.
+	Respects the `GIT_GRAFT_FILE` environment override. The path is
+	returned regardless of whether the file currently exists on disk.
+
+`path.grafts.relative`::
+	The path to the repository's graft file relative to the current
+	working directory. Respects the `GIT_GRAFT_FILE` environment
+	override. The path is returned regardless of whether the file
+	currently exists on disk.
+
 `path.hooks.absolute`::
 	The canonical absolute path to the repository's hooks directory.
 	Respects the `core.hooksPath` configuration. If `core.hooksPath` is
diff --git a/builtin/repo.c b/builtin/repo.c
index 2a15327094..779240109d 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_grafts_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *graft_file = repo_get_graft_file(repo);
+
+	if (!graft_file)
+		return error(_("unable to get graft file"));
+
+	format_path(buf, graft_file, "", PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_grafts_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *graft_file = repo_get_graft_file(repo);
+
+	if (!graft_file)
+		return error(_("unable to get graft file"));
+
+	format_path(buf, graft_file, repo->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)
 {
 	struct strbuf hooks_path = STRBUF_INIT;
@@ -230,6 +252,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.grafts.absolute", get_path_grafts_absolute },
+	{ "path.grafts.relative", get_path_grafts_relative },
 	{ "path.hooks.absolute", get_path_hooks_absolute },
 	{ "path.hooks.relative", get_path_hooks_relative },
 	{ "path.index.absolute", get_path_index_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 431a4842d4..adc4a92487 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -221,6 +221,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'grafts standard' 'grafts' '.git/info/grafts'
+
+test_repo_info_path 'grafts with GIT_GRAFT_FILE override' 'grafts' \
+	'custom-graft-file' \
+	'GIT_GRAFT_FILE="$ROOT/custom-graft-file" && export GIT_GRAFT_FILE'
+
 test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'
 
 test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
-- 
2.55.GIT
K JayatheerthAug 25, 2026, 17:58 UTC in reply to K Jayatheerth on lore

[GSoC Patch v5 6/7] repo: add path.git-prefix

Scripts sometimes need the path from the repository's working tree root to the current working directory. While this information can be derived through existing Git commands, `git repo info` does not currently expose it as a scriptable key.

Introduce the `path.git-prefix` key to `git repo info`. The key returns the path from the working tree root to the current working directory, returning the empty string when invoked from the working tree root.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  5 +++++
 builtin/repo.c              | 11 +++++++++++
 t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++
 3 files changed, 39 insertions(+)
Show changes to 3 files +39 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 868ab0ed9f..fb5aceae8f 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -113,6 +113,11 @@ values that they return:
 	The path to the Git repository's common directory relative to
 	the current working directory.
 
+`path.git-prefix`::
+	The path from the root of the working tree to the current working
+	directory. Returns the empty string when the current working directory
+	is the root of the working tree.
+
 `path.gitdir.absolute`::
 	The canonical absolute path to the Git repository directory (the `.git` directory).
 
diff --git a/builtin/repo.c b/builtin/repo.c
index 779240109d..c0f99b6869 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -100,6 +100,16 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b
 	return 0;
 }
 
+static int get_path_git_prefix(struct repository *repo, struct strbuf *buf)
+{
+	/*
+	 * repo->prefix is NULL when the current working directory is
+	 * the worktree root.
+	 */
+	strbuf_addstr(buf, repo->prefix ? repo->prefix : "");
+	return 0;
+}
+
 static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *git_dir = repo_get_git_dir(repo);
@@ -250,6 +260,7 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "object.format", get_object_format },
 	{ "path.commondir.absolute", get_path_commondir_absolute },
 	{ "path.commondir.relative", get_path_commondir_relative },
+	{ "path.git-prefix", get_path_git_prefix },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
 	{ "path.grafts.absolute", get_path_grafts_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index adc4a92487..b689445b7a 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -215,6 +215,29 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.git-prefix at repository root' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		echo "path.git-prefix=" >expect &&
+		git repo info path.git-prefix >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'path.git-prefix in subdirectory' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	mkdir -p repo/sub/dir &&
+	(
+		cd repo/sub/dir &&
+		echo "path.git-prefix=sub/dir/" >expect &&
+		git repo info path.git-prefix >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_repo_info_path 'gitdir standard' 'gitdir' '.git'
 
 test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
-- 
2.55.GIT
K JayatheerthAug 25, 2026, 17:58 UTC in reply to K Jayatheerth on lore

[GSoC Patch v5 7/7] repo: add path.cdup

Scripts sometimes need the relative path from the current working directory to the repository's working tree root (cdup). While this information can be retrieved through `git rev-parse --show-cdup`, `git repo info` does not currently expose it as a scriptable key.

Introduce the `path.cdup` key to `git repo info`. The key returns the path from the current working directory to the root of the working tree, returning the empty string when invoked from the working tree root.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  5 +++++
 builtin/repo.c              | 15 +++++++++++++++
 t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++
 3 files changed, 43 insertions(+)
Show changes to 3 files +43 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index fb5aceae8f..0a30c0d6ab 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -104,6 +104,11 @@ values that they return:
 `object.format`::
 	The object format (hash algorithm) used in the repository.
 
+`path.cdup`::
+	The path to the root of the working tree relative to the current
+	working directory. Returns the empty string when the current
+	working directory is the root of the working tree.
+
 `path.commondir.absolute`::
 	The canonical absolute path to the Git repository's common
 	directory (the shared `.git` directory containing objects,
diff --git a/builtin/repo.c b/builtin/repo.c
index c0f99b6869..2658e8be5c 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -78,6 +78,20 @@ static int get_object_format(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_cdup(struct repository *repo, struct strbuf *buf)
+{
+	const char *pfx = repo->prefix;
+
+	while (pfx) {
+		pfx = strchr(pfx, '/');
+		if (pfx) {
+			pfx++;
+			strbuf_addstr(buf, "../");
+		}
+	}
+	return 0;
+}
+
 static int get_path_commondir_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *common_dir = repo_get_common_dir(repo);
@@ -258,6 +272,7 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "layout.bare", get_layout_bare },
 	{ "layout.shallow", get_layout_shallow },
 	{ "object.format", get_object_format },
+	{ "path.cdup", get_path_cdup },
 	{ "path.commondir.absolute", get_path_commondir_absolute },
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.git-prefix", get_path_git_prefix },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index b689445b7a..aa8e59510f 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -215,6 +215,29 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.cdup at repository root' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		echo "path.cdup=" >expect &&
+		git repo info path.cdup >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'path.cdup in subdirectory' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	mkdir -p repo/sub/dir &&
+	(
+		cd repo/sub/dir &&
+		echo "path.cdup=../../" >expect &&
+		git repo info path.cdup >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_expect_success 'path.git-prefix at repository root' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
-- 
2.55.GIT
Junio C HamanoAug 25, 2026, 19:31 UTC in reply to K Jayatheerth on lore

Re: [GSoC Patch v5 7/7] repo: add path.cdup

K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
Show 13 quoted lines
> +static int get_path_cdup(struct repository *repo, struct strbuf *buf)
> +{
> +	const char *pfx = repo->prefix;
> +
> +	while (pfx) {
> +		pfx = strchr(pfx, '/');
> +		if (pfx) {
> +			pfx++;
> +			strbuf_addstr(buf, "../");
> +		}
> +	}
> +	return 0;
> +}

Can we have a small common helper library (e.g., repo-info.c at the top level) shared between this code and builtin/rev-parse.c so that we have only a single implementation of 'cdup'?

The same comment applies to all the other features that duplicate implementations from rev-parse and elsewhere in the repo-info file.

SZEDER GáborAug 26, 2026, 07:23 UTC in reply to K Jayatheerth on lore

Re: [GSoC Patch v5 0/7] add more path keys to git repo info

Junio,
On Tue, Aug 25, 2026 at 11:28:11PM +0530, K Jayatheerth wrote:
Show 7 quoted lines
>     +@@ builtin/repo.c: static int get_object_format(struct repository *repo, struct strbuf *buf)
>     + 	return 0;
>     + }
>     + 
>     ++static int get_path_cdup(struct repository *repo, struct strbuf *buf)
>     ++{
>     ++	const char *pfx = repo->prefix;

This patch series depends on "repo->prefix", but it's queued on top of 41365c2a9b (The 4th batch for Git 2.56, 2026-07-16), where this field doesn't exist yet, resulting in not buildable commits.

The series introducing "repo->prefix" was merged in 2688b28699 (Merge branch 'ps/setup-split-discovery-and-setup', 2026-07-19).

Junio C HamanoAug 26, 2026, 14:18 UTC in reply to SZEDER Gábor on lore

Re: [GSoC Patch v5 0/7] add more path keys to git repo info

SZEDER Gábor <szeder.dev@gmail.com> writes:
> This patch series depends on "repo->prefix", but it's queued on top of
> 41365c2a9b (The 4th batch for Git 2.56, 2026-07-16), where this field
> doesn't exist yet, resulting in not buildable commits.
Thanks.  Will queue on top of more recent master.
K JayatheerthSep 4, 2026, 14:37 UTC in reply to Junio C Hamano on lore

Re: [GSoC Patch v5 7/7] repo: add path.cdup

Hey Junio,
On Wed, Aug 26, 2026 at 1:01 AM Junio C Hamano <gitster@pobox.com> wrote:
Show 23 quoted lines
>
> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
>
> > +static int get_path_cdup(struct repository *repo, struct strbuf *buf)
> > +{
> > +     const char *pfx = repo->prefix;
> > +
> > +     while (pfx) {
> > +             pfx = strchr(pfx, '/');
> > +             if (pfx) {
> > +                     pfx++;
> > +                     strbuf_addstr(buf, "../");
> > +             }
> > +     }
> > +     return 0;
> > +}
>
> Can we have a small common helper library (e.g., repo-info.c at the
> top level) shared between this code and builtin/rev-parse.c so
> that we have only a single implementation of 'cdup'?
>
> The same comment applies to all the other features that duplicate
> implementations from rev-parse and elsewhere in the repo-info file.
Apologies for the late response, I had a hectic exam week : )

I started working on this, but I wanted to check how to structure the commits cleanly.

Would it be okay to queue this `path.*` series as-is first, and then address the deduplication in a follow-up refactoring series right after (provided there are no other concerns)?

Grouping the new `git repo info` keys with the cleanup of `git rev-parse` in a single series would mix feature additions with legacy refactoring. Additionally, since earlier path keys are already merged, moving the shared library to a follow-up series keeps both topics clear.

In that follow-up set, I can introduce the library alongside porting the first command to use it, and then port the remaining overlapping commands one by one in subsequent commits so each patch remains a self-contained, bisectable change.

Please let me know if this works for you!
Regards,
- K Jayatheerth
Junio C HamanoSep 4, 2026, 22:09 UTC in reply to K Jayatheerth on lore

Re: [GSoC Patch v5 2/7] repo: add path.superproject-root with absolute and relative suffixes

K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
Show 22 quoted lines
> +static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
> +{
> +	struct strbuf superproject = STRBUF_INIT;
> +
> +	if (!get_superproject_working_tree(&superproject)) {
> +		strbuf_release(&superproject);
> +		return 0;
> +	}
> +
> +	format_path(buf, superproject.buf, "", PATH_FORMAT_CANONICAL);
> +	strbuf_release(&superproject);
> +	return 0;
> +}
> +
> +static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)
> +{
> +	struct strbuf superproject = STRBUF_INIT;
> +
> +	if (!get_superproject_working_tree(&superproject)) {
> +		strbuf_release(&superproject);
> +		return 0;
> +	}

Here get_superproject_working_tree() does not care what repository we are working on. Shouldn't it be updated to take "repo" as a parameter?

Since it begins like this:
    int get_superproject_working_tree(struct strbuf *buf)
    {
            struct child_process cp = CHILD_PROCESS_INIT;
            struct strbuf sb = STRBUF_INIT;
            struct strbuf one_up = STRBUF_INIT;
            char *cwd = xgetcwd();
            int ret = 0;
I suspect that it based its decision on where you happen to be.

It means that when I have a checkout of "git", with a submodule "sha1collisiondetection" at its top level already populated, in, say, /var/tmp/x/ directory, the following happens.

    $ cd /var/tmp/x/git
    $ git repo info path.gitdir.absolute
    path.gitdir.absolute=/var/tmp/x/git/.git
    $ git -C sha1collisiondetection repo info path.gitdir.absolute
    path.gitdir.absolute=/var/tmp/x/git/.git/modules/sha1collisiondetection
    $ D=/var/tmp/x/git/.git/modules/sha1collisiondetection
    $ git --git-dir="$D" repo info path.superproject-root.absolute
    path.superproject-root.absolute=
    $ git -C sha1collisiondetection repo info path.superproject-root.absolute
    path.superproject-root.absolute=/var/tmp/x/git
The last two ought to match, but only the latter works correctly.

Before this series starts reporting path.superproject-root, get_superproject_working_tree() needs to be corrected to work on the repository in question (instead of relying on where the process happens to be), no?

K JayatheerthSep 5, 2026, 10:01 UTC in reply to Junio C Hamano on lore

Re: [GSoC Patch v5 2/7] repo: add path.superproject-root with absolute and relative suffixes

On Sat, Sep 5, 2026 at 3:39 AM Junio C Hamano <gitster@pobox.com> wrote:
Show 64 quoted lines
>
> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
>
> > +static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
> > +{
> > +     struct strbuf superproject = STRBUF_INIT;
> > +
> > +     if (!get_superproject_working_tree(&superproject)) {
> > +             strbuf_release(&superproject);
> > +             return 0;
> > +     }
> > +
> > +     format_path(buf, superproject.buf, "", PATH_FORMAT_CANONICAL);
> > +     strbuf_release(&superproject);
> > +     return 0;
> > +}
> > +
> > +static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)
> > +{
> > +     struct strbuf superproject = STRBUF_INIT;
> > +
> > +     if (!get_superproject_working_tree(&superproject)) {
> > +             strbuf_release(&superproject);
> > +             return 0;
> > +     }
>
> Here get_superproject_working_tree() does not care what repository
> we are working on.  Shouldn't it be updated to take "repo" as a
> parameter?
>
> Since it begins like this:
>
>     int get_superproject_working_tree(struct strbuf *buf)
>     {
>             struct child_process cp = CHILD_PROCESS_INIT;
>             struct strbuf sb = STRBUF_INIT;
>             struct strbuf one_up = STRBUF_INIT;
>             char *cwd = xgetcwd();
>             int ret = 0;
>
> I suspect that it based its decision on where you happen to be.
>
> It means that when I have a checkout of "git", with a submodule
> "sha1collisiondetection" at its top level already populated, in,
> say, /var/tmp/x/ directory, the following happens.
>
>     $ cd /var/tmp/x/git
>     $ git repo info path.gitdir.absolute
>     path.gitdir.absolute=/var/tmp/x/git/.git
>     $ git -C sha1collisiondetection repo info path.gitdir.absolute
>     path.gitdir.absolute=/var/tmp/x/git/.git/modules/sha1collisiondetection
>
>     $ D=/var/tmp/x/git/.git/modules/sha1collisiondetection
>     $ git --git-dir="$D" repo info path.superproject-root.absolute
>     path.superproject-root.absolute=
>     $ git -C sha1collisiondetection repo info path.superproject-root.absolute
>     path.superproject-root.absolute=/var/tmp/x/git
>
> The last two ought to match, but only the latter works correctly.
>
> Before this series starts reporting path.superproject-root,
> get_superproject_working_tree() needs to be corrected to work on the
> repository in question (instead of relying on where the process
> happens to be), no?

Yes, precisely. I didn't think of it that way.

`get_superproject_working_tree()` currently relies on xgetcwd() rather than inspecting the target repository, which causes explicit repository contexts like --git-dir to break.

I will update `get_superproject_working_tree()` to take repo and update its callers accordingly, along with adding test coverage for --git-dir in v6.

Regards,
- K Jayatheerth
K JayatheerthSep 11, 2026, 14:45 UTC in reply to K Jayatheerth on lore

[GSoC Patch v6 0/7] add more path keys to git repo info

Series adds keys to git repo info.
Keys output paths of repository components:
* path.toplevel: repository tree.
* path.superproject-root: superproject tree from submodules.
* path.hooks: repository hooks.
* path.index: repository index.
* path.grafts: repository grafts.
* path.git-prefix: prefix offset.
* path.cdup: relative path to top level from subdirectory.

Keys support suffixes for format. Commits contain documentation and tests.

changes since v5:
* Refactored get_superproject_working_tree(struct repository *r, ...): Updated
  function signature and callers (builtin/rev-parse.c, builtin/repo.c)
  to accept repo, eliminating xgetcwd().

Note on path.cdup duplication: As discussed in v5, path.cdup currently duplicates the prefix-counting logic from rev-parse to keep this feature arc self-contained. A follow-up refactoring series will be sent immediately after this to introduce a shared repo-info helper library, deduplicating this and other overlapping path logic across repo and rev-parse.

K Jayatheerth (7):
  repo: add path.toplevel with absolute and relative suffix formatting
  repo: add path.superproject-root with absolute and relative suffixes
  repo: add path.hooks with absolute and relative suffixes
  repo: add path.index with absolute and relative suffixes
  repo: add path.grafts with absolute and relative suffixes
  repo: add path.git-prefix
  repo: add path.cdup
 Documentation/git-repo.adoc |  62 ++++++++++++
 builtin/repo.c              | 151 ++++++++++++++++++++++++++++
 builtin/rev-parse.c         |   2 +-
 submodule.c                 |  43 ++++----
 submodule.h                 |   2 +-
 t/t1900-repo-info.sh        | 195 +++++++++++++++++++++++++++++++++++-
 6 files changed, 430 insertions(+), 25 deletions(-)
Range-diff against v5:
1:  4ce9dcb24e = 1:  3880485020 repo: add path.toplevel with absolute and relative suffix formatting
2:  c431008d40 ! 2:  e75e17b44e repo: add path.superproject-root with absolute and relative suffixes
    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str
      	return 0;
      }
      
    -+static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
    ++static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)
     +{
     +	struct strbuf superproject = STRBUF_INIT;
     +
    -+	if (!get_superproject_working_tree(&superproject)) {
    ++	if (!get_superproject_working_tree(repo, &superproject)) {
     +		strbuf_release(&superproject);
     +		return 0;
     +	}
    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str
     +{
     +	struct strbuf superproject = STRBUF_INIT;
     +
    -+	if (!get_superproject_working_tree(&superproject)) {
    ++	if (!get_superproject_working_tree(repo, &superproject)) {
     +		strbuf_release(&superproject);
     +		return 0;
     +	}
    @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {
      	{ "path.toplevel.relative", get_path_toplevel_relative },
      	{ "references.format", get_references_format },
     
    + ## builtin/rev-parse.c ##
    +@@ builtin/rev-parse.c: int cmd_rev_parse(int argc,
    + 			}
    + 			if (!strcmp(arg, "--show-superproject-working-tree")) {
    + 				struct strbuf superproject = STRBUF_INIT;
    +-				if (get_superproject_working_tree(&superproject))
    ++				if (get_superproject_working_tree(the_repository, &superproject))
    + 					print_path(superproject.buf, prefix, format, DEFAULT_UNMODIFIED);
    + 				strbuf_release(&superproject);
    + 				continue;
    +
    + ## submodule.c ##
    +@@ submodule.c: void absorb_git_dir_into_superproject(const char *path,
    + 	absorb_git_dir_into_superproject_recurse(path, super_prefix);
    + }
    + 
    +-int get_superproject_working_tree(struct strbuf *buf)
    ++int get_superproject_working_tree(struct repository *r, struct strbuf *buf)
    + {
    + 	struct child_process cp = CHILD_PROCESS_INIT;
    + 	struct strbuf sb = STRBUF_INIT;
    + 	struct strbuf one_up = STRBUF_INIT;
    +-	char *cwd = xgetcwd();
    ++	struct strbuf target_wt = STRBUF_INIT;
    ++	const char *worktree;
    + 	int ret = 0;
    + 	const char *subpath;
    + 	int code;
    + 	ssize_t len;
    + 
    +-	if (!is_inside_work_tree(the_repository))
    +-		/*
    +-		 * FIXME:
    +-		 * We might have a superproject, but it is harder
    +-		 * to determine.
    +-		 */
    ++	worktree = repo_get_work_tree(r);
    ++	if (!worktree)
    ++		goto out;
    ++
    ++	if (!strbuf_realpath(&target_wt, worktree, 0))
    + 		goto out;
    + 
    +-	if (!strbuf_realpath(&one_up, "../", 0))
    ++	strbuf_addf(&one_up, "%s/..", target_wt.buf);
    ++	if (!strbuf_realpath(&one_up, one_up.buf, 0))
    + 		goto out;
    + 
    +-	subpath = relative_path(cwd, one_up.buf, &sb);
    ++	subpath = relative_path(target_wt.buf, one_up.buf, &sb);
    + 
    + 	prepare_submodule_repo_env(&cp.env);
    + 	strvec_pop(&cp.env);
    + 
    +-	strvec_pushl(&cp.args, "--literal-pathspecs", "-C", "..",
    ++	strvec_pushl(&cp.args, "--literal-pathspecs", "-C", one_up.buf,
    + 		     "ls-files", "-z", "--stage", "--full-name", "--",
    + 		     subpath, NULL);
    + 	strbuf_reset(&sb);
    +@@ submodule.c: int get_superproject_working_tree(struct strbuf *buf)
    + 	cp.git_cmd = 1;
    + 
    + 	if (start_command(&cp))
    +-		die(_("could not start ls-files in .."));
    ++		die(_("could not start ls-files in %s"), one_up.buf);
    + 
    + 	len = strbuf_read(&sb, cp.out, PATH_MAX);
    + 	close(cp.out);
    + 
    + 	if (starts_with(sb.buf, "160000")) {
    + 		int super_sub_len;
    +-		int cwd_len = strlen(cwd);
    ++		int wt_len = target_wt.len;
    + 		char *super_sub, *super_wt;
    + 
    + 		/*
    +@@ submodule.c: int get_superproject_working_tree(struct strbuf *buf)
    + 		super_sub = strchr(sb.buf, '\t') + 1;
    + 		super_sub_len = strlen(super_sub);
    + 
    +-		if (super_sub_len > cwd_len ||
    +-		    strcmp(&cwd[cwd_len - super_sub_len], super_sub))
    +-			BUG("returned path string doesn't match cwd?");
    ++		if (super_sub_len > wt_len ||
    ++		    strcmp(&target_wt.buf[wt_len - super_sub_len], super_sub))
    ++			BUG("returned path string doesn't match worktree?");
    + 
    +-		super_wt = xstrdup(cwd);
    +-		super_wt[cwd_len - super_sub_len] = '\0';
    ++		super_wt = xstrdup(target_wt.buf);
    ++		super_wt[wt_len - super_sub_len] = '\0';
    + 
    + 		strbuf_realpath(buf, super_wt, 1);
    + 		ret = 1;
    +@@ submodule.c: int get_superproject_working_tree(struct strbuf *buf)
    + 	code = finish_command(&cp);
    + 
    + 	if (code == 128)
    +-		/* '../' is not a git repository */
    ++		/* parent directory is not a git repository */
    + 		ret = 0;
    + 	else if (code == 0 && len == 0)
    +-		/* There is an unrelated git repository at '../' */
    ++		/* There is an unrelated git repository at parent directory */
    + 		ret = 0;
    + 	else if (code)
    + 		die(_("ls-tree returned unexpected return code %d"), code);
    +@@ submodule.c: int get_superproject_working_tree(struct strbuf *buf)
    + out:
    + 	strbuf_release(&sb);
    + 	strbuf_release(&one_up);
    +-	free(cwd);
    ++	strbuf_release(&target_wt);
    + 	return ret;
    + }
    + 
    +
    + ## submodule.h ##
    +@@ submodule.h: void absorb_git_dir_into_superproject(const char *path,
    +  * project is a submodule of. If this repository is not a submodule of
    +  * another repository, return 0.
    +  */
    +-int get_superproject_working_tree(struct strbuf *buf);
    ++int get_superproject_working_tree(struct repository *r, struct strbuf *buf);
    + 
    + #endif
    +
      ## t/t1900-repo-info.sh ##
     @@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
      	'.git' \
    @@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir
      test_expect_success 'path.toplevel absolute and relative' '
      	test_when_finished "rm -rf repo" &&
      	git init repo &&
    +@@ t/t1900-repo-info.sh: test_expect_success 'path.toplevel absolute and relative in a bare repository' '
    + 	)
    + '
    + 
    ++test_expect_success 'path.superproject-root works with --git-dir' '
    ++	test_when_finished "rm -rf sub super" &&
    ++	git init sub &&
    ++	test_commit -C sub initial &&
    ++	git init super &&
    ++	(
    ++		cd super &&
    ++		git -c protocol.file.allow=always submodule add "../sub" sub &&
    ++		git commit -m "add submodule" &&
    ++
    ++		SUPER_ROOT="$(test-tool path-utils real_path .)" &&
    ++		MODULE_DIR="$SUPER_ROOT/.git/modules/sub" &&
    ++
    ++		echo "path.superproject-root.absolute=$SUPER_ROOT" >expect &&
    ++		git --git-dir="$MODULE_DIR" repo info path.superproject-root.absolute >actual &&
    ++		test_cmp expect actual
    ++	)
    ++'
    + test_done
3:  8664f8aaae ! 3:  ae8a2592b3 repo: add path.hooks with absolute and relative suffixes
    @@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, str
     +	return 0;
     +}
     +
    - static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
    + static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)
      {
      	struct strbuf superproject = STRBUF_INIT;
     @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {
4:  438f7e4e16 ! 4:  7f27bf14b9 repo: add path.index with absolute and relative suffixes
    @@ builtin/repo.c: static int get_path_hooks_relative(struct repository *repo, stru
     +	return 0;
     +}
     +
    - static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
    + static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)
      {
      	struct strbuf superproject = STRBUF_INIT;
     @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {
5:  82be68426c = 5:  cc52587980 repo: add path.grafts with absolute and relative suffixes
6:  346f1654d3 = 6:  77bf284a05 repo: add path.git-prefix
7:  3ccdd9b5e4 = 7:  e30d98f5da repo: add path.cdup
-- 
2.55.GIT
K JayatheerthSep 11, 2026, 14:45 UTC in reply to K Jayatheerth on lore

[GSoC Patch v6 1/7] repo: add path.toplevel with absolute and relative suffix formatting

Scripts frequently need to find the root directory of a repository's working tree. Currently, this requires using `git rev-parse --show-toplevel` or inferring it from other repository information.

Introduce `path.toplevel.absolute` and `path.toplevel.relative` keys to `git repo info`. This allows scripts to retrieve the top-level working tree path in a predictable, strictly formatted manner without relying on `rev-parse`.

If requested in a bare repository where no working tree exists, the command returns an empty string.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 10 ++++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 35 +++++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+)
Show changes to 3 files +69 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index ed7d80c690..e34abe5fea 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,16 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.toplevel.absolute`::
+	The canonical absolute path to the top-level directory of the
+	repository's working tree. Outputs an empty string if the repository
+	is bare.
+
+`path.toplevel.relative`::
+	The path to the top-level directory of the repository's working
+	tree relative to the current working directory. Outputs an empty
+	string if the repository is bare.
+
 `references.format`::
 	The reference storage format. The valid values are:
 +
diff --git a/builtin/repo.c b/builtin/repo.c
index 84e012f83f..c31e9cfa70 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -121,6 +121,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *work_tree = repo_get_work_tree(repo);
+
+	if (!work_tree)
+		return 0;
+
+	format_path(buf, work_tree, "", PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *work_tree = repo_get_work_tree(repo);
+
+	if (!work_tree)
+		return 0;
+
+	format_path(buf, work_tree, repo->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_references_format(struct repository *repo, struct strbuf *buf)
 {
 	strbuf_addstr(buf,
@@ -137,6 +159,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.toplevel.absolute", get_path_toplevel_absolute },
+	{ "path.toplevel.relative", get_path_toplevel_relative },
 	{ "references.format", get_references_format },
 };
 
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index c85d390f43..9417d1ab65 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,4 +213,39 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.toplevel absolute and relative' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		mkdir -p repo/sub &&
+		cd repo/sub &&
+
+		ROOT="$(test-tool path-utils real_path ..)" &&
+
+		echo "path.toplevel.absolute=$ROOT" >expect.abs &&
+		git repo info path.toplevel.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.toplevel.relative=../" >expect.rel &&
+		git repo info path.toplevel.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
+test_expect_success 'path.toplevel absolute and relative in a bare repository' '
+	test_when_finished "rm -rf bare.git" &&
+	git init --bare bare.git &&
+	(
+		cd bare.git &&
+
+		echo "path.toplevel.absolute=" >expect.abs &&
+		git repo info path.toplevel.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.toplevel.relative=" >expect.rel &&
+		git repo info path.toplevel.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
 test_done
-- 
2.55.GIT
K JayatheerthSep 11, 2026, 14:45 UTC in reply to K Jayatheerth on lore

[GSoC Patch v6 2/7] repo: add path.superproject-root with absolute and relative suffixes

Scripts working in multi-repository setups often need to identify the top-level working tree of a superproject from within a submodule. Currently, this is only exposed via `git rev-parse --show-superproject-working-tree`.

Introduce `path.superproject-root.absolute` and `path.superproject-root.relative` keys to `git repo info`. This exposes the core submodule context via a scriptable config-like key using standard format rules.

If requested when not inside a submodule, the command returns an empty string.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 10 +++++++
 builtin/repo.c              | 31 ++++++++++++++++++++
 builtin/rev-parse.c         |  2 +-
 submodule.c                 | 43 ++++++++++++++--------------
 submodule.h                 |  2 +-
 t/t1900-repo-info.sh        | 57 +++++++++++++++++++++++++++++++++++++
 6 files changed, 122 insertions(+), 23 deletions(-)
Show changes to 6 files +122 −23

Documentation/git-repo.adoc, builtin/repo.c, builtin/rev-parse.c, submodule.c, submodule.h, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index e34abe5fea..e524a07f53 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,16 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.superproject-root.absolute`::
+	The canonical absolute path to the working tree root of the superproject
+	if the current repository is an initialized submodule. Outputs an empty
+	string if not in a submodule.
+
+`path.superproject-root.relative`::
+	The path to the working tree root of the superproject relative to the
+	current working directory if the current repository is an initialized
+	submodule. Outputs an empty string if not in a submodule.
+
 `path.toplevel.absolute`::
 	The canonical absolute path to the top-level directory of the
 	repository's working tree. Outputs an empty string if the repository
diff --git a/builtin/repo.c b/builtin/repo.c
index c31e9cfa70..27ebb7a1c9 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -18,6 +18,7 @@
 #include "strbuf.h"
 #include "string-list.h"
 #include "shallow.h"
+#include "submodule.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "utf8.h"
@@ -121,6 +122,34 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf superproject = STRBUF_INIT;
+
+	if (!get_superproject_working_tree(repo, &superproject)) {
+		strbuf_release(&superproject);
+		return 0;
+	}
+
+	format_path(buf, superproject.buf, "", PATH_FORMAT_CANONICAL);
+	strbuf_release(&superproject);
+	return 0;
+}
+
+static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf superproject = STRBUF_INIT;
+
+	if (!get_superproject_working_tree(repo, &superproject)) {
+		strbuf_release(&superproject);
+		return 0;
+	}
+
+	format_path(buf, superproject.buf, repo->prefix, PATH_FORMAT_RELATIVE);
+	strbuf_release(&superproject);
+	return 0;
+}
+
 static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *work_tree = repo_get_work_tree(repo);
@@ -159,6 +188,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.superproject-root.absolute", get_path_superproject_absolute },
+	{ "path.superproject-root.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
 	{ "path.toplevel.relative", get_path_toplevel_relative },
 	{ "references.format", get_references_format },
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 43693454d5..e1a6da0076 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -997,7 +997,7 @@ int cmd_rev_parse(int argc,
 			}
 			if (!strcmp(arg, "--show-superproject-working-tree")) {
 				struct strbuf superproject = STRBUF_INIT;
-				if (get_superproject_working_tree(&superproject))
+				if (get_superproject_working_tree(the_repository, &superproject))
 					print_path(superproject.buf, prefix, format, DEFAULT_UNMODIFIED);
 				strbuf_release(&superproject);
 				continue;
diff --git a/submodule.c b/submodule.c
index 5c92575888..78856f9840 100644
--- a/submodule.c
+++ b/submodule.c
@@ -2610,34 +2610,35 @@ void absorb_git_dir_into_superproject(const char *path,
 	absorb_git_dir_into_superproject_recurse(path, super_prefix);
 }
 
-int get_superproject_working_tree(struct strbuf *buf)
+int get_superproject_working_tree(struct repository *r, struct strbuf *buf)
 {
 	struct child_process cp = CHILD_PROCESS_INIT;
 	struct strbuf sb = STRBUF_INIT;
 	struct strbuf one_up = STRBUF_INIT;
-	char *cwd = xgetcwd();
+	struct strbuf target_wt = STRBUF_INIT;
+	const char *worktree;
 	int ret = 0;
 	const char *subpath;
 	int code;
 	ssize_t len;
 
-	if (!is_inside_work_tree(the_repository))
-		/*
-		 * FIXME:
-		 * We might have a superproject, but it is harder
-		 * to determine.
-		 */
+	worktree = repo_get_work_tree(r);
+	if (!worktree)
+		goto out;
+
+	if (!strbuf_realpath(&target_wt, worktree, 0))
 		goto out;
 
-	if (!strbuf_realpath(&one_up, "../", 0))
+	strbuf_addf(&one_up, "%s/..", target_wt.buf);
+	if (!strbuf_realpath(&one_up, one_up.buf, 0))
 		goto out;
 
-	subpath = relative_path(cwd, one_up.buf, &sb);
+	subpath = relative_path(target_wt.buf, one_up.buf, &sb);
 
 	prepare_submodule_repo_env(&cp.env);
 	strvec_pop(&cp.env);
 
-	strvec_pushl(&cp.args, "--literal-pathspecs", "-C", "..",
+	strvec_pushl(&cp.args, "--literal-pathspecs", "-C", one_up.buf,
 		     "ls-files", "-z", "--stage", "--full-name", "--",
 		     subpath, NULL);
 	strbuf_reset(&sb);
@@ -2648,14 +2649,14 @@ int get_superproject_working_tree(struct strbuf *buf)
 	cp.git_cmd = 1;
 
 	if (start_command(&cp))
-		die(_("could not start ls-files in .."));
+		die(_("could not start ls-files in %s"), one_up.buf);
 
 	len = strbuf_read(&sb, cp.out, PATH_MAX);
 	close(cp.out);
 
 	if (starts_with(sb.buf, "160000")) {
 		int super_sub_len;
-		int cwd_len = strlen(cwd);
+		int wt_len = target_wt.len;
 		char *super_sub, *super_wt;
 
 		/*
@@ -2666,12 +2667,12 @@ int get_superproject_working_tree(struct strbuf *buf)
 		super_sub = strchr(sb.buf, '\t') + 1;
 		super_sub_len = strlen(super_sub);
 
-		if (super_sub_len > cwd_len ||
-		    strcmp(&cwd[cwd_len - super_sub_len], super_sub))
-			BUG("returned path string doesn't match cwd?");
+		if (super_sub_len > wt_len ||
+		    strcmp(&target_wt.buf[wt_len - super_sub_len], super_sub))
+			BUG("returned path string doesn't match worktree?");
 
-		super_wt = xstrdup(cwd);
-		super_wt[cwd_len - super_sub_len] = '\0';
+		super_wt = xstrdup(target_wt.buf);
+		super_wt[wt_len - super_sub_len] = '\0';
 
 		strbuf_realpath(buf, super_wt, 1);
 		ret = 1;
@@ -2681,10 +2682,10 @@ int get_superproject_working_tree(struct strbuf *buf)
 	code = finish_command(&cp);
 
 	if (code == 128)
-		/* '../' is not a git repository */
+		/* parent directory is not a git repository */
 		ret = 0;
 	else if (code == 0 && len == 0)
-		/* There is an unrelated git repository at '../' */
+		/* There is an unrelated git repository at parent directory */
 		ret = 0;
 	else if (code)
 		die(_("ls-tree returned unexpected return code %d"), code);
@@ -2692,7 +2693,7 @@ int get_superproject_working_tree(struct strbuf *buf)
 out:
 	strbuf_release(&sb);
 	strbuf_release(&one_up);
-	free(cwd);
+	strbuf_release(&target_wt);
 	return ret;
 }
 
diff --git a/submodule.h b/submodule.h
index b10e16e6c0..1a465a1208 100644
--- a/submodule.h
+++ b/submodule.h
@@ -170,6 +170,6 @@ void absorb_git_dir_into_superproject(const char *path,
  * project is a submodule of. If this repository is not a submodule of
  * another repository, return 0.
  */
-int get_superproject_working_tree(struct strbuf *buf);
+int get_superproject_working_tree(struct repository *r, struct strbuf *buf);
 
 #endif
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 9417d1ab65..2dc0c0fbc5 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,45 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.superproject-root absolute and relative' '
+	test_when_finished "rm -rf sub super" &&
+	git init sub &&
+	test_commit -C sub initial &&
+	git init super &&
+	(
+		cd super &&
+		git -c protocol.file.allow=always submodule add "../sub" sub &&
+		git commit -m "add submodule" &&
+
+		cd sub &&
+		ROOT="$(test-tool path-utils real_path ..)" &&
+
+		echo "path.superproject-root.absolute=$ROOT" >expect.abs &&
+		git repo info path.superproject-root.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.superproject-root.relative=../" >expect.rel &&
+		git repo info path.superproject-root.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
+test_expect_success 'path.superproject-root returns empty when not in a submodule' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		echo "path.superproject-root.absolute=" >expect.abs &&
+		git repo info path.superproject-root.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.superproject-root.relative=" >expect.rel &&
+		git repo info path.superproject-root.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
 test_expect_success 'path.toplevel absolute and relative' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
@@ -248,4 +287,22 @@ test_expect_success 'path.toplevel absolute and relative in a bare repository' '
 	)
 '
 
+test_expect_success 'path.superproject-root works with --git-dir' '
+	test_when_finished "rm -rf sub super" &&
+	git init sub &&
+	test_commit -C sub initial &&
+	git init super &&
+	(
+		cd super &&
+		git -c protocol.file.allow=always submodule add "../sub" sub &&
+		git commit -m "add submodule" &&
+
+		SUPER_ROOT="$(test-tool path-utils real_path .)" &&
+		MODULE_DIR="$SUPER_ROOT/.git/modules/sub" &&
+
+		echo "path.superproject-root.absolute=$SUPER_ROOT" >expect &&
+		git --git-dir="$MODULE_DIR" repo info path.superproject-root.absolute >actual &&
+		test_cmp expect actual
+	)
+'
 test_done
-- 
2.55.GIT
K JayatheerthSep 11, 2026, 14:45 UTC in reply to K Jayatheerth on lore

[GSoC Patch v6 3/7] repo: add path.hooks with absolute and relative suffixes

Hooks are an integral part of a repository's configuration and are commonly used by tooling to automate repository-specific workflows. Currently, scripts typically retrieve the hooks directory by invoking `git rev-parse --git-path hooks`.

Introduce `path.hooks.absolute` and `path.hooks.relative` keys to `git repo info`. This exposes the hooks directory as a scriptable config-like key using standard format rules, allowing scripts to retrieve it through the same interface as other repository path information.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  9 +++++++++
 builtin/repo.c              | 22 ++++++++++++++++++++++
 t/t1900-repo-info.sh        | 28 ++++++++++++++++++++++++++--
 3 files changed, 57 insertions(+), 2 deletions(-)
Show changes to 3 files +57 −2

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index e524a07f53..20836cf8f6 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,15 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.hooks.absolute`::
+	The canonical absolute path to the repository's hooks directory.
+	Respects the `core.hooksPath` configuration. If `core.hooksPath` is
+	set to `/dev/null`, that value is returned unchanged.
+
+`path.hooks.relative`::
+	The path to the repository's hooks directory relative to the current
+	working directory. Respects the `core.hooksPath` configuration.
+
 `path.superproject-root.absolute`::
 	The canonical absolute path to the working tree root of the superproject
 	if the current repository is an initialized submodule. Outputs an empty
diff --git a/builtin/repo.c b/builtin/repo.c
index 27ebb7a1c9..01666dc17c 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,26 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf hooks_path = STRBUF_INIT;
+
+	repo_git_path_replace(repo, &hooks_path, "hooks");
+	format_path(buf, hooks_path.buf, "", PATH_FORMAT_CANONICAL);
+	strbuf_release(&hooks_path);
+	return 0;
+}
+
+static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf hooks_path = STRBUF_INIT;
+
+	repo_git_path_replace(repo, &hooks_path, "hooks");
+	format_path(buf, hooks_path.buf, repo->prefix, PATH_FORMAT_RELATIVE);
+	strbuf_release(&hooks_path);
+	return 0;
+}
+
 static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)
 {
 	struct strbuf superproject = STRBUF_INIT;
@@ -188,6 +208,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.hooks.absolute", get_path_hooks_absolute },
+	{ "path.hooks.relative", get_path_hooks_relative },
 	{ "path.superproject-root.absolute", get_path_superproject_absolute },
 	{ "path.superproject-root.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 2dc0c0fbc5..2e7b3867bd 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -174,7 +174,11 @@ test_repo_info_path () {
 			cd repo/sub &&
 			ROOT="$(test-tool path-utils real_path ..)" && export ROOT &&
 			eval "$init_command" &&
-			echo "path.$field_name.absolute=$ROOT/$expected_dir" >expect &&
+			case "$expected_dir" in
+			/*) EXPECT_ABS="$expected_dir" ;;
+			*) EXPECT_ABS="$ROOT/$expected_dir" ;;
+			esac &&
+			echo "path.$field_name.absolute=$EXPECT_ABS" >expect &&
 			git repo info "path.$field_name.absolute" >actual &&
 			test_cmp expect actual
 		)
@@ -188,7 +192,11 @@ test_repo_info_path () {
 			cd repo/sub &&
 			ROOT="$(test-tool path-utils real_path ..)" && export ROOT &&
 			eval "$init_command" &&
-			echo "path.$field_name.relative=../$expected_dir" >expect &&
+			case "$expected_dir" in
+			/*) EXPECT_REL="$(test-tool path-utils relative_path "$expected_dir" "$PWD")" ;;
+			*) EXPECT_REL="../$expected_dir" ;;
+			esac &&
+			echo "path.$field_name.relative=$EXPECT_REL" >expect &&
 			git repo info "path.$field_name.relative" >actual &&
 			test_cmp expect actual
 		)
@@ -213,6 +221,22 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'
+
+test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
+	'custom-hooks' \
+	'git config core.hooksPath "$ROOT/custom-hooks" && mkdir -p "$ROOT/custom-hooks"'
+
+# /dev/null is not a real, canonicalizable filesystem path on Windows,
+# so path resolution for core.hooksPath=/dev/null cannot be expected to
+# produce a literal "/dev/null" the way it does on POSIX systems.
+if ! test_have_prereq MINGW
+then
+	test_repo_info_path 'hooks with core.hooksPath=/dev/null' 'hooks' \
+		'/dev/null' \
+		'git config core.hooksPath /dev/null'
+fi
+
 test_expect_success 'path.superproject-root absolute and relative' '
 	test_when_finished "rm -rf sub super" &&
 	git init sub &&
-- 
2.55.GIT
K JayatheerthSep 11, 2026, 14:45 UTC in reply to K Jayatheerth on lore

[GSoC Patch v6 4/7] repo: add path.index with absolute and relative suffixes

The repository index is a fundamental component used by Git and related tooling to track the working tree state. Scripts that interact with the index currently retrieve its location by invoking `git rev-parse --git-path index`.

Introduce `path.index.absolute` and `path.index.relative` keys to `git repo info`. This exposes the index file location as a scriptable config-like key using standard format rules, allowing scripts to retrieve it through the same interface as other repository path information.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 12 ++++++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++
 3 files changed, 59 insertions(+)
Show changes to 3 files +59 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 20836cf8f6..08ef47750c 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -128,6 +128,18 @@ values that they return:
 	The path to the repository's hooks directory relative to the current
 	working directory. Respects the `core.hooksPath` configuration.
 
+`path.index.absolute`::
+	The canonical absolute path to the repository's current index file.
+	Respects the `GIT_INDEX_FILE` environment override. Returns the
+	configured index path even if the repository is bare or the file does
+	not exist.
+
+`path.index.relative`::
+	The path to the repository's current index file relative to the current
+	working directory. Respects the `GIT_INDEX_FILE` environment override.
+	Returns the configured index path even if the repository is bare or the
+	file does not exist.
+
 `path.superproject-root.absolute`::
 	The canonical absolute path to the working tree root of the superproject
 	if the current repository is an initialized submodule. Outputs an empty
diff --git a/builtin/repo.c b/builtin/repo.c
index 01666dc17c..9f1858a127 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -142,6 +142,28 @@ static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *index_file = repo_get_index_file(repo);
+
+	if (!index_file)
+		return error(_("unable to get index file"));
+
+	format_path(buf, index_file, "", PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *index_file = repo_get_index_file(repo);
+
+	if (!index_file)
+		return error(_("unable to get index file"));
+
+	format_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)
 {
 	struct strbuf superproject = STRBUF_INIT;
@@ -210,6 +232,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.gitdir.relative", get_path_gitdir_relative },
 	{ "path.hooks.absolute", get_path_hooks_absolute },
 	{ "path.hooks.relative", get_path_hooks_relative },
+	{ "path.index.absolute", get_path_index_absolute },
+	{ "path.index.relative", get_path_index_relative },
 	{ "path.superproject-root.absolute", get_path_superproject_absolute },
 	{ "path.superproject-root.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 2e7b3867bd..80e025e040 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -237,6 +237,29 @@ then
 		'git config core.hooksPath /dev/null'
 fi
 
+test_repo_info_path 'index standard' 'index' '.git/index'
+
+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \
+	'custom-index-file' \
+	'GIT_INDEX_FILE="$ROOT/custom-index-file" && export GIT_INDEX_FILE'
+
+test_expect_success 'path.index in a bare repository returns default index location' '
+	test_when_finished "rm -rf bare.git" &&
+	git init --bare bare.git &&
+	(
+		cd bare.git &&
+		ROOT="$(test-tool path-utils real_path .)" &&
+
+		echo "path.index.absolute=$ROOT/index" >expect.abs &&
+		git repo info path.index.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.index.relative=index" >expect.rel &&
+		git repo info path.index.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
 test_expect_success 'path.superproject-root absolute and relative' '
 	test_when_finished "rm -rf sub super" &&
 	git init sub &&
-- 
2.55.GIT
K JayatheerthSep 11, 2026, 14:45 UTC in reply to K Jayatheerth on lore

[GSoC Patch v6 5/7] repo: add path.grafts with absolute and relative suffixes

The repository grafts file specifies alternate parent relationships for commits and may be used by repository tooling that needs to inspect or manage grafts. Scripts currently retrieve its location by invoking `git rev-parse --git-path info/grafts`.

Introduce `path.grafts.absolute` and `path.grafts.relative` keys to `git repo info`. This exposes the grafts file location as a scriptable config-like key using standard format rules, allowing scripts to retrieve it through the same interface as other repository path information.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 11 +++++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        |  6 ++++++
 3 files changed, 41 insertions(+)
Show changes to 3 files +41 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 08ef47750c..868ab0ed9f 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,17 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.grafts.absolute`::
+	The canonical absolute path to the repository's graft file.
+	Respects the `GIT_GRAFT_FILE` environment override. The path is
+	returned regardless of whether the file currently exists on disk.
+
+`path.grafts.relative`::
+	The path to the repository's graft file relative to the current
+	working directory. Respects the `GIT_GRAFT_FILE` environment
+	override. The path is returned regardless of whether the file
+	currently exists on disk.
+
 `path.hooks.absolute`::
 	The canonical absolute path to the repository's hooks directory.
 	Respects the `core.hooksPath` configuration. If `core.hooksPath` is
diff --git a/builtin/repo.c b/builtin/repo.c
index 9f1858a127..33f97b03af 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_grafts_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *graft_file = repo_get_graft_file(repo);
+
+	if (!graft_file)
+		return error(_("unable to get graft file"));
+
+	format_path(buf, graft_file, "", PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_grafts_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *graft_file = repo_get_graft_file(repo);
+
+	if (!graft_file)
+		return error(_("unable to get graft file"));
+
+	format_path(buf, graft_file, repo->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)
 {
 	struct strbuf hooks_path = STRBUF_INIT;
@@ -230,6 +252,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.grafts.absolute", get_path_grafts_absolute },
+	{ "path.grafts.relative", get_path_grafts_relative },
 	{ "path.hooks.absolute", get_path_hooks_absolute },
 	{ "path.hooks.relative", get_path_hooks_relative },
 	{ "path.index.absolute", get_path_index_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 80e025e040..7b0bb0ed42 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -221,6 +221,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'grafts standard' 'grafts' '.git/info/grafts'
+
+test_repo_info_path 'grafts with GIT_GRAFT_FILE override' 'grafts' \
+	'custom-graft-file' \
+	'GIT_GRAFT_FILE="$ROOT/custom-graft-file" && export GIT_GRAFT_FILE'
+
 test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'
 
 test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
-- 
2.55.GIT
K JayatheerthSep 11, 2026, 14:45 UTC in reply to K Jayatheerth on lore

[GSoC Patch v6 6/7] repo: add path.git-prefix

Scripts sometimes need the path from the repository's working tree root to the current working directory. While this information can be derived through existing Git commands, `git repo info` does not currently expose it as a scriptable key.

Introduce the `path.git-prefix` key to `git repo info`. The key returns the path from the working tree root to the current working directory, returning the empty string when invoked from the working tree root.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  5 +++++
 builtin/repo.c              | 11 +++++++++++
 t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++
 3 files changed, 39 insertions(+)
Show changes to 3 files +39 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 868ab0ed9f..fb5aceae8f 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -113,6 +113,11 @@ values that they return:
 	The path to the Git repository's common directory relative to
 	the current working directory.
 
+`path.git-prefix`::
+	The path from the root of the working tree to the current working
+	directory. Returns the empty string when the current working directory
+	is the root of the working tree.
+
 `path.gitdir.absolute`::
 	The canonical absolute path to the Git repository directory (the `.git` directory).
 
diff --git a/builtin/repo.c b/builtin/repo.c
index 33f97b03af..b5e1567809 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -100,6 +100,16 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b
 	return 0;
 }
 
+static int get_path_git_prefix(struct repository *repo, struct strbuf *buf)
+{
+	/*
+	 * repo->prefix is NULL when the current working directory is
+	 * the worktree root.
+	 */
+	strbuf_addstr(buf, repo->prefix ? repo->prefix : "");
+	return 0;
+}
+
 static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *git_dir = repo_get_git_dir(repo);
@@ -250,6 +260,7 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "object.format", get_object_format },
 	{ "path.commondir.absolute", get_path_commondir_absolute },
 	{ "path.commondir.relative", get_path_commondir_relative },
+	{ "path.git-prefix", get_path_git_prefix },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
 	{ "path.grafts.absolute", get_path_grafts_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 7b0bb0ed42..10bbec8139 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -215,6 +215,29 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.git-prefix at repository root' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		echo "path.git-prefix=" >expect &&
+		git repo info path.git-prefix >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'path.git-prefix in subdirectory' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	mkdir -p repo/sub/dir &&
+	(
+		cd repo/sub/dir &&
+		echo "path.git-prefix=sub/dir/" >expect &&
+		git repo info path.git-prefix >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_repo_info_path 'gitdir standard' 'gitdir' '.git'
 
 test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
-- 
2.55.GIT
K JayatheerthSep 11, 2026, 14:45 UTC in reply to K Jayatheerth on lore

[GSoC Patch v6 7/7] repo: add path.cdup

Scripts sometimes need the relative path from the current working directory to the repository's working tree root (cdup). While this information can be retrieved through `git rev-parse --show-cdup`, `git repo info` does not currently expose it as a scriptable key.

Introduce the `path.cdup` key to `git repo info`. The key returns the path from the current working directory to the root of the working tree, returning the empty string when invoked from the working tree root.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  5 +++++
 builtin/repo.c              | 15 +++++++++++++++
 t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++
 3 files changed, 43 insertions(+)
Show changes to 3 files +43 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index fb5aceae8f..0a30c0d6ab 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -104,6 +104,11 @@ values that they return:
 `object.format`::
 	The object format (hash algorithm) used in the repository.
 
+`path.cdup`::
+	The path to the root of the working tree relative to the current
+	working directory. Returns the empty string when the current
+	working directory is the root of the working tree.
+
 `path.commondir.absolute`::
 	The canonical absolute path to the Git repository's common
 	directory (the shared `.git` directory containing objects,
diff --git a/builtin/repo.c b/builtin/repo.c
index b5e1567809..bb129c9fee 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -78,6 +78,20 @@ static int get_object_format(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_cdup(struct repository *repo, struct strbuf *buf)
+{
+	const char *pfx = repo->prefix;
+
+	while (pfx) {
+		pfx = strchr(pfx, '/');
+		if (pfx) {
+			pfx++;
+			strbuf_addstr(buf, "../");
+		}
+	}
+	return 0;
+}
+
 static int get_path_commondir_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *common_dir = repo_get_common_dir(repo);
@@ -258,6 +272,7 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "layout.bare", get_layout_bare },
 	{ "layout.shallow", get_layout_shallow },
 	{ "object.format", get_object_format },
+	{ "path.cdup", get_path_cdup },
 	{ "path.commondir.absolute", get_path_commondir_absolute },
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.git-prefix", get_path_git_prefix },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 10bbec8139..5790d70316 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -215,6 +215,29 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.cdup at repository root' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		echo "path.cdup=" >expect &&
+		git repo info path.cdup >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'path.cdup in subdirectory' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	mkdir -p repo/sub/dir &&
+	(
+		cd repo/sub/dir &&
+		echo "path.cdup=../../" >expect &&
+		git repo info path.cdup >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_expect_success 'path.git-prefix at repository root' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
-- 
2.55.GIT
Junio C HamanoSep 11, 2026, 16:55 UTC in reply to K Jayatheerth on lore

Re: [GSoC Patch v6 4/7] repo: add path.index with absolute and relative suffixes

K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
Show 6 quoted lines
> +static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)
> +{
> +	const char *index_file = repo_get_index_file(repo);
> +
> +	if (!index_file)
> +		return error(_("unable to get index file"));

This is a dead code, as repo_get_index_file() calls BUG("") when it finds that the repo instance is not ready to return this information.

Hence I am debating myself if this is a better alternative:
	const char *index_file = repo->index_file;
	if (!index_file)
		return error(...);
I dunno.
Show 10 quoted lines
> +static int get_path_index_relative(struct repository *repo, struct strbuf *buf)
> +{
> +	const char *index_file = repo_get_index_file(repo);
> +
> +	if (!index_file)
> +		return error(_("unable to get index file"));
> +
> +	format_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);
> +	return 0;
> +}
Ditto.
Junio C HamanoSep 11, 2026, 17:37 UTC in reply to K Jayatheerth on lore

Re: [GSoC Patch v6 7/7] repo: add path.cdup

K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
Show 30 quoted lines
> Scripts sometimes need the relative path from the current working
> directory to the repository's working tree root (cdup). While this
> information can be retrieved through `git rev-parse --show-cdup`,
> `git repo info` does not currently expose it as a scriptable key.
>
> Introduce the `path.cdup` key to `git repo info`. The key returns the
> path from the current working directory to the root of the working tree,
> returning the empty string when invoked from the working tree root.
>
> Mentored-by: Justin Tobler <jltobler@gmail.com>
> Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
> Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
> ---
>  Documentation/git-repo.adoc |  5 +++++
>  builtin/repo.c              | 15 +++++++++++++++
>  t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++
>  3 files changed, 43 insertions(+)
>
> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
> index fb5aceae8f..0a30c0d6ab 100644
> --- a/Documentation/git-repo.adoc
> +++ b/Documentation/git-repo.adoc
> @@ -104,6 +104,11 @@ values that they return:
>  `object.format`::
>  	The object format (hash algorithm) used in the repository.
>  
> +`path.cdup`::
> +	The path to the root of the working tree relative to the current
> +	working directory. Returns the empty string when the current
> +	working directory is the root of the working tree.

What happens when the current working directory is outside the working tree? For example:

    $ GIT_WORK_TREE=/tmp/x; export GIT_WORK_TREE
    $ GIT_DIR=$(pwd)/.git; export GIT_DIR
The current implementation yields an empty string:
    $ git repo info path.cdup
    path.cdup=
However, 'git rev-parse --show-cdup' produces:
    $ git rev-parse --show-cdup
    /var/tmp/x

Whether this discrepancy matters is unclear. However, if the goal is to consolidate all repository path-related items under the 'git repo info' umbrella, this difference sticks out.

Junio C HamanoSep 11, 2026, 22:08 UTC in reply to K Jayatheerth on lore

Re: [GSoC Patch v6 2/7] repo: add path.superproject-root with absolute and relative suffixes

K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
Show 24 quoted lines
> Scripts working in multi-repository setups often need to identify the
> top-level working tree of a superproject from within a submodule.
> Currently, this is only exposed via `git rev-parse
> --show-superproject-working-tree`.
>
> Introduce `path.superproject-root.absolute` and
> `path.superproject-root.relative` keys to `git repo info`.
> This exposes the core submodule context via a scriptable config-like key
> using standard format rules.
>
> If requested when not inside a submodule, the command returns an empty
> string.
>
> Mentored-by: Justin Tobler <jltobler@gmail.com>
> Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
> Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
> ---
>  Documentation/git-repo.adoc | 10 +++++++
>  builtin/repo.c              | 31 ++++++++++++++++++++
>  builtin/rev-parse.c         |  2 +-
>  submodule.c                 | 43 ++++++++++++++--------------
>  submodule.h                 |  2 +-
>  t/t1900-repo-info.sh        | 57 +++++++++++++++++++++++++++++++++++++
>  6 files changed, 122 insertions(+), 23 deletions(-)

Correcting get_superproject_working_tree(), which was introduced by bf0231c661 (rev-parse: add --show-superproject-working-tree, 2017-03-08), is a major part of this step. The old commit added a single test for very basic use but this step makes not-so-basic use cases to also work correctly, right?

It may be a good idea to split this step into two patches:
 - a patch that corrects get_superproject_working_tree(), that adds
   a new test or two to t1500-rev-parse.sh, next to where bf0231c661
   (rev-parse: add --show-superproject-working-tree, 2017-03-08)
   added its test.  The test would fail without the code fix to
   demonstrate what we improved.
 - another patch that uses improved get_superproject_working_tree()
   to add path.superproject-root.* keys, together with the updates
   to t1900-repo-info.sh to add tests for the new feature.
K JayatheerthSep 23, 2026, 17:25 UTC in reply to Junio C Hamano on lore

Re: [GSoC Patch v6 4/7] repo: add path.index with absolute and relative suffixes

Show 15 quoted lines
> > +     if (!index_file)
> > +             return error(_("unable to get index file"));
>
> This is a dead code, as repo_get_index_file() calls BUG("") when it
> finds that the repo instance is not ready to return this information.
>
> Hence I am debating myself if this is a better alternative:
>
>         const char *index_file = repo->index_file;
>
>         if (!index_file)
>                 return error(...);
>
> I dunno.
>

I think I am just gonna remove the if condition from get_path_index_relative() and let repo_get_index_file handle the bug instead of rewriting the logic.

Show 12 quoted lines
> > +static int get_path_index_relative(struct repository *repo, struct strbuf *buf)
> > +{
> > +     const char *index_file = repo_get_index_file(repo);
> > +
> > +     if (!index_file)
> > +             return error(_("unable to get index file"));
> > +
> > +     format_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);
> > +     return 0;
> > +}
>
> Ditto.
Same for this.
K JayatheerthSep 23, 2026, 17:37 UTC in reply to Junio C Hamano on lore

Re: [GSoC Patch v6 7/7] repo: add path.cdup

On Fri, Sep 11, 2026 at 11:07 PM Junio C Hamano <gitster@pobox.com> wrote:
Show 32 quoted lines
>
> K Jayatheerth <jayatheerthkulkarni2005@gmail.com> writes:
>
> > Scripts sometimes need the relative path from the current working
> > directory to the repository's working tree root (cdup). While this
> > information can be retrieved through `git rev-parse --show-cdup`,
> > `git repo info` does not currently expose it as a scriptable key.
> >  [...]
> > +`path.cdup`::
> > +     The path to the root of the working tree relative to the current
> > +     working directory. Returns the empty string when the current
> > +     working directory is the root of the working tree.
>
> What happens when the current working directory is outside the
> working tree?  For example:
>
>     $ GIT_WORK_TREE=/tmp/x; export GIT_WORK_TREE
>     $ GIT_DIR=$(pwd)/.git; export GIT_DIR
>
> The current implementation yields an empty string:
>
>     $ git repo info path.cdup
>     path.cdup=
>
> However, 'git rev-parse --show-cdup' produces:
>
>     $ git rev-parse --show-cdup
>     /var/tmp/x
>
> Whether this discrepancy matters is unclear.  However, if the goal
> is to consolidate all repository path-related items under the
> 'git repo info' umbrella, this difference sticks out.

yeah in rev-parse i could find this

if (!is_inside_work_tree(the_repository)) { const char *work_tree = repo_get_work_tree(the_repository); if (work_tree) printf("%s\n", work_tree); continue; }

adding this logic in repo.c should fix it!
Thanks
K JayatheerthSep 23, 2026, 17:40 UTC in reply to Junio C Hamano on lore

Re: [GSoC Patch v6 2/7] repo: add path.superproject-root with absolute and relative suffixes

On Sat, Sep 12, 2026 at 3:39 AM Junio C Hamano <gitster@pobox.com> wrote:
Show 19 quoted lines
>
>
> Correcting get_superproject_working_tree(), which was introduced by
> bf0231c661 (rev-parse: add --show-superproject-working-tree,
> 2017-03-08), is a major part of this step.  The old commit added a
> single test for very basic use but this step makes not-so-basic use
> cases to also work correctly, right?
>
> It may be a good idea to split this step into two patches:
>
>  - a patch that corrects get_superproject_working_tree(), that adds
>    a new test or two to t1500-rev-parse.sh, next to where bf0231c661
>    (rev-parse: add --show-superproject-working-tree, 2017-03-08)
>    added its test.  The test would fail without the code fix to
>    demonstrate what we improved.
>
>  - another patch that uses improved get_superproject_working_tree()
>    to add path.superproject-root.* keys, together with the updates
>    to t1900-repo-info.sh to add tests for the new feature.

Fair enough. Will do, but I don't think t1500 is a good location tho Cause the bug affects repo as well I will find a good location.

K JayatheerthSep 27, 2026, 11:44 UTC in reply to K Jayatheerth on lore

[GSoC Patch v7 0/8] add more path keys to git repo info

Series adds keys to git repo info.
Keys output paths of repository components:
* path.toplevel: repository tree.
* path.superproject-root: superproject tree from submodules.
* path.hooks: repository hooks.
* path.index: repository index.
* path.grafts: repository grafts.
* path.git-prefix: prefix offset.
* path.cdup: relative path to top level from subdirectory.

Keys support suffixes for format. Commits contain documentation and tests.

fix a superproject bug where it now takes repo parameter instead of just depending on cwd.

changes since v6:
* The superproject bug fix is now a commit of its own.
* The dead code in index is removed, we simply rely on the helper itself
  and not write any new conditions.
* cdup now works even when the worktree is outside the current working directory.
K Jayatheerth (8):
  repo: add path.toplevel with absolute and relative suffix formatting
  submodule: use repository to find superproject
  repo: add path.superproject-root with absolute and relative suffixes
  repo: add path.hooks with absolute and relative suffixes
  repo: add path.index with absolute and relative suffixes
  repo: add path.grafts with absolute and relative suffixes
  repo: add path.git-prefix
  repo: add path.cdup
 Documentation/git-repo.adoc |  62 ++++++++++++
 builtin/repo.c              | 153 ++++++++++++++++++++++++++++
 builtin/rev-parse.c         |   2 +-
 submodule.c                 |  43 ++++----
 submodule.h                 |   2 +-
 t/t1900-repo-info.sh        | 194 +++++++++++++++++++++++++++++++++++-
 t/t7400-submodule-basic.sh  |  19 ++++
 7 files changed, 450 insertions(+), 25 deletions(-)
Range-diff against v6:
-:  ---------- > 1:  27b9811163 repo: add path.toplevel with absolute and relative suffix formatting
1:  d4f3253256 ! 2:  7c90603a1b repo: add path.superproject-root with absolute and relative suffixes
    @@ Metadata
     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
     
      ## Commit message ##
    -    repo: add path.superproject-root with absolute and relative suffixes
    +    submodule: use repository to find superproject
     
    -    Scripts working in multi-repository setups often need to identify the
    -    top-level working tree of a superproject from within a submodule.
    -    Currently, this is only exposed via `git rev-parse
    -    --show-superproject-working-tree`.
    +    `get_superproject_working_tree()` currently uses `xgetcwd()` to
    +    determine the repository whose superproject should be checked.
     
    -    Introduce `path.superproject-root.absolute` and
    -    `path.superproject-root.relative` keys to `git repo info`.
    -    This exposes the core submodule context via a scriptable config-like key
    -    using standard format rules.
    +    This is incorrect when `--git-dir` points to a repository different
    +    from the one associated with the current working directory. In that
    +    case, the current working directory may cause the function to return
    +    the wrong superproject or an empty result.
     
    -    If requested when not inside a submodule, the command returns an empty
    -    string.
    +    Pass the `repository` to `get_superproject_working_tree()` so that the
    +    superproject is determined from the repository being inspected rather
    +    than the current working directory.
     
         Mentored-by: Justin Tobler <jltobler@gmail.com>
         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
         Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
     
    - ## Documentation/git-repo.adoc ##
    -@@ Documentation/git-repo.adoc: values that they return:
    - `path.gitdir.relative`::
    - 	The path to the Git repository directory relative to the current working directory.
    - 
    -+`path.superproject-root.absolute`::
    -+	The canonical absolute path to the working tree root of the superproject
    -+	if the current repository is an initialized submodule. Outputs an empty
    -+	string if not in a submodule.
    -+
    -+`path.superproject-root.relative`::
    -+	The path to the working tree root of the superproject relative to the
    -+	current working directory if the current repository is an initialized
    -+	submodule. Outputs an empty string if not in a submodule.
    -+
    - `path.toplevel.absolute`::
    - 	The canonical absolute path to the top-level directory of the
    - 	repository's working tree. Outputs an empty string if the repository
    -
    - ## builtin/repo.c ##
    -@@
    - #include "strbuf.h"
    - #include "string-list.h"
    - #include "shallow.h"
    -+#include "submodule.h"
    - #include "tree.h"
    - #include "tree-walk.h"
    - #include "utf8.h"
    -@@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
    - 	return 0;
    - }
    - 
    -+static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)
    -+{
    -+	struct strbuf superproject = STRBUF_INIT;
    -+
    -+	if (!get_superproject_working_tree(repo, &superproject)) {
    -+		strbuf_release(&superproject);
    -+		return 0;
    -+	}
    -+
    -+	format_path(buf, superproject.buf, "", PATH_FORMAT_CANONICAL);
    -+	strbuf_release(&superproject);
    -+	return 0;
    -+}
    -+
    -+static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)
    -+{
    -+	struct strbuf superproject = STRBUF_INIT;
    -+
    -+	if (!get_superproject_working_tree(repo, &superproject)) {
    -+		strbuf_release(&superproject);
    -+		return 0;
    -+	}
    -+
    -+	format_path(buf, superproject.buf, repo->prefix, PATH_FORMAT_RELATIVE);
    -+	strbuf_release(&superproject);
    -+	return 0;
    -+}
    -+
    - static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
    - {
    - 	const char *work_tree = repo_get_work_tree(repo);
    -@@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {
    - 	{ "path.commondir.relative", get_path_commondir_relative },
    - 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
    - 	{ "path.gitdir.relative", get_path_gitdir_relative },
    -+	{ "path.superproject-root.absolute", get_path_superproject_absolute },
    -+	{ "path.superproject-root.relative", get_path_superproject_relative },
    - 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
    - 	{ "path.toplevel.relative", get_path_toplevel_relative },
    - 	{ "references.format", get_references_format },
    -
      ## builtin/rev-parse.c ##
     @@ builtin/rev-parse.c: int cmd_rev_parse(int argc,
      			}
    @@ submodule.h: void absorb_git_dir_into_superproject(const char *path,
      
      #endif
     
    - ## t/t1900-repo-info.sh ##
    -@@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
    - 	'.git' \
    - 	'GIT_DIR="../.git" && export GIT_DIR'
    - 
    -+test_expect_success 'path.superproject-root absolute and relative' '
    -+	test_when_finished "rm -rf sub super" &&
    -+	git init sub &&
    -+	test_commit -C sub initial &&
    -+	git init super &&
    -+	(
    -+		cd super &&
    -+		git -c protocol.file.allow=always submodule add "../sub" sub &&
    -+		git commit -m "add submodule" &&
    -+
    -+		cd sub &&
    -+		ROOT="$(test-tool path-utils real_path ..)" &&
    -+
    -+		echo "path.superproject-root.absolute=$ROOT" >expect.abs &&
    -+		git repo info path.superproject-root.absolute >actual.abs &&
    -+		test_cmp expect.abs actual.abs &&
    -+
    -+		echo "path.superproject-root.relative=../" >expect.rel &&
    -+		git repo info path.superproject-root.relative >actual.rel &&
    -+		test_cmp expect.rel actual.rel
    -+	)
    -+'
    -+
    -+test_expect_success 'path.superproject-root returns empty when not in a submodule' '
    -+	test_when_finished "rm -rf repo" &&
    -+	git init repo &&
    -+	(
    -+		cd repo &&
    -+
    -+		echo "path.superproject-root.absolute=" >expect.abs &&
    -+		git repo info path.superproject-root.absolute >actual.abs &&
    -+		test_cmp expect.abs actual.abs &&
    -+
    -+		echo "path.superproject-root.relative=" >expect.rel &&
    -+		git repo info path.superproject-root.relative >actual.rel &&
    -+		test_cmp expect.rel actual.rel
    -+	)
    -+'
    -+
    - test_expect_success 'path.toplevel absolute and relative' '
    - 	test_when_finished "rm -rf repo" &&
    - 	git init repo &&
    -@@ t/t1900-repo-info.sh: test_expect_success 'path.toplevel absolute and relative in a bare repository' '
    + ## t/t7400-submodule-basic.sh ##
    +@@ t/t7400-submodule-basic.sh: test_expect_success 'submodule add fails when name is reused' '
      	)
      '
      
    @@ t/t1900-repo-info.sh: test_expect_success 'path.toplevel absolute and relative i
     +		test_cmp expect actual
     +	)
     +'
    ++
      test_done
3:  1860d19d01 ! 3:  1bfb0c8c1d repo: add path.index with absolute and relative suffixes
    @@ Metadata
     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
     
      ## Commit message ##
    -    repo: add path.index with absolute and relative suffixes
    +    repo: add path.superproject-root with absolute and relative suffixes
     
    -    The repository index is a fundamental component used by Git and related
    -    tooling to track the working tree state. Scripts that interact with the
    -    index currently retrieve its location by invoking
    -    `git rev-parse --git-path index`.
    +    Scripts working in multi-repository setups often need to identify the
    +    top-level working tree of a superproject from within a submodule.
    +    Currently, this is only exposed via `git rev-parse
    +    --show-superproject-working-tree`.
     
    -    Introduce `path.index.absolute` and `path.index.relative` keys to
    -    `git repo info`. This exposes the index file location as a scriptable
    -    config-like key using standard format rules, allowing scripts to
    -    retrieve it through the same interface as other repository path
    -    information.
    +    Introduce `path.superproject-root.absolute` and
    +    `path.superproject-root.relative` keys to `git repo info`.
    +    This exposes the core submodule context via a scriptable config-like key
    +    using standard format rules.
    +
    +    If requested when not inside a submodule, the command returns an empty
    +    string.
     
         Mentored-by: Justin Tobler <jltobler@gmail.com>
         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
    @@ Commit message
     
      ## Documentation/git-repo.adoc ##
     @@ Documentation/git-repo.adoc: values that they return:
    - 	The path to the repository's hooks directory relative to the current
    - 	working directory. Respects the `core.hooksPath` configuration.
    + `path.gitdir.relative`::
    + 	The path to the Git repository directory relative to the current working directory.
      
    -+`path.index.absolute`::
    -+	The canonical absolute path to the repository's current index file.
    -+	Respects the `GIT_INDEX_FILE` environment override. Returns the
    -+	configured index path even if the repository is bare or the file does
    -+	not exist.
    ++`path.superproject-root.absolute`::
    ++	The canonical absolute path to the working tree root of the superproject
    ++	if the current repository is an initialized submodule. Outputs an empty
    ++	string if not in a submodule.
     +
    -+`path.index.relative`::
    -+	The path to the repository's current index file relative to the current
    -+	working directory. Respects the `GIT_INDEX_FILE` environment override.
    -+	Returns the configured index path even if the repository is bare or the
    -+	file does not exist.
    ++`path.superproject-root.relative`::
    ++	The path to the working tree root of the superproject relative to the
    ++	current working directory if the current repository is an initialized
    ++	submodule. Outputs an empty string if not in a submodule.
     +
    - `path.superproject-root.absolute`::
    - 	The canonical absolute path to the working tree root of the superproject
    - 	if the current repository is an initialized submodule. Outputs an empty
    + `path.toplevel.absolute`::
    + 	The canonical absolute path to the top-level directory of the
    + 	repository's working tree. Outputs an empty string if the repository
     
      ## builtin/repo.c ##
    -@@ builtin/repo.c: static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
    +@@
    + #include "strbuf.h"
    + #include "string-list.h"
    + #include "shallow.h"
    ++#include "submodule.h"
    + #include "tree.h"
    + #include "tree-walk.h"
    + #include "utf8.h"
    +@@ builtin/repo.c: static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
      	return 0;
      }
      
    -+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)
    ++static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)
     +{
    -+	const char *index_file = repo_get_index_file(repo);
    ++	struct strbuf superproject = STRBUF_INIT;
     +
    -+	if (!index_file)
    -+		return error(_("unable to get index file"));
    ++	if (!get_superproject_working_tree(repo, &superproject)) {
    ++		strbuf_release(&superproject);
    ++		return 0;
    ++	}
     +
    -+	format_path(buf, index_file, "", PATH_FORMAT_CANONICAL);
    ++	format_path(buf, superproject.buf, "", PATH_FORMAT_CANONICAL);
    ++	strbuf_release(&superproject);
     +	return 0;
     +}
     +
    -+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)
    ++static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)
     +{
    -+	const char *index_file = repo_get_index_file(repo);
    ++	struct strbuf superproject = STRBUF_INIT;
     +
    -+	if (!index_file)
    -+		return error(_("unable to get index file"));
    ++	if (!get_superproject_working_tree(repo, &superproject)) {
    ++		strbuf_release(&superproject);
    ++		return 0;
    ++	}
     +
    -+	format_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);
    ++	format_path(buf, superproject.buf, repo->prefix, PATH_FORMAT_RELATIVE);
    ++	strbuf_release(&superproject);
     +	return 0;
     +}
     +
    - static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)
    + static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
      {
    - 	struct strbuf superproject = STRBUF_INIT;
    + 	const char *work_tree = repo_get_work_tree(repo);
     @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {
    + 	{ "path.commondir.relative", get_path_commondir_relative },
    + 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
      	{ "path.gitdir.relative", get_path_gitdir_relative },
    - 	{ "path.hooks.absolute", get_path_hooks_absolute },
    - 	{ "path.hooks.relative", get_path_hooks_relative },
    -+	{ "path.index.absolute", get_path_index_absolute },
    -+	{ "path.index.relative", get_path_index_relative },
    - 	{ "path.superproject-root.absolute", get_path_superproject_absolute },
    - 	{ "path.superproject-root.relative", get_path_superproject_relative },
    ++	{ "path.superproject-root.absolute", get_path_superproject_absolute },
    ++	{ "path.superproject-root.relative", get_path_superproject_relative },
      	{ "path.toplevel.absolute", get_path_toplevel_absolute },
    + 	{ "path.toplevel.relative", get_path_toplevel_relative },
    + 	{ "references.format", get_references_format },
     
      ## t/t1900-repo-info.sh ##
    -@@ t/t1900-repo-info.sh: then
    - 		'git config core.hooksPath /dev/null'
    - fi
    +@@ t/t1900-repo-info.sh: test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
    + 	'.git' \
    + 	'GIT_DIR="../.git" && export GIT_DIR'
      
    -+test_repo_info_path 'index standard' 'index' '.git/index'
    ++test_expect_success 'path.superproject-root absolute and relative' '
    ++	test_when_finished "rm -rf sub super" &&
    ++	git init sub &&
    ++	test_commit -C sub initial &&
    ++	git init super &&
    ++	(
    ++		cd super &&
    ++		git -c protocol.file.allow=always submodule add "../sub" sub &&
    ++		git commit -m "add submodule" &&
    ++
    ++		cd sub &&
    ++		ROOT="$(test-tool path-utils real_path ..)" &&
    ++
    ++		echo "path.superproject-root.absolute=$ROOT" >expect.abs &&
    ++		git repo info path.superproject-root.absolute >actual.abs &&
    ++		test_cmp expect.abs actual.abs &&
     +
    -+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \
    -+	'custom-index-file' \
    -+	'GIT_INDEX_FILE="$ROOT/custom-index-file" && export GIT_INDEX_FILE'
    ++		echo "path.superproject-root.relative=../" >expect.rel &&
    ++		git repo info path.superproject-root.relative >actual.rel &&
    ++		test_cmp expect.rel actual.rel
    ++	)
    ++'
     +
    -+test_expect_success 'path.index in a bare repository returns default index location' '
    -+	test_when_finished "rm -rf bare.git" &&
    -+	git init --bare bare.git &&
    ++test_expect_success 'path.superproject-root returns empty when not in a submodule' '
    ++	test_when_finished "rm -rf repo" &&
    ++	git init repo &&
     +	(
    -+		cd bare.git &&
    -+		ROOT="$(test-tool path-utils real_path .)" &&
    ++		cd repo &&
     +
    -+		echo "path.index.absolute=$ROOT/index" >expect.abs &&
    -+		git repo info path.index.absolute >actual.abs &&
    ++		echo "path.superproject-root.absolute=" >expect.abs &&
    ++		git repo info path.superproject-root.absolute >actual.abs &&
     +		test_cmp expect.abs actual.abs &&
     +
    -+		echo "path.index.relative=index" >expect.rel &&
    -+		git repo info path.index.relative >actual.rel &&
    ++		echo "path.superproject-root.relative=" >expect.rel &&
    ++		git repo info path.superproject-root.relative >actual.rel &&
     +		test_cmp expect.rel actual.rel
     +	)
     +'
     +
    - test_expect_success 'path.superproject-root absolute and relative' '
    - 	test_when_finished "rm -rf sub super" &&
    - 	git init sub &&
    + test_expect_success 'path.toplevel absolute and relative' '
    + 	test_when_finished "rm -rf repo" &&
    + 	git init repo &&
2:  dcbaf1cb96 = 4:  09d2937fcd repo: add path.hooks with absolute and relative suffixes
-:  ---------- > 5:  d0c186f150 repo: add path.index with absolute and relative suffixes
4:  ced5b0cb8b = 6:  351b4b7de7 repo: add path.grafts with absolute and relative suffixes
5:  ac766e49e3 = 7:  ef69d4aa8c repo: add path.git-prefix
6:  0a0c3dd924 ! 8:  3e61728dba repo: add path.cdup
    @@ Documentation/git-repo.adoc: values that they return:
      	The object format (hash algorithm) used in the repository.
      
     +`path.cdup`::
    -+	The path to the root of the working tree relative to the current
    -+	working directory. Returns the empty string when the current
    -+	working directory is the root of the working tree.
    ++	When the command is invoked from a subdirectory, show the
    ++	path of the top-level directory relative to the current
    ++	directory (typically a sequence of "../", or an empty string).
     +
      `path.commondir.absolute`::
      	The canonical absolute path to the Git repository's common
    @@ builtin/repo.c: static int get_object_format(struct repository *repo, struct str
     +{
     +	const char *pfx = repo->prefix;
     +
    ++	if (!is_inside_work_tree(repo)) {
    ++		const char *worktree = repo_get_work_tree(repo);
    ++
    ++		if (worktree) {
    ++			strbuf_addstr(buf, worktree);
    ++		}
    ++	}
    ++
     +	while (pfx) {
     +		pfx = strchr(pfx, '/');
     +		if (pfx) {
    @@ t/t1900-repo-info.sh: test_repo_info_path 'commondir with only GIT_DIR' 'commond
     +		test_cmp expect actual
     +	)
     +'
    ++
    ++test_expect_success 'path.cdup cwd outside the working tree' '
    ++	test_when_finished "rm -rf repo" &&
    ++	mkdir -p repo/tmp/x &&
    ++	cd repo &&
    ++	git init test &&
    ++	(
    ++		echo path.cdup=$(pwd)/tmp/x >./test/expect &&
    ++		cd test &&
    ++		GIT_WORK_TREE=../tmp/x &&
    ++		export GIT_WORK_TREE &&
    ++		GIT_DIR=$(pwd)/.git &&
    ++		export GIT_DIR &&
    ++		git repo info path.cdup >actual &&
    ++		test_cmp expect actual
    ++	)
    ++'
     +
      test_expect_success 'path.git-prefix at repository root' '
      	test_when_finished "rm -rf repo" &&
-- 
2.56.0-rc2
K JayatheerthSep 27, 2026, 11:44 UTC in reply to K Jayatheerth on lore

[GSoC Patch v7 1/8] repo: add path.toplevel with absolute and relative suffix formatting

Scripts frequently need to find the root directory of a repository's working tree. Currently, this requires using `git rev-parse --show-toplevel` or inferring it from other repository information.

Introduce `path.toplevel.absolute` and `path.toplevel.relative` keys to `git repo info`. This allows scripts to retrieve the top-level working tree path in a predictable, strictly formatted manner without relying on `rev-parse`.

If requested in a bare repository where no working tree exists, the command returns an empty string.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 10 ++++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 35 +++++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+)
Show changes to 3 files +69 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index ed7d80c690..e34abe5fea 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,16 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.toplevel.absolute`::
+	The canonical absolute path to the top-level directory of the
+	repository's working tree. Outputs an empty string if the repository
+	is bare.
+
+`path.toplevel.relative`::
+	The path to the top-level directory of the repository's working
+	tree relative to the current working directory. Outputs an empty
+	string if the repository is bare.
+
 `references.format`::
 	The reference storage format. The valid values are:
 +
diff --git a/builtin/repo.c b/builtin/repo.c
index 84e012f83f..c31e9cfa70 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -121,6 +121,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *work_tree = repo_get_work_tree(repo);
+
+	if (!work_tree)
+		return 0;
+
+	format_path(buf, work_tree, "", PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *work_tree = repo_get_work_tree(repo);
+
+	if (!work_tree)
+		return 0;
+
+	format_path(buf, work_tree, repo->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_references_format(struct repository *repo, struct strbuf *buf)
 {
 	strbuf_addstr(buf,
@@ -137,6 +159,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.toplevel.absolute", get_path_toplevel_absolute },
+	{ "path.toplevel.relative", get_path_toplevel_relative },
 	{ "references.format", get_references_format },
 };
 
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index c85d390f43..9417d1ab65 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,4 +213,39 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.toplevel absolute and relative' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		mkdir -p repo/sub &&
+		cd repo/sub &&
+
+		ROOT="$(test-tool path-utils real_path ..)" &&
+
+		echo "path.toplevel.absolute=$ROOT" >expect.abs &&
+		git repo info path.toplevel.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.toplevel.relative=../" >expect.rel &&
+		git repo info path.toplevel.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
+test_expect_success 'path.toplevel absolute and relative in a bare repository' '
+	test_when_finished "rm -rf bare.git" &&
+	git init --bare bare.git &&
+	(
+		cd bare.git &&
+
+		echo "path.toplevel.absolute=" >expect.abs &&
+		git repo info path.toplevel.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.toplevel.relative=" >expect.rel &&
+		git repo info path.toplevel.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
 test_done
-- 
2.56.0-rc2
K JayatheerthSep 27, 2026, 11:44 UTC in reply to K Jayatheerth on lore

[GSoC Patch v7 2/8] submodule: use repository to find superproject

`get_superproject_working_tree()` currently uses `xgetcwd()` to determine the repository whose superproject should be checked.

This is incorrect when `--git-dir` points to a repository different from the one associated with the current working directory. In that case, the current working directory may cause the function to return the wrong superproject or an empty result.

Pass the `repository` to `get_superproject_working_tree()` so that the superproject is determined from the repository being inspected rather than the current working directory.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 builtin/rev-parse.c        |  2 +-
 submodule.c                | 43 +++++++++++++++++++-------------------
 submodule.h                |  2 +-
 t/t7400-submodule-basic.sh | 19 +++++++++++++++++
 4 files changed, 43 insertions(+), 23 deletions(-)
Show changes to 4 files +43 −23

builtin/rev-parse.c, submodule.c, submodule.h, t/t7400-submodule-basic.sh

diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 43693454d5..e1a6da0076 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -997,7 +997,7 @@ int cmd_rev_parse(int argc,
 			}
 			if (!strcmp(arg, "--show-superproject-working-tree")) {
 				struct strbuf superproject = STRBUF_INIT;
-				if (get_superproject_working_tree(&superproject))
+				if (get_superproject_working_tree(the_repository, &superproject))
 					print_path(superproject.buf, prefix, format, DEFAULT_UNMODIFIED);
 				strbuf_release(&superproject);
 				continue;
diff --git a/submodule.c b/submodule.c
index 6fcb606f7e..4db90ef9b0 100644
--- a/submodule.c
+++ b/submodule.c
@@ -2610,34 +2610,35 @@ void absorb_git_dir_into_superproject(const char *path,
 	absorb_git_dir_into_superproject_recurse(path, super_prefix);
 }
 
-int get_superproject_working_tree(struct strbuf *buf)
+int get_superproject_working_tree(struct repository *r, struct strbuf *buf)
 {
 	struct child_process cp = CHILD_PROCESS_INIT;
 	struct strbuf sb = STRBUF_INIT;
 	struct strbuf one_up = STRBUF_INIT;
-	char *cwd = xgetcwd();
+	struct strbuf target_wt = STRBUF_INIT;
+	const char *worktree;
 	int ret = 0;
 	const char *subpath;
 	int code;
 	ssize_t len;
 
-	if (!is_inside_work_tree(the_repository))
-		/*
-		 * FIXME:
-		 * We might have a superproject, but it is harder
-		 * to determine.
-		 */
+	worktree = repo_get_work_tree(r);
+	if (!worktree)
+		goto out;
+
+	if (!strbuf_realpath(&target_wt, worktree, 0))
 		goto out;
 
-	if (!strbuf_realpath(&one_up, "../", 0))
+	strbuf_addf(&one_up, "%s/..", target_wt.buf);
+	if (!strbuf_realpath(&one_up, one_up.buf, 0))
 		goto out;
 
-	subpath = relative_path(cwd, one_up.buf, &sb);
+	subpath = relative_path(target_wt.buf, one_up.buf, &sb);
 
 	prepare_submodule_repo_env(&cp.env);
 	strvec_pop(&cp.env);
 
-	strvec_pushl(&cp.args, "--literal-pathspecs", "-C", "..",
+	strvec_pushl(&cp.args, "--literal-pathspecs", "-C", one_up.buf,
 		     "ls-files", "-z", "--stage", "--full-name", "--",
 		     subpath, NULL);
 	strbuf_reset(&sb);
@@ -2648,14 +2649,14 @@ int get_superproject_working_tree(struct strbuf *buf)
 	cp.git_cmd = 1;
 
 	if (start_command(&cp))
-		die(_("could not start ls-files in .."));
+		die(_("could not start ls-files in %s"), one_up.buf);
 
 	len = strbuf_read(&sb, cp.out, PATH_MAX);
 	close(cp.out);
 
 	if (starts_with(sb.buf, "160000")) {
 		int super_sub_len;
-		int cwd_len = strlen(cwd);
+		int wt_len = target_wt.len;
 		char *super_sub, *super_wt;
 
 		/*
@@ -2666,12 +2667,12 @@ int get_superproject_working_tree(struct strbuf *buf)
 		super_sub = strchr(sb.buf, '\t') + 1;
 		super_sub_len = strlen(super_sub);
 
-		if (super_sub_len > cwd_len ||
-		    strcmp(&cwd[cwd_len - super_sub_len], super_sub))
-			BUG("returned path string doesn't match cwd?");
+		if (super_sub_len > wt_len ||
+		    strcmp(&target_wt.buf[wt_len - super_sub_len], super_sub))
+			BUG("returned path string doesn't match worktree?");
 
-		super_wt = xstrdup(cwd);
-		super_wt[cwd_len - super_sub_len] = '\0';
+		super_wt = xstrdup(target_wt.buf);
+		super_wt[wt_len - super_sub_len] = '\0';
 
 		strbuf_realpath(buf, super_wt, 1);
 		ret = 1;
@@ -2681,10 +2682,10 @@ int get_superproject_working_tree(struct strbuf *buf)
 	code = finish_command(&cp);
 
 	if (code == 128)
-		/* '../' is not a git repository */
+		/* parent directory is not a git repository */
 		ret = 0;
 	else if (code == 0 && len == 0)
-		/* There is an unrelated git repository at '../' */
+		/* There is an unrelated git repository at parent directory */
 		ret = 0;
 	else if (code)
 		die(_("ls-tree returned unexpected return code %d"), code);
@@ -2692,7 +2693,7 @@ int get_superproject_working_tree(struct strbuf *buf)
 out:
 	strbuf_release(&sb);
 	strbuf_release(&one_up);
-	free(cwd);
+	strbuf_release(&target_wt);
 	return ret;
 }
 
diff --git a/submodule.h b/submodule.h
index b10e16e6c0..1a465a1208 100644
--- a/submodule.h
+++ b/submodule.h
@@ -170,6 +170,6 @@ void absorb_git_dir_into_superproject(const char *path,
  * project is a submodule of. If this repository is not a submodule of
  * another repository, return 0.
  */
-int get_superproject_working_tree(struct strbuf *buf);
+int get_superproject_working_tree(struct repository *r, struct strbuf *buf);
 
 #endif
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index eefdecb0bd..5c3d9f2829 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -1549,4 +1549,23 @@ test_expect_success 'submodule add fails when name is reused' '
 	)
 '
 
+test_expect_success 'path.superproject-root works with --git-dir' '
+	test_when_finished "rm -rf sub super" &&
+	git init sub &&
+	test_commit -C sub initial &&
+	git init super &&
+	(
+		cd super &&
+		git -c protocol.file.allow=always submodule add "../sub" sub &&
+		git commit -m "add submodule" &&
+
+		SUPER_ROOT="$(test-tool path-utils real_path .)" &&
+		MODULE_DIR="$SUPER_ROOT/.git/modules/sub" &&
+
+		echo "path.superproject-root.absolute=$SUPER_ROOT" >expect &&
+		git --git-dir="$MODULE_DIR" repo info path.superproject-root.absolute >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done
-- 
2.56.0-rc2
K JayatheerthSep 27, 2026, 11:44 UTC in reply to K Jayatheerth on lore

[GSoC Patch v7 3/8] repo: add path.superproject-root with absolute and relative suffixes

Scripts working in multi-repository setups often need to identify the top-level working tree of a superproject from within a submodule. Currently, this is only exposed via `git rev-parse --show-superproject-working-tree`.

Introduce `path.superproject-root.absolute` and `path.superproject-root.relative` keys to `git repo info`. This exposes the core submodule context via a scriptable config-like key using standard format rules.

If requested when not inside a submodule, the command returns an empty string.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 10 ++++++++++
 builtin/repo.c              | 31 +++++++++++++++++++++++++++++
 t/t1900-repo-info.sh        | 39 +++++++++++++++++++++++++++++++++++++
 3 files changed, 80 insertions(+)
Show changes to 3 files +80 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index e34abe5fea..e524a07f53 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,16 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.superproject-root.absolute`::
+	The canonical absolute path to the working tree root of the superproject
+	if the current repository is an initialized submodule. Outputs an empty
+	string if not in a submodule.
+
+`path.superproject-root.relative`::
+	The path to the working tree root of the superproject relative to the
+	current working directory if the current repository is an initialized
+	submodule. Outputs an empty string if not in a submodule.
+
 `path.toplevel.absolute`::
 	The canonical absolute path to the top-level directory of the
 	repository's working tree. Outputs an empty string if the repository
diff --git a/builtin/repo.c b/builtin/repo.c
index c31e9cfa70..27ebb7a1c9 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -18,6 +18,7 @@
 #include "strbuf.h"
 #include "string-list.h"
 #include "shallow.h"
+#include "submodule.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "utf8.h"
@@ -121,6 +122,34 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf superproject = STRBUF_INIT;
+
+	if (!get_superproject_working_tree(repo, &superproject)) {
+		strbuf_release(&superproject);
+		return 0;
+	}
+
+	format_path(buf, superproject.buf, "", PATH_FORMAT_CANONICAL);
+	strbuf_release(&superproject);
+	return 0;
+}
+
+static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf superproject = STRBUF_INIT;
+
+	if (!get_superproject_working_tree(repo, &superproject)) {
+		strbuf_release(&superproject);
+		return 0;
+	}
+
+	format_path(buf, superproject.buf, repo->prefix, PATH_FORMAT_RELATIVE);
+	strbuf_release(&superproject);
+	return 0;
+}
+
 static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *work_tree = repo_get_work_tree(repo);
@@ -159,6 +188,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.superproject-root.absolute", get_path_superproject_absolute },
+	{ "path.superproject-root.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
 	{ "path.toplevel.relative", get_path_toplevel_relative },
 	{ "references.format", get_references_format },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 9417d1ab65..eec576a1d9 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -213,6 +213,45 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.superproject-root absolute and relative' '
+	test_when_finished "rm -rf sub super" &&
+	git init sub &&
+	test_commit -C sub initial &&
+	git init super &&
+	(
+		cd super &&
+		git -c protocol.file.allow=always submodule add "../sub" sub &&
+		git commit -m "add submodule" &&
+
+		cd sub &&
+		ROOT="$(test-tool path-utils real_path ..)" &&
+
+		echo "path.superproject-root.absolute=$ROOT" >expect.abs &&
+		git repo info path.superproject-root.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.superproject-root.relative=../" >expect.rel &&
+		git repo info path.superproject-root.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
+test_expect_success 'path.superproject-root returns empty when not in a submodule' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		echo "path.superproject-root.absolute=" >expect.abs &&
+		git repo info path.superproject-root.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.superproject-root.relative=" >expect.rel &&
+		git repo info path.superproject-root.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
 test_expect_success 'path.toplevel absolute and relative' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
-- 
2.56.0-rc2
K JayatheerthSep 27, 2026, 11:44 UTC in reply to K Jayatheerth on lore

[GSoC Patch v7 4/8] repo: add path.hooks with absolute and relative suffixes

Hooks are an integral part of a repository's configuration and are commonly used by tooling to automate repository-specific workflows. Currently, scripts typically retrieve the hooks directory by invoking `git rev-parse --git-path hooks`.

Introduce `path.hooks.absolute` and `path.hooks.relative` keys to `git repo info`. This exposes the hooks directory as a scriptable config-like key using standard format rules, allowing scripts to retrieve it through the same interface as other repository path information.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  9 +++++++++
 builtin/repo.c              | 22 ++++++++++++++++++++++
 t/t1900-repo-info.sh        | 28 ++++++++++++++++++++++++++--
 3 files changed, 57 insertions(+), 2 deletions(-)
Show changes to 3 files +57 −2

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index e524a07f53..20836cf8f6 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,15 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.hooks.absolute`::
+	The canonical absolute path to the repository's hooks directory.
+	Respects the `core.hooksPath` configuration. If `core.hooksPath` is
+	set to `/dev/null`, that value is returned unchanged.
+
+`path.hooks.relative`::
+	The path to the repository's hooks directory relative to the current
+	working directory. Respects the `core.hooksPath` configuration.
+
 `path.superproject-root.absolute`::
 	The canonical absolute path to the working tree root of the superproject
 	if the current repository is an initialized submodule. Outputs an empty
diff --git a/builtin/repo.c b/builtin/repo.c
index 27ebb7a1c9..01666dc17c 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,26 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf hooks_path = STRBUF_INIT;
+
+	repo_git_path_replace(repo, &hooks_path, "hooks");
+	format_path(buf, hooks_path.buf, "", PATH_FORMAT_CANONICAL);
+	strbuf_release(&hooks_path);
+	return 0;
+}
+
+static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
+{
+	struct strbuf hooks_path = STRBUF_INIT;
+
+	repo_git_path_replace(repo, &hooks_path, "hooks");
+	format_path(buf, hooks_path.buf, repo->prefix, PATH_FORMAT_RELATIVE);
+	strbuf_release(&hooks_path);
+	return 0;
+}
+
 static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)
 {
 	struct strbuf superproject = STRBUF_INIT;
@@ -188,6 +208,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.hooks.absolute", get_path_hooks_absolute },
+	{ "path.hooks.relative", get_path_hooks_relative },
 	{ "path.superproject-root.absolute", get_path_superproject_absolute },
 	{ "path.superproject-root.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index eec576a1d9..1da5db4942 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -174,7 +174,11 @@ test_repo_info_path () {
 			cd repo/sub &&
 			ROOT="$(test-tool path-utils real_path ..)" && export ROOT &&
 			eval "$init_command" &&
-			echo "path.$field_name.absolute=$ROOT/$expected_dir" >expect &&
+			case "$expected_dir" in
+			/*) EXPECT_ABS="$expected_dir" ;;
+			*) EXPECT_ABS="$ROOT/$expected_dir" ;;
+			esac &&
+			echo "path.$field_name.absolute=$EXPECT_ABS" >expect &&
 			git repo info "path.$field_name.absolute" >actual &&
 			test_cmp expect actual
 		)
@@ -188,7 +192,11 @@ test_repo_info_path () {
 			cd repo/sub &&
 			ROOT="$(test-tool path-utils real_path ..)" && export ROOT &&
 			eval "$init_command" &&
-			echo "path.$field_name.relative=../$expected_dir" >expect &&
+			case "$expected_dir" in
+			/*) EXPECT_REL="$(test-tool path-utils relative_path "$expected_dir" "$PWD")" ;;
+			*) EXPECT_REL="../$expected_dir" ;;
+			esac &&
+			echo "path.$field_name.relative=$EXPECT_REL" >expect &&
 			git repo info "path.$field_name.relative" >actual &&
 			test_cmp expect actual
 		)
@@ -213,6 +221,22 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'
+
+test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
+	'custom-hooks' \
+	'git config core.hooksPath "$ROOT/custom-hooks" && mkdir -p "$ROOT/custom-hooks"'
+
+# /dev/null is not a real, canonicalizable filesystem path on Windows,
+# so path resolution for core.hooksPath=/dev/null cannot be expected to
+# produce a literal "/dev/null" the way it does on POSIX systems.
+if ! test_have_prereq MINGW
+then
+	test_repo_info_path 'hooks with core.hooksPath=/dev/null' 'hooks' \
+		'/dev/null' \
+		'git config core.hooksPath /dev/null'
+fi
+
 test_expect_success 'path.superproject-root absolute and relative' '
 	test_when_finished "rm -rf sub super" &&
 	git init sub &&
-- 
2.56.0-rc2
K JayatheerthSep 27, 2026, 11:44 UTC in reply to K Jayatheerth on lore

[GSoC Patch v7 5/8] repo: add path.index with absolute and relative suffixes

The repository index is a fundamental component used by Git and related tooling to track the working tree state. Scripts that interact with the index currently retrieve its location by invoking `git rev-parse --git-path index`.

Introduce `path.index.absolute` and `path.index.relative` keys to `git repo info`. This exposes the index file location as a scriptable config-like key using standard format rules, allowing scripts to retrieve it through the same interface as other repository path information.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 12 ++++++++++++
 builtin/repo.c              | 18 ++++++++++++++++++
 t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++
 3 files changed, 53 insertions(+)
Show changes to 3 files +53 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 20836cf8f6..08ef47750c 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -128,6 +128,18 @@ values that they return:
 	The path to the repository's hooks directory relative to the current
 	working directory. Respects the `core.hooksPath` configuration.
 
+`path.index.absolute`::
+	The canonical absolute path to the repository's current index file.
+	Respects the `GIT_INDEX_FILE` environment override. Returns the
+	configured index path even if the repository is bare or the file does
+	not exist.
+
+`path.index.relative`::
+	The path to the repository's current index file relative to the current
+	working directory. Respects the `GIT_INDEX_FILE` environment override.
+	Returns the configured index path even if the repository is bare or the
+	file does not exist.
+
 `path.superproject-root.absolute`::
 	The canonical absolute path to the working tree root of the superproject
 	if the current repository is an initialized submodule. Outputs an empty
diff --git a/builtin/repo.c b/builtin/repo.c
index 01666dc17c..a4ea2022eb 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -142,6 +142,22 @@ static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_index_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *index_file = repo_get_index_file(repo);
+
+	format_path(buf, index_file, "", PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_index_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *index_file = repo_get_index_file(repo);
+
+	format_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_superproject_absolute(struct repository *repo, struct strbuf *buf)
 {
 	struct strbuf superproject = STRBUF_INIT;
@@ -210,6 +226,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.gitdir.relative", get_path_gitdir_relative },
 	{ "path.hooks.absolute", get_path_hooks_absolute },
 	{ "path.hooks.relative", get_path_hooks_relative },
+	{ "path.index.absolute", get_path_index_absolute },
+	{ "path.index.relative", get_path_index_relative },
 	{ "path.superproject-root.absolute", get_path_superproject_absolute },
 	{ "path.superproject-root.relative", get_path_superproject_relative },
 	{ "path.toplevel.absolute", get_path_toplevel_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 1da5db4942..431a4842d4 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -237,6 +237,29 @@ then
 		'git config core.hooksPath /dev/null'
 fi
 
+test_repo_info_path 'index standard' 'index' '.git/index'
+
+test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \
+	'custom-index-file' \
+	'GIT_INDEX_FILE="$ROOT/custom-index-file" && export GIT_INDEX_FILE'
+
+test_expect_success 'path.index in a bare repository returns default index location' '
+	test_when_finished "rm -rf bare.git" &&
+	git init --bare bare.git &&
+	(
+		cd bare.git &&
+		ROOT="$(test-tool path-utils real_path .)" &&
+
+		echo "path.index.absolute=$ROOT/index" >expect.abs &&
+		git repo info path.index.absolute >actual.abs &&
+		test_cmp expect.abs actual.abs &&
+
+		echo "path.index.relative=index" >expect.rel &&
+		git repo info path.index.relative >actual.rel &&
+		test_cmp expect.rel actual.rel
+	)
+'
+
 test_expect_success 'path.superproject-root absolute and relative' '
 	test_when_finished "rm -rf sub super" &&
 	git init sub &&
-- 
2.56.0-rc2
K JayatheerthSep 27, 2026, 11:44 UTC in reply to K Jayatheerth on lore

[GSoC Patch v7 6/8] repo: add path.grafts with absolute and relative suffixes

The repository grafts file specifies alternate parent relationships for commits and may be used by repository tooling that needs to inspect or manage grafts. Scripts currently retrieve its location by invoking `git rev-parse --git-path info/grafts`.

Introduce `path.grafts.absolute` and `path.grafts.relative` keys to `git repo info`. This exposes the grafts file location as a scriptable config-like key using standard format rules, allowing scripts to retrieve it through the same interface as other repository path information.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc | 11 +++++++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        |  6 ++++++
 3 files changed, 41 insertions(+)
Show changes to 3 files +41 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 08ef47750c..868ab0ed9f 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -119,6 +119,17 @@ values that they return:
 `path.gitdir.relative`::
 	The path to the Git repository directory relative to the current working directory.
 
+`path.grafts.absolute`::
+	The canonical absolute path to the repository's graft file.
+	Respects the `GIT_GRAFT_FILE` environment override. The path is
+	returned regardless of whether the file currently exists on disk.
+
+`path.grafts.relative`::
+	The path to the repository's graft file relative to the current
+	working directory. Respects the `GIT_GRAFT_FILE` environment
+	override. The path is returned regardless of whether the file
+	currently exists on disk.
+
 `path.hooks.absolute`::
 	The canonical absolute path to the repository's hooks directory.
 	Respects the `core.hooksPath` configuration. If `core.hooksPath` is
diff --git a/builtin/repo.c b/builtin/repo.c
index a4ea2022eb..8144b74361 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -122,6 +122,28 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_grafts_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *graft_file = repo_get_graft_file(repo);
+
+	if (!graft_file)
+		return error(_("unable to get graft file"));
+
+	format_path(buf, graft_file, "", PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_grafts_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *graft_file = repo_get_graft_file(repo);
+
+	if (!graft_file)
+		return error(_("unable to get graft file"));
+
+	format_path(buf, graft_file, repo->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf)
 {
 	struct strbuf hooks_path = STRBUF_INIT;
@@ -224,6 +246,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
+	{ "path.grafts.absolute", get_path_grafts_absolute },
+	{ "path.grafts.relative", get_path_grafts_relative },
 	{ "path.hooks.absolute", get_path_hooks_absolute },
 	{ "path.hooks.relative", get_path_hooks_relative },
 	{ "path.index.absolute", get_path_index_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 431a4842d4..adc4a92487 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -221,6 +221,12 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'grafts standard' 'grafts' '.git/info/grafts'
+
+test_repo_info_path 'grafts with GIT_GRAFT_FILE override' 'grafts' \
+	'custom-graft-file' \
+	'GIT_GRAFT_FILE="$ROOT/custom-graft-file" && export GIT_GRAFT_FILE'
+
 test_repo_info_path 'hooks standard' 'hooks' '.git/hooks'
 
 test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \
-- 
2.56.0-rc2
K JayatheerthSep 27, 2026, 11:44 UTC in reply to K Jayatheerth on lore

[GSoC Patch v7 7/8] repo: add path.git-prefix

Scripts sometimes need the path from the repository's working tree root to the current working directory. While this information can be derived through existing Git commands, `git repo info` does not currently expose it as a scriptable key.

Introduce the `path.git-prefix` key to `git repo info`. The key returns the path from the working tree root to the current working directory, returning the empty string when invoked from the working tree root.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  5 +++++
 builtin/repo.c              | 11 +++++++++++
 t/t1900-repo-info.sh        | 23 +++++++++++++++++++++++
 3 files changed, 39 insertions(+)
Show changes to 3 files +39 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 868ab0ed9f..fb5aceae8f 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -113,6 +113,11 @@ values that they return:
 	The path to the Git repository's common directory relative to
 	the current working directory.
 
+`path.git-prefix`::
+	The path from the root of the working tree to the current working
+	directory. Returns the empty string when the current working directory
+	is the root of the working tree.
+
 `path.gitdir.absolute`::
 	The canonical absolute path to the Git repository directory (the `.git` directory).
 
diff --git a/builtin/repo.c b/builtin/repo.c
index 8144b74361..ce78cdc44e 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -100,6 +100,16 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b
 	return 0;
 }
 
+static int get_path_git_prefix(struct repository *repo, struct strbuf *buf)
+{
+	/*
+	 * repo->prefix is NULL when the current working directory is
+	 * the worktree root.
+	 */
+	strbuf_addstr(buf, repo->prefix ? repo->prefix : "");
+	return 0;
+}
+
 static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *git_dir = repo_get_git_dir(repo);
@@ -244,6 +254,7 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "object.format", get_object_format },
 	{ "path.commondir.absolute", get_path_commondir_absolute },
 	{ "path.commondir.relative", get_path_commondir_relative },
+	{ "path.git-prefix", get_path_git_prefix },
 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
 	{ "path.gitdir.relative", get_path_gitdir_relative },
 	{ "path.grafts.absolute", get_path_grafts_absolute },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index adc4a92487..b689445b7a 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -215,6 +215,29 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.git-prefix at repository root' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		echo "path.git-prefix=" >expect &&
+		git repo info path.git-prefix >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'path.git-prefix in subdirectory' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	mkdir -p repo/sub/dir &&
+	(
+		cd repo/sub/dir &&
+		echo "path.git-prefix=sub/dir/" >expect &&
+		git repo info path.git-prefix >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_repo_info_path 'gitdir standard' 'gitdir' '.git'
 
 test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
-- 
2.56.0-rc2
K JayatheerthSep 27, 2026, 11:44 UTC in reply to K Jayatheerth on lore

[GSoC Patch v7 8/8] repo: add path.cdup

Scripts sometimes need the relative path from the current working directory to the repository's working tree root (cdup). While this information can be retrieved through `git rev-parse --show-cdup`, `git repo info` does not currently expose it as a scriptable key.

Introduce the `path.cdup` key to `git repo info`. The key returns the path from the current working directory to the root of the working tree, returning the empty string when invoked from the working tree root.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  5 +++++
 builtin/repo.c              | 23 +++++++++++++++++++++
 t/t1900-repo-info.sh        | 40 +++++++++++++++++++++++++++++++++++++
 3 files changed, 68 insertions(+)
Show changes to 3 files +68 −0

Documentation/git-repo.adoc, builtin/repo.c, t/t1900-repo-info.sh

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index fb5aceae8f..7e14aac244 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -104,6 +104,11 @@ values that they return:
 `object.format`::
 	The object format (hash algorithm) used in the repository.
 
+`path.cdup`::
+	When the command is invoked from a subdirectory, show the
+	path of the top-level directory relative to the current
+	directory (typically a sequence of "../", or an empty string).
+
 `path.commondir.absolute`::
 	The canonical absolute path to the Git repository's common
 	directory (the shared `.git` directory containing objects,
diff --git a/builtin/repo.c b/builtin/repo.c
index ce78cdc44e..d4083abedc 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -78,6 +78,28 @@ static int get_object_format(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_cdup(struct repository *repo, struct strbuf *buf)
+{
+	const char *pfx = repo->prefix;
+
+	if (!is_inside_work_tree(repo)) {
+		const char *worktree = repo_get_work_tree(repo);
+
+		if (worktree) {
+			strbuf_addstr(buf, worktree);
+		}
+	}
+
+	while (pfx) {
+		pfx = strchr(pfx, '/');
+		if (pfx) {
+			pfx++;
+			strbuf_addstr(buf, "../");
+		}
+	}
+	return 0;
+}
+
 static int get_path_commondir_absolute(struct repository *repo, struct strbuf *buf)
 {
 	const char *common_dir = repo_get_common_dir(repo);
@@ -252,6 +274,7 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "layout.bare", get_layout_bare },
 	{ "layout.shallow", get_layout_shallow },
 	{ "object.format", get_object_format },
+	{ "path.cdup", get_path_cdup },
 	{ "path.commondir.absolute", get_path_commondir_absolute },
 	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "path.git-prefix", get_path_git_prefix },
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index b689445b7a..c600074c24 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -215,6 +215,46 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
 	'.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_expect_success 'path.cdup at repository root' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		echo "path.cdup=" >expect &&
+		git repo info path.cdup >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'path.cdup in subdirectory' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	mkdir -p repo/sub/dir &&
+	(
+		cd repo/sub/dir &&
+		echo "path.cdup=../../" >expect &&
+		git repo info path.cdup >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'path.cdup cwd outside the working tree' '
+	test_when_finished "rm -rf repo" &&
+	mkdir -p repo/tmp/x &&
+	cd repo &&
+	git init test &&
+	(
+		echo path.cdup=$(pwd)/tmp/x >./test/expect &&
+		cd test &&
+		GIT_WORK_TREE=../tmp/x &&
+		export GIT_WORK_TREE &&
+		GIT_DIR=$(pwd)/.git &&
+		export GIT_DIR &&
+		git repo info path.cdup >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_expect_success 'path.git-prefix at repository root' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
-- 
2.56.0-rc2

Back to recent threads