setup: propagate prefix via repository discovery

In the preceding commits we have started to propagate all information required for the configuration of the repository via a new `struct repo_discovery`. The only exception is the repository's prefix, which we still return via the return parameter. This is conceptually fine, but somewhat inconsistent. Refactor this to instead propagate the prefix via the repository discovery, too. While at it, drop a static variable in `repo_discover_bare_gitdir()`. We apply its value to the repository discovery anyway, so we don't have to keep it around afterwards anymore. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jul 7, 2026 at 09:21 UTC 0bf8e1464331c7d611a79e7355a3826969c00405
1 file changed +45 -56
setup.c
+45 -56
@@ -1094,6 +1094,7 @@ struct repo_discovery {
1094 struct repository_format format;
1095 char *gitdir;
1096 char *worktree;
1097 + char *prefix;
1098 };
1099
1100 #define REPO_DISCOVERY_INIT { \
@@ -1105,6 +1106,7 @@ static void repo_discovery_release(struct repo_discovery *r)
1106 clear_repository_format(&r->format);
1107 free(r->gitdir);
1108 free(r->worktree);
1109 + free(r->prefix);
1110 }
1111
1112 static void repo_discovery_set_gitdir(struct repo_discovery *r,
@@ -1128,10 +1130,10 @@ static void repo_discovery_set_worktree(struct repo_discovery *r,
1130 r->worktree = real_pathdup(worktree, 1);
1131 }
1132
1131 -static const char *repo_discover_explicit_gitdir(struct repo_discovery *discovery,
1132 - const char *gitdirenv,
1133 - struct strbuf *cwd,
1134 - int *nongit_ok)
1133 +static void repo_discover_explicit_gitdir(struct repo_discovery *discovery,
1134 + const char *gitdirenv,
1135 + struct strbuf *cwd,
1136 + int *nongit_ok)
1137 {
1138 const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
1139 char *gitfile;
@@ -1149,16 +1151,13 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover
1151 if (!is_git_directory(gitdirenv)) {
1152 if (nongit_ok) {
1153 *nongit_ok = 1;
1152 - free(gitfile);
1153 - return NULL;
1154 + goto out;
1155 }
1156 die(_("not a git repository: '%s'"), gitdirenv);
1157 }
1158
1158 - if (read_and_verify_repository_format(&discovery->format, gitdirenv, nongit_ok)) {
1159 - free(gitfile);
1160 - return NULL;
1161 - }
1159 + if (read_and_verify_repository_format(&discovery->format, gitdirenv, nongit_ok))
1160 + goto out;
1161
1162 /* #3, #7, #11, #15, #19, #23, #27, #31 (see t1510) */
1163 if (work_tree_env) {
@@ -1173,8 +1172,7 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover
1172 } else if (discovery->format.is_bare > 0) {
1173 /* #18, #26 */
1174 repo_discovery_set_gitdir(discovery, gitdirenv, 0);
1176 - free(gitfile);
1177 - return NULL;
1175 + goto out;
1176 } else if (discovery->format.work_tree) { /* #6, #14 */
1177 if (is_absolute_path(discovery->format.work_tree)) {
1178 repo_discovery_set_worktree(discovery, discovery->format.work_tree);
@@ -1193,8 +1191,7 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover
1191 } else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {
1192 /* #16d */
1193 repo_discovery_set_gitdir(discovery, gitdirenv, 0);
1196 - free(gitfile);
1197 - return NULL;
1194 + goto out;
1195 } else { /* #2, #10 */
1196 repo_discovery_set_worktree(discovery, ".");
1197 }
@@ -1202,8 +1199,7 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover
1199 /* both the worktree and cwd are already normalized */
1200 if (!strcmp(cwd->buf, discovery->worktree)) { /* cwd == worktree */
1201 repo_discovery_set_gitdir(discovery, gitdirenv, 0);
1205 - free(gitfile);
1206 - return NULL;
1202 + goto out;
1203 }
1204
1205 offset = dir_inside_of(cwd->buf, discovery->worktree);
@@ -1211,38 +1207,37 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover
1207 repo_discovery_set_gitdir(discovery, gitdirenv, 1);
1208 if (chdir(discovery->worktree))
1209 die_errno(_("cannot chdir to '%s'"), discovery->worktree);
1214 - strbuf_addch(cwd, '/');
1215 - free(gitfile);
1216 - return cwd->buf + offset;
1210 + discovery->prefix = xstrfmt("%s/", cwd->buf + offset);
1211 + goto out;
1212 }
1213
1214 /* cwd outside worktree */
1215 repo_discovery_set_gitdir(discovery, gitdirenv, 0);
1216 +
1217 +out:
1218 free(gitfile);
1222 - return NULL;
1219 }
1220
1225 -static const char *repo_discover_implicit_gitdir(struct repo_discovery *discovery,
1226 - const char *gitdir,
1227 - struct strbuf *cwd, int offset,
1228 - int *nongit_ok)
1221 +static void repo_discover_implicit_gitdir(struct repo_discovery *discovery,
1222 + const char *gitdir,
1223 + struct strbuf *cwd, int offset,
1224 + int *nongit_ok)
1225 {
1226 if (read_and_verify_repository_format(&discovery->format, gitdir, nongit_ok))
1231 - return NULL;
1227 + return;
1228
1229 /* --work-tree is set without --git-dir; use discovered one */
1230 if (getenv(GIT_WORK_TREE_ENVIRONMENT) || discovery->format.work_tree) {
1231 char *to_free = NULL;
1236 - const char *ret;
1232
1233 if (offset != cwd->len && !is_absolute_path(gitdir))
1234 gitdir = to_free = real_pathdup(gitdir, 1);
1235 if (chdir(cwd->buf))
1236 die_errno(_("cannot come back to cwd"));
1242 - ret = repo_discover_explicit_gitdir(discovery, gitdir, cwd,
1243 - nongit_ok);
1237 + repo_discover_explicit_gitdir(discovery, gitdir, cwd,
1238 + nongit_ok);
1239 free(to_free);
1245 - return ret;
1240 + return;
1241 }
1242
1243 /* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
@@ -1250,7 +1245,7 @@ static const char *repo_discover_implicit_gitdir(struct repo_discovery *discover
1245 repo_discovery_set_gitdir(discovery, gitdir, (offset != cwd->len));
1246 if (chdir(cwd->buf))
1247 die_errno(_("cannot come back to cwd"));
1253 - return NULL;
1248 + return;
1249 }
1250
1251 /* #0, #1, #5, #8, #9, #12, #13 */
@@ -1258,37 +1253,34 @@ static const char *repo_discover_implicit_gitdir(struct repo_discovery *discover
1253 if (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT))
1254 repo_discovery_set_gitdir(discovery, gitdir, 0);
1255 if (offset >= cwd->len)
1261 - return NULL;
1256 + return;
1257
1258 /* Make "offset" point past the '/' (already the case for root dirs) */
1259 if (offset != offset_1st_component(cwd->buf))
1260 offset++;
1266 - /* Add a '/' at the end */
1267 - strbuf_addch(cwd, '/');
1268 - return cwd->buf + offset;
1261 + discovery->prefix = xstrfmt("%s/", cwd->buf + offset);
1262 }
1263
1264 /* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */
1272 -static const char *repo_discover_bare_gitdir(struct repo_discovery *discovery,
1273 - struct strbuf *cwd, int offset,
1274 - int *nongit_ok)
1265 +static void repo_discover_bare_gitdir(struct repo_discovery *discovery,
1266 + struct strbuf *cwd, int offset,
1267 + int *nongit_ok)
1268 {
1269 int root_len;
1270
1271 if (read_and_verify_repository_format(&discovery->format, ".", nongit_ok))
1279 - return NULL;
1272 + return;
1273
1274 setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
1275
1276 /* --work-tree is set without --git-dir; use discovered one */
1277 if (getenv(GIT_WORK_TREE_ENVIRONMENT) || discovery->format.work_tree) {
1285 - static const char *gitdir;
1286 -
1287 - gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset);
1278 + char *gitdir = offset == cwd->len ? xstrdup(".") : xmemdupz(cwd->buf, offset);
1279 if (chdir(cwd->buf))
1280 die_errno(_("cannot come back to cwd"));
1290 - return repo_discover_explicit_gitdir(discovery, gitdir, cwd,
1291 - nongit_ok);
1281 + repo_discover_explicit_gitdir(discovery, gitdir, cwd, nongit_ok);
1282 + free(gitdir);
1283 + return;
1284 }
1285
1286 if (offset != cwd->len) {
@@ -1297,10 +1289,9 @@ static const char *repo_discover_bare_gitdir(struct repo_discovery *discovery,
1289 root_len = offset_1st_component(cwd->buf);
1290 strbuf_setlen(cwd, offset > root_len ? offset : root_len);
1291 repo_discovery_set_gitdir(discovery, cwd->buf, 0);
1300 - }
1301 - else
1292 + } else {
1293 repo_discovery_set_gitdir(discovery, ".", 0);
1303 - return NULL;
1294 + }
1295 }
1296
1297 static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len)
@@ -1936,7 +1927,6 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
1927 struct strbuf cwd = STRBUF_INIT;
1928 struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;
1929 struct repo_discovery discovery = REPO_DISCOVERY_INIT;
1939 - const char *prefix = NULL;
1930
1931 /*
1932 * We may have read an incomplete configuration before
@@ -1961,20 +1951,19 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
1951
1952 switch (repo_discovery_find_dir(&dir, &gitdir, &report, 1)) {
1953 case GIT_DIR_EXPLICIT:
1964 - prefix = repo_discover_explicit_gitdir(&discovery, gitdir.buf, &cwd,
1965 - nongit_ok);
1954 + repo_discover_explicit_gitdir(&discovery, gitdir.buf, &cwd,
1955 + nongit_ok);
1956 break;
1957 case GIT_DIR_DISCOVERED:
1958 if (dir.len < cwd.len && chdir(dir.buf))
1959 die(_("cannot change to '%s'"), dir.buf);
1970 - prefix = repo_discover_implicit_gitdir(&discovery, gitdir.buf, &cwd, dir.len,
1971 - nongit_ok);
1960 + repo_discover_implicit_gitdir(&discovery, gitdir.buf, &cwd, dir.len,
1961 + nongit_ok);
1962 break;
1963 case GIT_DIR_BARE:
1964 if (dir.len < cwd.len && chdir(dir.buf))
1965 die(_("cannot change to '%s'"), dir.buf);
1976 - prefix = repo_discover_bare_gitdir(&discovery, &cwd, dir.len,
1977 - nongit_ok);
1966 + repo_discover_bare_gitdir(&discovery, &cwd, dir.len, nongit_ok);
1967 break;
1968 case GIT_DIR_HIT_CEILING:
1969 if (!nongit_ok)
@@ -2103,10 +2092,10 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
2092 * out where the repository is, i.e. a preparation
2093 * for calling repo_config_get_bool().
2094 */
2106 - if (prefix) {
2107 - prefix = precompose_string_if_needed(prefix);
2095 + if (discovery.prefix) {
2096 + const char *prefix = precompose_string_if_needed(discovery.prefix);
2097 repo->prefix = xstrdup(prefix);
2109 - setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
2098 + setenv(GIT_PREFIX_ENVIRONMENT, repo->prefix, 1);
2099 } else {
2100 FREE_AND_NULL(repo->prefix);
2101 setenv(GIT_PREFIX_ENVIRONMENT, "", 1);