repo: add path.commondir with absolute and relative suffix formatting

Scripts working with worktree setups need a reliable way to discover the common directory, which diverges from the git directory when multiple worktrees are in use. There is no way to retrieve this path from git repo info today. Introduce path.commondir.absolute and path.commondir.relative keys. Exposing explicit format variants rather than a single key with a default avoids ambiguity for scripts that require predictable output. 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 Jun 24, 2026 at 09:07 UTC 1efca6d0b2304360901dccfcbd5ea4f276ecf8c9
3 files changed +87
Documentation/git-repo.adoc
+9
@@ -104,6 +104,15 @@ values that they return:
104 `object.format`::
105 The object format (hash algorithm) used in the repository.
106
107 +`path.commondir.absolute`::
108 + The canonical absolute path to the Git repository's common
109 + directory (the shared `.git` directory containing objects,
110 + refs, and global configuration).
111 +
112 +`path.commondir.relative`::
113 + The path to the Git repository's common directory relative to
114 + the current working directory.
115 +
116 `references.format`::
117 The reference storage format. The valid values are:
118 +
builtin/repo.c
+26
@@ -7,12 +7,14 @@
7 #include "hex.h"
8 #include "odb.h"
9 #include "parse-options.h"
10 +#include "path.h"
11 #include "path-walk.h"
12 #include "progress.h"
13 #include "quote.h"
14 #include "ref-filter.h"
15 #include "refs.h"
16 #include "revision.h"
17 +#include "setup.h"
18 #include "strbuf.h"
19 #include "string-list.h"
20 #include "shallow.h"
@@ -75,6 +77,28 @@ static int get_object_format(struct repository *repo, struct strbuf *buf)
77 return 0;
78 }
79
80 +static int get_path_commondir_absolute(struct repository *repo, struct strbuf *buf)
81 +{
82 + const char *common_dir = repo_get_common_dir(repo);
83 +
84 + if (!common_dir)
85 + return error(_("unable to get common directory"));
86 +
87 + format_path(buf, common_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);
88 + return 0;
89 +}
90 +
91 +static int get_path_commondir_relative(struct repository *repo, struct strbuf *buf)
92 +{
93 + const char *common_dir = repo_get_common_dir(repo);
94 +
95 + if (!common_dir)
96 + return error(_("unable to get common directory"));
97 +
98 + format_path(buf, common_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);
99 + return 0;
100 +}
101 +
102 static int get_references_format(struct repository *repo, struct strbuf *buf)
103 {
104 strbuf_addstr(buf,
@@ -87,6 +111,8 @@ static const struct repo_info_field repo_info_field[] = {
111 { "layout.bare", get_layout_bare },
112 { "layout.shallow", get_layout_shallow },
113 { "object.format", get_object_format },
114 + { "path.commondir.absolute", get_path_commondir_absolute },
115 + { "path.commondir.relative", get_path_commondir_relative },
116 { "references.format", get_references_format },
117 };
118
t/t1900-repo-info.sh
+52
@@ -155,4 +155,56 @@ test_expect_success 'git repo info -h shows only repo info usage' '
155 test_grep ! "git repo structure" actual
156 '
157
158 +# Helper function to test path keys in both absolute and relative formats.
159 +# $1: label for the test
160 +# $2: field_name (e.g., commondir)
161 +# $3: expected_dir (the directory name, e.g., .git or custom-common)
162 +# $4: init_command (extra setup like exporting env vars)
163 +test_repo_info_path () {
164 + label=$1
165 + field_name=$2
166 + expected_dir=$3
167 + init_command=$4
168 +
169 + test_expect_success "absolute: $label" '
170 + test_when_finished "rm -rf repo" &&
171 + git init repo &&
172 + (
173 + mkdir -p repo/sub &&
174 + cd repo/sub &&
175 + ROOT="$(test-tool path-utils real_path ..)" && export ROOT &&
176 + eval "$init_command" &&
177 + echo "path.$field_name.absolute=$ROOT/$expected_dir" >expect &&
178 + git repo info "path.$field_name.absolute" >actual &&
179 + test_cmp expect actual
180 + )
181 + '
182 +
183 + test_expect_success "relative: $label" '
184 + test_when_finished "rm -rf repo" &&
185 + git init repo &&
186 + (
187 + mkdir -p repo/sub &&
188 + cd repo/sub &&
189 + ROOT="$(test-tool path-utils real_path ..)" && export ROOT &&
190 + eval "$init_command" &&
191 + echo "path.$field_name.relative=../$expected_dir" >expect &&
192 + git repo info "path.$field_name.relative" >actual &&
193 + test_cmp expect actual
194 + )
195 + '
196 +}
197 +
198 +test_repo_info_path 'commondir standard' 'commondir' '.git'
199 +
200 +test_repo_info_path 'commondir with GIT_COMMON_DIR and GIT_DIR' 'commondir' \
201 + 'custom-common' \
202 + 'GIT_COMMON_DIR="$ROOT/custom-common" && export GIT_COMMON_DIR &&
203 + GIT_DIR="../.git" && export GIT_DIR &&
204 + git init --bare "$ROOT/custom-common"'
205 +
206 +test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
207 + '.git' \
208 + 'GIT_DIR="../.git" && export GIT_DIR'
209 +
210 test_done