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> Signed-off-by: Junio C Hamano <gitster@pobox.com>

K Jayatheerth committed Jul 26, 2026 at 16:13 UTC 0b038af81bb89712e93543b2bf672cfbb27d4c2c
3 files changed +77
Documentation/git-repo.adoc
+10
@@ -119,6 +119,16 @@ values that they return:
119 `path.gitdir.relative`::
120 The path to the Git repository directory relative to the current working directory.
121
122 +`path.superproject-working-tree.absolute`::
123 + The canonical absolute path to the working tree root of the superproject
124 + if the current repository is an initialized submodule. Outputs an empty
125 + string if not in a submodule.
126 +
127 +`path.superproject-working-tree.relative`::
128 + The path to the working tree root of the superproject relative to the
129 + current working directory if the current repository is an initialized
130 + submodule. Outputs an empty string if not in a submodule.
131 +
132 `path.toplevel.absolute`::
133 The canonical absolute path to the top-level directory of the
134 repository's working tree. Outputs an empty string if the repository
builtin/repo.c
+33
@@ -18,6 +18,7 @@
18 #include "strbuf.h"
19 #include "string-list.h"
20 #include "shallow.h"
21 +#include "submodule.h"
22 #include "tree.h"
23 #include "tree-walk.h"
24 #include "utf8.h"
@@ -121,6 +122,36 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
122 return 0;
123 }
124
125 +static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
126 +{
127 + struct strbuf superproject = STRBUF_INIT;
128 +
129 + if (!get_superproject_working_tree(&superproject)) {
130 + strbuf_release(&superproject);
131 + strbuf_addstr(buf, "");
132 + return 0;
133 + }
134 +
135 + format_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_CANONICAL);
136 + strbuf_release(&superproject);
137 + return 0;
138 +}
139 +
140 +static int get_path_superproject_relative(struct repository *repo UNUSED, struct strbuf *buf)
141 +{
142 + struct strbuf superproject = STRBUF_INIT;
143 +
144 + if (!get_superproject_working_tree(&superproject)) {
145 + strbuf_release(&superproject);
146 + strbuf_addstr(buf, "");
147 + return 0;
148 + }
149 +
150 + format_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);
151 + strbuf_release(&superproject);
152 + return 0;
153 +}
154 +
155 static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
156 {
157 const char *work_tree = repo_get_work_tree(repo);
@@ -163,6 +194,8 @@ static const struct repo_info_field repo_info_field[] = {
194 { "path.commondir.relative", get_path_commondir_relative },
195 { "path.gitdir.absolute", get_path_gitdir_absolute },
196 { "path.gitdir.relative", get_path_gitdir_relative },
197 + { "path.superproject-working-tree.absolute", get_path_superproject_absolute },
198 + { "path.superproject-working-tree.relative", get_path_superproject_relative },
199 { "path.toplevel.absolute", get_path_toplevel_absolute },
200 { "path.toplevel.relative", get_path_toplevel_relative },
201 { "references.format", get_references_format },
t/t1900-repo-info.sh
+34
@@ -213,6 +213,40 @@ test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
213 '.git' \
214 'GIT_DIR="../.git" && export GIT_DIR'
215
216 +test_expect_success 'path.superproject-working-tree absolute and relative' '
217 + test_when_finished "rm -rf sub super" &&
218 + git init sub &&
219 + test_commit -C sub initial &&
220 + git init super &&
221 + (
222 + cd super &&
223 + git -c protocol.file.allow=always submodule add "../sub" sub &&
224 + git commit -m "add submodule" &&
225 +
226 + cd sub &&
227 + ROOT="$(test-tool path-utils real_path ..)" &&
228 +
229 + echo "path.superproject-working-tree.absolute=$ROOT" >expect.abs &&
230 + git repo info path.superproject-working-tree.absolute >actual.abs &&
231 + test_cmp expect.abs actual.abs &&
232 +
233 + echo "path.superproject-working-tree.relative=../" >expect.rel &&
234 + git repo info path.superproject-working-tree.relative >actual.rel &&
235 + test_cmp expect.rel actual.rel
236 + )
237 +'
238 +
239 +test_expect_success 'path.superproject-working-tree returns empty when not in a submodule' '
240 + test_when_finished "rm -rf repo" &&
241 + git init repo &&
242 + (
243 + cd repo &&
244 + echo "path.superproject-working-tree.absolute=" >expect &&
245 + git repo info path.superproject-working-tree.absolute >actual &&
246 + test_cmp expect actual
247 + )
248 +'
249 +
250 test_expect_success 'path.toplevel absolute and relative' '
251 test_when_finished "rm -rf repo" &&
252 git init repo &&