setup: stop using `the_repository` in `setup_work_tree()`

Stop using `the_repository` in `setup_work_tree()` and instead accept the repository as a parameter. The injection of `the_repository` is thus bumped one level higher, where callers now pass it in explicitly. Note that the function tracks two bits of information via global variables. This of course doesn't make much sense anymore now that we can set up worktrees for arbitrary repositories: - We track whether the worktree has already been initialized and, if so, we skip the call to `chdir_notify()` and setenv(3p). It does not make much sense to store this info in the repository, as we _would_ want to update the environment when switching between worktrees back and forth. So instead of storing this info in the repository, we drop this state entirely and live with the fact that we may execute the logic twice. It should ultimately be idempotent though and thus not be much of a problem. - We track whether the worktree configuration is bogus. If so, and if later on some caller tries to setup the worktree, then we'll die instead. This is indeed information that we can move into the repository itself. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed May 19, 2026 at 11:52 UTC bd2851d84ffe438cf4621da48abbff1877935d9a
21 files changed +38 -42
blame.c
+1 -1
@@ -2813,7 +2813,7 @@ void setup_scoreboard(struct blame_scoreboard *sb,
2813 }
2814
2815 if (!sb->contents_from)
2816 - setup_work_tree();
2816 + setup_work_tree(the_repository);
2817
2818 sb->final = fake_working_tree_commit(sb->repo,
2819 &sb->revs->diffopt,
builtin/check-attr.c
+1 -1
@@ -117,7 +117,7 @@ int cmd_check_attr(int argc,
117 int cnt, i, doubledash, filei;
118
119 if (!is_bare_repository())
120 - setup_work_tree();
120 + setup_work_tree(the_repository);
121
122 repo_config(the_repository, git_default_config, NULL);
123
builtin/clone.c
+1 -1
@@ -669,7 +669,7 @@ static int checkout(int submodule_progress,
669 }
670
671 /* We need to be in the new work tree for the checkout */
672 - setup_work_tree();
672 + setup_work_tree(the_repository);
673
674 repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
675
builtin/describe.c
+1 -1
@@ -781,7 +781,7 @@ int cmd_describe(int argc,
781 struct rev_info revs;
782 int fd;
783
784 - setup_work_tree();
784 + setup_work_tree(the_repository);
785 prepare_repo_settings(the_repository);
786 the_repository->settings.command_requires_full_index = 0;
787 repo_read_index(the_repository);
builtin/diff-index.c
+1 -1
@@ -69,7 +69,7 @@ int cmd_diff_index(int argc,
69 rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
70 usage(diff_cache_usage);
71 if (!(option & DIFF_INDEX_CACHED)) {
72 - setup_work_tree();
72 + setup_work_tree(the_repository);
73 if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) {
74 perror("repo_read_index_preload");
75 return -1;
builtin/diff.c
+2 -2
@@ -159,7 +159,7 @@ static void builtin_diff_index(struct rev_info *revs,
159 revs->max_age != -1)
160 usage(builtin_diff_usage);
161 if (!(option & DIFF_INDEX_CACHED)) {
162 - setup_work_tree();
162 + setup_work_tree(the_repository);
163 if (repo_read_index_preload(the_repository,
164 &revs->diffopt.pathspec, 0) < 0) {
165 die_errno("repo_read_index_preload");
@@ -281,7 +281,7 @@ static void builtin_diff_files(struct rev_info *revs, int argc, const char **arg
281 (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
282 diff_merges_set_dense_combined_if_unset(revs);
283
284 - setup_work_tree();
284 + setup_work_tree(the_repository);
285 if (repo_read_index_preload(the_repository, &revs->diffopt.pathspec,
286 0) < 0) {
287 die_errno("repo_read_index_preload");
builtin/difftool.c
+1 -1
@@ -767,7 +767,7 @@ int cmd_difftool(int argc,
767 die(_("difftool requires worktree or --no-index"));
768
769 if (!no_index){
770 - setup_work_tree();
770 + setup_work_tree(repo);
771 setenv(GIT_DIR_ENVIRONMENT, absolute_path(repo_get_git_dir(repo)), 1);
772 setenv(GIT_WORK_TREE_ENVIRONMENT, absolute_path(repo_get_work_tree(repo)), 1);
773 } else if (dir_diff)
builtin/grep.c
+1 -1
@@ -1272,7 +1272,7 @@ int cmd_grep(int argc,
1272 die(_("--[no-]exclude-standard cannot be used for tracked contents"));
1273 } else if (!list.nr) {
1274 if (!cached)
1275 - setup_work_tree();
1275 + setup_work_tree(the_repository);
1276
1277 hit = grep_cache(&opt, &pathspec, cached);
1278 } else {
builtin/ls-files.c
+1 -1
@@ -704,7 +704,7 @@ int cmd_ls_files(int argc,
704 exc_given = 1;
705
706 if (require_work_tree && !is_inside_work_tree(repo))
707 - setup_work_tree();
707 + setup_work_tree(repo);
708
709 if (recurse_submodules &&
710 (show_deleted || show_others || show_unmerged ||
builtin/read-tree.c
+1 -1
@@ -229,7 +229,7 @@ int cmd_read_tree(int argc,
229 opts.preserve_ignored = 0;
230 /* otherwise, opts.preserve_ignored is irrelevant */
231 if (opts.merge && !opts.index_only)
232 - setup_work_tree();
232 + setup_work_tree(the_repository);
233
234 if (opts.skip_sparse_checkout)
235 ensure_full_index(the_repository->index);
builtin/reset.c
+1 -1
@@ -468,7 +468,7 @@ int cmd_reset(int argc,
468 trace2_cmd_mode(reset_type_names[reset_type]);
469
470 if (reset_type != SOFT && (reset_type != MIXED || repo_get_work_tree(the_repository)))
471 - setup_work_tree();
471 + setup_work_tree(the_repository);
472
473 if (reset_type == MIXED && is_bare_repository())
474 die(_("%s reset is not allowed in a bare repository"),
builtin/rm.c
+1 -1
@@ -296,7 +296,7 @@ int cmd_rm(int argc,
296 die(_("No pathspec was given. Which files should I remove?"));
297
298 if (!index_only)
299 - setup_work_tree();
299 + setup_work_tree(the_repository);
300
301 prepare_repo_settings(the_repository);
302 the_repository->settings.command_requires_full_index = 0;
builtin/sparse-checkout.c
+8 -8
@@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,
63 int res;
64 struct repo_config_values *cfg = repo_config_values(the_repository);
65
66 - setup_work_tree();
66 + setup_work_tree(the_repository);
67 if (!cfg->apply_sparse_checkout)
68 die(_("this worktree is not sparse"));
69
@@ -229,7 +229,7 @@ static int update_working_directory(struct repository *r,
229 o.dst_index = r->index;
230 o.skip_sparse_checkout = 0;
231
232 - setup_work_tree();
232 + setup_work_tree(the_repository);
233
234 repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR);
235
@@ -468,7 +468,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix,
468 OPT_END(),
469 };
470
471 - setup_work_tree();
471 + setup_work_tree(the_repository);
472 repo_read_index(repo);
473
474 init_opts.cone_mode = -1;
@@ -802,7 +802,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
802 int ret;
803 struct repo_config_values *cfg = repo_config_values(the_repository);
804
805 - setup_work_tree();
805 + setup_work_tree(the_repository);
806 if (!cfg->apply_sparse_checkout)
807 die(_("no sparse-checkout to add to"));
808
@@ -856,7 +856,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
856 struct strvec patterns = STRVEC_INIT;
857 int ret;
858
859 - setup_work_tree();
859 + setup_work_tree(the_repository);
860 repo_read_index(repo);
861
862 set_opts.cone_mode = -1;
@@ -912,7 +912,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,
912 };
913 struct repo_config_values *cfg = repo_config_values(the_repository);
914
915 - setup_work_tree();
915 + setup_work_tree(the_repository);
916 if (!cfg->apply_sparse_checkout)
917 die(_("must be in a sparse-checkout to reapply sparsity patterns"));
918
@@ -975,7 +975,7 @@ static int sparse_checkout_clean(int argc, const char **argv,
975 OPT_END(),
976 };
977
978 - setup_work_tree();
978 + setup_work_tree(the_repository);
979 if (!cfg->apply_sparse_checkout)
980 die(_("must be in a sparse-checkout to clean directories"));
981 if (!core_sparse_checkout_cone)
@@ -1053,7 +1053,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
1053 * forcibly return to a dense checkout regardless of initial state.
1054 */
1055
1056 - setup_work_tree();
1056 + setup_work_tree(the_repository);
1057 argc = parse_options(argc, argv, prefix,
1058 builtin_sparse_checkout_disable_options,
1059 builtin_sparse_checkout_disable_usage, 0);
builtin/submodule--helper.c
+1 -1
@@ -1250,7 +1250,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
1250
1251 if (!info->cached) {
1252 if (diff_cmd == DIFF_INDEX)
1253 - setup_work_tree();
1253 + setup_work_tree(the_repository);
1254 if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) {
1255 perror("repo_read_index_preload");
1256 ret = -1;
builtin/update-index.c
+5 -5
@@ -732,7 +732,7 @@ struct refresh_params {
732
733 static int refresh(struct refresh_params *o, unsigned int flag)
734 {
735 - setup_work_tree();
735 + setup_work_tree(the_repository);
736 repo_read_index(the_repository);
737 *o->has_errors |= refresh_index(the_repository->index, o->flags | flag, NULL,
738 NULL, NULL);
@@ -901,7 +901,7 @@ static enum parse_opt_result reupdate_callback(
901 BUG_ON_OPT_ARG(arg);
902
903 /* consume remaining arguments. */
904 - setup_work_tree();
904 + setup_work_tree(the_repository);
905 *has_errors = do_reupdate(ctx->argv + 1, prefix);
906 if (*has_errors)
907 the_repository->index->cache_changed = 0;
@@ -1157,7 +1157,7 @@ int cmd_update_index(int argc,
1157 transaction = NULL;
1158 }
1159
1160 - setup_work_tree();
1160 + setup_work_tree(the_repository);
1161 p = prefix_path(the_repository, prefix, prefix_length, path);
1162 update_one(p);
1163 if (set_executable_bit)
@@ -1199,7 +1199,7 @@ int cmd_update_index(int argc,
1199 struct strbuf buf = STRBUF_INIT;
1200 struct strbuf unquoted = STRBUF_INIT;
1201
1202 - setup_work_tree();
1202 + setup_work_tree(the_repository);
1203 while (getline_fn(&buf, stdin) != EOF) {
1204 char *p;
1205 if (!nul_term_line && buf.buf[0] == '"') {
@@ -1253,7 +1253,7 @@ int cmd_update_index(int argc,
1253 report(_("Untracked cache disabled"));
1254 break;
1255 case UC_TEST:
1256 - setup_work_tree();
1256 + setup_work_tree(the_repository);
1257 return !test_if_untracked_cache_is_supported();
1258 case UC_ENABLE:
1259 case UC_FORCE:
git.c
+1 -1
@@ -497,7 +497,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv, struct
497 commit_pager_choice();
498
499 if (!help && p->option & NEED_WORK_TREE)
500 - setup_work_tree();
500 + setup_work_tree(the_repository);
501
502 trace_argv_printf(argv, "trace: built-in: git");
503 trace2_cmd_name(p->cmd);
repository.h
+1
@@ -114,6 +114,7 @@ struct repository {
114 * A NULL value indicates that there is no working directory.
115 */
116 char *worktree;
117 + bool worktree_config_is_bogus;
118
119 /*
120 * Path from the root of the top-level superproject down to this
setup.c
+4 -11
@@ -26,7 +26,6 @@
26 #include "trace2.h"
27 #include "worktree.h"
28
29 -static int work_tree_config_is_bogus;
29 enum allowed_bare_repo {
30 ALLOWED_BARE_REPO_EXPLICIT = 0,
31 ALLOWED_BARE_REPO_ALL,
@@ -494,18 +493,14 @@ int is_inside_work_tree(struct repository *repo)
493 return ret;
494 }
495
497 -void setup_work_tree(void)
496 +void setup_work_tree(struct repository *repo)
497 {
498 const char *work_tree;
500 - static int initialized = 0;
499
502 - if (initialized)
503 - return;
504 -
505 - if (work_tree_config_is_bogus)
500 + if (repo->worktree_config_is_bogus)
501 die(_("unable to set up work tree using invalid config"));
502
508 - work_tree = repo_get_work_tree(the_repository);
503 + work_tree = repo_get_work_tree(repo);
504 if (!work_tree || chdir_notify(work_tree))
505 die(_("this operation must be run in a work tree"));
506
@@ -515,8 +510,6 @@ void setup_work_tree(void)
510 */
511 if (getenv(GIT_WORK_TREE_ENVIRONMENT))
512 setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
518 -
519 - initialized = 1;
513 }
514
515 static void setup_original_cwd(struct repository *repo)
@@ -1164,7 +1157,7 @@ static const char *setup_explicit_git_dir(struct repository *repo,
1157 if (git_work_tree_cfg) {
1158 /* #22.2, #30 */
1159 warning("core.bare and core.worktree do not make sense");
1167 - work_tree_config_is_bogus = 1;
1160 + repo->worktree_config_is_bogus = true;
1161 }
1162
1163 /* #18, #26 */
setup.h
+1 -1
@@ -56,7 +56,7 @@ const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
56 void die_upon_dubious_ownership(const char *gitfile, const char *worktree,
57 const char *gitdir);
58
59 -void setup_work_tree(void);
59 +void setup_work_tree(struct repository *repo);
60
61 /*
62 * discover_git_directory_reason() is similar to discover_git_directory(),
t/helper/test-subprocess.c
+3 -1
@@ -1,3 +1,5 @@
1 +#define USE_THE_REPOSITORY_VARIABLE
2 +
3 #include "test-tool.h"
4 #include "run-command.h"
5 #include "setup.h"
@@ -11,7 +13,7 @@ int cmd__subprocess(int argc, const char **argv)
13 if (nogit)
14 die("No git repo found");
15 if (argc > 1 && !strcmp(argv[1], "--setup-work-tree")) {
14 - setup_work_tree();
16 + setup_work_tree(the_repository);
17 argv++;
18 }
19 cp.git_cmd = 1;
wt-status.c
+1 -1
@@ -1206,7 +1206,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
1206 status_printf_ln(s, c,
1207 "--------------------------------------------------");
1208 status_printf_ln(s, c, _("Changes not staged for commit:"));
1209 - setup_work_tree();
1209 + setup_work_tree(the_repository);
1210 rev.diffopt.a_prefix = "i/";
1211 rev.diffopt.b_prefix = "w/";
1212 run_diff_files(&rev, 0);