From: Shreyansh Paliwal Date: Sat, 07 Feb 2026 10:00:45 GMT Subject: [PATCH v3 0/3] wt-status: reduce reliance on global state Message-ID: <20260207100322.1786368-1-shreyanshpaliwalcmsmn@gmail.com> In-Reply-To: <20260205101524.125452-1-shreyanshpaliwalcmsmn@gmail.com> In wt-status.c code still relies on some global variables, including the_repository and the_hash_algo, even in cases where a repository instance is already available via struct wt_status or struct worktree. In patch 1/3, update function parameters and callers to pass struct repository where no local repository access was available. In patch 2/3, replace direct uses of the_repository with repository instances already available in local structs. In patch 3/3, replace remaining uses of the global the_hash_algo with the hash algorithm stored in the respective repository instance. These changes remove all direct uses of the_repository and the_hash_algo from wt-status.c and reduce its dependence on global state. The 'USE_THE_REPOSITORY_VARIABLE' macro cannot yet be removed, since these patches only eliminate direct uses of the_repository and the_hash_algo, while other global variables are still referenced. In particular wt-status.c still relies on the following globals, * core_apply_sparse_checkout, this is already being addressed in an ongoing patch series [1]. * comment_line_str and DEFAULT_ABBREV, these both still are used in wt-status.c but they dont have any equivalent local instances. [1]- https://lore.kernel.org/git/5e56e1cc4172cfff9e917a068184e102aa70bf1d.1769256839.git.belkid98@gmail.com/t/#u Shreyansh Paliwal (3): wt-status: pass struct repository through function parameters wt-status: replace uses of the_repository with local repository instances wt-status: use hash_algo from local repository instead of global the_hash_algo branch.c | 4 +-- worktree.c | 4 +-- wt-status.c | 102 ++++++++++++++++++++++++++-------------------------- wt-status.h | 6 ++-- 4 files changed, 60 insertions(+), 56 deletions(-) --- Changes in v2: - Reordered the first 2 patches for better flow. - Instead of relying on wt->repo in wt_status_check_rebase() and wt_status_check_bisect() addded struct repository. - Added extra explainations in the commit message for the usage of wt and being it not NULL. Range-diff against v2: 2: 2af0113f6f ! 1: 960216e45c wt-status: pass struct repository and wt_status through function parameters @@ Metadata Author: Shreyansh Paliwal ## Commit message ## - wt-status: pass struct repository and wt_status through function parameters + wt-status: pass struct repository through function parameters - Some functions in wt-status.c relied on the_repository because no - repository instance was available in their local scope. - There is also a specific case in wt_status_check_rebase() where the - worktree can be NULL, so accessing wt->repo may lead to a segfault. + Some functions in wt-status.c (count_stash_entries(), + read_line_from_git_path(), abbrev_oid_in_line(), read_rebase_todolist()) + do not have access to a local repository instance and rely on the_repository. - Update these functions to accept a struct repository or struct - wt_status parameter, and adjust callers accordingly. Replace the - remaining uses of the_repository in these functions with the - passed-in repository instance. + Add a struct repository *r parameter to these functions, and pass the local + repository through the callers. - This removes the use of the_repository global variable from - wt-status.c completely. + get_branch(), wt_status_check_rebase() and wt_status_check_bisect() already + receive a struct worktree *, which can provide access to the repository. + However, some callers pass NULL as the worktree like in wt_status_get_state(), + which would make using wt->repo unsafe and lead to segfault issues. + Add an explicit struct repository * parameter to these functions as well, + and pass the repository through the callers. + + Both wt_status_check_rebase() and wt_status_check_bisect() are called from + branch.c and worktree.c, + + * In branch.c, wt is always non-NULL as the functions are called within an + interation over worktrees in prepare_checked_out_branches(). + * In worktree.c the functions are called from is_worktree_being_rebased() and + is_worktree_being_bisected() respectively which are further called from + builtin/branch.c in reject_rebase_or_bisect_branch() which has a non-NULL + worktree as it is called inside an iteration over worktrees as well. Signed-off-by: Shreyansh Paliwal @@ branch.c: static void prepare_checked_out_branches(void) (state.rebase_in_progress || state.rebase_interactive_in_progress) && state.branch) { struct strbuf ref = STRBUF_INIT; +@@ branch.c: static void prepare_checked_out_branches(void) + } + wt_status_state_free_buffers(&state); + +- if (wt_status_check_bisect(wt, &state) && ++ if (wt_status_check_bisect(wt->repo, wt, &state) && + state.bisecting_from) { + struct strbuf ref = STRBUF_INIT; + strbuf_addf(&ref, "refs/heads/%s", state.bisecting_from); ## worktree.c ## @@ worktree.c: int is_worktree_being_rebased(const struct worktree *wt, @@ worktree.c: int is_worktree_being_rebased(const struct worktree *wt, (state.rebase_in_progress || state.rebase_interactive_in_progress) && state.branch && +@@ worktree.c: int is_worktree_being_bisected(const struct worktree *wt, + int found_bisect; + + memset(&state, 0, sizeof(state)); +- found_bisect = wt_status_check_bisect(wt, &state) && ++ found_bisect = wt_status_check_bisect(wt->repo, wt, &state) && + state.bisecting_from && + skip_prefix(target, "refs/heads/", &target) && + !strcmp(state.bisecting_from, target); ## wt-status.c ## @@ wt-status.c: static int stash_count_refs(const char *refname UNUSED, @@ wt-status.c: static int stash_count_refs(const char *refname UNUSED, +static int count_stash_entries(struct repository *r) { int n = 0; -- refs_for_each_reflog_ent(get_main_ref_store(the_repository), -+ refs_for_each_reflog_ent(get_main_ref_store(r), - "refs/stash", stash_count_refs, &n); - return n; - } + refs_for_each_reflog_ent(get_main_ref_store(the_repository), +@@ wt-status.c: static int count_stash_entries(void) static void wt_longstatus_print_stash_summary(struct wt_status *s) { @@ wt-status.c: static void show_am_in_progress(struct wt_status *s, } -static char *read_line_from_git_path(const char *filename) -+static char *read_line_from_git_path(struct repository *r, const char *filename) ++static char *read_line_from_git_path(struct repository *r, char *filename) { struct strbuf buf = STRBUF_INIT; -- FILE *fp = fopen_or_warn(repo_git_path_append(the_repository, &buf, -+ FILE *fp = fopen_or_warn(repo_git_path_append(r, &buf, - "%s", filename), "r"); - - if (!fp) { + FILE *fp = fopen_or_warn(repo_git_path_append(the_repository, &buf, @@ wt-status.c: static int split_commit_in_progress(struct wt_status *s) if (head_flags & REF_ISSYMREF || orig_head_flags & REF_ISSYMREF) return 0; @@ wt-status.c: static int split_commit_in_progress(struct wt_status *s) { struct string_list split = STRING_LIST_INIT_DUP; struct object_id oid; -@@ wt-status.c: static void abbrev_oid_in_line(struct strbuf *line) - return; - - if ((2 <= string_list_split(&split, line->buf, " ", 2)) && -- !repo_get_oid(the_repository, split.items[1].string, &oid)) { -+ !repo_get_oid(r, split.items[1].string, &oid)) { - strbuf_reset(line); - strbuf_addf(line, "%s ", split.items[0].string); - strbuf_add_unique_abbrev(line, &oid, DEFAULT_ABBREV); @@ wt-status.c: static void abbrev_oid_in_line(struct strbuf *line) string_list_clear(&split, 0); } -static int read_rebase_todolist(const char *fname, struct string_list *lines) -+static int read_rebase_todolist(struct repository *r, const char *fname, struct string_list *lines) ++static int read_rebase_todolist(struct repository *r, char *fname, struct string_list *lines) { struct strbuf buf = STRBUF_INIT; -- FILE *f = fopen(repo_git_path_append(the_repository, &buf, "%s", fname), "r"); -+ FILE *f = fopen(repo_git_path_append(r, &buf, "%s", fname), "r"); - int ret; - - if (!f) { -@@ wt-status.c: static int read_rebase_todolist(const char *fname, struct string_list *lines) - goto out; - } - die_errno("Could not open file %s for reading", -- repo_git_path_replace(the_repository, &buf, "%s", fname)); -+ repo_git_path_replace(r, &buf, "%s", fname)); - } - while (!strbuf_getline_lf(&buf, f)) { - if (starts_with(buf.buf, comment_line_str)) + FILE *f = fopen(repo_git_path_append(the_repository, &buf, "%s", fname), "r"); @@ wt-status.c: static int read_rebase_todolist(const char *fname, struct string_list *lines) strbuf_trim(&buf); if (!buf.len) @@ wt-status.c: static void show_rebase_information(struct wt_status *s, &yet_to_do)) status_printf_ln(s, color, _("git-rebase-todo is missing.")); +@@ wt-status.c: static void show_sparse_checkout_in_use(struct wt_status *s, + /* + * Extract branch information from rebase/bisect + */ +-static char *get_branch(const struct worktree *wt, const char *path) ++static char *get_branch(struct repository *r, struct worktree *wt, const char *path) + { + struct strbuf sb = STRBUF_INIT; + struct object_id oid; @@ wt-status.c: static void wt_status_get_detached_from(struct repository *r, strbuf_release(&cb.buf); } @@ wt-status.c: static void wt_status_get_detached_from(struct repository *r, -int wt_status_check_rebase(const struct worktree *wt, - struct wt_status_state *state) +int wt_status_check_rebase(struct repository *r, -+ const struct worktree *wt, -+ struct wt_status_state *state) ++ const struct worktree *wt, ++ struct wt_status_state *state) { struct stat st; -- if (!stat(worktree_git_path(the_repository, wt, "rebase-apply"), &st)) { -- if (!stat(worktree_git_path(the_repository, wt, "rebase-apply/applying"), &st)) { -+ if (!stat(worktree_git_path(r, wt, "rebase-apply"), &st)) { -+ if (!stat(worktree_git_path(r, wt, "rebase-apply/applying"), &st)) { - state->am_in_progress = 1; -- if (!stat(worktree_git_path(the_repository, wt, "rebase-apply/patch"), &st) && !st.st_size) -+ if (!stat(worktree_git_path(r, wt, "rebase-apply/patch"), &st) && !st.st_size) +@@ wt-status.c: int wt_status_check_rebase(const struct worktree *wt, state->am_empty_patch = 1; } else { state->rebase_in_progress = 1; - state->branch = get_branch(wt, "rebase-apply/head-name"); - state->onto = get_branch(wt, "rebase-apply/onto"); +- state->branch = get_branch(wt, "rebase-apply/head-name"); +- state->onto = get_branch(wt, "rebase-apply/onto"); ++ state->branch = get_branch(r, wt, "rebase-apply/head-name"); ++ state->onto = get_branch(r, wt, "rebase-apply/onto"); } -- } else if (!stat(worktree_git_path(the_repository, wt, "rebase-merge"), &st)) { -- if (!stat(worktree_git_path(the_repository, wt, "rebase-merge/interactive"), &st)) -+ } else if (!stat(worktree_git_path(r, wt, "rebase-merge"), &st)) { -+ if (!stat(worktree_git_path(r, wt, "rebase-merge/interactive"), &st)) + } else if (!stat(worktree_git_path(the_repository, wt, "rebase-merge"), &st)) { + if (!stat(worktree_git_path(the_repository, wt, "rebase-merge/interactive"), &st)) state->rebase_interactive_in_progress = 1; else state->rebase_in_progress = 1; +- state->branch = get_branch(wt, "rebase-merge/head-name"); +- state->onto = get_branch(wt, "rebase-merge/onto"); ++ state->branch = get_branch(r, wt, "rebase-merge/head-name"); ++ state->onto = get_branch(r, wt, "rebase-merge/onto"); + } else + return 0; + return 1; + } + +-int wt_status_check_bisect(const struct worktree *wt, ++int wt_status_check_bisect(struct repository *r, ++ struct worktree *wt, + struct wt_status_state *state) + { + struct stat st; + + if (!stat(worktree_git_path(the_repository, wt, "BISECT_LOG"), &st)) { + state->bisect_in_progress = 1; +- state->bisecting_from = get_branch(wt, "BISECT_START"); ++ state->bisecting_from = get_branch(r, wt, "BISECT_START"); + return 1; + } + return 0; @@ wt-status.c: void wt_status_get_state(struct repository *r, enum replay_action action; @@ wt-status.c: void wt_status_get_state(struct repository *r, ; /* all set */ } else if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") && !repo_get_oid(r, "CHERRY_PICK_HEAD", &oid)) { + state->cherry_pick_in_progress = 1; + oidcpy(&state->cherry_pick_head_oid, &oid); + } +- wt_status_check_bisect(NULL, state); ++ wt_status_check_bisect(r, NULL, state); + if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD") && + !repo_get_oid(r, "REVERT_HEAD", &oid)) { + state->revert_in_progress = 1; @@ wt-status.c: static void wt_porcelain_v2_print_tracking(struct wt_status *s) */ static void wt_porcelain_v2_print_stash(struct wt_status *s) @@ wt-status.h: void wt_status_state_free_buffers(struct wt_status_state *s); struct wt_status_state *state, int get_detached_from); -int wt_status_check_rebase(const struct worktree *wt, -- struct wt_status_state *state); +int wt_status_check_rebase(struct repository *r, -+ const struct worktree *wt, -+ struct wt_status_state *state); - int wt_status_check_bisect(const struct worktree *wt, ++ struct worktree *wt, + struct wt_status_state *state); +-int wt_status_check_bisect(const struct worktree *wt, ++int wt_status_check_bisect(struct repository *r, ++ struct worktree *wt, struct wt_status_state *state); + __attribute__((format (printf, 3, 4))) 1: 556735dfd4 ! 2: 906e682cd7 wt-status: replace uses of the_repository with local repository instances @@ Commit message wt-status: replace uses of the_repository with local repository instances wt-status.c uses the global the_repository in several places even when - a repository instance is already available via struct wt_status or - struct worktree. + a repository instance is already available via struct wt_status *s or + struct repository *r. - Replace these direct uses of the_repository with the repository carried - by the local structs (e.g. s->repo, wt->repo). + Replace these uses of the_repository with the repository available + in the local context (eg. s->repo or r). The replacements of all the_repository with s->repo are mostly to cases where a repository instance is already available via - struct wt_status. All functions operating on struct wt_status *s - are only used after s is initialized by wt_status_prepare(), + struct wt_status *s and struct repository *r, all functions operating on + struct wt_status *s are only used after s is initialized by wt_status_prepare(), which sets s->repo from the repository provided by the caller. - As a result, s->repo is guaranteed to be available and consistent - whenever these functions are invoked. + As a result, s->repo is guaranteed to be available and consistent whenever + these functions are invoked. This reduces reliance on global state and keeps wt-status consistent, though many functions operating on struct wt_status *s @@ wt-status.c: void wt_status_prepare(struct repository *r, struct wt_status *s) s->reference = "HEAD"; s->fp = stdout; - s->index_file = repo_get_index_file(the_repository); -+ s->index_file = repo_get_index_file(s->repo); ++ s->index_file = repo_get_index_file(r); s->change.strdup_strings = 1; s->untracked.strdup_strings = 1; s->ignored.strdup_strings = 1; @@ wt-status.c: static void wt_status_collect_changes_index(struct wt_status *s) setup_revisions(0, NULL, &rev, &opt); rev.diffopt.flags.override_submodule_config = 1; +@@ wt-status.c: static int stash_count_refs(const char *refname UNUSED, + static int count_stash_entries(struct repository *r) + { + int n = 0; +- refs_for_each_reflog_ent(get_main_ref_store(the_repository), ++ refs_for_each_reflog_ent(get_main_ref_store(r), + "refs/stash", stash_count_refs, &n); + return n; + } @@ wt-status.c: static void wt_longstatus_print_verbose(struct wt_status *s) rev.diffopt.ita_invisible_in_index = 1; @@ wt-status.c: static void wt_longstatus_print_verbose(struct wt_status *s) setup_revisions(0, NULL, &rev, &opt); rev.diffopt.output_format |= DIFF_FORMAT_PATCH; +@@ wt-status.c: static void show_am_in_progress(struct wt_status *s, + static char *read_line_from_git_path(struct repository *r, char *filename) + { + struct strbuf buf = STRBUF_INIT; +- FILE *fp = fopen_or_warn(repo_git_path_append(the_repository, &buf, ++ FILE *fp = fopen_or_warn(repo_git_path_append(r, &buf, + "%s", filename), "r"); + + if (!fp) { @@ wt-status.c: static int split_commit_in_progress(struct wt_status *s) !s->branch || strcmp(s->branch, "HEAD")) return 0; @@ wt-status.c: static int split_commit_in_progress(struct wt_status *s) &orig_head_oid, &orig_head_flags)) return 0; if (head_flags & REF_ISSYMREF || orig_head_flags & REF_ISSYMREF) +@@ wt-status.c: static void abbrev_oid_in_line(struct repository *r, struct strbuf *line) + return; + + if ((2 <= string_list_split(&split, line->buf, " ", 2)) && +- !repo_get_oid(the_repository, split.items[1].string, &oid)) { ++ !repo_get_oid(r, split.items[1].string, &oid)) { + strbuf_reset(line); + strbuf_addf(line, "%s ", split.items[0].string); + strbuf_add_unique_abbrev(line, &oid, DEFAULT_ABBREV); +@@ wt-status.c: static void abbrev_oid_in_line(struct repository *r, struct strbuf *line) + static int read_rebase_todolist(struct repository *r, char *fname, struct string_list *lines) + { + struct strbuf buf = STRBUF_INIT; +- FILE *f = fopen(repo_git_path_append(the_repository, &buf, "%s", fname), "r"); ++ FILE *f = fopen(repo_git_path_append(r, &buf, "%s", fname), "r"); + int ret; + + if (!f) { +@@ wt-status.c: static int read_rebase_todolist(struct repository *r, char *fname, struct string + goto out; + } + die_errno("Could not open file %s for reading", +- repo_git_path_replace(the_repository, &buf, "%s", fname)); ++ repo_git_path_replace(r, &buf, "%s", fname)); + } + while (!strbuf_getline_lf(&buf, f)) { + if (starts_with(buf.buf, comment_line_str)) @@ wt-status.c: static void show_rebase_information(struct wt_status *s, i++) status_printf_ln(s, color, " %s", have_done.items[i].string); @@ wt-status.c: static void show_revert_in_progress(struct wt_status *s, DEFAULT_ABBREV)); if (s->hints) { if (has_unmerged(s)) -@@ wt-status.c: static char *get_branch(const struct worktree *wt, const char *path) +@@ wt-status.c: static char *get_branch(struct repository *r, struct worktree *wt, const char *p struct object_id oid; const char *branch_name; - if (strbuf_read_file(&sb, worktree_git_path(the_repository, wt, "%s", path), 0) <= 0) -+ if (strbuf_read_file(&sb, worktree_git_path(wt->repo, wt, "%s", path), 0) <= 0) ++ if (strbuf_read_file(&sb, worktree_git_path(r, wt, "%s", path), 0) <= 0) goto got_nothing; while (sb.len && sb.buf[sb.len - 1] == '\n') @@ wt-status.c: static void wt_status_get_detached_from(struct repository *r, strbuf_release(&cb.buf); return; } -@@ wt-status.c: int wt_status_check_bisect(const struct worktree *wt, +@@ wt-status.c: int wt_status_check_rebase(struct repository *r, + { + struct stat st; + +- if (!stat(worktree_git_path(the_repository, wt, "rebase-apply"), &st)) { +- if (!stat(worktree_git_path(the_repository, wt, "rebase-apply/applying"), &st)) { ++ if (!stat(worktree_git_path(r, wt, "rebase-apply"), &st)) { ++ if (!stat(worktree_git_path(r, wt, "rebase-apply/applying"), &st)) { + state->am_in_progress = 1; +- if (!stat(worktree_git_path(the_repository, wt, "rebase-apply/patch"), &st) && !st.st_size) ++ if (!stat(worktree_git_path(r, wt, "rebase-apply/patch"), &st) && !st.st_size) + state->am_empty_patch = 1; + } else { + state->rebase_in_progress = 1; + state->branch = get_branch(r, wt, "rebase-apply/head-name"); + state->onto = get_branch(r, wt, "rebase-apply/onto"); + } +- } else if (!stat(worktree_git_path(the_repository, wt, "rebase-merge"), &st)) { +- if (!stat(worktree_git_path(the_repository, wt, "rebase-merge/interactive"), &st)) ++ } else if (!stat(worktree_git_path(r, wt, "rebase-merge"), &st)) { ++ if (!stat(worktree_git_path(r, wt, "rebase-merge/interactive"), &st)) + state->rebase_interactive_in_progress = 1; + else + state->rebase_in_progress = 1; +@@ wt-status.c: int wt_status_check_bisect(struct repository *r, { struct stat st; - if (!stat(worktree_git_path(the_repository, wt, "BISECT_LOG"), &st)) { -+ if (!stat(worktree_git_path(wt->repo, wt, "BISECT_LOG"), &st)) { ++ if (!stat(worktree_git_path(r, wt, "BISECT_LOG"), &st)) { state->bisect_in_progress = 1; - state->bisecting_from = get_branch(wt, "BISECT_START"); + state->bisecting_from = get_branch(r, wt, "BISECT_START"); return 1; @@ wt-status.c: static void wt_shortstatus_print_tracking(struct wt_status *s) upstream_is_gone = 1; 3: b745a08d96 = 3: 9c0a1d82ad wt-status: use hash_algo from local repository instead of global the_hash_algo -- 2.52.0