{"thread":{"id":"65939","subject":"[PATCH v2 01/13] setup: rename `check_repository_format_gently()`","startedAt":"2026-07-07T07:21:36Z","lastAt":"2026-07-08T09:42:57Z","messageCount":18,"participants":["Patrick Steinhardt","Justin Tobler","Junio C Hamano","Toon Claes"],"isPatch":true,"patchVersion":2,"patchTotal":13},"messages":[{"id":"547298","messageId":"20260707-pks-setup-split-discovery-and-setup-v2-1-aab372cd227c@pks.im","threadId":"65939","inReplyTo":"20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im","subject":"[PATCH v2 01/13] setup: rename `check_repository_format_gently()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-07-07T07:21:20Z","receivedAt":"2026-07-07T07:21:36Z","isPatch":true,"body":"The function `check_repository_format_gently()` receives a format as\ninput. An unknowing reader may thus suspect that this function actually\nchecks the passed-in format for consistency. While the function indeed\nchecks the repository format, it actually serves two purposes:\n\n  - It reads the repository's format and populates the passed-in format\n    with that information.\n\n  - It then indeed checks whether the format is consistent.\n\nRename the function to `read_and_verify_repository_format()` to clarify\nits functionality. While at it, reorder the parameters so that the\nformat comes first to better match other functions that pass around the\nformat.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n setup.c | 38 +++++++++++++++++++-------------------\n 1 file changed, 19 insertions(+), 19 deletions(-)\n\ndiff --git a/setup.c b/setup.c\nindex 951ab9eedb..118416e350 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -749,9 +749,9 @@ static int check_repo_format(const char *var, const char *value,\n \treturn read_worktree_config(var, value, ctx, vdata);\n }\n \n-static int check_repository_format_gently(const char *gitdir,\n-\t\t\t\t\t  struct repository_format *candidate,\n-\t\t\t\t\t  int *nongit_ok)\n+static int read_and_verify_repository_format(struct repository_format *format,\n+\t\t\t\t\t     const char *gitdir,\n+\t\t\t\t\t     int *nongit_ok)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf err = STRBUF_INIT;\n@@ -759,7 +759,7 @@ static int check_repository_format_gently(const char *gitdir,\n \n \thas_common = get_common_dir(&sb, gitdir);\n \tstrbuf_addstr(&sb, \"/config\");\n-\tread_repository_format(candidate, sb.buf);\n+\tread_repository_format(format, sb.buf);\n \tstrbuf_release(&sb);\n \n \t/*\n@@ -767,10 +767,10 @@ static int check_repository_format_gently(const char *gitdir,\n \t * we treat a missing config as a silent \"ok\", even when nongit_ok\n \t * is unset.\n \t */\n-\tif (candidate->version < 0)\n+\tif (format->version < 0)\n \t\treturn 0;\n \n-\tif (verify_repository_format(candidate, &err) < 0) {\n+\tif (verify_repository_format(format, &err) < 0) {\n \t\tif (nongit_ok) {\n \t\t\twarning(\"%s\", err.buf);\n \t\t\tstrbuf_release(&err);\n@@ -780,37 +780,37 @@ static int check_repository_format_gently(const char *gitdir,\n \t\tdie(\"%s\", err.buf);\n \t}\n \n-\tstring_list_clear(&candidate->unknown_extensions, 0);\n-\tstring_list_clear(&candidate->v1_only_extensions, 0);\n+\tstring_list_clear(&format->unknown_extensions, 0);\n+\tstring_list_clear(&format->v1_only_extensions, 0);\n \n-\tif (candidate->worktree_config) {\n+\tif (format->worktree_config) {\n \t\t/*\n \t\t * pick up core.bare and core.worktree from per-worktree\n \t\t * config if present\n \t\t */\n \t\tstrbuf_addf(&sb, \"%s/config.worktree\", gitdir);\n-\t\tgit_config_from_file(read_worktree_config, sb.buf, candidate);\n+\t\tgit_config_from_file(read_worktree_config, sb.buf, format);\n \t\tstrbuf_release(&sb);\n \t\thas_common = 0;\n \t}\n \n \tif (startup_info->force_bare_repository) {\n-\t\tcandidate->is_bare = 1;\n-\t\tFREE_AND_NULL(candidate->work_tree);\n+\t\tformat->is_bare = 1;\n+\t\tFREE_AND_NULL(format->work_tree);\n \t} else if (has_common) {\n \t\t/*\n \t\t * When sharing a common dir with another repository (e.g. a\n \t\t * linked worktree), do not let this repository's config\n \t\t * dictate bareness; it is inherited from the main worktree.\n \t\t */\n-\t\tcandidate->is_bare = -1;\n+\t\tformat->is_bare = -1;\n \n \t\t/*\n \t\t * Furthermore, \"core.worktree\" is supposed to be ignored when\n \t\t * we have a commondir configured, unless it comes from the\n \t\t * per-worktree configuration.\n \t\t */\n-\t\tFREE_AND_NULL(candidate->work_tree);\n+\t\tFREE_AND_NULL(format->work_tree);\n \t}\n \n \treturn 0;\n@@ -1141,7 +1141,7 @@ static const char *setup_explicit_git_dir(struct repository *repo,\n \t\tdie(_(\"not a git repository: '%s'\"), gitdirenv);\n \t}\n \n-\tif (check_repository_format_gently(gitdirenv, repo_fmt, nongit_ok)) {\n+\tif (read_and_verify_repository_format(repo_fmt, gitdirenv, nongit_ok)) {\n \t\tfree(gitfile);\n \t\treturn NULL;\n \t}\n@@ -1218,7 +1218,7 @@ static const char *setup_discovered_git_dir(struct repository *repo,\n \t\t\t\t\t    struct repository_format *repo_fmt,\n \t\t\t\t\t    int *nongit_ok)\n {\n-\tif (check_repository_format_gently(gitdir, repo_fmt, nongit_ok))\n+\tif (read_and_verify_repository_format(repo_fmt, gitdir, nongit_ok))\n \t\treturn NULL;\n \n \t/* --work-tree is set without --git-dir; use discovered one */\n@@ -1266,7 +1266,7 @@ static const char *setup_bare_git_dir(struct repository *repo,\n {\n \tint root_len;\n \n-\tif (check_repository_format_gently(\".\", repo_fmt, nongit_ok))\n+\tif (read_and_verify_repository_format(repo_fmt, \".\", nongit_ok))\n \t\treturn NULL;\n \n \tsetenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, \"0\", 1);\n@@ -1874,7 +1874,7 @@ const char *enter_repo(struct repository *repo, const char *path, unsigned flags\n \t\tstruct strbuf err = STRBUF_INIT;\n \n \t\tset_git_dir(repo, \".\", 0);\n-\t\tcheck_repository_format_gently(\".\", &fmt, NULL);\n+\t\tread_and_verify_repository_format(&fmt, \".\", NULL);\n \t\tif (apply_repository_format(repo, &fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)\n \t\t\tdie(\"%s\", err.buf);\n \t\tstartup_info->have_repository = 1;\n@@ -2836,7 +2836,7 @@ int init_db(struct repository *repo,\n \t * config file, so this will not fail.  What we are catching\n \t * is an attempt to reinitialize new repository with an old tool.\n \t */\n-\tcheck_repository_format_gently(repo_get_git_dir(repo), &repo_fmt, NULL);\n+\tread_and_verify_repository_format(&repo_fmt, repo_get_git_dir(repo), NULL);\n \trepository_format_configure(&repo_fmt, hash, ref_storage_format);\n \tif (apply_repository_format(repo, &repo_fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)\n \t\tdie(\"%s\", err.buf);\n\n-- \n2.55.0.141.g00534a21ce.dirty\n\n"},{"id":"547299","messageId":"20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im","threadId":"65939","inReplyTo":"20260630-pks-setup-split-discovery-and-setup-v1-0-13864eb5a032@pks.im","subject":"[PATCH v2 00/13] setup: split up repository discovery and setup","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-07-07T07:21:19Z","receivedAt":"2026-07-07T07:21:37Z","isPatch":true,"body":"Hi,\n\nthis patch series is the next set of refactorings to simplify how we\nconfigure repositories in \"setup.c\".\n\nThe setup of the repository is essentially happening in two phases:\n\n  1. We discover the location of the repository as well as its format.\n\n  2. We then use this information to configure the repository.\n\nSo far so sensible. In our code base though these two phases are quite\nintertwined with one another, as we continue to repeatedly call\n`set_git_dir()` and `set_work_tree()` on the repository as we discover\nits locations. This makes it hard to follow the logic, and it basically\nleaves us with a partially-configured repository.\n\nThis patch series splits this up into two proper phases that are\ncompletely separate from one another. The first phase now populates a\n`struct repo_discovery` structure, without even having access to any\nrepository. The second phase then takes that structure and configures\nthe repository accordingly.\n\nUltimately, the motivation of this whole exercise is that eventually we\ncan unify configuration of the repository into `repo_init()` instead of\nhaving bits and pieces thereof distributed across \"repository.c\" and\n\"setup.c\".\n\nThis series is built on top of v2.55.0 with the following three branches\nmerged into it:\n\n  - ps/refs-onbranch-fixes at d6522d01df (refs: protect against\n    chicken-and-egg recursion, 2026-06-25).\n\n  - ps/setup-drop-global-state at 1ceee7431b (treewide: drop\n    USE_THE_REPOSITORY_VARIABLE, 2026-06-11).\n\n  - jk/repo-info-path-keys at 3ac28d832a (repo: add path.gitdir with\n    absolute and relative suffix formatting, 2026-06-24).\n\nChanges in v2:\n  - Expand commit message to talk about precedence order between\n    the \"GIT_SHALLOW_FILE\" environment variable and the \"--shallow-file\"\n    command line switch.\n  - Remove a now-unused parameter in `set_alternate_shallow_file()`.\n  - Fix a typo.\n  - Link to v1: https://patch.msgid.link/20260630-pks-setup-split-discovery-and-setup-v1-0-13864eb5a032@pks.im\n\nThanks!\n\nPatrick\n\n---\nPatrick Steinhardt (13):\n      setup: rename `check_repository_format_gently()`\n      setup: mark bogus worktree in `apply_repository_format()`\n      setup: unify setup of shallow file\n      setup: split up concerns of `setup_git_env_internal()`\n      setup: introduce explicit repository discovery\n      setup: embed repository format in discovery\n      setup: move prefix into repository\n      setup: drop static `cwd` variable\n      setup: propagate prefix via repository discovery\n      setup: make repository discovery self-contained\n      setup: drop redundant configuration of `startup_info->have_repository`\n      setup: pass worktree to `init_db()`\n      setup: mark `set_git_work_tree()` as file-local\n\n builtin/clone.c        |   8 +-\n builtin/init-db.c      |  34 ++--\n builtin/repo.c         |   8 +-\n builtin/rev-parse.c    |   5 +-\n builtin/update-index.c |   4 +-\n common-init.c          |  20 +++\n git.c                  |   2 +-\n object-name.c          |   4 +-\n repository.c           |   1 +\n repository.h           |   8 +\n setup.c                | 419 ++++++++++++++++++++++++++-----------------------\n setup.h                |   7 +-\n shallow.c              |   4 +-\n shallow.h              |   2 +-\n trace.c                |   4 +-\n 15 files changed, 285 insertions(+), 245 deletions(-)\n\nRange-diff versus v1:\n\n 1:  19230b18cf =  1:  f4db0a6a10 setup: rename `check_repository_format_gently()`\n 2:  246e8caf8f !  2:  72f51e01ff setup: mark bogus worktree in `apply_repository_format()`\n    @@ setup.c: static const char *setup_explicit_git_dir(struct repository *repo,\n     +\t\t * The environment variable overrides \"core.worktree\". This\n     +\t\t * also has the consequence that we don't want to flag cases as\n     +\t\t * bogus where we have both \"core.worktree\" and \"core.bare\", so\n    -+\t\t * we have to exlicitly unset the configuration.\n    ++\t\t * we have to explicitly unset the configuration.\n     +\t\t */\n     +\t\tFREE_AND_NULL(repo_fmt->work_tree);\n      \t\tset_git_work_tree(repo, work_tree_env);\n 3:  06ea13242f !  3:  1542e52523 setup: unify setup of shallow file\n    @@ Commit message\n         of this patch series. Consequently, it will become possible for us to\n         completely discard `the_repository` and populate it anew.\n     \n    +    Note that on first sight, this change looks like it might change the\n    +    precedence order. Before this change, we used to configure the shallow\n    +    file in the arguments handler first, and then it looks like we override\n    +    it via the environment variable. What's important to note though is the\n    +    last parameter to `set_alternate_shallow_file()`, which tells us whether\n    +    we want to overwrite a preexisting value, and when applying the value\n    +    from the environment we tell it not to overwrite preexisting values. So\n    +    in effect, the command line has precedence over the environment. After\n    +    this change, we now overwrite preexisting environment variables when we\n    +    see the argument, and consequently we keep the precedence order in tact.\n    +\n    +    With this change though we don't need the final parameter anymore that\n    +    tells `set_alternate_shallow_file()` whether or not to overwrite. We\n    +    only have a single callsite for this function now, and that function is\n    +    itself only ever called exactly once. Remove that parameter.\n    +\n         Signed-off-by: Patrick Steinhardt <ps@pks.im>\n     \n      ## git.c ##\n    @@ setup.c: int apply_repository_format(struct repository *repo,\n      \t\talternate_object_directories = xstrdup_or_null(getenv(ALTERNATE_DB_ENVIRONMENT));\n     +\t\tshallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);\n     +\t\tif (shallow_file)\n    -+\t\t\tset_alternate_shallow_file(repo, shallow_file, 0);\n    ++\t\t\tset_alternate_shallow_file(repo, shallow_file);\n      \t}\n      \n      \trepo->bare_cfg = format->is_bare;\n    +\n    + ## shallow.c ##\n    +@@\n    + #include \"statinfo.h\"\n    + #include \"trace.h\"\n    + \n    +-void set_alternate_shallow_file(struct repository *r, const char *path, int override)\n    ++void set_alternate_shallow_file(struct repository *r, const char *path)\n    + {\n    + \tif (r->parsed_objects->is_shallow != -1)\n    + \t\tBUG(\"is_repository_shallow must not be called before set_alternate_shallow_file\");\n    +-\tif (r->parsed_objects->alternate_shallow_file && !override)\n    +-\t\treturn;\n    + \tfree(r->parsed_objects->alternate_shallow_file);\n    + \tr->parsed_objects->alternate_shallow_file = xstrdup_or_null(path);\n    + }\n    +\n    + ## shallow.h ##\n    +@@\n    + struct oid_array;\n    + struct strvec;\n    + \n    +-void set_alternate_shallow_file(struct repository *r, const char *path, int override);\n    ++void set_alternate_shallow_file(struct repository *r, const char *path);\n    + int register_shallow(struct repository *r, const struct object_id *oid);\n    + int unregister_shallow(const struct object_id *oid);\n    + int is_repository_shallow(struct repository *r);\n 4:  add8007726 =  4:  5a2b4132a1 setup: split up concerns of `setup_git_env_internal()`\n 5:  4bc374b957 !  5:  8190a24acf setup: introduce explicit repository discovery\n    @@ setup.c: static void apply_and_export_relative_gitdir(struct repository *repo, c\n      \tint offset;\n      \n     @@ setup.c: static const char *setup_explicit_git_dir(struct repository *repo,\n    - \t\t * we have to exlicitly unset the configuration.\n    + \t\t * we have to explicitly unset the configuration.\n      \t\t */\n      \t\tFREE_AND_NULL(repo_fmt->work_tree);\n     -\t\tset_git_work_tree(repo, work_tree_env);\n 6:  687443fcac !  6:  869b6cf7cb setup: embed repository format in discovery\n    @@ setup.c: static const char *repo_discover_explicit_gitdir(struct repo_discovery\n      \t}\n     @@ setup.c: static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover\n      \t\t * bogus where we have both \"core.worktree\" and \"core.bare\", so\n    - \t\t * we have to exlicitly unset the configuration.\n    + \t\t * we have to explicitly unset the configuration.\n      \t\t */\n     -\t\tFREE_AND_NULL(repo_fmt->work_tree);\n     +\t\tFREE_AND_NULL(discovery->format.work_tree);\n 7:  6e2e1caf30 =  7:  af482fd82c setup: move prefix into repository\n 8:  9dda7f521b =  8:  8ad046bc79 setup: drop static `cwd` variable\n 9:  4fc0bbd6a2 =  9:  231c98255b setup: propagate prefix via repository discovery\n10:  54ddf6a854 = 10:  ee241b765d setup: make repository discovery self-contained\n11:  46dbc1ac4c = 11:  4fa953a970 setup: drop redundant configuration of `startup_info->have_repository`\n12:  c42085145a = 12:  4299a4aadb setup: pass worktree to `init_db()`\n13:  9a9a4dea01 = 13:  1add08fce7 setup: mark `set_git_work_tree()` as file-local\n\n---\nbase-commit: b340fc4c4f3850656b726ff757b42d2020215378\nchange-id: 20260618-pks-setup-split-discovery-and-setup-d7f23831803c\n\n"},{"id":"547300","messageId":"20260707-pks-setup-split-discovery-and-setup-v2-2-aab372cd227c@pks.im","threadId":"65939","inReplyTo":"20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im","subject":"[PATCH v2 02/13] setup: mark bogus worktree in `apply_repository_format()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-07-07T07:21:21Z","receivedAt":"2026-07-07T07:21:39Z","isPatch":true,"body":"When a repository is configured to have both \"core.worktree\" and\n\"core.bare\" we emit a warning and mark the worktree configuration as\nbogus so that the next call to `setup_work_tree()` will cause us to die.\nThis allows us to still use the misconfigured repository, at least as\nlong as we don't try to use its worktree.\n\nThis condition is handled in `setup_explicit_git_dir()`. In a subsequent\ncommit we'll refactor this function so that it doesn't receive a repo as\ninput anymore though, and consequently we cannot set the \"bogus\" bit\nanymore.\n\nMove the logic into `apply_repository_format()` instead to prepare for\nthis. While at it, fix up formatting a bit.\n\nNote that this change requires us to also explicitly unset the value of\n\"core.worktree\" in case we have the \"GIT_WORK_TREE\" environment variable\nset. This is because the environment variable overrides the repository's\nconfiguration, and we don't want to warn or die in case the work tree\nhas been configured explicitly regardless of whether or not \"core.bare\"\nis set.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n setup.c | 37 +++++++++++++++++++++----------------\n 1 file changed, 21 insertions(+), 16 deletions(-)\n\ndiff --git a/setup.c b/setup.c\nindex 118416e350..1d8c193375 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -1147,24 +1147,24 @@ static const char *setup_explicit_git_dir(struct repository *repo,\n \t}\n \n \t/* #3, #7, #11, #15, #19, #23, #27, #31 (see t1510) */\n-\tif (work_tree_env)\n+\tif (work_tree_env) {\n+\t\t/*\n+\t\t * The environment variable overrides \"core.worktree\". This\n+\t\t * also has the consequence that we don't want to flag cases as\n+\t\t * bogus where we have both \"core.worktree\" and \"core.bare\", so\n+\t\t * we have to explicitly unset the configuration.\n+\t\t */\n+\t\tFREE_AND_NULL(repo_fmt->work_tree);\n \t\tset_git_work_tree(repo, work_tree_env);\n-\telse if (repo_fmt->is_bare > 0) {\n-\t\tif (repo_fmt->work_tree) {\n-\t\t\t/* #22.2, #30 */\n-\t\t\twarning(\"core.bare and core.worktree do not make sense\");\n-\t\t\trepo->worktree_config_is_bogus = true;\n-\t\t}\n-\n+\t} else if (repo_fmt->is_bare > 0) {\n \t\t/* #18, #26 */\n \t\tset_git_dir(repo, gitdirenv, 0);\n \t\tfree(gitfile);\n \t\treturn NULL;\n-\t}\n-\telse if (repo_fmt->work_tree) { /* #6, #14 */\n-\t\tif (is_absolute_path(repo_fmt->work_tree))\n+\t} else if (repo_fmt->work_tree) { /* #6, #14 */\n+\t\tif (is_absolute_path(repo_fmt->work_tree)) {\n \t\t\tset_git_work_tree(repo, repo_fmt->work_tree);\n-\t\telse {\n+\t\t} else {\n \t\t\tchar *core_worktree;\n \t\t\tif (chdir(gitdirenv))\n \t\t\t\tdie_errno(_(\"cannot chdir to '%s'\"), gitdirenv);\n@@ -1176,15 +1176,14 @@ static const char *setup_explicit_git_dir(struct repository *repo,\n \t\t\tset_git_work_tree(repo, core_worktree);\n \t\t\tfree(core_worktree);\n \t\t}\n-\t}\n-\telse if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {\n+\t} else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {\n \t\t/* #16d */\n \t\tset_git_dir(repo, gitdirenv, 0);\n \t\tfree(gitfile);\n \t\treturn NULL;\n-\t}\n-\telse /* #2, #10 */\n+\t} else { /* #2, #10 */\n \t\tset_git_work_tree(repo, \".\");\n+\t}\n \n \t/* set_git_work_tree() must have been called by now */\n \tworktree = repo_get_work_tree(repo);\n@@ -1768,6 +1767,12 @@ int apply_repository_format(struct repository *repo,\n \tif (verify_repository_format(format, err) < 0)\n \t\treturn -1;\n \n+\tif (format->is_bare > 0 && format->work_tree) {\n+\t\t/* #22.2, #30 */\n+\t\twarning(\"core.bare and core.worktree do not make sense\");\n+\t\trepo->worktree_config_is_bogus = true;\n+\t}\n+\n \tif (flags & APPLY_REPOSITORY_FORMAT_HONOR_ENV) {\n \t\tobject_directory = xstrdup_or_null(getenv(DB_ENVIRONMENT));\n \t\talternate_object_directories = xstrdup_or_null(getenv(ALTERNATE_DB_ENVIRONMENT));\n\n-- \n2.55.0.141.g00534a21ce.dirty\n\n"},{"id":"547301","messageId":"20260707-pks-setup-split-discovery-and-setup-v2-3-aab372cd227c@pks.im","threadId":"65939","inReplyTo":"20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im","subject":"[PATCH v2 03/13] setup: unify setup of shallow file","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-07-07T07:21:22Z","receivedAt":"2026-07-07T07:21:42Z","isPatch":true,"body":"It is possible to configure an arbitrary \"shallow\" file via two\nmechanisms, and the respective logic to handle these is split across two\nlocations:\n\n  - Via the \"GIT_SHALLOW_FILE\" environment variable, which is handled in\n    `setup_git_env_internal()`.\n\n  - Via the global \"--shallow-file=\" command line option, which is\n    handled in `handle_options()`.\n\nWe can rather easily unify this logic by not configuring the shallow\nfile in `handle_options()`, but instead overwriting the environment\nvariable. The environment variable itself is then handled inside of\n`apply_repository_format()`, which is responsible for configuring a\ndiscovered Git directory.\n\nThis new logic is similar in nature to how we handle the other global\noptions already, all of which end up setting an environment variable.\nSo for one this gives us more consistency. But more importantly, this\nchange means that `the_repository` will not contain any relevant state\nanymore before we hit `apply_repository_format()` once we're at the end\nof this patch series. Consequently, it will become possible for us to\ncompletely discard `the_repository` and populate it anew.\n\nNote that on first sight, this change looks like it might change the\nprecedence order. Before this change, we used to configure the shallow\nfile in the arguments handler first, and then it looks like we override\nit via the environment variable. What's important to note though is the\nlast parameter to `set_alternate_shallow_file()`, which tells us whether\nwe want to overwrite a preexisting value, and when applying the value\nfrom the environment we tell it not to overwrite preexisting values. So\nin effect, the command line has precedence over the environment. After\nthis change, we now overwrite preexisting environment variables when we\nsee the argument, and consequently we keep the precedence order in tact.\n\nWith this change though we don't need the final parameter anymore that\ntells `set_alternate_shallow_file()` whether or not to overwrite. We\nonly have a single callsite for this function now, and that function is\nitself only ever called exactly once. Remove that parameter.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n git.c     |  2 +-\n setup.c   | 10 +++++-----\n shallow.c |  4 +---\n shallow.h |  2 +-\n 4 files changed, 8 insertions(+), 10 deletions(-)\n\ndiff --git a/git.c b/git.c\nindex 387eabe38c..e5f1811b6b 100644\n--- a/git.c\n+++ b/git.c\n@@ -306,7 +306,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t} else if (!strcmp(cmd, \"--shallow-file\")) {\n \t\t\t(*argv)++;\n \t\t\t(*argc)--;\n-\t\t\tset_alternate_shallow_file(the_repository, (*argv)[0], 1);\n+\t\t\tsetenv(GIT_SHALLOW_FILE_ENVIRONMENT, (*argv)[0], 1);\n \t\t\tif (envchanged)\n \t\t\t\t*envchanged = 1;\n \t\t} else if (!strcmp(cmd, \"-C\")) {\ndiff --git a/setup.c b/setup.c\nindex 1d8c193375..85fad0d77d 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -1046,7 +1046,6 @@ static void setup_git_env_internal(struct repository *repo,\n \t\t\t\t   const char *git_dir)\n {\n \tchar *git_replace_ref_base;\n-\tconst char *shallow_file;\n \tconst char *replace_ref_base;\n \tstruct set_gitdir_args args = { NULL };\n \tstruct strvec to_free = STRVEC_INIT;\n@@ -1067,10 +1066,6 @@ static void setup_git_env_internal(struct repository *repo,\n \t\t\t\t\t\t\t  : \"refs/replace/\");\n \tupdate_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base);\n \n-\tshallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);\n-\tif (shallow_file)\n-\t\tset_alternate_shallow_file(repo, shallow_file, 0);\n-\n \tif (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))\n \t\tfetch_if_missing = 0;\n }\n@@ -1774,8 +1769,13 @@ int apply_repository_format(struct repository *repo,\n \t}\n \n \tif (flags & APPLY_REPOSITORY_FORMAT_HONOR_ENV) {\n+\t\tconst char *shallow_file;\n+\n \t\tobject_directory = xstrdup_or_null(getenv(DB_ENVIRONMENT));\n \t\talternate_object_directories = xstrdup_or_null(getenv(ALTERNATE_DB_ENVIRONMENT));\n+\t\tshallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);\n+\t\tif (shallow_file)\n+\t\t\tset_alternate_shallow_file(repo, shallow_file);\n \t}\n \n \trepo->bare_cfg = format->is_bare;\ndiff --git a/shallow.c b/shallow.c\nindex 07cae44ae5..c063b3deaf 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -21,12 +21,10 @@\n #include \"statinfo.h\"\n #include \"trace.h\"\n \n-void set_alternate_shallow_file(struct repository *r, const char *path, int override)\n+void set_alternate_shallow_file(struct repository *r, const char *path)\n {\n \tif (r->parsed_objects->is_shallow != -1)\n \t\tBUG(\"is_repository_shallow must not be called before set_alternate_shallow_file\");\n-\tif (r->parsed_objects->alternate_shallow_file && !override)\n-\t\treturn;\n \tfree(r->parsed_objects->alternate_shallow_file);\n \tr->parsed_objects->alternate_shallow_file = xstrdup_or_null(path);\n }\ndiff --git a/shallow.h b/shallow.h\nindex e20ca4c21b..6a64db42c9 100644\n--- a/shallow.h\n+++ b/shallow.h\n@@ -10,7 +10,7 @@\n struct oid_array;\n struct strvec;\n \n-void set_alternate_shallow_file(struct repository *r, const char *path, int override);\n+void set_alternate_shallow_file(struct repository *r, const char *path);\n int register_shallow(struct repository *r, const struct object_id *oid);\n int unregister_shallow(const struct object_id *oid);\n int is_repository_shallow(struct repository *r);\n\n-- \n2.55.0.141.g00534a21ce.dirty\n\n"},{"id":"547302","messageId":"20260707-pks-setup-split-discovery-and-setup-v2-4-aab372cd227c@pks.im","threadId":"65939","inReplyTo":"20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im","subject":"[PATCH v2 04/13] setup: split up concerns of `setup_git_env_internal()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-07-07T07:21:23Z","receivedAt":"2026-07-07T07:21:43Z","isPatch":true,"body":"The function `setup_git_env_internal()` does two completely unrelated\nthings:\n\n  - It configures the repository's gitdir and propagates environment\n    variables into it.\n\n  - It configures a couple of global parameters via environment\n    variables.\n\nThe function is called when we initialize the repository's path, but\nit's also called via `chdir_notify_register()` whenever we change the\ncurrent working directory. While we indeed have to reconfigure the\ngitdir in case it's a relative path, it doesn't make sense to reapply\nthe global environment variables.\n\nSplit up concerns of this function along the above delineation. Handling\nof the global environment variables is moved into `init_git()`, as they\ncan be considered part of our setup procedure.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n common-init.c | 20 ++++++++++++++++\n setup.c       | 73 +++++++++++++++++++++++------------------------------------\n 2 files changed, 48 insertions(+), 45 deletions(-)\n\ndiff --git a/common-init.c b/common-init.c\nindex 5cc73f058c..d26c9c1f20 100644\n--- a/common-init.c\n+++ b/common-init.c\n@@ -5,7 +5,10 @@\n #include \"exec-cmd.h\"\n #include \"gettext.h\"\n #include \"attr.h\"\n+#include \"odb.h\"\n+#include \"parse.h\"\n #include \"repository.h\"\n+#include \"replace-object.h\"\n #include \"setup.h\"\n #include \"strbuf.h\"\n #include \"trace2.h\"\n@@ -31,6 +34,22 @@ static void restore_sigpipe_to_default(void)\n \tsignal(SIGPIPE, SIG_DFL);\n }\n \n+static void setup_environment(void)\n+{\n+\tchar *git_replace_ref_base;\n+\tconst char *replace_ref_base;\n+\n+\tif (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))\n+\t\tdisable_replace_refs();\n+\treplace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT);\n+\tgit_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base\n+\t\t\t\t\t\t\t  : \"refs/replace/\");\n+\tupdate_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base);\n+\n+\tif (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))\n+\t\tfetch_if_missing = 0;\n+}\n+\n void init_git(const char **argv)\n {\n \tstruct strbuf tmp = STRBUF_INIT;\n@@ -51,6 +70,7 @@ void init_git(const char **argv)\n \tgit_setup_gettext();\n \n \tinitialize_repository(the_repository);\n+\tsetup_environment();\n \n \tattr_start();\n \ndiff --git a/setup.c b/setup.c\nindex 85fad0d77d..324a235dd1 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -10,7 +10,6 @@\n #include \"object-file.h\"\n #include \"object-name.h\"\n #include \"refs.h\"\n-#include \"replace-object.h\"\n #include \"repository.h\"\n #include \"config.h\"\n #include \"dir.h\"\n@@ -1042,38 +1041,19 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)\n \treturn error_code ? NULL : path;\n }\n \n-static void setup_git_env_internal(struct repository *repo,\n-\t\t\t\t   const char *git_dir)\n+static void apply_gitdir_and_environment(struct repository *repo, const char *path)\n {\n-\tchar *git_replace_ref_base;\n-\tconst char *replace_ref_base;\n-\tstruct set_gitdir_args args = { NULL };\n \tstruct strvec to_free = STRVEC_INIT;\n+\tstruct set_gitdir_args args = {\n+\t\t.commondir = getenv_safe(&to_free, GIT_COMMON_DIR_ENVIRONMENT),\n+\t\t.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT),\n+\t\t.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT),\n+\t\t.disable_ref_updates = !!getenv(GIT_QUARANTINE_ENVIRONMENT),\n+\t};\n \n-\targs.commondir = getenv_safe(&to_free, GIT_COMMON_DIR_ENVIRONMENT);\n-\targs.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT);\n-\targs.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT);\n-\tif (getenv(GIT_QUARANTINE_ENVIRONMENT))\n-\t\targs.disable_ref_updates = true;\n+\trepo_set_gitdir(repo, path, &args);\n \n-\trepo_set_gitdir(repo, git_dir, &args);\n \tstrvec_clear(&to_free);\n-\n-\tif (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))\n-\t\tdisable_replace_refs();\n-\treplace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT);\n-\tgit_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base\n-\t\t\t\t\t\t\t  : \"refs/replace/\");\n-\tupdate_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base);\n-\n-\tif (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))\n-\t\tfetch_if_missing = 0;\n-}\n-\n-static void set_git_dir_1(struct repository *repo, const char *path)\n-{\n-\txsetenv(GIT_DIR_ENVIRONMENT, path, 1);\n-\tsetup_git_env_internal(repo, path);\n }\n \n static void update_relative_gitdir(const char *name UNUSED,\n@@ -1087,11 +1067,12 @@ static void update_relative_gitdir(const char *name UNUSED,\n \ttrace_printf_key(&trace_setup_key,\n \t\t\t \"setup: move $GIT_DIR to '%s'\",\n \t\t\t path);\n-\tset_git_dir_1(repo, path);\n+\tapply_gitdir_and_environment(repo, path);\n+\txsetenv(GIT_DIR_ENVIRONMENT, path, 1);\n \tfree(path);\n }\n \n-static void set_git_dir(struct repository *repo, const char *path, int make_realpath)\n+static void apply_and_export_relative_gitdir(struct repository *repo, const char *path, int make_realpath)\n {\n \tstruct strbuf realpath = STRBUF_INIT;\n \n@@ -1100,7 +1081,9 @@ static void set_git_dir(struct repository *repo, const char *path, int make_real\n \t\tpath = realpath.buf;\n \t}\n \n-\tset_git_dir_1(repo, path);\n+\tapply_gitdir_and_environment(repo, path);\n+\txsetenv(GIT_DIR_ENVIRONMENT, path, 1);\n+\n \tif (!is_absolute_path(path))\n \t\tchdir_notify_register(NULL, update_relative_gitdir, repo);\n \n@@ -1153,7 +1136,7 @@ static const char *setup_explicit_git_dir(struct repository *repo,\n \t\tset_git_work_tree(repo, work_tree_env);\n \t} else if (repo_fmt->is_bare > 0) {\n \t\t/* #18, #26 */\n-\t\tset_git_dir(repo, gitdirenv, 0);\n+\t\tapply_and_export_relative_gitdir(repo, gitdirenv, 0);\n \t\tfree(gitfile);\n \t\treturn NULL;\n \t} else if (repo_fmt->work_tree) { /* #6, #14 */\n@@ -1173,7 +1156,7 @@ static const char *setup_explicit_git_dir(struct repository *repo,\n \t\t}\n \t} else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {\n \t\t/* #16d */\n-\t\tset_git_dir(repo, gitdirenv, 0);\n+\t\tapply_and_export_relative_gitdir(repo, gitdirenv, 0);\n \t\tfree(gitfile);\n \t\treturn NULL;\n \t} else { /* #2, #10 */\n@@ -1185,14 +1168,14 @@ static const char *setup_explicit_git_dir(struct repository *repo,\n \n \t/* both repo_get_work_tree() and cwd are already normalized */\n \tif (!strcmp(cwd->buf, worktree)) { /* cwd == worktree */\n-\t\tset_git_dir(repo, gitdirenv, 0);\n+\t\tapply_and_export_relative_gitdir(repo, gitdirenv, 0);\n \t\tfree(gitfile);\n \t\treturn NULL;\n \t}\n \n \toffset = dir_inside_of(cwd->buf, worktree);\n \tif (offset >= 0) {\t/* cwd inside worktree? */\n-\t\tset_git_dir(repo, gitdirenv, 1);\n+\t\tapply_and_export_relative_gitdir(repo, gitdirenv, 1);\n \t\tif (chdir(worktree))\n \t\t\tdie_errno(_(\"cannot chdir to '%s'\"), worktree);\n \t\tstrbuf_addch(cwd, '/');\n@@ -1201,7 +1184,7 @@ static const char *setup_explicit_git_dir(struct repository *repo,\n \t}\n \n \t/* cwd outside worktree */\n-\tset_git_dir(repo, gitdirenv, 0);\n+\tapply_and_export_relative_gitdir(repo, gitdirenv, 0);\n \tfree(gitfile);\n \treturn NULL;\n }\n@@ -1231,7 +1214,7 @@ static const char *setup_discovered_git_dir(struct repository *repo,\n \n \t/* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */\n \tif (repo_fmt->is_bare > 0) {\n-\t\tset_git_dir(repo, gitdir, (offset != cwd->len));\n+\t\tapply_and_export_relative_gitdir(repo, gitdir, (offset != cwd->len));\n \t\tif (chdir(cwd->buf))\n \t\t\tdie_errno(_(\"cannot come back to cwd\"));\n \t\treturn NULL;\n@@ -1240,7 +1223,7 @@ static const char *setup_discovered_git_dir(struct repository *repo,\n \t/* #0, #1, #5, #8, #9, #12, #13 */\n \tset_git_work_tree(repo, \".\");\n \tif (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT))\n-\t\tset_git_dir(repo, gitdir, 0);\n+\t\tapply_and_export_relative_gitdir(repo, gitdir, 0);\n \tif (offset >= cwd->len)\n \t\treturn NULL;\n \n@@ -1280,10 +1263,10 @@ static const char *setup_bare_git_dir(struct repository *repo,\n \t\t\tdie_errno(_(\"cannot come back to cwd\"));\n \t\troot_len = offset_1st_component(cwd->buf);\n \t\tstrbuf_setlen(cwd, offset > root_len ? offset : root_len);\n-\t\tset_git_dir(repo, cwd->buf, 0);\n+\t\tapply_and_export_relative_gitdir(repo, cwd->buf, 0);\n \t}\n \telse\n-\t\tset_git_dir(repo, \".\", 0);\n+\t\tapply_and_export_relative_gitdir(repo, \".\", 0);\n \treturn NULL;\n }\n \n@@ -1878,7 +1861,7 @@ const char *enter_repo(struct repository *repo, const char *path, unsigned flags\n \t\tstruct repository_format fmt = REPOSITORY_FORMAT_INIT;\n \t\tstruct strbuf err = STRBUF_INIT;\n \n-\t\tset_git_dir(repo, \".\", 0);\n+\t\tapply_and_export_relative_gitdir(repo, \".\", 0);\n \t\tread_and_verify_repository_format(&fmt, \".\", NULL);\n \t\tif (apply_repository_format(repo, &fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)\n \t\t\tdie(\"%s\", err.buf);\n@@ -2022,7 +2005,7 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \t\tstartup_info->have_repository = 1;\n \n \t/*\n-\t * Not all paths through the setup code will call 'set_git_dir()' (which\n+\t * Not all paths through the setup code will call 'apply_and_export_relative_gitdir()' (which\n \t * directly sets up the environment) so in order to guarantee that the\n \t * environment is in a consistent state after setup, explicitly setup\n \t * the environment if we have a repository.\n@@ -2040,7 +2023,7 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \t\t\tconst char *gitdir = getenv(GIT_DIR_ENVIRONMENT);\n \t\t\tif (!gitdir)\n \t\t\t\tgitdir = DEFAULT_GIT_DIR_ENVIRONMENT;\n-\t\t\tsetup_git_env_internal(repo, gitdir);\n+\t\t\tapply_gitdir_and_environment(repo, gitdir);\n \t\t}\n \n \t\tif (startup_info->have_repository) {\n@@ -2825,12 +2808,12 @@ int init_db(struct repository *repo,\n \t\tif (!exist_ok && !stat(real_git_dir, &st))\n \t\t\tdie(_(\"%s already exists\"), real_git_dir);\n \n-\t\tset_git_dir(repo, real_git_dir, 1);\n+\t\tapply_and_export_relative_gitdir(repo, real_git_dir, 1);\n \t\tgit_dir = repo_get_git_dir(repo);\n \t\tseparate_git_dir(git_dir, original_git_dir);\n \t}\n \telse {\n-\t\tset_git_dir(repo, git_dir, 1);\n+\t\tapply_and_export_relative_gitdir(repo, git_dir, 1);\n \t\tgit_dir = repo_get_git_dir(repo);\n \t}\n \tstartup_info->have_repository = 1;\n\n-- \n2.55.0.141.g00534a21ce.dirty\n\n"},{"id":"547304","messageId":"20260707-pks-setup-split-discovery-and-setup-v2-5-aab372cd227c@pks.im","threadId":"65939","inReplyTo":"20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im","subject":"[PATCH v2 05/13] setup: introduce explicit repository discovery","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-07-07T07:21:24Z","receivedAt":"2026-07-07T07:21:47Z","isPatch":true,"body":"When setting up the global repository we intermix repository discovery\nand repository configuration: we repeatedly call `set_git_work_tree()`\nand `apply_and_export_relative_gitdir()` until we're happy with the\nresult. The result of this is then a partially-configured repository\nthat we use for further setup.\n\nThis process is quite hard to follow, as it's never quite clear which\nparts of the repository have been configured already and which haven't.\nFurthermore, it means that the repository configuration is distributed\nacross many different places instead of having it neatly contained in a\nsingle location. Ultimately, this is the reason that we cannot use a\ncentral function like `repo_init()`.\n\nRefactor the logic so that we stop partially-configuring a repository\nand instead populate a new `struct repo_discovery`. This allow us to\nessentially split repository setup into two phases:\n\n  - The first phase only figures out parameters required to configure\n    the repository.\n\n  - The second phase then takes these parameters and applies them to the\n    repository.\n\nLike this, we'll never end up with a partially-configured repository and\ncan eventually extend `repo_init()` to handle the full initialization\nfor us.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n setup.c | 155 ++++++++++++++++++++++++++++++++++++++++------------------------\n 1 file changed, 98 insertions(+), 57 deletions(-)\n\ndiff --git a/setup.c b/setup.c\nindex 324a235dd1..f713d024f7 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -1090,14 +1090,47 @@ static void apply_and_export_relative_gitdir(struct repository *repo, const char\n \tstrbuf_release(&realpath);\n }\n \n-static const char *setup_explicit_git_dir(struct repository *repo,\n-\t\t\t\t\t  const char *gitdirenv,\n-\t\t\t\t\t  struct strbuf *cwd,\n-\t\t\t\t\t  struct repository_format *repo_fmt,\n-\t\t\t\t\t  int *nongit_ok)\n+struct repo_discovery {\n+\tchar *gitdir;\n+\tchar *worktree;\n+};\n+\n+#define REPO_DISCOVERY_INIT { 0 }\n+\n+static void repo_discovery_release(struct repo_discovery *r)\n+{\n+\tfree(r->gitdir);\n+\tfree(r->worktree);\n+}\n+\n+static void repo_discovery_set_gitdir(struct repo_discovery *r,\n+\t\t\t\t      const char *gitdir,\n+\t\t\t\t      int make_realpath)\n+{\n+\tfree(r->gitdir);\n+\tif (make_realpath) {\n+\t\tstruct strbuf realpath = STRBUF_INIT;\n+\t\tstrbuf_realpath(&realpath, gitdir, 1);\n+\t\tr->gitdir = strbuf_detach(&realpath, NULL);\n+\t} else {\n+\t\tr->gitdir = xstrdup(gitdir);\n+\t}\n+}\n+\n+static void repo_discovery_set_worktree(struct repo_discovery *r,\n+\t\t\t\t\tconst char *worktree)\n+{\n+\tfree(r->worktree);\n+\tr->worktree = real_pathdup(worktree, 1);\n+}\n+\n+static const char *repo_discover_explicit_gitdir(struct repo_discovery *discovery,\n+\t\t\t\t\t\t const char *gitdirenv,\n+\t\t\t\t\t\t struct strbuf *cwd,\n+\t\t\t\t\t\t struct repository_format *repo_fmt,\n+\t\t\t\t\t\t int *nongit_ok)\n {\n \tconst char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);\n-\tconst char *worktree;\n \tchar *gitfile;\n \tint offset;\n \n@@ -1133,15 +1166,15 @@ static const char *setup_explicit_git_dir(struct repository *repo,\n \t\t * we have to explicitly unset the configuration.\n \t\t */\n \t\tFREE_AND_NULL(repo_fmt->work_tree);\n-\t\tset_git_work_tree(repo, work_tree_env);\n+\t\trepo_discovery_set_worktree(discovery, work_tree_env);\n \t} else if (repo_fmt->is_bare > 0) {\n \t\t/* #18, #26 */\n-\t\tapply_and_export_relative_gitdir(repo, gitdirenv, 0);\n+\t\trepo_discovery_set_gitdir(discovery, gitdirenv, 0);\n \t\tfree(gitfile);\n \t\treturn NULL;\n \t} else if (repo_fmt->work_tree) { /* #6, #14 */\n \t\tif (is_absolute_path(repo_fmt->work_tree)) {\n-\t\t\tset_git_work_tree(repo, repo_fmt->work_tree);\n+\t\t\trepo_discovery_set_worktree(discovery, repo_fmt->work_tree);\n \t\t} else {\n \t\t\tchar *core_worktree;\n \t\t\tif (chdir(gitdirenv))\n@@ -1151,49 +1184,46 @@ static const char *setup_explicit_git_dir(struct repository *repo,\n \t\t\tcore_worktree = xgetcwd();\n \t\t\tif (chdir(cwd->buf))\n \t\t\t\tdie_errno(_(\"cannot come back to cwd\"));\n-\t\t\tset_git_work_tree(repo, core_worktree);\n+\t\t\trepo_discovery_set_worktree(discovery, core_worktree);\n \t\t\tfree(core_worktree);\n \t\t}\n \t} else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {\n \t\t/* #16d */\n-\t\tapply_and_export_relative_gitdir(repo, gitdirenv, 0);\n+\t\trepo_discovery_set_gitdir(discovery, gitdirenv, 0);\n \t\tfree(gitfile);\n \t\treturn NULL;\n \t} else { /* #2, #10 */\n-\t\tset_git_work_tree(repo, \".\");\n+\t\trepo_discovery_set_worktree(discovery, \".\");\n \t}\n \n-\t/* set_git_work_tree() must have been called by now */\n-\tworktree = repo_get_work_tree(repo);\n-\n-\t/* both repo_get_work_tree() and cwd are already normalized */\n-\tif (!strcmp(cwd->buf, worktree)) { /* cwd == worktree */\n-\t\tapply_and_export_relative_gitdir(repo, gitdirenv, 0);\n+\t/* both the worktree and cwd are already normalized */\n+\tif (!strcmp(cwd->buf, discovery->worktree)) { /* cwd == worktree */\n+\t\trepo_discovery_set_gitdir(discovery, gitdirenv, 0);\n \t\tfree(gitfile);\n \t\treturn NULL;\n \t}\n \n-\toffset = dir_inside_of(cwd->buf, worktree);\n-\tif (offset >= 0) {\t/* cwd inside worktree? */\n-\t\tapply_and_export_relative_gitdir(repo, gitdirenv, 1);\n-\t\tif (chdir(worktree))\n-\t\t\tdie_errno(_(\"cannot chdir to '%s'\"), worktree);\n+\toffset = dir_inside_of(cwd->buf, discovery->worktree);\n+\tif (offset >= 0) {\t/* cwd inside discovery->worktree? */\n+\t\trepo_discovery_set_gitdir(discovery, gitdirenv, 1);\n+\t\tif (chdir(discovery->worktree))\n+\t\t\tdie_errno(_(\"cannot chdir to '%s'\"), discovery->worktree);\n \t\tstrbuf_addch(cwd, '/');\n \t\tfree(gitfile);\n \t\treturn cwd->buf + offset;\n \t}\n \n \t/* cwd outside worktree */\n-\tapply_and_export_relative_gitdir(repo, gitdirenv, 0);\n+\trepo_discovery_set_gitdir(discovery, gitdirenv, 0);\n \tfree(gitfile);\n \treturn NULL;\n }\n \n-static const char *setup_discovered_git_dir(struct repository *repo,\n-\t\t\t\t\t    const char *gitdir,\n-\t\t\t\t\t    struct strbuf *cwd, int offset,\n-\t\t\t\t\t    struct repository_format *repo_fmt,\n-\t\t\t\t\t    int *nongit_ok)\n+static const char *repo_discover_implicit_gitdir(struct repo_discovery *discovery,\n+\t\t\t\t\t\t const char *gitdir,\n+\t\t\t\t\t\t struct strbuf *cwd, int offset,\n+\t\t\t\t\t\t struct repository_format *repo_fmt,\n+\t\t\t\t\t\t int *nongit_ok)\n {\n \tif (read_and_verify_repository_format(repo_fmt, gitdir, nongit_ok))\n \t\treturn NULL;\n@@ -1207,23 +1237,24 @@ static const char *setup_discovered_git_dir(struct repository *repo,\n \t\t\tgitdir = to_free = real_pathdup(gitdir, 1);\n \t\tif (chdir(cwd->buf))\n \t\t\tdie_errno(_(\"cannot come back to cwd\"));\n-\t\tret = setup_explicit_git_dir(repo, gitdir, cwd, repo_fmt, nongit_ok);\n+\t\tret = repo_discover_explicit_gitdir(discovery, gitdir, cwd,\n+\t\t\t\t\t\t    repo_fmt, nongit_ok);\n \t\tfree(to_free);\n \t\treturn ret;\n \t}\n \n \t/* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */\n \tif (repo_fmt->is_bare > 0) {\n-\t\tapply_and_export_relative_gitdir(repo, gitdir, (offset != cwd->len));\n+\t\trepo_discovery_set_gitdir(discovery, gitdir, (offset != cwd->len));\n \t\tif (chdir(cwd->buf))\n \t\t\tdie_errno(_(\"cannot come back to cwd\"));\n \t\treturn NULL;\n \t}\n \n \t/* #0, #1, #5, #8, #9, #12, #13 */\n-\tset_git_work_tree(repo, \".\");\n+\trepo_discovery_set_worktree(discovery, \".\");\n \tif (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT))\n-\t\tapply_and_export_relative_gitdir(repo, gitdir, 0);\n+\t\trepo_discovery_set_gitdir(discovery, gitdir, 0);\n \tif (offset >= cwd->len)\n \t\treturn NULL;\n \n@@ -1236,10 +1267,10 @@ static const char *setup_discovered_git_dir(struct repository *repo,\n }\n \n /* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */\n-static const char *setup_bare_git_dir(struct repository *repo,\n-\t\t\t\t      struct strbuf *cwd, int offset,\n-\t\t\t\t      struct repository_format *repo_fmt,\n-\t\t\t\t      int *nongit_ok)\n+static const char *repo_discover_bare_gitdir(struct repo_discovery *discovery,\n+\t\t\t\t\t     struct strbuf *cwd, int offset,\n+\t\t\t\t\t     struct repository_format *repo_fmt,\n+\t\t\t\t\t     int *nongit_ok)\n {\n \tint root_len;\n \n@@ -1255,7 +1286,8 @@ static const char *setup_bare_git_dir(struct repository *repo,\n \t\tgitdir = offset == cwd->len ? \".\" : xmemdupz(cwd->buf, offset);\n \t\tif (chdir(cwd->buf))\n \t\t\tdie_errno(_(\"cannot come back to cwd\"));\n-\t\treturn setup_explicit_git_dir(repo, gitdir, cwd, repo_fmt, nongit_ok);\n+\t\treturn repo_discover_explicit_gitdir(discovery, gitdir, cwd,\n+\t\t\t\t\t\t     repo_fmt, nongit_ok);\n \t}\n \n \tif (offset != cwd->len) {\n@@ -1263,10 +1295,10 @@ static const char *setup_bare_git_dir(struct repository *repo,\n \t\t\tdie_errno(_(\"cannot come back to cwd\"));\n \t\troot_len = offset_1st_component(cwd->buf);\n \t\tstrbuf_setlen(cwd, offset > root_len ? offset : root_len);\n-\t\tapply_and_export_relative_gitdir(repo, cwd->buf, 0);\n+\t\trepo_discovery_set_gitdir(discovery, cwd->buf, 0);\n \t}\n \telse\n-\t\tapply_and_export_relative_gitdir(repo, \".\", 0);\n+\t\trepo_discovery_set_gitdir(discovery, \".\", 0);\n \treturn NULL;\n }\n \n@@ -1525,10 +1557,10 @@ static int is_implicit_bare_repo(const char *path)\n  * the discovered .git/ directory, if any. If `gitdir` is not absolute, it\n  * is relative to `dir` (i.e. *not* necessarily the cwd).\n  */\n-static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,\n-\t\t\t\t\t\t\t  struct strbuf *gitdir,\n-\t\t\t\t\t\t\t  struct strbuf *report,\n-\t\t\t\t\t\t\t  int die_on_error)\n+static enum discovery_result repo_discovery_find_dir(struct strbuf *dir,\n+\t\t\t\t\t\t     struct strbuf *gitdir,\n+\t\t\t\t\t\t     struct strbuf *report,\n+\t\t\t\t\t\t     int die_on_error)\n {\n \tconst char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);\n \tstruct string_list ceiling_dirs = STRING_LIST_INIT_DUP;\n@@ -1695,7 +1727,7 @@ enum discovery_result discover_git_directory_reason(struct strbuf *commondir,\n \t\treturn GIT_DIR_CWD_FAILURE;\n \n \tcwd_len = dir.len;\n-\tresult = setup_git_directory_gently_1(&dir, gitdir, NULL, 0);\n+\tresult = repo_discovery_find_dir(&dir, gitdir, NULL, 0);\n \tif (result <= 0) {\n \t\tstrbuf_release(&dir);\n \t\treturn result;\n@@ -1902,6 +1934,7 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n {\n \tstatic struct strbuf cwd = STRBUF_INIT;\n \tstruct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;\n+\tstruct repo_discovery discovery = REPO_DISCOVERY_INIT;\n \tconst char *prefix = NULL;\n \tstruct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;\n \n@@ -1926,20 +1959,22 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \t\tdie_errno(_(\"Unable to read current working directory\"));\n \tstrbuf_addbuf(&dir, &cwd);\n \n-\tswitch (setup_git_directory_gently_1(&dir, &gitdir, &report, 1)) {\n+\tswitch (repo_discovery_find_dir(&dir, &gitdir, &report, 1)) {\n \tcase GIT_DIR_EXPLICIT:\n-\t\tprefix = setup_explicit_git_dir(repo, gitdir.buf, &cwd, &repo_fmt, nongit_ok);\n+\t\tprefix = repo_discover_explicit_gitdir(&discovery, gitdir.buf, &cwd,\n+\t\t\t\t\t\t       &repo_fmt, nongit_ok);\n \t\tbreak;\n \tcase GIT_DIR_DISCOVERED:\n \t\tif (dir.len < cwd.len && chdir(dir.buf))\n \t\t\tdie(_(\"cannot change to '%s'\"), dir.buf);\n-\t\tprefix = setup_discovered_git_dir(repo, gitdir.buf, &cwd, dir.len,\n-\t\t\t\t\t\t  &repo_fmt, nongit_ok);\n+\t\tprefix = repo_discover_implicit_gitdir(&discovery, gitdir.buf, &cwd, dir.len,\n+\t\t\t\t\t\t       &repo_fmt, nongit_ok);\n \t\tbreak;\n \tcase GIT_DIR_BARE:\n \t\tif (dir.len < cwd.len && chdir(dir.buf))\n \t\t\tdie(_(\"cannot change to '%s'\"), dir.buf);\n-\t\tprefix = setup_bare_git_dir(repo, &cwd, dir.len, &repo_fmt, nongit_ok);\n+\t\tprefix = repo_discover_bare_gitdir(&discovery, &cwd, dir.len,\n+\t\t\t\t\t\t   &repo_fmt, nongit_ok);\n \t\tbreak;\n \tcase GIT_DIR_HIT_CEILING:\n \t\tif (!nongit_ok)\n@@ -1980,13 +2015,13 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \tcase GIT_DIR_CWD_FAILURE:\n \tcase GIT_DIR_INVALID_FORMAT:\n \t\t/*\n-\t\t * As a safeguard against setup_git_directory_gently_1 returning\n+\t\t * As a safeguard against repo_discovery_find_dir returning\n \t\t * these values, fallthrough to BUG. Otherwise it is possible to\n \t\t * set startup_info->have_repository to 1 when we did nothing to\n \t\t * find a repository.\n \t\t */\n \tdefault:\n-\t\tBUG(\"unhandled setup_git_directory_gently_1() result\");\n+\t\tBUG(\"unhandled repo_discovery_find_dir() result\");\n \t}\n \n \t/*\n@@ -2005,10 +2040,10 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \t\tstartup_info->have_repository = 1;\n \n \t/*\n-\t * Not all paths through the setup code will call 'apply_and_export_relative_gitdir()' (which\n-\t * directly sets up the environment) so in order to guarantee that the\n-\t * environment is in a consistent state after setup, explicitly setup\n-\t * the environment if we have a repository.\n+\t * Not all paths through the setup code will have recorded a gitdir\n+\t * above, so in order to guarantee that the environment is in a\n+\t * consistent state after setup, explicitly set up the gitdir and\n+\t * environment if we have a repository.\n \t *\n \t * NEEDSWORK: currently we allow bogus GIT_DIR values to be set in some\n \t * code paths so we also need to explicitly setup the environment if\n@@ -2019,7 +2054,12 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \t    startup_info->have_repository ||\n \t    /* GIT_DIR_EXPLICIT */\n \t    getenv(GIT_DIR_ENVIRONMENT)) {\n-\t\tif (!repo->gitdir) {\n+\t\tif (discovery.worktree)\n+\t\t\tset_git_work_tree(repo, discovery.worktree);\n+\n+\t\tif (discovery.gitdir) {\n+\t\t\tapply_and_export_relative_gitdir(repo, discovery.gitdir, 0);\n+\t\t} else {\n \t\t\tconst char *gitdir = getenv(GIT_DIR_ENVIRONMENT);\n \t\t\tif (!gitdir)\n \t\t\t\tgitdir = DEFAULT_GIT_DIR_ENVIRONMENT;\n@@ -2074,6 +2114,7 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \n \tsetup_original_cwd(repo);\n \n+\trepo_discovery_release(&discovery);\n \tstrbuf_release(&dir);\n \tstrbuf_release(&gitdir);\n \tstrbuf_release(&report);\n\n-- \n2.55.0.141.g00534a21ce.dirty\n\n"},{"id":"547303","messageId":"20260707-pks-setup-split-discovery-and-setup-v2-6-aab372cd227c@pks.im","threadId":"65939","inReplyTo":"20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im","subject":"[PATCH v2 06/13] setup: embed repository format in discovery","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-07-07T07:21:25Z","receivedAt":"2026-07-07T07:21:50Z","isPatch":true,"body":"All functions related to repository discovery receive both a `struct\nrepository_discovery` and `struct repository_format` as input, and the\nexpectation is that both will be populated. Refactor this so that the\nrepository format is part of the discovery result.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n setup.c | 60 +++++++++++++++++++++++++++++-------------------------------\n 1 file changed, 29 insertions(+), 31 deletions(-)\n\ndiff --git a/setup.c b/setup.c\nindex f713d024f7..b755693572 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -1091,14 +1091,18 @@ static void apply_and_export_relative_gitdir(struct repository *repo, const char\n }\n \n struct repo_discovery {\n+\tstruct repository_format format;\n \tchar *gitdir;\n \tchar *worktree;\n };\n \n-#define REPO_DISCOVERY_INIT { 0 }\n+#define REPO_DISCOVERY_INIT { \\\n+\t.format = REPOSITORY_FORMAT_INIT, \\\n+}\n \n static void repo_discovery_release(struct repo_discovery *r)\n {\n+\tclear_repository_format(&r->format);\n \tfree(r->gitdir);\n \tfree(r->worktree);\n }\n@@ -1127,7 +1131,6 @@ static void repo_discovery_set_worktree(struct repo_discovery *r,\n static const char *repo_discover_explicit_gitdir(struct repo_discovery *discovery,\n \t\t\t\t\t\t const char *gitdirenv,\n \t\t\t\t\t\t struct strbuf *cwd,\n-\t\t\t\t\t\t struct repository_format *repo_fmt,\n \t\t\t\t\t\t int *nongit_ok)\n {\n \tconst char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);\n@@ -1152,7 +1155,7 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover\n \t\tdie(_(\"not a git repository: '%s'\"), gitdirenv);\n \t}\n \n-\tif (read_and_verify_repository_format(repo_fmt, gitdirenv, nongit_ok)) {\n+\tif (read_and_verify_repository_format(&discovery->format, gitdirenv, nongit_ok)) {\n \t\tfree(gitfile);\n \t\treturn NULL;\n \t}\n@@ -1165,22 +1168,22 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover\n \t\t * bogus where we have both \"core.worktree\" and \"core.bare\", so\n \t\t * we have to explicitly unset the configuration.\n \t\t */\n-\t\tFREE_AND_NULL(repo_fmt->work_tree);\n+\t\tFREE_AND_NULL(discovery->format.work_tree);\n \t\trepo_discovery_set_worktree(discovery, work_tree_env);\n-\t} else if (repo_fmt->is_bare > 0) {\n+\t} else if (discovery->format.is_bare > 0) {\n \t\t/* #18, #26 */\n \t\trepo_discovery_set_gitdir(discovery, gitdirenv, 0);\n \t\tfree(gitfile);\n \t\treturn NULL;\n-\t} else if (repo_fmt->work_tree) { /* #6, #14 */\n-\t\tif (is_absolute_path(repo_fmt->work_tree)) {\n-\t\t\trepo_discovery_set_worktree(discovery, repo_fmt->work_tree);\n+\t} else if (discovery->format.work_tree) { /* #6, #14 */\n+\t\tif (is_absolute_path(discovery->format.work_tree)) {\n+\t\t\trepo_discovery_set_worktree(discovery, discovery->format.work_tree);\n \t\t} else {\n \t\t\tchar *core_worktree;\n \t\t\tif (chdir(gitdirenv))\n \t\t\t\tdie_errno(_(\"cannot chdir to '%s'\"), gitdirenv);\n-\t\t\tif (chdir(repo_fmt->work_tree))\n-\t\t\t\tdie_errno(_(\"cannot chdir to '%s'\"), repo_fmt->work_tree);\n+\t\t\tif (chdir(discovery->format.work_tree))\n+\t\t\t\tdie_errno(_(\"cannot chdir to '%s'\"), discovery->format.work_tree);\n \t\t\tcore_worktree = xgetcwd();\n \t\t\tif (chdir(cwd->buf))\n \t\t\t\tdie_errno(_(\"cannot come back to cwd\"));\n@@ -1222,14 +1225,13 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover\n static const char *repo_discover_implicit_gitdir(struct repo_discovery *discovery,\n \t\t\t\t\t\t const char *gitdir,\n \t\t\t\t\t\t struct strbuf *cwd, int offset,\n-\t\t\t\t\t\t struct repository_format *repo_fmt,\n \t\t\t\t\t\t int *nongit_ok)\n {\n-\tif (read_and_verify_repository_format(repo_fmt, gitdir, nongit_ok))\n+\tif (read_and_verify_repository_format(&discovery->format, gitdir, nongit_ok))\n \t\treturn NULL;\n \n \t/* --work-tree is set without --git-dir; use discovered one */\n-\tif (getenv(GIT_WORK_TREE_ENVIRONMENT) || repo_fmt->work_tree) {\n+\tif (getenv(GIT_WORK_TREE_ENVIRONMENT) || discovery->format.work_tree) {\n \t\tchar *to_free = NULL;\n \t\tconst char *ret;\n \n@@ -1238,13 +1240,13 @@ static const char *repo_discover_implicit_gitdir(struct repo_discovery *discover\n \t\tif (chdir(cwd->buf))\n \t\t\tdie_errno(_(\"cannot come back to cwd\"));\n \t\tret = repo_discover_explicit_gitdir(discovery, gitdir, cwd,\n-\t\t\t\t\t\t    repo_fmt, nongit_ok);\n+\t\t\t\t\t\t    nongit_ok);\n \t\tfree(to_free);\n \t\treturn ret;\n \t}\n \n \t/* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */\n-\tif (repo_fmt->is_bare > 0) {\n+\tif (discovery->format.is_bare > 0) {\n \t\trepo_discovery_set_gitdir(discovery, gitdir, (offset != cwd->len));\n \t\tif (chdir(cwd->buf))\n \t\t\tdie_errno(_(\"cannot come back to cwd\"));\n@@ -1269,25 +1271,24 @@ static const char *repo_discover_implicit_gitdir(struct repo_discovery *discover\n /* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */\n static const char *repo_discover_bare_gitdir(struct repo_discovery *discovery,\n \t\t\t\t\t     struct strbuf *cwd, int offset,\n-\t\t\t\t\t     struct repository_format *repo_fmt,\n \t\t\t\t\t     int *nongit_ok)\n {\n \tint root_len;\n \n-\tif (read_and_verify_repository_format(repo_fmt, \".\", nongit_ok))\n+\tif (read_and_verify_repository_format(&discovery->format, \".\", nongit_ok))\n \t\treturn NULL;\n \n \tsetenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, \"0\", 1);\n \n \t/* --work-tree is set without --git-dir; use discovered one */\n-\tif (getenv(GIT_WORK_TREE_ENVIRONMENT) || repo_fmt->work_tree) {\n+\tif (getenv(GIT_WORK_TREE_ENVIRONMENT) || discovery->format.work_tree) {\n \t\tstatic const char *gitdir;\n \n \t\tgitdir = offset == cwd->len ? \".\" : xmemdupz(cwd->buf, offset);\n \t\tif (chdir(cwd->buf))\n \t\t\tdie_errno(_(\"cannot come back to cwd\"));\n \t\treturn repo_discover_explicit_gitdir(discovery, gitdir, cwd,\n-\t\t\t\t\t\t     repo_fmt, nongit_ok);\n+\t\t\t\t\t\t     nongit_ok);\n \t}\n \n \tif (offset != cwd->len) {\n@@ -1936,7 +1937,6 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \tstruct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;\n \tstruct repo_discovery discovery = REPO_DISCOVERY_INIT;\n \tconst char *prefix = NULL;\n-\tstruct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;\n \n \t/*\n \t * We may have read an incomplete configuration before\n@@ -1962,19 +1962,19 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \tswitch (repo_discovery_find_dir(&dir, &gitdir, &report, 1)) {\n \tcase GIT_DIR_EXPLICIT:\n \t\tprefix = repo_discover_explicit_gitdir(&discovery, gitdir.buf, &cwd,\n-\t\t\t\t\t\t       &repo_fmt, nongit_ok);\n+\t\t\t\t\t\t       nongit_ok);\n \t\tbreak;\n \tcase GIT_DIR_DISCOVERED:\n \t\tif (dir.len < cwd.len && chdir(dir.buf))\n \t\t\tdie(_(\"cannot change to '%s'\"), dir.buf);\n \t\tprefix = repo_discover_implicit_gitdir(&discovery, gitdir.buf, &cwd, dir.len,\n-\t\t\t\t\t\t       &repo_fmt, nongit_ok);\n+\t\t\t\t\t\t       nongit_ok);\n \t\tbreak;\n \tcase GIT_DIR_BARE:\n \t\tif (dir.len < cwd.len && chdir(dir.buf))\n \t\t\tdie(_(\"cannot change to '%s'\"), dir.buf);\n \t\tprefix = repo_discover_bare_gitdir(&discovery, &cwd, dir.len,\n-\t\t\t\t\t\t   &repo_fmt, nongit_ok);\n+\t\t\t\t\t\t   nongit_ok);\n \t\tbreak;\n \tcase GIT_DIR_HIT_CEILING:\n \t\tif (!nongit_ok)\n@@ -2078,21 +2078,21 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \t\t\tif (ref_backend_uri) {\n \t\t\t\tchar *format;\n \n-\t\t\t\tfree(repo_fmt.ref_storage_payload);\n+\t\t\t\tfree(discovery.format.ref_storage_payload);\n \n-\t\t\t\tparse_reference_uri(ref_backend_uri, &format, &repo_fmt.ref_storage_payload);\n-\t\t\t\trepo_fmt.ref_storage_format = ref_storage_format_by_name(format);\n-\t\t\t\tif (repo_fmt.ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)\n+\t\t\t\tparse_reference_uri(ref_backend_uri, &format, &discovery.format.ref_storage_payload);\n+\t\t\t\tdiscovery.format.ref_storage_format = ref_storage_format_by_name(format);\n+\t\t\t\tif (discovery.format.ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)\n \t\t\t\t\tdie(_(\"unknown ref storage format: '%s'\"), format);\n \n \t\t\t\tfree(format);\n \t\t\t}\n \n-\t\t\tif (apply_repository_format(repo, &repo_fmt,\n+\t\t\tif (apply_repository_format(repo, &discovery.format,\n \t\t\t\t\t\t    APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)\n \t\t\t\tdie(\"%s\", err.buf);\n \n-\t\t\tclear_repository_format(&repo_fmt);\n+\t\t\tclear_repository_format(&discovery.format);\n \t\t\tstrbuf_release(&err);\n \t\t}\n \t}\n@@ -2118,8 +2118,6 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \tstrbuf_release(&dir);\n \tstrbuf_release(&gitdir);\n \tstrbuf_release(&report);\n-\tclear_repository_format(&repo_fmt);\n-\n \treturn prefix;\n }\n \n\n-- \n2.55.0.141.g00534a21ce.dirty\n\n"},{"id":"547305","messageId":"20260707-pks-setup-split-discovery-and-setup-v2-8-aab372cd227c@pks.im","threadId":"65939","inReplyTo":"20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im","subject":"[PATCH v2 08/13] setup: drop static `cwd` variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-07-07T07:21:27Z","receivedAt":"2026-07-07T07:21:52Z","isPatch":true,"body":"The current working directory is stored as part of a static strbuf\nvariable. This variable had to have a lifetime longer than its\ncontaining function because the value we return typically points into\nthat buffer.\n\nIn the preceding commit we have moved the prefix into the repository\nthough. Consequently, we can now return the repository's prefix instead\nof the local one and thus properly manage the lifecycle of this local\nvariable.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n setup.c | 5 +++--\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/setup.c b/setup.c\nindex 6cc9fa2de8..f8e4cf100b 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -1933,7 +1933,7 @@ void set_git_work_tree(struct repository *repo, const char *new_work_tree)\n \n const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n {\n-\tstatic struct strbuf cwd = STRBUF_INIT;\n+\tstruct strbuf cwd = STRBUF_INIT;\n \tstruct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;\n \tstruct repo_discovery discovery = REPO_DISCOVERY_INIT;\n \tconst char *prefix = NULL;\n@@ -2116,9 +2116,10 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \n \trepo_discovery_release(&discovery);\n \tstrbuf_release(&dir);\n+\tstrbuf_release(&cwd);\n \tstrbuf_release(&gitdir);\n \tstrbuf_release(&report);\n-\treturn prefix;\n+\treturn repo->prefix;\n }\n \n int git_config_perm(const char *var, const char *value)\n\n-- \n2.55.0.141.g00534a21ce.dirty\n\n"},{"id":"547306","messageId":"20260707-pks-setup-split-discovery-and-setup-v2-7-aab372cd227c@pks.im","threadId":"65939","inReplyTo":"20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im","subject":"[PATCH v2 07/13] setup: move prefix into repository","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-07-07T07:21:26Z","receivedAt":"2026-07-07T07:21:52Z","isPatch":true,"body":"The repository prefix is currently stored in the startup info. This\nfeels somewhat awkward though, as it is inherently a property of a given\nrepository.\n\nMove the prefix into the repository accordingly.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/repo.c         | 8 ++++----\n builtin/rev-parse.c    | 5 +++--\n builtin/update-index.c | 4 ++--\n object-name.c          | 4 ++--\n repository.c           | 1 +\n repository.h           | 8 ++++++++\n setup.c                | 6 +++---\n setup.h                | 1 -\n trace.c                | 4 ++--\n 9 files changed, 25 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 042d6de558..84e012f83f 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -84,7 +84,7 @@ static int get_path_commondir_absolute(struct repository *repo, struct strbuf *b\n \tif (!common_dir)\n \t\treturn error(_(\"unable to get common directory\"));\n \n-\tformat_path(buf, common_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\tformat_path(buf, common_dir, repo->prefix, PATH_FORMAT_CANONICAL);\n \treturn 0;\n }\n \n@@ -95,7 +95,7 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b\n \tif (!common_dir)\n \t\treturn error(_(\"unable to get common directory\"));\n \n-\tformat_path(buf, common_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\tformat_path(buf, common_dir, repo->prefix, PATH_FORMAT_RELATIVE);\n \treturn 0;\n }\n \n@@ -106,7 +106,7 @@ static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)\n \tif (!git_dir)\n \t\treturn error(_(\"unable to get git directory\"));\n \n-\tformat_path(buf, git_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);\n+\tformat_path(buf, git_dir, repo->prefix, PATH_FORMAT_CANONICAL);\n \treturn 0;\n }\n \n@@ -117,7 +117,7 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)\n \tif (!git_dir)\n \t\treturn error(_(\"unable to get git directory\"));\n \n-\tformat_path(buf, git_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);\n+\tformat_path(buf, git_dir, repo->prefix, PATH_FORMAT_RELATIVE);\n \treturn 0;\n }\n \ndiff --git a/builtin/rev-parse.c b/builtin/rev-parse.c\nindex 5e04b0e2bd..43693454d5 100644\n--- a/builtin/rev-parse.c\n+++ b/builtin/rev-parse.c\n@@ -255,7 +255,7 @@ static int show_file(const char *arg, int output_prefix)\n \tshow_default();\n \tif ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {\n \t\tif (output_prefix) {\n-\t\t\tconst char *prefix = startup_info->prefix;\n+\t\t\tconst char *prefix = the_repository->prefix;\n \t\t\tchar *fname = prefix_filename(prefix, arg);\n \t\t\tshow(fname);\n \t\t\tfree(fname);\n@@ -832,7 +832,8 @@ int cmd_rev_parse(int argc,\n \t\t\t\tprefix = argv[++i];\n \t\t\t\tif (!prefix)\n \t\t\t\t\tdie(_(\"--prefix requires an argument\"));\n-\t\t\t\tstartup_info->prefix = prefix;\n+\t\t\t\tFREE_AND_NULL(the_repository->prefix);\n+\t\t\t\tthe_repository->prefix = xstrdup(prefix);\n \t\t\t\toutput_prefix = 1;\n \t\t\t\tcontinue;\n \t\t\t}\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 3d6646c318..f43d150eb3 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -875,7 +875,7 @@ static enum parse_opt_result unresolve_callback(\n \tconst char *arg, int unset)\n {\n \tint *has_errors = opt->value;\n-\tconst char *prefix = startup_info->prefix;\n+\tconst char *prefix = the_repository->prefix;\n \n \tBUG_ON_OPT_NEG(unset);\n \tBUG_ON_OPT_ARG(arg);\n@@ -896,7 +896,7 @@ static enum parse_opt_result reupdate_callback(\n \tconst char *arg, int unset)\n {\n \tint *has_errors = opt->value;\n-\tconst char *prefix = startup_info->prefix;\n+\tconst char *prefix = the_repository->prefix;\n \n \tBUG_ON_OPT_NEG(unset);\n \tBUG_ON_OPT_ARG(arg);\ndiff --git a/object-name.c b/object-name.c\nindex 46159466ac..fc70acc9e0 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -1708,8 +1708,8 @@ static char *resolve_relative_path(struct repository *r, const char *rel)\n \t\tdie(_(\"relative path syntax can't be used outside working tree\"));\n \n \t/* die() inside prefix_path() if resolved path is outside worktree */\n-\treturn prefix_path(the_repository, startup_info->prefix,\n-\t\t\t   startup_info->prefix ? strlen(startup_info->prefix) : 0,\n+\treturn prefix_path(the_repository, the_repository->prefix,\n+\t\t\t   the_repository->prefix ? strlen(the_repository->prefix) : 0,\n \t\t\t   rel);\n }\n \ndiff --git a/repository.c b/repository.c\nindex 73d80bcffd..2ef0778846 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -376,6 +376,7 @@ void repo_clear(struct repository *repo)\n \n \tFREE_AND_NULL(repo->gitdir);\n \tFREE_AND_NULL(repo->commondir);\n+\tFREE_AND_NULL(repo->prefix);\n \tFREE_AND_NULL(repo->graft_file);\n \tFREE_AND_NULL(repo->index_file);\n \tFREE_AND_NULL(repo->worktree);\ndiff --git a/repository.h b/repository.h\nindex 7d649e32e7..b767307911 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -52,6 +52,14 @@ struct repository {\n \t */\n \tchar *commondir;\n \n+\t/*\n+\t * The \"prefix\", a path to the current working directory relative to\n+\t * the work tree root, or NULL, if the current working directory is not\n+\t * a strict subdirectory of the work tree root. The prefix always ends\n+\t * with a '/' character.\n+\t */\n+\tchar *prefix;\n+\n \t/*\n \t * Holds any information related to accessing the raw object content.\n \t */\ndiff --git a/setup.c b/setup.c\nindex b755693572..6cc9fa2de8 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -2030,7 +2030,7 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \t * repository and that the caller expects startup_info to reflect\n \t * this.\n \t *\n-\t * Regardless of the state of nongit_ok, startup_info->prefix and\n+\t * Regardless of the state of nongit_ok, the_repository->prefix and\n \t * the GIT_PREFIX environment variable must always match. For details\n \t * see Documentation/config/alias.adoc.\n \t */\n@@ -2105,10 +2105,10 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \t */\n \tif (prefix) {\n \t\tprefix = precompose_string_if_needed(prefix);\n-\t\tstartup_info->prefix = prefix;\n+\t\trepo->prefix = xstrdup(prefix);\n \t\tsetenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);\n \t} else {\n-\t\tstartup_info->prefix = NULL;\n+\t\tFREE_AND_NULL(repo->prefix);\n \t\tsetenv(GIT_PREFIX_ENVIRONMENT, \"\", 1);\n \t}\n \ndiff --git a/setup.h b/setup.h\nindex b9fd96bea6..c01a244fe9 100644\n--- a/setup.h\n+++ b/setup.h\n@@ -299,7 +299,6 @@ struct startup_info {\n \tbool force_bare_repository;\n \n \tint have_repository;\n-\tconst char *prefix;\n \tconst char *original_cwd;\n };\n extern struct startup_info *startup_info;\ndiff --git a/trace.c b/trace.c\nindex 9b99460db8..515b99e7f5 100644\n--- a/trace.c\n+++ b/trace.c\n@@ -299,7 +299,7 @@ static const char *quote_crnl(const char *path)\n \n void trace_repo_setup(struct repository *r)\n {\n-\tconst char *git_work_tree, *prefix = startup_info->prefix;\n+\tconst char *git_work_tree, *prefix = r->prefix;\n \tchar *cwd;\n \n \tif (!trace_want(&trace_setup_key))\n@@ -310,7 +310,7 @@ void trace_repo_setup(struct repository *r)\n \tif (!(git_work_tree = repo_get_work_tree(r)))\n \t\tgit_work_tree = \"(null)\";\n \n-\tif (!startup_info->prefix)\n+\tif (!r->prefix)\n \t\tprefix = \"(null)\";\n \n \ttrace_printf_key(&trace_setup_key, \"setup: git_dir: %s\\n\", quote_crnl(repo_get_git_dir(r)));\n\n-- \n2.55.0.141.g00534a21ce.dirty\n\n"},{"id":"547307","messageId":"20260707-pks-setup-split-discovery-and-setup-v2-9-aab372cd227c@pks.im","threadId":"65939","inReplyTo":"20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im","subject":"[PATCH v2 09/13] setup: propagate prefix via repository discovery","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-07-07T07:21:28Z","receivedAt":"2026-07-07T07:21:56Z","isPatch":true,"body":"In the preceding commits we have started to propagate all information\nrequired for the configuration of the repository via a new `struct\nrepo_discovery`. The only exception is the repository's prefix, which we\nstill return via the return parameter.\n\nThis is conceptually fine, but somewhat inconsistent. Refactor this to\ninstead propagate the prefix via the repository discovery, too.\n\nWhile at it, drop a static variable in `repo_discover_bare_gitdir()`.\nWe apply its value to the repository discovery anyway, so we don't have\nto keep it around afterwards anymore.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n setup.c | 101 +++++++++++++++++++++++++++++-----------------------------------\n 1 file changed, 45 insertions(+), 56 deletions(-)\n\ndiff --git a/setup.c b/setup.c\nindex f8e4cf100b..d1db0a4ca0 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -1094,6 +1094,7 @@ struct repo_discovery {\n \tstruct repository_format format;\n \tchar *gitdir;\n \tchar *worktree;\n+\tchar *prefix;\n };\n \n #define REPO_DISCOVERY_INIT { \\\n@@ -1105,6 +1106,7 @@ static void repo_discovery_release(struct repo_discovery *r)\n \tclear_repository_format(&r->format);\n \tfree(r->gitdir);\n \tfree(r->worktree);\n+\tfree(r->prefix);\n }\n \n static void repo_discovery_set_gitdir(struct repo_discovery *r,\n@@ -1128,10 +1130,10 @@ static void repo_discovery_set_worktree(struct repo_discovery *r,\n \tr->worktree = real_pathdup(worktree, 1);\n }\n \n-static const char *repo_discover_explicit_gitdir(struct repo_discovery *discovery,\n-\t\t\t\t\t\t const char *gitdirenv,\n-\t\t\t\t\t\t struct strbuf *cwd,\n-\t\t\t\t\t\t int *nongit_ok)\n+static void repo_discover_explicit_gitdir(struct repo_discovery *discovery,\n+\t\t\t\t\t  const char *gitdirenv,\n+\t\t\t\t\t  struct strbuf *cwd,\n+\t\t\t\t\t  int *nongit_ok)\n {\n \tconst char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);\n \tchar *gitfile;\n@@ -1149,16 +1151,13 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover\n \tif (!is_git_directory(gitdirenv)) {\n \t\tif (nongit_ok) {\n \t\t\t*nongit_ok = 1;\n-\t\t\tfree(gitfile);\n-\t\t\treturn NULL;\n+\t\t\tgoto out;\n \t\t}\n \t\tdie(_(\"not a git repository: '%s'\"), gitdirenv);\n \t}\n \n-\tif (read_and_verify_repository_format(&discovery->format, gitdirenv, nongit_ok)) {\n-\t\tfree(gitfile);\n-\t\treturn NULL;\n-\t}\n+\tif (read_and_verify_repository_format(&discovery->format, gitdirenv, nongit_ok))\n+\t\tgoto out;\n \n \t/* #3, #7, #11, #15, #19, #23, #27, #31 (see t1510) */\n \tif (work_tree_env) {\n@@ -1173,8 +1172,7 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover\n \t} else if (discovery->format.is_bare > 0) {\n \t\t/* #18, #26 */\n \t\trepo_discovery_set_gitdir(discovery, gitdirenv, 0);\n-\t\tfree(gitfile);\n-\t\treturn NULL;\n+\t\tgoto out;\n \t} else if (discovery->format.work_tree) { /* #6, #14 */\n \t\tif (is_absolute_path(discovery->format.work_tree)) {\n \t\t\trepo_discovery_set_worktree(discovery, discovery->format.work_tree);\n@@ -1193,8 +1191,7 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover\n \t} else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {\n \t\t/* #16d */\n \t\trepo_discovery_set_gitdir(discovery, gitdirenv, 0);\n-\t\tfree(gitfile);\n-\t\treturn NULL;\n+\t\tgoto out;\n \t} else { /* #2, #10 */\n \t\trepo_discovery_set_worktree(discovery, \".\");\n \t}\n@@ -1202,8 +1199,7 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover\n \t/* both the worktree and cwd are already normalized */\n \tif (!strcmp(cwd->buf, discovery->worktree)) { /* cwd == worktree */\n \t\trepo_discovery_set_gitdir(discovery, gitdirenv, 0);\n-\t\tfree(gitfile);\n-\t\treturn NULL;\n+\t\tgoto out;\n \t}\n \n \toffset = dir_inside_of(cwd->buf, discovery->worktree);\n@@ -1211,38 +1207,37 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover\n \t\trepo_discovery_set_gitdir(discovery, gitdirenv, 1);\n \t\tif (chdir(discovery->worktree))\n \t\t\tdie_errno(_(\"cannot chdir to '%s'\"), discovery->worktree);\n-\t\tstrbuf_addch(cwd, '/');\n-\t\tfree(gitfile);\n-\t\treturn cwd->buf + offset;\n+\t\tdiscovery->prefix = xstrfmt(\"%s/\", cwd->buf + offset);\n+\t\tgoto out;\n \t}\n \n \t/* cwd outside worktree */\n \trepo_discovery_set_gitdir(discovery, gitdirenv, 0);\n+\n+out:\n \tfree(gitfile);\n-\treturn NULL;\n }\n \n-static const char *repo_discover_implicit_gitdir(struct repo_discovery *discovery,\n-\t\t\t\t\t\t const char *gitdir,\n-\t\t\t\t\t\t struct strbuf *cwd, int offset,\n-\t\t\t\t\t\t int *nongit_ok)\n+static void repo_discover_implicit_gitdir(struct repo_discovery *discovery,\n+\t\t\t\t\t  const char *gitdir,\n+\t\t\t\t\t  struct strbuf *cwd, int offset,\n+\t\t\t\t\t  int *nongit_ok)\n {\n \tif (read_and_verify_repository_format(&discovery->format, gitdir, nongit_ok))\n-\t\treturn NULL;\n+\t\treturn;\n \n \t/* --work-tree is set without --git-dir; use discovered one */\n \tif (getenv(GIT_WORK_TREE_ENVIRONMENT) || discovery->format.work_tree) {\n \t\tchar *to_free = NULL;\n-\t\tconst char *ret;\n \n \t\tif (offset != cwd->len && !is_absolute_path(gitdir))\n \t\t\tgitdir = to_free = real_pathdup(gitdir, 1);\n \t\tif (chdir(cwd->buf))\n \t\t\tdie_errno(_(\"cannot come back to cwd\"));\n-\t\tret = repo_discover_explicit_gitdir(discovery, gitdir, cwd,\n-\t\t\t\t\t\t    nongit_ok);\n+\t\trepo_discover_explicit_gitdir(discovery, gitdir, cwd,\n+\t\t\t\t\t      nongit_ok);\n \t\tfree(to_free);\n-\t\treturn ret;\n+\t\treturn;\n \t}\n \n \t/* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */\n@@ -1250,7 +1245,7 @@ static const char *repo_discover_implicit_gitdir(struct repo_discovery *discover\n \t\trepo_discovery_set_gitdir(discovery, gitdir, (offset != cwd->len));\n \t\tif (chdir(cwd->buf))\n \t\t\tdie_errno(_(\"cannot come back to cwd\"));\n-\t\treturn NULL;\n+\t\treturn;\n \t}\n \n \t/* #0, #1, #5, #8, #9, #12, #13 */\n@@ -1258,37 +1253,34 @@ static const char *repo_discover_implicit_gitdir(struct repo_discovery *discover\n \tif (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT))\n \t\trepo_discovery_set_gitdir(discovery, gitdir, 0);\n \tif (offset >= cwd->len)\n-\t\treturn NULL;\n+\t\treturn;\n \n \t/* Make \"offset\" point past the '/' (already the case for root dirs) */\n \tif (offset != offset_1st_component(cwd->buf))\n \t\toffset++;\n-\t/* Add a '/' at the end */\n-\tstrbuf_addch(cwd, '/');\n-\treturn cwd->buf + offset;\n+\tdiscovery->prefix = xstrfmt(\"%s/\", cwd->buf + offset);\n }\n \n /* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */\n-static const char *repo_discover_bare_gitdir(struct repo_discovery *discovery,\n-\t\t\t\t\t     struct strbuf *cwd, int offset,\n-\t\t\t\t\t     int *nongit_ok)\n+static void repo_discover_bare_gitdir(struct repo_discovery *discovery,\n+\t\t\t\t      struct strbuf *cwd, int offset,\n+\t\t\t\t      int *nongit_ok)\n {\n \tint root_len;\n \n \tif (read_and_verify_repository_format(&discovery->format, \".\", nongit_ok))\n-\t\treturn NULL;\n+\t\treturn;\n \n \tsetenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, \"0\", 1);\n \n \t/* --work-tree is set without --git-dir; use discovered one */\n \tif (getenv(GIT_WORK_TREE_ENVIRONMENT) || discovery->format.work_tree) {\n-\t\tstatic const char *gitdir;\n-\n-\t\tgitdir = offset == cwd->len ? \".\" : xmemdupz(cwd->buf, offset);\n+\t\tchar *gitdir = offset == cwd->len ? xstrdup(\".\") : xmemdupz(cwd->buf, offset);\n \t\tif (chdir(cwd->buf))\n \t\t\tdie_errno(_(\"cannot come back to cwd\"));\n-\t\treturn repo_discover_explicit_gitdir(discovery, gitdir, cwd,\n-\t\t\t\t\t\t     nongit_ok);\n+\t\trepo_discover_explicit_gitdir(discovery, gitdir, cwd, nongit_ok);\n+\t\tfree(gitdir);\n+\t\treturn;\n \t}\n \n \tif (offset != cwd->len) {\n@@ -1297,10 +1289,9 @@ static const char *repo_discover_bare_gitdir(struct repo_discovery *discovery,\n \t\troot_len = offset_1st_component(cwd->buf);\n \t\tstrbuf_setlen(cwd, offset > root_len ? offset : root_len);\n \t\trepo_discovery_set_gitdir(discovery, cwd->buf, 0);\n-\t}\n-\telse\n+\t} else {\n \t\trepo_discovery_set_gitdir(discovery, \".\", 0);\n-\treturn NULL;\n+\t}\n }\n \n static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len)\n@@ -1936,7 +1927,6 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \tstruct strbuf cwd = STRBUF_INIT;\n \tstruct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;\n \tstruct repo_discovery discovery = REPO_DISCOVERY_INIT;\n-\tconst char *prefix = NULL;\n \n \t/*\n \t * We may have read an incomplete configuration before\n@@ -1961,20 +1951,19 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \n \tswitch (repo_discovery_find_dir(&dir, &gitdir, &report, 1)) {\n \tcase GIT_DIR_EXPLICIT:\n-\t\tprefix = repo_discover_explicit_gitdir(&discovery, gitdir.buf, &cwd,\n-\t\t\t\t\t\t       nongit_ok);\n+\t\trepo_discover_explicit_gitdir(&discovery, gitdir.buf, &cwd,\n+\t\t\t\t\t      nongit_ok);\n \t\tbreak;\n \tcase GIT_DIR_DISCOVERED:\n \t\tif (dir.len < cwd.len && chdir(dir.buf))\n \t\t\tdie(_(\"cannot change to '%s'\"), dir.buf);\n-\t\tprefix = repo_discover_implicit_gitdir(&discovery, gitdir.buf, &cwd, dir.len,\n-\t\t\t\t\t\t       nongit_ok);\n+\t\trepo_discover_implicit_gitdir(&discovery, gitdir.buf, &cwd, dir.len,\n+\t\t\t\t\t      nongit_ok);\n \t\tbreak;\n \tcase GIT_DIR_BARE:\n \t\tif (dir.len < cwd.len && chdir(dir.buf))\n \t\t\tdie(_(\"cannot change to '%s'\"), dir.buf);\n-\t\tprefix = repo_discover_bare_gitdir(&discovery, &cwd, dir.len,\n-\t\t\t\t\t\t   nongit_ok);\n+\t\trepo_discover_bare_gitdir(&discovery, &cwd, dir.len, nongit_ok);\n \t\tbreak;\n \tcase GIT_DIR_HIT_CEILING:\n \t\tif (!nongit_ok)\n@@ -2103,10 +2092,10 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \t * out where the repository is, i.e. a preparation\n \t * for calling repo_config_get_bool().\n \t */\n-\tif (prefix) {\n-\t\tprefix = precompose_string_if_needed(prefix);\n+\tif (discovery.prefix) {\n+\t\tconst char *prefix = precompose_string_if_needed(discovery.prefix);\n \t\trepo->prefix = xstrdup(prefix);\n-\t\tsetenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);\n+\t\tsetenv(GIT_PREFIX_ENVIRONMENT, repo->prefix, 1);\n \t} else {\n \t\tFREE_AND_NULL(repo->prefix);\n \t\tsetenv(GIT_PREFIX_ENVIRONMENT, \"\", 1);\n\n-- \n2.55.0.141.g00534a21ce.dirty\n\n"},{"id":"547309","messageId":"20260707-pks-setup-split-discovery-and-setup-v2-10-aab372cd227c@pks.im","threadId":"65939","inReplyTo":"20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im","subject":"[PATCH v2 10/13] setup: make repository discovery self-contained","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-07-07T07:21:29Z","receivedAt":"2026-07-07T07:22:00Z","isPatch":true,"body":"In the preceding commits we have introduced a separate repository\ndiscovery phase and refactored the logic so that we have two clear\nphases:\n\n  1. Repository discovery, which doesn't modify the repository itself at\n     all.\n\n  2. Repository configuration, which takes the information we have\n     discovered to set up the repository.\n\nExtract the first phase into a new function `repo_discover()` to further\nstress these two different phases.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n setup.c | 43 +++++++++++++++++++++++++------------------\n 1 file changed, 25 insertions(+), 18 deletions(-)\n\ndiff --git a/setup.c b/setup.c\nindex d1db0a4ca0..d4de8c2900 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -1922,20 +1922,10 @@ void set_git_work_tree(struct repository *repo, const char *new_work_tree)\n \trepo_set_worktree(repo, new_work_tree);\n }\n \n-const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n+static void repo_discover(struct repo_discovery *discovery, int *nongit_ok)\n {\n \tstruct strbuf cwd = STRBUF_INIT;\n \tstruct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;\n-\tstruct repo_discovery discovery = REPO_DISCOVERY_INIT;\n-\n-\t/*\n-\t * We may have read an incomplete configuration before\n-\t * setting-up the git directory. If so, clear the cache so\n-\t * that the next queries to the configuration reload complete\n-\t * configuration (including the per-repo config file that we\n-\t * ignored previously).\n-\t */\n-\trepo_config_clear(repo);\n \n \t/*\n \t * Let's assume that we are in a git repository.\n@@ -1951,19 +1941,19 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \n \tswitch (repo_discovery_find_dir(&dir, &gitdir, &report, 1)) {\n \tcase GIT_DIR_EXPLICIT:\n-\t\trepo_discover_explicit_gitdir(&discovery, gitdir.buf, &cwd,\n+\t\trepo_discover_explicit_gitdir(discovery, gitdir.buf, &cwd,\n \t\t\t\t\t      nongit_ok);\n \t\tbreak;\n \tcase GIT_DIR_DISCOVERED:\n \t\tif (dir.len < cwd.len && chdir(dir.buf))\n \t\t\tdie(_(\"cannot change to '%s'\"), dir.buf);\n-\t\trepo_discover_implicit_gitdir(&discovery, gitdir.buf, &cwd, dir.len,\n+\t\trepo_discover_implicit_gitdir(discovery, gitdir.buf, &cwd, dir.len,\n \t\t\t\t\t      nongit_ok);\n \t\tbreak;\n \tcase GIT_DIR_BARE:\n \t\tif (dir.len < cwd.len && chdir(dir.buf))\n \t\t\tdie(_(\"cannot change to '%s'\"), dir.buf);\n-\t\trepo_discover_bare_gitdir(&discovery, &cwd, dir.len, nongit_ok);\n+\t\trepo_discover_bare_gitdir(discovery, &cwd, dir.len, nongit_ok);\n \t\tbreak;\n \tcase GIT_DIR_HIT_CEILING:\n \t\tif (!nongit_ok)\n@@ -2013,6 +2003,27 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \t\tBUG(\"unhandled repo_discovery_find_dir() result\");\n \t}\n \n+\tstrbuf_release(&dir);\n+\tstrbuf_release(&cwd);\n+\tstrbuf_release(&gitdir);\n+\tstrbuf_release(&report);\n+}\n+\n+const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n+{\n+\tstruct repo_discovery discovery = REPO_DISCOVERY_INIT;\n+\n+\t/*\n+\t * We may have read an incomplete configuration before\n+\t * setting-up the git directory. If so, clear the cache so\n+\t * that the next queries to the configuration reload complete\n+\t * configuration (including the per-repo config file that we\n+\t * ignored previously).\n+\t */\n+\trepo_config_clear(repo);\n+\n+\trepo_discover(&discovery, nongit_ok);\n+\n \t/*\n \t * At this point, nongit_ok is stable. If it is non-NULL and points\n \t * to a non-zero value, then this means that we haven't found a\n@@ -2104,10 +2115,6 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \tsetup_original_cwd(repo);\n \n \trepo_discovery_release(&discovery);\n-\tstrbuf_release(&dir);\n-\tstrbuf_release(&cwd);\n-\tstrbuf_release(&gitdir);\n-\tstrbuf_release(&report);\n \treturn repo->prefix;\n }\n \n\n-- \n2.55.0.141.g00534a21ce.dirty\n\n"},{"id":"547308","messageId":"20260707-pks-setup-split-discovery-and-setup-v2-11-aab372cd227c@pks.im","threadId":"65939","inReplyTo":"20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im","subject":"[PATCH v2 11/13] setup: drop redundant configuration of `startup_info->have_repository`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-07-07T07:21:30Z","receivedAt":"2026-07-07T07:22:01Z","isPatch":true,"body":"In `init_db()` we set `startup_info->have_repository` twice: once before\nreading and applying the repository format and once after. This is\nredundant though, as configuring the repository format does not rely on\nthis variable at all.\n\nRemove the first such site. While at it, fix up formatting a bit.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n setup.c | 4 +---\n 1 file changed, 1 insertion(+), 3 deletions(-)\n\ndiff --git a/setup.c b/setup.c\nindex d4de8c2900..088e7b85f7 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -2847,12 +2847,10 @@ int init_db(struct repository *repo,\n \t\tapply_and_export_relative_gitdir(repo, real_git_dir, 1);\n \t\tgit_dir = repo_get_git_dir(repo);\n \t\tseparate_git_dir(git_dir, original_git_dir);\n-\t}\n-\telse {\n+\t} else {\n \t\tapply_and_export_relative_gitdir(repo, git_dir, 1);\n \t\tgit_dir = repo_get_git_dir(repo);\n \t}\n-\tstartup_info->have_repository = 1;\n \n \t/*\n \t * Check to see if the repository version is right.\n\n-- \n2.55.0.141.g00534a21ce.dirty\n\n"},{"id":"547310","messageId":"20260707-pks-setup-split-discovery-and-setup-v2-12-aab372cd227c@pks.im","threadId":"65939","inReplyTo":"20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im","subject":"[PATCH v2 12/13] setup: pass worktree to `init_db()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-07-07T07:21:31Z","receivedAt":"2026-07-07T07:22:04Z","isPatch":true,"body":"In the preceding commits we have refactored how we discover and set up\nrepositories so that we cannot end up with partially-configured repos.\nInstead, we apply the gitdir, worktree and repository format in a single\nlocation, only.\n\nInitializing a new repository has the same antipattern though: while\nmost of the information for the new repository is passed via parameters,\nthe work tree is instead propagated by configuring the repository's work\ntree.\n\nRefactor the code so that we also pass the work tree as an explicit\nparameter. Like this, configuration fo the repository happens in a\nsingle spot, too, just as with repository discovery.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/clone.c   |  8 ++++----\n builtin/init-db.c | 34 ++++++++++------------------------\n setup.c           |  7 ++++++-\n setup.h           |  4 +++-\n 4 files changed, 23 insertions(+), 30 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex d60d1b60bc..9d08cd8722 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -1116,7 +1116,6 @@ int cmd_clone(int argc,\n \t\t\tdie_errno(_(\"could not create work tree dir '%s'\"),\n \t\t\t\t  work_tree);\n \t\tjunk_work_tree = work_tree;\n-\t\tset_git_work_tree(the_repository, work_tree);\n \t}\n \n \tif (real_git_dir) {\n@@ -1186,9 +1185,10 @@ int cmd_clone(int argc,\n \t * repository, and reference backends may persist that information into\n \t * their on-disk data structures.\n \t */\n-\tinit_db(the_repository, git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN,\n-\t\tref_storage_format, NULL,\n-\t\tdo_not_override_repo_unix_permissions, INIT_DB_QUIET | INIT_DB_SKIP_REFDB);\n+\tinit_db(the_repository, git_dir, real_git_dir, work_tree, option_template,\n+\t\tGIT_HASH_UNKNOWN, ref_storage_format, NULL,\n+\t\tdo_not_override_repo_unix_permissions,\n+\t\tINIT_DB_QUIET | INIT_DB_SKIP_REFDB);\n \n \tif (real_git_dir) {\n \t\tfree((char *)git_dir);\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 566732c9f4..e96b1283b7 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -231,39 +231,25 @@ int cmd_init_db(int argc,\n \tif (!bare) {\n \t\tconst char *git_dir_parent = strrchr(git_dir, '/');\n \n-\t\tif (work_tree) {\n-\t\t\tset_git_work_tree(the_repository, work_tree);\n-\t\t} else {\n-\t\t\tchar *work_tree_cfg = NULL;\n-\n+\t\tif (!work_tree) {\n \t\t\tif (git_dir_parent) {\n \t\t\t\tchar *rel = xstrndup(git_dir, git_dir_parent - git_dir);\n-\t\t\t\twork_tree_cfg = real_pathdup(rel, 1);\n+\t\t\t\twork_tree = real_pathdup(rel, 1);\n \t\t\t\tfree(rel);\n+\t\t\t} else {\n+\t\t\t\twork_tree = xgetcwd();\n \t\t\t}\n-\n-\t\t\tif (!work_tree_cfg)\n-\t\t\t\twork_tree_cfg = xgetcwd();\n-\n-\t\t\tset_git_work_tree(the_repository, work_tree_cfg);\n-\n-\t\t\tfree(work_tree_cfg);\n \t\t}\n \n-\t\tif (access(repo_get_work_tree(the_repository), X_OK))\n-\t\t\tdie_errno (_(\"Cannot access work tree '%s'\"),\n-\t\t\t\t   repo_get_work_tree(the_repository));\n-\t}\n-\telse {\n-\t\tif (real_git_dir)\n-\t\t\tdie(_(\"--separate-git-dir incompatible with bare repository\"));\n-\t\tif (work_tree)\n-\t\t\tset_git_work_tree(the_repository, work_tree);\n+\t\tif (access(work_tree, X_OK))\n+\t\t\tdie_errno (_(\"Cannot access work tree '%s'\"), work_tree);\n+\t} else if (real_git_dir) {\n+\t\tdie(_(\"--separate-git-dir incompatible with bare repository\"));\n \t}\n \n \tflags |= INIT_DB_EXIST_OK;\n-\tret = init_db(the_repository, git_dir, real_git_dir, template_dir, hash_algo,\n-\t\t      ref_storage_format, initial_branch,\n+\tret = init_db(the_repository, git_dir, real_git_dir, work_tree,\n+\t\t      template_dir, hash_algo, ref_storage_format, initial_branch,\n \t\t      init_shared_repository, flags);\n \n \tfree(template_dir_to_free);\ndiff --git a/setup.c b/setup.c\nindex 088e7b85f7..683b8e65a2 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -2823,7 +2823,9 @@ static void repository_format_configure(struct repository_format *repo_fmt,\n }\n \n int init_db(struct repository *repo,\n-\t    const char *git_dir, const char *real_git_dir,\n+\t    const char *git_dir,\n+\t    const char *real_git_dir,\n+\t    const char *worktree,\n \t    const char *template_dir, int hash,\n \t    enum ref_storage_format ref_storage_format,\n \t    const char *initial_branch,\n@@ -2852,6 +2854,9 @@ int init_db(struct repository *repo,\n \t\tgit_dir = repo_get_git_dir(repo);\n \t}\n \n+\tif (worktree)\n+\t\tset_git_work_tree(repo, worktree);\n+\n \t/*\n \t * Check to see if the repository version is right.\n \t * Note that a newly created repository does not have\ndiff --git a/setup.h b/setup.h\nindex c01a244fe9..bf3e3f3ea6 100644\n--- a/setup.h\n+++ b/setup.h\n@@ -263,7 +263,9 @@ const char *get_template_dir(const char *option_template);\n #define INIT_DB_SKIP_REFDB (1 << 2)\n \n int init_db(struct repository *repo,\n-\t    const char *git_dir, const char *real_git_dir,\n+\t    const char *git_dir,\n+\t    const char *real_git_dir,\n+\t    const char *worktree,\n \t    const char *template_dir, int hash_algo,\n \t    enum ref_storage_format ref_storage_format,\n \t    const char *initial_branch, int init_shared_repository,\n\n-- \n2.55.0.141.g00534a21ce.dirty\n\n"},{"id":"547311","messageId":"20260707-pks-setup-split-discovery-and-setup-v2-13-aab372cd227c@pks.im","threadId":"65939","inReplyTo":"20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im","subject":"[PATCH v2 13/13] setup: mark `set_git_work_tree()` as file-local","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-07-07T07:21:32Z","receivedAt":"2026-07-07T07:22:05Z","isPatch":true,"body":"In the preceding commit we have removed the last callers of\n`set_git_work_tree()` that is located outside of \"setup.c\". Remove its\ndeclaration and mark the function as file-local.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n setup.c | 2 +-\n setup.h | 2 --\n 2 files changed, 1 insertion(+), 3 deletions(-)\n\ndiff --git a/setup.c b/setup.c\nindex 683b8e65a2..b632c2bf8c 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -1904,7 +1904,7 @@ const char *enter_repo(struct repository *repo, const char *path, unsigned flags\n  * primarily to support git-clone to work in a new repository it just\n  * created, and is not meant to flip between different work trees.\n  */\n-void set_git_work_tree(struct repository *repo, const char *new_work_tree)\n+static void set_git_work_tree(struct repository *repo, const char *new_work_tree)\n {\n \tif (repo->worktree_initialized) {\n \t\tstruct strbuf realpath = STRBUF_INIT;\ndiff --git a/setup.h b/setup.h\nindex bf3e3f3ea6..bb24ee8f0f 100644\n--- a/setup.h\n+++ b/setup.h\n@@ -96,8 +96,6 @@ static inline int discover_git_directory(struct strbuf *commondir,\n \treturn 0;\n }\n \n-void set_git_work_tree(struct repository *repo, const char *tree);\n-\n /* Flags that can be passed to `enter_repo()`. */\n enum {\n \t/*\n\n-- \n2.55.0.141.g00534a21ce.dirty\n\n"},{"id":"547335","messageId":"ak0U46-J4qmwL2FD@denethor","threadId":"65939","inReplyTo":"20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im","subject":"Re: [PATCH v2 00/13] setup: split up repository discovery and setup","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2026-07-07T15:02:58Z","receivedAt":"2026-07-07T15:03:03Z","isPatch":true,"body":"On 26/07/07 09:21AM, Patrick Steinhardt wrote:\n> Changes in v2:\n>   - Expand commit message to talk about precedence order between\n>     the \"GIT_SHALLOW_FILE\" environment variable and the \"--shallow-file\"\n>     command line switch.\n>   - Remove a now-unused parameter in `set_alternate_shallow_file()`.\n>   - Fix a typo.\n>   - Link to v1: https://patch.msgid.link/20260630-pks-setup-split-discovery-and-setup-v1-0-13864eb5a032@pks.im\n\nThe changes in this version look good to me. Thanks.\n\n-Justin\n"},{"id":"547376","messageId":"xmqqldbm4r86.fsf@gitster.g","threadId":"65939","inReplyTo":"ak0U46-J4qmwL2FD@denethor","subject":"Re: [PATCH v2 00/13] setup: split up repository discovery and setup","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-07-07T17:58:01Z","receivedAt":"2026-07-07T17:58:03Z","isPatch":true,"body":"Justin Tobler <jltobler@gmail.com> writes:\n\n> On 26/07/07 09:21AM, Patrick Steinhardt wrote:\n>> Changes in v2:\n>>   - Expand commit message to talk about precedence order between\n>>     the \"GIT_SHALLOW_FILE\" environment variable and the \"--shallow-file\"\n>>     command line switch.\n>>   - Remove a now-unused parameter in `set_alternate_shallow_file()`.\n>>   - Fix a typo.\n>>   - Link to v1: https://patch.msgid.link/20260630-pks-setup-split-discovery-and-setup-v1-0-13864eb5a032@pks.im\n>\n> The changes in this version look good to me. Thanks.\n\nThanks, both.  These indeed look good.\n\nWill replace.\n"},{"id":"547479","messageId":"87ldblomh3.fsf@emacs.iotcl.com","threadId":"65939","inReplyTo":"20260707-pks-setup-split-discovery-and-setup-v2-5-aab372cd227c@pks.im","subject":"Re: [PATCH v2 05/13] setup: introduce explicit repository discovery","fromName":"Toon Claes","fromEmail":"toon@iotcl.com","sentAt":"2026-07-08T09:32:40Z","receivedAt":"2026-07-08T09:33:01Z","isPatch":true,"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> When setting up the global repository we intermix repository discovery\n> and repository configuration: we repeatedly call `set_git_work_tree()`\n> and `apply_and_export_relative_gitdir()` until we're happy with the\n> result. The result of this is then a partially-configured repository\n> that we use for further setup.\n>\n> This process is quite hard to follow, as it's never quite clear which\n> parts of the repository have been configured already and which haven't.\n> Furthermore, it means that the repository configuration is distributed\n> across many different places instead of having it neatly contained in a\n> single location. Ultimately, this is the reason that we cannot use a\n> central function like `repo_init()`.\n>\n> Refactor the logic so that we stop partially-configuring a repository\n> and instead populate a new `struct repo_discovery`. This allow us to\n> essentially split repository setup into two phases:\n>\n>   - The first phase only figures out parameters required to configure\n>     the repository.\n>\n>   - The second phase then takes these parameters and applies them to the\n>     repository.\n>\n> Like this, we'll never end up with a partially-configured repository and\n> can eventually extend `repo_init()` to handle the full initialization\n> for us.\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  setup.c | 155 ++++++++++++++++++++++++++++++++++++++++------------------------\n>  1 file changed, 98 insertions(+), 57 deletions(-)\n>\n> diff --git a/setup.c b/setup.c\n> index 324a235dd1..f713d024f7 100644\n> --- a/setup.c\n> +++ b/setup.c\n> @@ -1090,14 +1090,47 @@ static void apply_and_export_relative_gitdir(struct repository *repo, const char\n>  \tstrbuf_release(&realpath);\n>  }\n>  \n> -static const char *setup_explicit_git_dir(struct repository *repo,\n> -\t\t\t\t\t  const char *gitdirenv,\n> -\t\t\t\t\t  struct strbuf *cwd,\n> -\t\t\t\t\t  struct repository_format *repo_fmt,\n> -\t\t\t\t\t  int *nongit_ok)\n> +struct repo_discovery {\n> +\tchar *gitdir;\n> +\tchar *worktree;\n> +};\n\nI like where you're going with this. It's nicer to have a struct that\ncaptures what we know and has accurate data, instead of having a\n`repository` that's only partly initialized.\n\n-- \nCheers,\nToon\n"},{"id":"547480","messageId":"87h5m9om0j.fsf@emacs.iotcl.com","threadId":"65939","inReplyTo":"xmqqldbm4r86.fsf@gitster.g","subject":"Re: [PATCH v2 00/13] setup: split up repository discovery and setup","fromName":"Toon Claes","fromEmail":"toon@iotcl.com","sentAt":"2026-07-08T09:42:36Z","receivedAt":"2026-07-08T09:42:57Z","isPatch":true,"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Justin Tobler <jltobler@gmail.com> writes:\n>\n>> The changes in this version look good to me. Thanks.\n>\n> Thanks, both.  These indeed look good.\n\nIt's nice to see the discovery being kept in this new struct that holds\naccurate information. There is quite some code shuffled around through\nthis series, but the changes look sensible. So as far as I am concerned,\nI agree this series looks good.\n\n\nCheers,\nToon\n"}]}