* [PATCH 0/3] worktree: stop using "the_repository" in is_current_worktree()
@ 2026-03-13 14:19 Phillip Wood
2026-03-13 14:19 ` [PATCH 1/3] worktree: remove "the_repository" from is_current_worktree() Phillip Wood
` (4 more replies)
0 siblings, 5 replies; 26+ messages in thread
From: Phillip Wood @ 2026-03-13 14:19 UTC (permalink / raw)
To: git
From: Phillip Wood <phillip.wood@dunelm.org.uk>
This is a follow up to pw/no-more-NULL-means-current-worktree that removes
"the_repository" from is_current_worktree() and get_worktree_git_dir().
The first patch removes the use of "the_repository" when determining
if a worktree is current. Patches 2 & 3 require a non-NULL worktree
when calling get_worktree_git_dir() to remove the last use of
"the_repository" in that function.
Base-Commit: 7f19e4e1b6a3ad259e2ed66033e01e03b8b74c5e
Published-As: https://github.com/phillipwood/git/releases/tag/pw%2Fworktree-is-current-use-repo%2Fv1
View-Changes-At: https://github.com/phillipwood/git/compare/7f19e4e1b...1151b5b30
Fetch-It-Via: git fetch https://github.com/phillipwood/git pw/worktree-is-current-use-repo/v1
Phillip Wood (3):
worktree: remove "the_repository" from is_current_worktree()
worktree add: stop reading ".git/HEAD"
worktree: reject NULL worktree in get_worktree_git_dir()
builtin/worktree.c | 21 ++-------------------
t/t2400-worktree-add.sh | 28 ++++++++++++----------------
worktree.c | 10 +++++-----
3 files changed, 19 insertions(+), 40 deletions(-)
--
2.52.0.362.g884e03848a9
^ permalink raw reply [flat|nested] 26+ messages in thread* [PATCH 1/3] worktree: remove "the_repository" from is_current_worktree() 2026-03-13 14:19 [PATCH 0/3] worktree: stop using "the_repository" in is_current_worktree() Phillip Wood @ 2026-03-13 14:19 ` Phillip Wood 2026-03-13 14:19 ` [PATCH 2/3] worktree add: stop reading ".git/HEAD" Phillip Wood ` (3 subsequent siblings) 4 siblings, 0 replies; 26+ messages in thread From: Phillip Wood @ 2026-03-13 14:19 UTC (permalink / raw) To: git; +Cc: Phillip Wood From: Phillip Wood <phillip.wood@dunelm.org.uk> is_current_worktree() compares the gitdir of the worktree to the gitdir of "the_repository" and returns true when they match. To get the gitdir of the worktree it calls get_workree_git_dir() which also depends on "the_repository". This has the effect that even if "wt->path" matches "wt->repo->worktree" is_current_worktree(wt) will return false when "wt->repo" is not "the_repository" which is confusing. The use of "the_repository" in is_current_wortree() comes from replacing get_git_dir() with repo_get_git_dir() in 246deeac951 (environment: make `get_git_dir()` accept a repository, 2024-09-12). In get_worktree_git_dir() it comes from replacing git_common_path() with repo_common_path() in 07242c2a5af (path: drop `git_common_path()` in favor of `repo_common_path()`, 2025-02-07). In both cases we have a repository instance available so use that instead. This means that a worktree "wt" is always considered current when "wt->path" matches "wt->repo->worktree" and so the worktree returned by get_worktree_from_repository() is always considered current. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> --- worktree.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/worktree.c b/worktree.c index e9ff6e6ef2e..344ad0c031b 100644 --- a/worktree.c +++ b/worktree.c @@ -58,7 +58,7 @@ static void add_head_info(struct worktree *wt) static int is_current_worktree(struct worktree *wt) { - char *git_dir = absolute_pathdup(repo_get_git_dir(the_repository)); + char *git_dir = absolute_pathdup(repo_get_git_dir(wt->repo)); char *wt_git_dir = get_worktree_git_dir(wt); int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir)); free(wt_git_dir); @@ -78,7 +78,7 @@ struct worktree *get_worktree_from_repository(struct repository *repo) wt->is_bare = !repo->worktree; if (fspathcmp(gitdir, commondir)) wt->id = xstrdup(find_last_dir_sep(gitdir) + 1); - wt->is_current = is_current_worktree(wt); + wt->is_current = true; add_head_info(wt); free(gitdir); @@ -229,9 +229,9 @@ char *get_worktree_git_dir(const struct worktree *wt) if (!wt) return xstrdup(repo_get_git_dir(the_repository)); else if (!wt->id) - return xstrdup(repo_get_common_dir(the_repository)); + return xstrdup(repo_get_common_dir(wt->repo)); else - return repo_common_path(the_repository, "worktrees/%s", wt->id); + return repo_common_path(wt->repo, "worktrees/%s", wt->id); } static struct worktree *find_worktree_by_suffix(struct worktree **list, -- 2.52.0.362.g884e03848a9 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 2/3] worktree add: stop reading ".git/HEAD" 2026-03-13 14:19 [PATCH 0/3] worktree: stop using "the_repository" in is_current_worktree() Phillip Wood 2026-03-13 14:19 ` [PATCH 1/3] worktree: remove "the_repository" from is_current_worktree() Phillip Wood @ 2026-03-13 14:19 ` Phillip Wood 2026-03-13 21:41 ` Junio C Hamano 2026-03-13 14:19 ` [PATCH 3/3] worktree: reject NULL worktree in get_worktree_git_dir() Phillip Wood ` (2 subsequent siblings) 4 siblings, 1 reply; 26+ messages in thread From: Phillip Wood @ 2026-03-13 14:19 UTC (permalink / raw) To: git; +Cc: Phillip Wood From: Phillip Wood <phillip.wood@dunelm.org.uk> The function can_use_local_refs() prints a warning if there are no local branches and HEAD is invalid or points to an unborn branch. As part of the warning it prints the contents of ".git/HEAD". In a repository using the reftable backend HEAD is not stored in the filesystem so reading that file is pointless. In a repository using the files backend it is unclear how useful printing it is - it would be better to diagnose the problem for the user. For now, simplify the warning by not printing the file contents and adjust the relevant test case accordingly. Also fixup the test case to use test_grep so that anyone trying to debug a test failure in the future is not met by a wall of silence. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> --- builtin/worktree.c | 21 ++------------------- t/t2400-worktree-add.sh | 28 ++++++++++++---------------- 2 files changed, 14 insertions(+), 35 deletions(-) diff --git a/builtin/worktree.c b/builtin/worktree.c index bc2d0d645ba..70410b53df3 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -692,25 +692,8 @@ static int can_use_local_refs(const struct add_opts *opts) if (refs_head_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) { return 1; } else if (refs_for_each_branch_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) { - if (!opts->quiet) { - struct strbuf path = STRBUF_INIT; - struct strbuf contents = STRBUF_INIT; - char *wt_gitdir = get_worktree_git_dir(NULL); - - strbuf_add_real_path(&path, wt_gitdir); - strbuf_addstr(&path, "/HEAD"); - strbuf_read_file(&contents, path.buf, 64); - strbuf_stripspace(&contents, NULL); - strbuf_strip_suffix(&contents, "\n"); - - warning(_("HEAD points to an invalid (or orphaned) reference.\n" - "HEAD path: '%s'\n" - "HEAD contents: '%s'"), - path.buf, contents.buf); - strbuf_release(&path); - strbuf_release(&contents); - free(wt_gitdir); - } + if (!opts->quiet) + warning(_("HEAD points to an invalid (or orphaned) reference.\n")); return 1; } return 0; diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh index 023e1301c8e..58b4445cc44 100755 --- a/t/t2400-worktree-add.sh +++ b/t/t2400-worktree-add.sh @@ -987,7 +987,7 @@ test_dwim_orphan () { then test_must_be_empty actual else - grep "$info_text" actual + test_grep "$info_text" actual fi elif [ "$outcome" = "no_infer" ] then @@ -996,39 +996,35 @@ test_dwim_orphan () { then test_must_be_empty actual else - ! grep "$info_text" actual + test_grep ! "$info_text" actual fi elif [ "$outcome" = "fetch_error" ] then test_must_fail git $dashc_args worktree add $args 2>actual && - grep "$fetch_error_text" actual + test_grep "$fetch_error_text" actual elif [ "$outcome" = "fatal_orphan_bad_combo" ] then test_must_fail git $dashc_args worktree add $args 2>actual && if [ $use_quiet -eq 1 ] then - ! grep "$info_text" actual + test_grep ! "$info_text" actual else - grep "$info_text" actual + test_grep "$info_text" actual fi && - grep "$bad_combo_regex" actual + test_grep "$bad_combo_regex" actual elif [ "$outcome" = "warn_bad_head" ] then test_must_fail git $dashc_args worktree add $args 2>actual && if [ $use_quiet -eq 1 ] then - grep "$invalid_ref_regex" actual && - ! grep "$orphan_hint" actual + test_grep "$invalid_ref_regex" actual && + test_grep ! "$orphan_hint" actual else - headpath=$(git $dashc_args rev-parse --path-format=absolute --git-path HEAD) && - headcontents=$(cat "$headpath") && - grep "HEAD points to an invalid (or orphaned) reference" actual && - grep "HEAD path: .$headpath." actual && - grep "HEAD contents: .$headcontents." actual && - grep "$orphan_hint" actual && - ! grep "$info_text" actual + test_grep "HEAD points to an invalid (or orphaned) reference" actual && + test_grep "$orphan_hint" actual && + test_grep ! "$info_text" actual fi && - grep "$invalid_ref_regex" actual + test_grep "$invalid_ref_regex" actual else # Unreachable false -- 2.52.0.362.g884e03848a9 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH 2/3] worktree add: stop reading ".git/HEAD" 2026-03-13 14:19 ` [PATCH 2/3] worktree add: stop reading ".git/HEAD" Phillip Wood @ 2026-03-13 21:41 ` Junio C Hamano 0 siblings, 0 replies; 26+ messages in thread From: Junio C Hamano @ 2026-03-13 21:41 UTC (permalink / raw) To: Phillip Wood; +Cc: git Phillip Wood <phillip.wood123@gmail.com> writes: > From: Phillip Wood <phillip.wood@dunelm.org.uk> > > The function can_use_local_refs() prints a warning if there are no local > branches and HEAD is invalid or points to an unborn branch. As part of > the warning it prints the contents of ".git/HEAD". In a repository using > the reftable backend HEAD is not stored in the filesystem so reading > that file is pointless. In a repository using the files backend it is > unclear how useful printing it is - it would be better to diagnose the > problem for the user. For now, simplify the warning by not printing > the file contents and adjust the relevant test case accordingly. Also > fixup the test case to use test_grep so that anyone trying to debug a > test failure in the future is not met by a wall of silence. > > Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> > --- > builtin/worktree.c | 21 ++------------------- > t/t2400-worktree-add.sh | 28 ++++++++++++---------------- > 2 files changed, 14 insertions(+), 35 deletions(-) > > diff --git a/builtin/worktree.c b/builtin/worktree.c > index bc2d0d645ba..70410b53df3 100644 > --- a/builtin/worktree.c > +++ b/builtin/worktree.c > @@ -692,25 +692,8 @@ static int can_use_local_refs(const struct add_opts *opts) > if (refs_head_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) { > return 1; > } else if (refs_for_each_branch_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) { > - if (!opts->quiet) { > - struct strbuf path = STRBUF_INIT; > - struct strbuf contents = STRBUF_INIT; > - char *wt_gitdir = get_worktree_git_dir(NULL); > - > - strbuf_add_real_path(&path, wt_gitdir); > - strbuf_addstr(&path, "/HEAD"); > - strbuf_read_file(&contents, path.buf, 64); > - strbuf_stripspace(&contents, NULL); > - strbuf_strip_suffix(&contents, "\n"); > - > - warning(_("HEAD points to an invalid (or orphaned) reference.\n" > - "HEAD path: '%s'\n" > - "HEAD contents: '%s'"), > - path.buf, contents.buf); > - strbuf_release(&path); > - strbuf_release(&contents); > - free(wt_gitdir); > - } > + if (!opts->quiet) > + warning(_("HEAD points to an invalid (or orphaned) reference.\n")); This is indented one level too deep, it seems. Other than that, I fully agree with the reasoning of the removal explained in the proposed log message, and the updated test to use test_grep does look much better. We seem to use "[ a = b ]" instead of "test a = b" in this test file, unlike everybody else, which I didn't notice before. Of course, this series has no need to touch them; it is just a tangent I happened to have noticed. Thanks. > diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh > index 023e1301c8e..58b4445cc44 100755 > --- a/t/t2400-worktree-add.sh > +++ b/t/t2400-worktree-add.sh > @@ -987,7 +987,7 @@ test_dwim_orphan () { > then > test_must_be_empty actual > else > - grep "$info_text" actual > + test_grep "$info_text" actual > fi > elif [ "$outcome" = "no_infer" ] > then > @@ -996,39 +996,35 @@ test_dwim_orphan () { > then > test_must_be_empty actual > else > - ! grep "$info_text" actual > + test_grep ! "$info_text" actual > fi > elif [ "$outcome" = "fetch_error" ] > then > test_must_fail git $dashc_args worktree add $args 2>actual && > - grep "$fetch_error_text" actual > + test_grep "$fetch_error_text" actual > elif [ "$outcome" = "fatal_orphan_bad_combo" ] > then > test_must_fail git $dashc_args worktree add $args 2>actual && > if [ $use_quiet -eq 1 ] > then > - ! grep "$info_text" actual > + test_grep ! "$info_text" actual > else > - grep "$info_text" actual > + test_grep "$info_text" actual > fi && > - grep "$bad_combo_regex" actual > + test_grep "$bad_combo_regex" actual > elif [ "$outcome" = "warn_bad_head" ] > then > test_must_fail git $dashc_args worktree add $args 2>actual && > if [ $use_quiet -eq 1 ] > then > - grep "$invalid_ref_regex" actual && > - ! grep "$orphan_hint" actual > + test_grep "$invalid_ref_regex" actual && > + test_grep ! "$orphan_hint" actual > else > - headpath=$(git $dashc_args rev-parse --path-format=absolute --git-path HEAD) && > - headcontents=$(cat "$headpath") && > - grep "HEAD points to an invalid (or orphaned) reference" actual && > - grep "HEAD path: .$headpath." actual && > - grep "HEAD contents: .$headcontents." actual && > - grep "$orphan_hint" actual && > - ! grep "$info_text" actual > + test_grep "HEAD points to an invalid (or orphaned) reference" actual && > + test_grep "$orphan_hint" actual && > + test_grep ! "$info_text" actual > fi && > - grep "$invalid_ref_regex" actual > + test_grep "$invalid_ref_regex" actual > else > # Unreachable > false ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 3/3] worktree: reject NULL worktree in get_worktree_git_dir() 2026-03-13 14:19 [PATCH 0/3] worktree: stop using "the_repository" in is_current_worktree() Phillip Wood 2026-03-13 14:19 ` [PATCH 1/3] worktree: remove "the_repository" from is_current_worktree() Phillip Wood 2026-03-13 14:19 ` [PATCH 2/3] worktree add: stop reading ".git/HEAD" Phillip Wood @ 2026-03-13 14:19 ` Phillip Wood 2026-03-13 21:42 ` Junio C Hamano 2026-03-15 16:18 ` [PATCH v2 0/3] worktree: stop using "the_repository" in is_current_worktree() Phillip Wood 2026-03-26 14:16 ` [PATCH v3 " Phillip Wood 4 siblings, 1 reply; 26+ messages in thread From: Phillip Wood @ 2026-03-13 14:19 UTC (permalink / raw) To: git; +Cc: Phillip Wood From: Phillip Wood <phillip.wood@dunelm.org.uk> This removes the final dependence on "the_repository" in get_worktree_git_dir(). The last commit removed only caller that passed a NULL worktree. get_worktree_git_dir() has the following callers: - branch.c:prepare_checked_out_branches() which loops over all worktrees. - builtin/fsck.c:cmd_fsck() which loops over all worktrees. - builtin/receive-pack.c:update_worktree() which is called from update() only when "worktree" is non-NULL. - builtin/worktree.c:validate_no_submodules() which is called from check_clean_worktree() and move_worktree(), both of which supply a non-NULL worktree. - reachable.c:add_rebase_files() which loops over all worktrees. - revision.c:add_index_objects_to_pending() which loops over all worktrees. - worktree.c:is_current_worktree() which expects a non-NULL worktree. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> --- worktree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worktree.c b/worktree.c index 344ad0c031b..1ed5e8c3cd2 100644 --- a/worktree.c +++ b/worktree.c @@ -227,7 +227,7 @@ struct worktree **get_worktrees_without_reading_head(void) char *get_worktree_git_dir(const struct worktree *wt) { if (!wt) - return xstrdup(repo_get_git_dir(the_repository)); + BUG("%s() called with NULL worktree", __func__); else if (!wt->id) return xstrdup(repo_get_common_dir(wt->repo)); else -- 2.52.0.362.g884e03848a9 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH 3/3] worktree: reject NULL worktree in get_worktree_git_dir() 2026-03-13 14:19 ` [PATCH 3/3] worktree: reject NULL worktree in get_worktree_git_dir() Phillip Wood @ 2026-03-13 21:42 ` Junio C Hamano 2026-03-14 20:09 ` Phillip Wood 0 siblings, 1 reply; 26+ messages in thread From: Junio C Hamano @ 2026-03-13 21:42 UTC (permalink / raw) To: Phillip Wood; +Cc: git Phillip Wood <phillip.wood123@gmail.com> writes: > From: Phillip Wood <phillip.wood@dunelm.org.uk> > > This removes the final dependence on "the_repository" in > get_worktree_git_dir(). The last commit removed only caller that > passed a NULL worktree. > > get_worktree_git_dir() has the following callers: > > - branch.c:prepare_checked_out_branches() which loops over all > worktrees. > > - builtin/fsck.c:cmd_fsck() which loops over all worktrees. > > - builtin/receive-pack.c:update_worktree() which is called from > update() only when "worktree" is non-NULL. > > - builtin/worktree.c:validate_no_submodules() which is called from > check_clean_worktree() and move_worktree(), both of which supply > a non-NULL worktree. > > - reachable.c:add_rebase_files() which loops over all worktrees. > > - revision.c:add_index_objects_to_pending() which loops over all > worktrees. > > - worktree.c:is_current_worktree() which expects a non-NULL worktree. > > Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> > --- > worktree.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/worktree.c b/worktree.c > index 344ad0c031b..1ed5e8c3cd2 100644 > --- a/worktree.c > +++ b/worktree.c > @@ -227,7 +227,7 @@ struct worktree **get_worktrees_without_reading_head(void) > char *get_worktree_git_dir(const struct worktree *wt) > { > if (!wt) > - return xstrdup(repo_get_git_dir(the_repository)); > + BUG("%s() called with NULL worktree", __func__); > else if (!wt->id) > return xstrdup(repo_get_common_dir(wt->repo)); > else <worktree.h> still has /* * Return git dir of the worktree. Note that the path may be relative. * If wt is NULL, git dir of current worktree is returned. */ char *get_worktree_git_dir(const struct worktree *wt); which needs a matching adjustment. ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 3/3] worktree: reject NULL worktree in get_worktree_git_dir() 2026-03-13 21:42 ` Junio C Hamano @ 2026-03-14 20:09 ` Phillip Wood 0 siblings, 0 replies; 26+ messages in thread From: Phillip Wood @ 2026-03-14 20:09 UTC (permalink / raw) To: Junio C Hamano; +Cc: git On 13/03/2026 21:42, Junio C Hamano wrote: > > <worktree.h> still has > > /* > * Return git dir of the worktree. Note that the path may be relative. > * If wt is NULL, git dir of current worktree is returned. > */ > char *get_worktree_git_dir(const struct worktree *wt); Good catch, I'll fix that and send a re-roll Thanks Phillip ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v2 0/3] worktree: stop using "the_repository" in is_current_worktree() 2026-03-13 14:19 [PATCH 0/3] worktree: stop using "the_repository" in is_current_worktree() Phillip Wood ` (2 preceding siblings ...) 2026-03-13 14:19 ` [PATCH 3/3] worktree: reject NULL worktree in get_worktree_git_dir() Phillip Wood @ 2026-03-15 16:18 ` Phillip Wood 2026-03-15 16:18 ` [PATCH v2 1/3] worktree: remove "the_repository" from is_current_worktree() Phillip Wood ` (3 more replies) 2026-03-26 14:16 ` [PATCH v3 " Phillip Wood 4 siblings, 4 replies; 26+ messages in thread From: Phillip Wood @ 2026-03-15 16:18 UTC (permalink / raw) To: git From: Phillip Wood <phillip.wood@dunelm.org.uk> This is a follow up to pw/no-more-NULL-means-current-worktree that removes "the_repository" from is_current_worktree() and get_worktree_git_dir(). The first patch removes the use of "the_repository" when determining if a worktree is current. Patches 2 & 3 require a non-NULL worktree when calling get_worktree_git_dir() to remove the last use of "the_repository" in that function. Changes since V1 - Patch 2: fixed indentation (thanks to Junio) - Patch 3: removed stale comment (thanks to Junio) Base-Commit: 7f19e4e1b6a3ad259e2ed66033e01e03b8b74c5e Published-As: https://github.com/phillipwood/git/releases/tag/pw%2Fworktree-is-current-use-repo%2Fv2 View-Changes-At: https://github.com/phillipwood/git/compare/7f19e4e1b...75eecc849 Fetch-It-Via: git fetch https://github.com/phillipwood/git pw/worktree-is-current-use-repo/v2 Phillip Wood (3): worktree: remove "the_repository" from is_current_worktree() worktree add: stop reading ".git/HEAD" worktree: reject NULL worktree in get_worktree_git_dir() builtin/worktree.c | 21 ++------------------- t/t2400-worktree-add.sh | 28 ++++++++++++---------------- worktree.c | 10 +++++----- worktree.h | 1 - 4 files changed, 19 insertions(+), 41 deletions(-) Range-diff against v1: 1: 075700a2256 = 1: 075700a2256 worktree: remove "the_repository" from is_current_worktree() 2: ae2a368e7e7 ! 2: c3c5767725d worktree add: stop reading ".git/HEAD" @@ builtin/worktree.c: static int can_use_local_refs(const struct add_opts *opts) - free(wt_gitdir); - } + if (!opts->quiet) -+ warning(_("HEAD points to an invalid (or orphaned) reference.\n")); ++ warning(_("HEAD points to an invalid (or orphaned) reference.\n")); return 1; } return 0; 3: 1151b5b3020 ! 3: 75eecc8492e worktree: reject NULL worktree in get_worktree_git_dir() @@ worktree.c: struct worktree **get_worktrees_without_reading_head(void) else if (!wt->id) return xstrdup(repo_get_common_dir(wt->repo)); else + + ## worktree.h ## +@@ worktree.h: int submodule_uses_worktrees(const char *path); + + /* + * Return git dir of the worktree. Note that the path may be relative. +- * If wt is NULL, git dir of current worktree is returned. + */ + char *get_worktree_git_dir(const struct worktree *wt); + -- 2.52.0.362.g884e03848a9 ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v2 1/3] worktree: remove "the_repository" from is_current_worktree() 2026-03-15 16:18 ` [PATCH v2 0/3] worktree: stop using "the_repository" in is_current_worktree() Phillip Wood @ 2026-03-15 16:18 ` Phillip Wood 2026-03-16 7:38 ` Patrick Steinhardt 2026-03-15 16:18 ` [PATCH v2 2/3] worktree add: stop reading ".git/HEAD" Phillip Wood ` (2 subsequent siblings) 3 siblings, 1 reply; 26+ messages in thread From: Phillip Wood @ 2026-03-15 16:18 UTC (permalink / raw) To: git; +Cc: Phillip Wood From: Phillip Wood <phillip.wood@dunelm.org.uk> is_current_worktree() compares the gitdir of the worktree to the gitdir of "the_repository" and returns true when they match. To get the gitdir of the worktree it calls get_workree_git_dir() which also depends on "the_repository". This has the effect that even if "wt->path" matches "wt->repo->worktree" is_current_worktree(wt) will return false when "wt->repo" is not "the_repository" which is confusing. The use of "the_repository" in is_current_wortree() comes from replacing get_git_dir() with repo_get_git_dir() in 246deeac951 (environment: make `get_git_dir()` accept a repository, 2024-09-12). In get_worktree_git_dir() it comes from replacing git_common_path() with repo_common_path() in 07242c2a5af (path: drop `git_common_path()` in favor of `repo_common_path()`, 2025-02-07). In both cases we have a repository instance available so use that instead. This means that a worktree "wt" is always considered current when "wt->path" matches "wt->repo->worktree" and so the worktree returned by get_worktree_from_repository() is always considered current. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> --- worktree.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/worktree.c b/worktree.c index e9ff6e6ef2e..344ad0c031b 100644 --- a/worktree.c +++ b/worktree.c @@ -58,7 +58,7 @@ static void add_head_info(struct worktree *wt) static int is_current_worktree(struct worktree *wt) { - char *git_dir = absolute_pathdup(repo_get_git_dir(the_repository)); + char *git_dir = absolute_pathdup(repo_get_git_dir(wt->repo)); char *wt_git_dir = get_worktree_git_dir(wt); int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir)); free(wt_git_dir); @@ -78,7 +78,7 @@ struct worktree *get_worktree_from_repository(struct repository *repo) wt->is_bare = !repo->worktree; if (fspathcmp(gitdir, commondir)) wt->id = xstrdup(find_last_dir_sep(gitdir) + 1); - wt->is_current = is_current_worktree(wt); + wt->is_current = true; add_head_info(wt); free(gitdir); @@ -229,9 +229,9 @@ char *get_worktree_git_dir(const struct worktree *wt) if (!wt) return xstrdup(repo_get_git_dir(the_repository)); else if (!wt->id) - return xstrdup(repo_get_common_dir(the_repository)); + return xstrdup(repo_get_common_dir(wt->repo)); else - return repo_common_path(the_repository, "worktrees/%s", wt->id); + return repo_common_path(wt->repo, "worktrees/%s", wt->id); } static struct worktree *find_worktree_by_suffix(struct worktree **list, -- 2.52.0.362.g884e03848a9 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH v2 1/3] worktree: remove "the_repository" from is_current_worktree() 2026-03-15 16:18 ` [PATCH v2 1/3] worktree: remove "the_repository" from is_current_worktree() Phillip Wood @ 2026-03-16 7:38 ` Patrick Steinhardt 2026-03-16 16:22 ` Phillip Wood 0 siblings, 1 reply; 26+ messages in thread From: Patrick Steinhardt @ 2026-03-16 7:38 UTC (permalink / raw) To: Phillip Wood; +Cc: git, Phillip Wood On Sun, Mar 15, 2026 at 04:18:50PM +0000, Phillip Wood wrote: > From: Phillip Wood <phillip.wood@dunelm.org.uk> > > is_current_worktree() compares the gitdir of the worktree to the gitdir > of "the_repository" and returns true when they match. To get the gitdir > of the worktree it calls get_workree_git_dir() which also depends on > "the_repository". This has the effect that even if "wt->path" matches > "wt->repo->worktree" is_current_worktree(wt) will return false when > "wt->repo" is not "the_repository" which is confusing. > > The use of "the_repository" in is_current_wortree() comes from > replacing get_git_dir() with repo_get_git_dir() in 246deeac951 > (environment: make `get_git_dir()` accept a repository, 2024-09-12). In > get_worktree_git_dir() it comes from replacing git_common_path() with > repo_common_path() in 07242c2a5af (path: drop `git_common_path()` > in favor of `repo_common_path()`, 2025-02-07). In both cases we have > a repository instance available so use that instead. This means > that a worktree "wt" is always considered current when "wt->path" > matches "wt->repo->worktree" and so the worktree returned by > get_worktree_from_repository() is always considered current. > > Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> > --- > worktree.c | 8 ++++---- > 1 file changed, 4 insertions(+), 4 deletions(-) > > diff --git a/worktree.c b/worktree.c > index e9ff6e6ef2e..344ad0c031b 100644 > --- a/worktree.c > +++ b/worktree.c > @@ -58,7 +58,7 @@ static void add_head_info(struct worktree *wt) > > static int is_current_worktree(struct worktree *wt) > { > - char *git_dir = absolute_pathdup(repo_get_git_dir(the_repository)); > + char *git_dir = absolute_pathdup(repo_get_git_dir(wt->repo)); > char *wt_git_dir = get_worktree_git_dir(wt); > int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir)); > free(wt_git_dir); Hm, okay. > @@ -78,7 +78,7 @@ struct worktree *get_worktree_from_repository(struct repository *repo) > wt->is_bare = !repo->worktree; > if (fspathcmp(gitdir, commondir)) > wt->id = xstrdup(find_last_dir_sep(gitdir) + 1); > - wt->is_current = is_current_worktree(wt); > + wt->is_current = true; > add_head_info(wt); > > free(gitdir); I have been staring at this code for longer than I want to admit, and I still haven't convinced myself that this is not a change in behaviour. I think what I'm wondering about is what `is_current_worktree()` is actually intended to do. In other words, what _do_ we consider to be "current"? Naively, I would consider the worktree "current" that the Git process has been invoked in. So if I pass a repo other than `the_repository`, or if I pass a worktree that is not the one that Git has been started in, then I would expect the function to return `false`. With that naive assumption your change would be breaking the existing logic. But I have no idea whether my assumption is correct or not, as there is not really any documentation of what the function or of the `struct worktree::is_current` field. And having a look at a couple of callers doesn't really make me all the wiser. It would be great if you could shine some light on this and then also add a bit of documentation to either the function, the field, or both :) Thanks! Patrick ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v2 1/3] worktree: remove "the_repository" from is_current_worktree() 2026-03-16 7:38 ` Patrick Steinhardt @ 2026-03-16 16:22 ` Phillip Wood 2026-03-17 10:24 ` Phillip Wood 0 siblings, 1 reply; 26+ messages in thread From: Phillip Wood @ 2026-03-16 16:22 UTC (permalink / raw) To: Patrick Steinhardt, Phillip Wood; +Cc: git Hi Patrick On 16/03/2026 07:38, Patrick Steinhardt wrote: > On Sun, Mar 15, 2026 at 04:18:50PM +0000, Phillip Wood wrote: >> From: Phillip Wood <phillip.wood@dunelm.org.uk> >> >> is_current_worktree() compares the gitdir of the worktree to the gitdir >> of "the_repository" and returns true when they match. To get the gitdir >> of the worktree it calls get_workree_git_dir() which also depends on >> "the_repository". This has the effect that even if "wt->path" matches >> "wt->repo->worktree" is_current_worktree(wt) will return false when >> "wt->repo" is not "the_repository" which is confusing. >> >> The use of "the_repository" in is_current_wortree() comes from >> replacing get_git_dir() with repo_get_git_dir() in 246deeac951 >> (environment: make `get_git_dir()` accept a repository, 2024-09-12). In >> get_worktree_git_dir() it comes from replacing git_common_path() with >> repo_common_path() in 07242c2a5af (path: drop `git_common_path()` >> in favor of `repo_common_path()`, 2025-02-07). In both cases we have >> a repository instance available so use that instead. This means >> that a worktree "wt" is always considered current when "wt->path" >> matches "wt->repo->worktree" and so the worktree returned by >> get_worktree_from_repository() is always considered current. >> >> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> >> --- >> worktree.c | 8 ++++---- >> 1 file changed, 4 insertions(+), 4 deletions(-) >> >> diff --git a/worktree.c b/worktree.c >> index e9ff6e6ef2e..344ad0c031b 100644 >> --- a/worktree.c >> +++ b/worktree.c >> @@ -58,7 +58,7 @@ static void add_head_info(struct worktree *wt) >> >> static int is_current_worktree(struct worktree *wt) >> { >> - char *git_dir = absolute_pathdup(repo_get_git_dir(the_repository)); >> + char *git_dir = absolute_pathdup(repo_get_git_dir(wt->repo)); >> char *wt_git_dir = get_worktree_git_dir(wt); >> int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir)); >> free(wt_git_dir); > > Hm, okay. > >> @@ -78,7 +78,7 @@ struct worktree *get_worktree_from_repository(struct repository *repo) >> wt->is_bare = !repo->worktree; >> if (fspathcmp(gitdir, commondir)) >> wt->id = xstrdup(find_last_dir_sep(gitdir) + 1); >> - wt->is_current = is_current_worktree(wt); >> + wt->is_current = true; >> add_head_info(wt); >> >> free(gitdir); > > I have been staring at this code for longer than I want to admit, and I > still haven't convinced myself that this is not a change in behaviour. > I think what I'm wondering about is what `is_current_worktree()` is > actually intended to do. In other words, what _do_ we consider to be > "current"? There was some discussion about that in [1] where reviewers were surprised that we needed to call is_current_worktree() here. This change is consistent with the rest of the patch but you're right that it is a change in behavior as at the moment the "current" worktree is set by the worktree that the process was started in. In practice we only ever have a single struct repository instance per process so there is no practical change in behavior here. At the moment, if you visit a set of submodules from the superproject by forking one process per submodule each submodule worktree is considered "current", but if you visit them by spinning up some threads in the current process they are not considered "current". That seems to me to be inconsistent as the process started by the user is in the superproject in both cases. The "is_current" field was added in [1] without any discussion in the commit message. It seems to have been added to stop the same branch being checked out in multiple worktrees [2]. Thanks Phillip [1] 750e8a60d69 (worktree.c: mark current worktree, 2016-04-22) [2] https://lore.kernel.org/git/CAJZYdzhG8h3s=Ep1fuGbam1cWhYkv0tW6tQ7pBGGj+fj6=Nrsw@mail.gmail.com/ > I would consider the worktree "current" that the Git process > has been invoked in. So if I pass a repo other than `the_repository`, or > if I pass a worktree that is not the one that Git has been started in, > then I would expect the function to return `false`. With that naive > assumption your change would be breaking the existing logic. > > But I have no idea whether my assumption is correct or not, as > there is not really any documentation of what the function or of the > `struct worktree::is_current` field. And having a look at a couple of > callers doesn't really make me all the wiser. > > It would be great if you could shine some light on this and then also > add a bit of documentation to either the function, the field, or both :) > > Thanks! > > Patrick > ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v2 1/3] worktree: remove "the_repository" from is_current_worktree() 2026-03-16 16:22 ` Phillip Wood @ 2026-03-17 10:24 ` Phillip Wood 2026-03-23 9:41 ` Shreyansh Paliwal 0 siblings, 1 reply; 26+ messages in thread From: Phillip Wood @ 2026-03-17 10:24 UTC (permalink / raw) To: Patrick Steinhardt, Phillip Wood; +Cc: git On 16/03/2026 16:22, Phillip Wood wrote: > >> I have been staring at this code for longer than I want to admit, and I >> still haven't convinced myself that this is not a change in behaviour. >> I think what I'm wondering about is what `is_current_worktree()` is >> actually intended to do. In other words, what _do_ we consider to be >> "current"? > > There was some discussion about that in [1] where reviewers were > surprised that we needed to call is_current_worktree() here. This change > is consistent with the rest of the patch but you're right that it is a > change in behavior as at the moment the "current" worktree is set by the > worktree that the process was started in. In practice we only ever have > a single struct repository instance per process so there is no practical > change in behavior here. At the moment, if you visit a set of submodules > from the superproject by forking one process per submodule each > submodule worktree is considered "current", but if you visit them by > spinning up some threads in the current process they are not considered > "current". That seems to me to be inconsistent as the process started by > the user is in the superproject in both cases. To add another example get_worktree_ref_store() checks `wt->is_current` to see if it should use the ref store in `wt->repo` or create a new store. That means that if we use `the_repository` to define the current worktree we'll end up opening a copy of the ref store in `wt->repo` when `repo != the_repository` and `wt->path` matches `wt->repo->worktree` when we could be using the ref store that's already open. It is a bit like the bug fixed by 1339cb3c47a (worktree: don't store main worktree twice, 2024-06-06) but for multiple repositories. I'll re-roll with a bit more description in the commit message but I'm going to be off the list for most of the rest of this week so it will probably be next week before I post a new version. Thanks Phillip > The "is_current" field was added in [1] without any discussion in the > commit message. It seems to have been added to stop the same branch > being checked out in multiple worktrees [2]. > > Thanks > > Phillip > > [1] 750e8a60d69 (worktree.c: mark current worktree, 2016-04-22) > [2] https://lore.kernel.org/git/ > CAJZYdzhG8h3s=Ep1fuGbam1cWhYkv0tW6tQ7pBGGj+fj6=Nrsw@mail.gmail.com/ > > >> I would consider the worktree "current" that the Git process >> has been invoked in. So if I pass a repo other than `the_repository`, or >> if I pass a worktree that is not the one that Git has been started in, >> then I would expect the function to return `false`. With that naive >> assumption your change would be breaking the existing logic. >> >> But I have no idea whether my assumption is correct or not, as >> there is not really any documentation of what the function or of the >> `struct worktree::is_current` field. And having a look at a couple of >> callers doesn't really make me all the wiser. >> >> It would be great if you could shine some light on this and then also >> add a bit of documentation to either the function, the field, or both :) >> >> Thanks! >> >> Patrick >> > ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v2 1/3] worktree: remove "the_repository" from is_current_worktree() 2026-03-17 10:24 ` Phillip Wood @ 2026-03-23 9:41 ` Shreyansh Paliwal 2026-03-23 14:37 ` Phillip Wood 0 siblings, 1 reply; 26+ messages in thread From: Shreyansh Paliwal @ 2026-03-23 9:41 UTC (permalink / raw) To: git; +Cc: phillip.wood, ps > On 16/03/2026 16:22, Phillip Wood wrote: > > > >> I have been staring at this code for longer than I want to admit, and I > >> still haven't convinced myself that this is not a change in behaviour. > >> I think what I'm wondering about is what `is_current_worktree()` is > >> actually intended to do. In other words, what _do_ we consider to be > >> "current"? > > > > There was some discussion about that in [1] where reviewers were > > surprised that we needed to call is_current_worktree() here. This change > > is consistent with the rest of the patch but you're right that it is a > > change in behavior as at the moment the "current" worktree is set by the > > worktree that the process was started in. In practice we only ever have > > a single struct repository instance per process so there is no practical > > change in behavior here. At the moment, if you visit a set of submodules > > from the superproject by forking one process per submodule each > > submodule worktree is considered "current", but if you visit them by > > spinning up some threads in the current process they are not considered > > "current". That seems to me to be inconsistent as the process started by > > the user is in the superproject in both cases. > > To add another example get_worktree_ref_store() checks `wt->is_current` > to see if it should use the ref store in `wt->repo` or create a new > store. That means that if we use `the_repository` to define the current > worktree we'll end up opening a copy of the ref store in `wt->repo` when > `repo != the_repository` and `wt->path` matches `wt->repo->worktree` > when we could be using the ref store that's already open. It is a bit > like the bug fixed by 1339cb3c47a (worktree: don't store main worktree > twice, 2024-06-06) but for multiple repositories. > > I'll re-roll with a bit more description in the commit message but I'm > going to be off the list for most of the rest of this week so it will > probably be next week before I post a new version. > > Thanks > > Phillip > > > The "is_current" field was added in [1] without any discussion in the > > commit message. It seems to have been added to stop the same branch > > being checked out in multiple worktrees [2]. > > > > Thanks > > > > Phillip > > > > [1] 750e8a60d69 (worktree.c: mark current worktree, 2016-04-22) > > [2] https://lore.kernel.org/git/ > > CAJZYdzhG8h3s=Ep1fuGbam1cWhYkv0tW6tQ7pBGGj+fj6=Nrsw@mail.gmail.com/ > > > > > >> I would consider the worktree "current" that the Git process > >> has been invoked in. So if I pass a repo other than `the_repository`, or > >> if I pass a worktree that is not the one that Git has been started in, > >> then I would expect the function to return `false`. With that naive > >> assumption your change would be breaking the existing logic. > >> > >> But I have no idea whether my assumption is correct or not, as > >> there is not really any documentation of what the function or of the > >> `struct worktree::is_current` field. And having a look at a couple of > >> callers doesn't really make me all the wiser. > >> > >> It would be great if you could shine some light on this and then also > >> add a bit of documentation to either the function, the field, or both :) > >> > >> Thanks! > >> > >> Patrick > >> > > Hi Phillip, This may be slightly out of scope for this series. My understanding so far has been that originally wt == NULL is used to represent the 'current worktree', which eventually meant following the process-wide state (the_repository). With the ongoing multi-repository work, the meaning is being changed to be interpreted as 'the worktree associated with the repository that we are working in'. However, in path.c there are some callers of repo_git_pathv() passing wt as 'NULL', I know that there is not involvement of the_repository state but it would be create less confusion if the semantics of worktrees are same everywhere. So if we replace those NULL callers with the current worktree and update the checks of (!wt) to (is_current_worktree(wt)), some tests are failing mostly related to refs of linked worktrees, and I think the error is originating from this, if (!wt) adjust_git_path(repo, buf, gitdir_len); So I am a bit confused to whether wt being NULL here could mean something else behaviour wise ? Best, Shreyansh ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v2 1/3] worktree: remove "the_repository" from is_current_worktree() 2026-03-23 9:41 ` Shreyansh Paliwal @ 2026-03-23 14:37 ` Phillip Wood 2026-03-23 17:05 ` Shreyansh Paliwal 0 siblings, 1 reply; 26+ messages in thread From: Phillip Wood @ 2026-03-23 14:37 UTC (permalink / raw) To: Shreyansh Paliwal, git; +Cc: phillip.wood, ps On 23/03/2026 09:41, Shreyansh Paliwal wrote: > > This may be slightly out of scope for this series. My understanding so far > has been that originally wt == NULL is used to represent the 'current worktree', > which eventually meant following the process-wide state (the_repository). > With the ongoing multi-repository work, the meaning is being changed to be > interpreted as 'the worktree associated with the repository that we are working in'. > However, in path.c there are some callers of repo_git_pathv() passing wt as 'NULL', > I know that there is not involvement of the_repository state but it would be create > less confusion if the semantics of worktrees are same everywhere. So if we replace > those NULL callers with the current worktree and update the checks of (!wt) to > (is_current_worktree(wt)), some tests are failing mostly related to refs of linked > worktrees, and I think the error is originating from this, > > if (!wt) > adjust_git_path(repo, buf, gitdir_len); > > So I am a bit confused to whether wt being NULL here could mean something else > behaviour wise ? That line comes from 543107333b3 (path: worktree_git_path() should not use file relocation, 2017-06-22) which explains why we don't adjust the patch when wt is non-NULL. worktree_git_path() is called from builtin/fsck.c in a loop over all worktrees so changing 'if (!wt)' 'if (!wt->is_current)' will change the behavior for the current worktree. While it might be nice to clean this up in the future, it is an internal helper function so I'm less worried it than if it were a public function. Thanks Phillip ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v2 1/3] worktree: remove "the_repository" from is_current_worktree() 2026-03-23 14:37 ` Phillip Wood @ 2026-03-23 17:05 ` Shreyansh Paliwal 0 siblings, 0 replies; 26+ messages in thread From: Shreyansh Paliwal @ 2026-03-23 17:05 UTC (permalink / raw) To: git; +Cc: phillip.wood, ps > On 23/03/2026 09:41, Shreyansh Paliwal wrote: > > > > This may be slightly out of scope for this series. My understanding so far > > has been that originally wt == NULL is used to represent the 'current worktree', > > which eventually meant following the process-wide state (the_repository). > > With the ongoing multi-repository work, the meaning is being changed to be > > interpreted as 'the worktree associated with the repository that we are working in'. > > However, in path.c there are some callers of repo_git_pathv() passing wt as 'NULL', > > I know that there is not involvement of the_repository state but it would be create > > less confusion if the semantics of worktrees are same everywhere. So if we replace > > those NULL callers with the current worktree and update the checks of (!wt) to > > (is_current_worktree(wt)), some tests are failing mostly related to refs of linked > > worktrees, and I think the error is originating from this, > > > > if (!wt) > > adjust_git_path(repo, buf, gitdir_len); > > > > So I am a bit confused to whether wt being NULL here could mean something else > > behaviour wise ? > > That line comes from 543107333b3 (path: worktree_git_path() should not > use file relocation, 2017-06-22) which explains why we don't adjust the > patch when wt is non-NULL. worktree_git_path() is called from > builtin/fsck.c in a loop over all worktrees so changing 'if (!wt)' 'if > (!wt->is_current)' will change the behavior for the current worktree. > While it might be nice to clean this up in the future, it is an internal > helper function so I'm less worried it than if it were a public function. Hmm, I see. That would end up calling adjust_git_path() for the current worktree in that list of worktrees, which wasn’t happening before, that makes sense. Thanks. ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v2 2/3] worktree add: stop reading ".git/HEAD" 2026-03-15 16:18 ` [PATCH v2 0/3] worktree: stop using "the_repository" in is_current_worktree() Phillip Wood 2026-03-15 16:18 ` [PATCH v2 1/3] worktree: remove "the_repository" from is_current_worktree() Phillip Wood @ 2026-03-15 16:18 ` Phillip Wood 2026-03-16 7:39 ` Patrick Steinhardt 2026-03-15 16:18 ` [PATCH v2 3/3] worktree: reject NULL worktree in get_worktree_git_dir() Phillip Wood 2026-03-15 21:17 ` [PATCH v2 0/3] worktree: stop using "the_repository" in is_current_worktree() Junio C Hamano 3 siblings, 1 reply; 26+ messages in thread From: Phillip Wood @ 2026-03-15 16:18 UTC (permalink / raw) To: git; +Cc: Phillip Wood From: Phillip Wood <phillip.wood@dunelm.org.uk> The function can_use_local_refs() prints a warning if there are no local branches and HEAD is invalid or points to an unborn branch. As part of the warning it prints the contents of ".git/HEAD". In a repository using the reftable backend HEAD is not stored in the filesystem so reading that file is pointless. In a repository using the files backend it is unclear how useful printing it is - it would be better to diagnose the problem for the user. For now, simplify the warning by not printing the file contents and adjust the relevant test case accordingly. Also fixup the test case to use test_grep so that anyone trying to debug a test failure in the future is not met by a wall of silence. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> --- builtin/worktree.c | 21 ++------------------- t/t2400-worktree-add.sh | 28 ++++++++++++---------------- 2 files changed, 14 insertions(+), 35 deletions(-) diff --git a/builtin/worktree.c b/builtin/worktree.c index bc2d0d645ba..9170b2e8981 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -692,25 +692,8 @@ static int can_use_local_refs(const struct add_opts *opts) if (refs_head_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) { return 1; } else if (refs_for_each_branch_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) { - if (!opts->quiet) { - struct strbuf path = STRBUF_INIT; - struct strbuf contents = STRBUF_INIT; - char *wt_gitdir = get_worktree_git_dir(NULL); - - strbuf_add_real_path(&path, wt_gitdir); - strbuf_addstr(&path, "/HEAD"); - strbuf_read_file(&contents, path.buf, 64); - strbuf_stripspace(&contents, NULL); - strbuf_strip_suffix(&contents, "\n"); - - warning(_("HEAD points to an invalid (or orphaned) reference.\n" - "HEAD path: '%s'\n" - "HEAD contents: '%s'"), - path.buf, contents.buf); - strbuf_release(&path); - strbuf_release(&contents); - free(wt_gitdir); - } + if (!opts->quiet) + warning(_("HEAD points to an invalid (or orphaned) reference.\n")); return 1; } return 0; diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh index 023e1301c8e..58b4445cc44 100755 --- a/t/t2400-worktree-add.sh +++ b/t/t2400-worktree-add.sh @@ -987,7 +987,7 @@ test_dwim_orphan () { then test_must_be_empty actual else - grep "$info_text" actual + test_grep "$info_text" actual fi elif [ "$outcome" = "no_infer" ] then @@ -996,39 +996,35 @@ test_dwim_orphan () { then test_must_be_empty actual else - ! grep "$info_text" actual + test_grep ! "$info_text" actual fi elif [ "$outcome" = "fetch_error" ] then test_must_fail git $dashc_args worktree add $args 2>actual && - grep "$fetch_error_text" actual + test_grep "$fetch_error_text" actual elif [ "$outcome" = "fatal_orphan_bad_combo" ] then test_must_fail git $dashc_args worktree add $args 2>actual && if [ $use_quiet -eq 1 ] then - ! grep "$info_text" actual + test_grep ! "$info_text" actual else - grep "$info_text" actual + test_grep "$info_text" actual fi && - grep "$bad_combo_regex" actual + test_grep "$bad_combo_regex" actual elif [ "$outcome" = "warn_bad_head" ] then test_must_fail git $dashc_args worktree add $args 2>actual && if [ $use_quiet -eq 1 ] then - grep "$invalid_ref_regex" actual && - ! grep "$orphan_hint" actual + test_grep "$invalid_ref_regex" actual && + test_grep ! "$orphan_hint" actual else - headpath=$(git $dashc_args rev-parse --path-format=absolute --git-path HEAD) && - headcontents=$(cat "$headpath") && - grep "HEAD points to an invalid (or orphaned) reference" actual && - grep "HEAD path: .$headpath." actual && - grep "HEAD contents: .$headcontents." actual && - grep "$orphan_hint" actual && - ! grep "$info_text" actual + test_grep "HEAD points to an invalid (or orphaned) reference" actual && + test_grep "$orphan_hint" actual && + test_grep ! "$info_text" actual fi && - grep "$invalid_ref_regex" actual + test_grep "$invalid_ref_regex" actual else # Unreachable false -- 2.52.0.362.g884e03848a9 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH v2 2/3] worktree add: stop reading ".git/HEAD" 2026-03-15 16:18 ` [PATCH v2 2/3] worktree add: stop reading ".git/HEAD" Phillip Wood @ 2026-03-16 7:39 ` Patrick Steinhardt 0 siblings, 0 replies; 26+ messages in thread From: Patrick Steinhardt @ 2026-03-16 7:39 UTC (permalink / raw) To: Phillip Wood; +Cc: git, Phillip Wood On Sun, Mar 15, 2026 at 04:18:51PM +0000, Phillip Wood wrote: > From: Phillip Wood <phillip.wood@dunelm.org.uk> > > The function can_use_local_refs() prints a warning if there are no local > branches and HEAD is invalid or points to an unborn branch. As part of > the warning it prints the contents of ".git/HEAD". In a repository using > the reftable backend HEAD is not stored in the filesystem so reading > that file is pointless. In a repository using the files backend it is > unclear how useful printing it is - it would be better to diagnose the > problem for the user. For now, simplify the warning by not printing > the file contents and adjust the relevant test case accordingly. Also > fixup the test case to use test_grep so that anyone trying to debug a > test failure in the future is not met by a wall of silence. Oh, interesting, and good catch. I fully agree that removing this makes sense. Patrick ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v2 3/3] worktree: reject NULL worktree in get_worktree_git_dir() 2026-03-15 16:18 ` [PATCH v2 0/3] worktree: stop using "the_repository" in is_current_worktree() Phillip Wood 2026-03-15 16:18 ` [PATCH v2 1/3] worktree: remove "the_repository" from is_current_worktree() Phillip Wood 2026-03-15 16:18 ` [PATCH v2 2/3] worktree add: stop reading ".git/HEAD" Phillip Wood @ 2026-03-15 16:18 ` Phillip Wood 2026-03-15 21:17 ` [PATCH v2 0/3] worktree: stop using "the_repository" in is_current_worktree() Junio C Hamano 3 siblings, 0 replies; 26+ messages in thread From: Phillip Wood @ 2026-03-15 16:18 UTC (permalink / raw) To: git; +Cc: Phillip Wood From: Phillip Wood <phillip.wood@dunelm.org.uk> This removes the final dependence on "the_repository" in get_worktree_git_dir(). The last commit removed only caller that passed a NULL worktree. get_worktree_git_dir() has the following callers: - branch.c:prepare_checked_out_branches() which loops over all worktrees. - builtin/fsck.c:cmd_fsck() which loops over all worktrees. - builtin/receive-pack.c:update_worktree() which is called from update() only when "worktree" is non-NULL. - builtin/worktree.c:validate_no_submodules() which is called from check_clean_worktree() and move_worktree(), both of which supply a non-NULL worktree. - reachable.c:add_rebase_files() which loops over all worktrees. - revision.c:add_index_objects_to_pending() which loops over all worktrees. - worktree.c:is_current_worktree() which expects a non-NULL worktree. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> --- worktree.c | 2 +- worktree.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/worktree.c b/worktree.c index 344ad0c031b..1ed5e8c3cd2 100644 --- a/worktree.c +++ b/worktree.c @@ -227,7 +227,7 @@ struct worktree **get_worktrees_without_reading_head(void) char *get_worktree_git_dir(const struct worktree *wt) { if (!wt) - return xstrdup(repo_get_git_dir(the_repository)); + BUG("%s() called with NULL worktree", __func__); else if (!wt->id) return xstrdup(repo_get_common_dir(wt->repo)); else diff --git a/worktree.h b/worktree.h index e450d1a3317..85d634c36c0 100644 --- a/worktree.h +++ b/worktree.h @@ -51,7 +51,6 @@ int submodule_uses_worktrees(const char *path); /* * Return git dir of the worktree. Note that the path may be relative. - * If wt is NULL, git dir of current worktree is returned. */ char *get_worktree_git_dir(const struct worktree *wt); -- 2.52.0.362.g884e03848a9 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH v2 0/3] worktree: stop using "the_repository" in is_current_worktree() 2026-03-15 16:18 ` [PATCH v2 0/3] worktree: stop using "the_repository" in is_current_worktree() Phillip Wood ` (2 preceding siblings ...) 2026-03-15 16:18 ` [PATCH v2 3/3] worktree: reject NULL worktree in get_worktree_git_dir() Phillip Wood @ 2026-03-15 21:17 ` Junio C Hamano 3 siblings, 0 replies; 26+ messages in thread From: Junio C Hamano @ 2026-03-15 21:17 UTC (permalink / raw) To: Phillip Wood; +Cc: git Phillip Wood <phillip.wood123@gmail.com> writes: > Range-diff against v1: > 1: 075700a2256 = 1: 075700a2256 worktree: remove "the_repository" from is_current_worktree() > 2: ae2a368e7e7 ! 2: c3c5767725d worktree add: stop reading ".git/HEAD" > @@ builtin/worktree.c: static int can_use_local_refs(const struct add_opts *opts) > - free(wt_gitdir); > - } > + if (!opts->quiet) > -+ warning(_("HEAD points to an invalid (or orphaned) reference.\n")); > ++ warning(_("HEAD points to an invalid (or orphaned) reference.\n")); > return 1; > } > return 0; > 3: 1151b5b3020 ! 3: 75eecc8492e worktree: reject NULL worktree in get_worktree_git_dir() > @@ worktree.c: struct worktree **get_worktrees_without_reading_head(void) > else if (!wt->id) > return xstrdup(repo_get_common_dir(wt->repo)); > else > + > + ## worktree.h ## > +@@ worktree.h: int submodule_uses_worktrees(const char *path); > + > + /* > + * Return git dir of the worktree. Note that the path may be relative. > +- * If wt is NULL, git dir of current worktree is returned. > + */ > + char *get_worktree_git_dir(const struct worktree *wt); > + Ah, I somehow expected to see that we say "passing NULL to wt is an error", but that is misleading. When something expects a worktree instance, and it accepts NULL as a special case, then that is worth commenting, but otherwise, it is not worth mentioning. All look good. Will replace. Thanks. ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v3 0/3] worktree: stop using "the_repository" in is_current_worktree() 2026-03-13 14:19 [PATCH 0/3] worktree: stop using "the_repository" in is_current_worktree() Phillip Wood ` (3 preceding siblings ...) 2026-03-15 16:18 ` [PATCH v2 0/3] worktree: stop using "the_repository" in is_current_worktree() Phillip Wood @ 2026-03-26 14:16 ` Phillip Wood 2026-03-26 14:16 ` [PATCH v3 1/3] worktree: remove "the_repository" from is_current_worktree() Phillip Wood ` (2 more replies) 4 siblings, 3 replies; 26+ messages in thread From: Phillip Wood @ 2026-03-26 14:16 UTC (permalink / raw) To: git; +Cc: Patrick Steinhardt From: Phillip Wood <phillip.wood@dunelm.org.uk> This is a follow up to pw/no-more-NULL-means-current-worktree that removes "the_repository" from is_current_worktree() and get_worktree_git_dir(). The first patch removes the use of "the_repository" when determining if a worktree is current. Patches 2 & 3 require a non-NULL worktree when calling get_worktree_git_dir() to remove the last use of "the_repository" in that function. Changes since V2 - Patch 1: expanded commit message and added a comment to is_current member of struct worktree. (thanks to Patrick) Changes since V1 - Patch 2: fixed indentation (thanks to Junio) - Patch 3: removed stale comment (thanks to Junio) Base-Commit: 7f19e4e1b6a3ad259e2ed66033e01e03b8b74c5e Published-As: https://github.com/phillipwood/git/releases/tag/pw%2Fworktree-is-current-use-repo%2Fv3 View-Changes-At: https://github.com/phillipwood/git/compare/7f19e4e1b...c33290280 Fetch-It-Via: git fetch https://github.com/phillipwood/git pw/worktree-is-current-use-repo/v3 Phillip Wood (3): worktree: remove "the_repository" from is_current_worktree() worktree add: stop reading ".git/HEAD" worktree: reject NULL worktree in get_worktree_git_dir() builtin/worktree.c | 21 ++------------------- t/t2400-worktree-add.sh | 28 ++++++++++++---------------- worktree.c | 10 +++++----- worktree.h | 3 +-- 4 files changed, 20 insertions(+), 42 deletions(-) Range-diff against v2: 1: 075700a2256 ! 1: 5357c0dd53e worktree: remove "the_repository" from is_current_worktree() @@ Metadata ## Commit message ## worktree: remove "the_repository" from is_current_worktree() - is_current_worktree() compares the gitdir of the worktree to the gitdir - of "the_repository" and returns true when they match. To get the gitdir - of the worktree it calls get_workree_git_dir() which also depends on - "the_repository". This has the effect that even if "wt->path" matches + The "is_current" member of struct worktree was added in 750e8a60d69 + (worktree.c: mark current worktree, 2016-04-22) and was used in + 8d9fdd7087d (worktree.c: check whether branch is rebased in another + worktree, 2016-04-22) to optionally skip the current worktree when + seeing if a branch is already checked out in die_if_checked_out(). + + To determine if a worktree is "current" is_current_worktree() compares + the gitdir of the worktree to the gitdir of "the_repository" + and returns true when they match. To get the gitdir of the + worktree it calls get_workree_git_dir() which also depends on + "the_repository". This means that even if "wt->path" matches "wt->repo->worktree" is_current_worktree(wt) will return false when - "wt->repo" is not "the_repository" which is confusing. + "wt->repo" is not "the_repository". Consequently die_if_checked_out() + will fail to skip such a worktree when checking if a branch is already + checked out and may die errounously. Fix this by using the worktree's + repository instance instead of "the_repository" when comparing gitdirs. The use of "the_repository" in is_current_wortree() comes from replacing get_git_dir() with repo_get_git_dir() in 246deeac951 (environment: make `get_git_dir()` accept a repository, 2024-09-12). In get_worktree_git_dir() it comes from replacing git_common_path() with repo_common_path() in 07242c2a5af (path: drop `git_common_path()` - in favor of `repo_common_path()`, 2025-02-07). In both cases we have - a repository instance available so use that instead. This means - that a worktree "wt" is always considered current when "wt->path" - matches "wt->repo->worktree" and so the worktree returned by - get_worktree_from_repository() is always considered current. + in favor of `repo_common_path()`, 2025-02-07). In both cases the + replacements appear to have been mechanical. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> @@ worktree.c: char *get_worktree_git_dir(const struct worktree *wt) } static struct worktree *find_worktree_by_suffix(struct worktree **list, + + ## worktree.h ## +@@ worktree.h: struct worktree { + struct object_id head_oid; + int is_detached; + int is_bare; +- int is_current; ++ int is_current; /* does `path` match `repo->worktree` */ + int lock_reason_valid; /* private */ + int prune_reason_valid; /* private */ + }; 2: c3c5767725d = 2: 4d50e6bcb2e worktree add: stop reading ".git/HEAD" 3: 75eecc8492e = 3: c3329028010 worktree: reject NULL worktree in get_worktree_git_dir() -- 2.52.0.362.g884e03848a9.dirty ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v3 1/3] worktree: remove "the_repository" from is_current_worktree() 2026-03-26 14:16 ` [PATCH v3 " Phillip Wood @ 2026-03-26 14:16 ` Phillip Wood 2026-03-26 15:48 ` Junio C Hamano 2026-03-26 14:16 ` [PATCH v3 2/3] worktree add: stop reading ".git/HEAD" Phillip Wood 2026-03-26 14:16 ` [PATCH v3 3/3] worktree: reject NULL worktree in get_worktree_git_dir() Phillip Wood 2 siblings, 1 reply; 26+ messages in thread From: Phillip Wood @ 2026-03-26 14:16 UTC (permalink / raw) To: git; +Cc: Patrick Steinhardt, Phillip Wood From: Phillip Wood <phillip.wood@dunelm.org.uk> The "is_current" member of struct worktree was added in 750e8a60d69 (worktree.c: mark current worktree, 2016-04-22) and was used in 8d9fdd7087d (worktree.c: check whether branch is rebased in another worktree, 2016-04-22) to optionally skip the current worktree when seeing if a branch is already checked out in die_if_checked_out(). To determine if a worktree is "current" is_current_worktree() compares the gitdir of the worktree to the gitdir of "the_repository" and returns true when they match. To get the gitdir of the worktree it calls get_workree_git_dir() which also depends on "the_repository". This means that even if "wt->path" matches "wt->repo->worktree" is_current_worktree(wt) will return false when "wt->repo" is not "the_repository". Consequently die_if_checked_out() will fail to skip such a worktree when checking if a branch is already checked out and may die errounously. Fix this by using the worktree's repository instance instead of "the_repository" when comparing gitdirs. The use of "the_repository" in is_current_wortree() comes from replacing get_git_dir() with repo_get_git_dir() in 246deeac951 (environment: make `get_git_dir()` accept a repository, 2024-09-12). In get_worktree_git_dir() it comes from replacing git_common_path() with repo_common_path() in 07242c2a5af (path: drop `git_common_path()` in favor of `repo_common_path()`, 2025-02-07). In both cases the replacements appear to have been mechanical. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> --- worktree.c | 8 ++++---- worktree.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/worktree.c b/worktree.c index e9ff6e6ef2e..344ad0c031b 100644 --- a/worktree.c +++ b/worktree.c @@ -58,7 +58,7 @@ static void add_head_info(struct worktree *wt) static int is_current_worktree(struct worktree *wt) { - char *git_dir = absolute_pathdup(repo_get_git_dir(the_repository)); + char *git_dir = absolute_pathdup(repo_get_git_dir(wt->repo)); char *wt_git_dir = get_worktree_git_dir(wt); int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir)); free(wt_git_dir); @@ -78,7 +78,7 @@ struct worktree *get_worktree_from_repository(struct repository *repo) wt->is_bare = !repo->worktree; if (fspathcmp(gitdir, commondir)) wt->id = xstrdup(find_last_dir_sep(gitdir) + 1); - wt->is_current = is_current_worktree(wt); + wt->is_current = true; add_head_info(wt); free(gitdir); @@ -229,9 +229,9 @@ char *get_worktree_git_dir(const struct worktree *wt) if (!wt) return xstrdup(repo_get_git_dir(the_repository)); else if (!wt->id) - return xstrdup(repo_get_common_dir(the_repository)); + return xstrdup(repo_get_common_dir(wt->repo)); else - return repo_common_path(the_repository, "worktrees/%s", wt->id); + return repo_common_path(wt->repo, "worktrees/%s", wt->id); } static struct worktree *find_worktree_by_suffix(struct worktree **list, diff --git a/worktree.h b/worktree.h index e450d1a3317..94ae58db973 100644 --- a/worktree.h +++ b/worktree.h @@ -16,7 +16,7 @@ struct worktree { struct object_id head_oid; int is_detached; int is_bare; - int is_current; + int is_current; /* does `path` match `repo->worktree` */ int lock_reason_valid; /* private */ int prune_reason_valid; /* private */ }; -- 2.52.0.362.g884e03848a9.dirty ^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH v3 1/3] worktree: remove "the_repository" from is_current_worktree() 2026-03-26 14:16 ` [PATCH v3 1/3] worktree: remove "the_repository" from is_current_worktree() Phillip Wood @ 2026-03-26 15:48 ` Junio C Hamano 2026-03-27 16:40 ` Phillip Wood 0 siblings, 1 reply; 26+ messages in thread From: Junio C Hamano @ 2026-03-26 15:48 UTC (permalink / raw) To: Phillip Wood; +Cc: git, Patrick Steinhardt Phillip Wood <phillip.wood123@gmail.com> writes: > ... it calls get_workree_git_dir() which also depends on > "the_repository". This means that even if "wt->path" matches > "wt->repo->worktree" is_current_worktree(wt) will return false when > "wt->repo" is not "the_repository". Consequently die_if_checked_out() > will fail to skip such a worktree when checking if a branch is already > checked out and may die errounously. Fix this by using the worktree's > repository instance instead of "the_repository" when comparing gitdirs. It sounds like the above is something we can write in a test to make sure the code with this fix won't regress in the future. Can we add one? > The use of "the_repository" in is_current_wortree() comes from > replacing get_git_dir() with repo_get_git_dir() in 246deeac951 > (environment: make `get_git_dir()` accept a repository, 2024-09-12). In > get_worktree_git_dir() it comes from replacing git_common_path() with > repo_common_path() in 07242c2a5af (path: drop `git_common_path()` > in favor of `repo_common_path()`, 2025-02-07). In both cases the > replacements appear to have been mechanical. > > Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> > --- > worktree.c | 8 ++++---- > worktree.h | 2 +- > 2 files changed, 5 insertions(+), 5 deletions(-) > > diff --git a/worktree.c b/worktree.c > index e9ff6e6ef2e..344ad0c031b 100644 > --- a/worktree.c > +++ b/worktree.c > @@ -58,7 +58,7 @@ static void add_head_info(struct worktree *wt) > > static int is_current_worktree(struct worktree *wt) > { > - char *git_dir = absolute_pathdup(repo_get_git_dir(the_repository)); > + char *git_dir = absolute_pathdup(repo_get_git_dir(wt->repo)); > char *wt_git_dir = get_worktree_git_dir(wt); > int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir)); > free(wt_git_dir); > @@ -78,7 +78,7 @@ struct worktree *get_worktree_from_repository(struct repository *repo) > wt->is_bare = !repo->worktree; > if (fspathcmp(gitdir, commondir)) > wt->id = xstrdup(find_last_dir_sep(gitdir) + 1); > - wt->is_current = is_current_worktree(wt); > + wt->is_current = true; > add_head_info(wt); > > free(gitdir); I think I found the semantics of get_worktree_from_repository() unclear when we discussed a different patch series, so I may have asked the same question already, but what exactly does it mean to "get worktree from repository", i.e., this function does? A repository can have one or more worktrees attached to it (that was the whole point of introducing the worktree mechanism), so "I have this repository, give me the worktree for it" is not a sensible request. The function's comment in <worktree.h> talks only at the implementation level "construct a worktree struct from repo->gitdir and repo->worktree" as if it is so obvious what the resulting worktree struct means at a higher layer's point of view, which does not help, either. I am asking the above because I find this unconditional assignment of "true" utterly confusing. Is it because "we created a brand new worktree structure that by definition is current out of a repository structure, so as far as the caller is concerned by definition it is what it is currently working on"? Stepping back a bit, how do "is_current_worktree(wt)" and "wt->is_current" relate to each other? Here is what happens in the former: static int is_current_worktree(struct worktree *wt) { char *git_dir = absolute_pathdup(repo_get_git_dir(wt->repo)); char *wt_git_dir = get_worktree_git_dir(wt); int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir)); free(wt_git_dir); free(git_dir); return is_current; } and given the definition of get_worktree_git_dir(), which gives either the "common" .git directory for the primary worktree, or the worktree specific .git directory for the secondaries, the is_current being computed here sounds more like "is wt the primary worktree among the ones that are attached to the same repository?" But given that there are API functions with "main" in their names, like is_main_worktree() and internal function get_main_worktree(), what I said apparently is not the intention of whoever built the worktree subsystem. I am still confused... X-<. > @@ -229,9 +229,9 @@ char *get_worktree_git_dir(const struct worktree *wt) > if (!wt) > return xstrdup(repo_get_git_dir(the_repository)); > else if (!wt->id) > - return xstrdup(repo_get_common_dir(the_repository)); > + return xstrdup(repo_get_common_dir(wt->repo)); > else > - return repo_common_path(the_repository, "worktrees/%s", wt->id); > + return repo_common_path(wt->repo, "worktrees/%s", wt->id); > } > > static struct worktree *find_worktree_by_suffix(struct worktree **list, > diff --git a/worktree.h b/worktree.h > index e450d1a3317..94ae58db973 100644 > --- a/worktree.h > +++ b/worktree.h > @@ -16,7 +16,7 @@ struct worktree { > struct object_id head_oid; > int is_detached; > int is_bare; > - int is_current; > + int is_current; /* does `path` match `repo->worktree` */ This new comment also talks only at the lowest implementation level. What significance does it have to the upper layer that calls into the API if `path` matches or does not match `repo->worktree` is totally unclear, at least to me. > int lock_reason_valid; /* private */ > int prune_reason_valid; /* private */ > }; Thanks. ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v3 1/3] worktree: remove "the_repository" from is_current_worktree() 2026-03-26 15:48 ` Junio C Hamano @ 2026-03-27 16:40 ` Phillip Wood 2026-03-27 17:07 ` Junio C Hamano 0 siblings, 1 reply; 26+ messages in thread From: Phillip Wood @ 2026-03-27 16:40 UTC (permalink / raw) To: Junio C Hamano; +Cc: git, Patrick Steinhardt, Eric Sunshine On 26/03/2026 15:48, Junio C Hamano wrote: > Phillip Wood <phillip.wood123@gmail.com> writes: > >> ... it calls get_workree_git_dir() which also depends on >> "the_repository". This means that even if "wt->path" matches >> "wt->repo->worktree" is_current_worktree(wt) will return false when >> "wt->repo" is not "the_repository". Consequently die_if_checked_out() >> will fail to skip such a worktree when checking if a branch is already >> checked out and may die errounously. Fix this by using the worktree's >> repository instance instead of "the_repository" when comparing gitdirs. > > It sounds like the above is something we can write in a test to make > sure the code with this fix won't regress in the future. Can we add > one? We'd need to create a struct worktree instance with a repository instance that is not "the_repository" - I'm not sure we can do that in a script. >> - wt->is_current = is_current_worktree(wt); >> + wt->is_current = true; >> add_head_info(wt); >> >> free(gitdir); > > I think I found the semantics of get_worktree_from_repository() > unclear when we discussed a different patch series, so I may have > asked the same question already, but what exactly does it mean to > "get worktree from repository", i.e., this function does? A > repository can have one or more worktrees attached to it (that was > the whole point of introducing the worktree mechanism), so "I have > this repository, give me the worktree for it" is not a sensible > request. A repository can have more that one worktree but a "struct repository" instance has "gitdir" and "worktree" members that point to a specific worktree within that repository. For example repo_get_oid(repo, "HEAD", &oid); reads "HEAD" from a specific worktree within the repository. > The function's comment in <worktree.h> talks only at the > implementation level "construct a worktree struct from repo->gitdir > and repo->worktree" as if it is so obvious what the resulting > worktree struct means at a higher layer's point of view, which does > not help, either. That comes from me thinking of a struct repository as referring to a specific worktree - would calling it "get_worktree_from_repository_instance" be clearer? > I am asking the above because I find this unconditional assignment > of "true" utterly confusing. Is it because "we created a brand new > worktree structure that by definition is current out of a repository > structure, so as far as the caller is concerned by definition it is > what it is currently working on"? Yes > Stepping back a bit, how do "is_current_worktree(wt)" and > "wt->is_current" relate to each other? "wt->current" is the cached return value of is_current_worktree() (see the implementation of get_main_worktree() and get_linked_worktree()) > Here is what happens in the > former: > > static int is_current_worktree(struct worktree *wt) > { > char *git_dir = absolute_pathdup(repo_get_git_dir(wt->repo)); > char *wt_git_dir = get_worktree_git_dir(wt); > int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir)); > free(wt_git_dir); > free(git_dir); > return is_current; > } > > and given the definition of get_worktree_git_dir(), which gives > either the "common" .git directory for the primary worktree, or the > worktree specific .git directory for the secondaries, the is_current > being computed here sounds more like "is wt the primary worktree > among the ones that are attached to the same repository?" No, the primary worktree (which the code calls the "main" worktree) is orthogonal to the "current" worktree. The main worktree is the one whose gitdir is $GIT_COMMON_DIR i.e. the worktree created by "git init". The "current" worktree is the one whose gitdir is wt->repo->gitdir and may or may not be the "main" worktree. > But given that there are API functions with "main" in their names, > like is_main_worktree() and internal function get_main_worktree(), > what I said apparently is not the intention of whoever built the > worktree subsystem. I am still confused... X-<. > >> @@ -229,9 +229,9 @@ char *get_worktree_git_dir(const struct worktree *wt) >> if (!wt) >> return xstrdup(repo_get_git_dir(the_repository)); >> else if (!wt->id) >> - return xstrdup(repo_get_common_dir(the_repository)); >> + return xstrdup(repo_get_common_dir(wt->repo)); >> else >> - return repo_common_path(the_repository, "worktrees/%s", wt->id); >> + return repo_common_path(wt->repo, "worktrees/%s", wt->id); >> } >> >> static struct worktree *find_worktree_by_suffix(struct worktree **list, >> diff --git a/worktree.h b/worktree.h >> index e450d1a3317..94ae58db973 100644 >> --- a/worktree.h >> +++ b/worktree.h >> @@ -16,7 +16,7 @@ struct worktree { >> struct object_id head_oid; >> int is_detached; >> int is_bare; >> - int is_current; >> + int is_current; /* does `path` match `repo->worktree` */ > > This new comment also talks only at the lowest implementation level. > What significance does it have to the upper layer that calls into > the API if `path` matches or does not match `repo->worktree` is > totally unclear, at least to me. Does it help to think about how "is_current" is used by functions like get_worktree_ref_store()? For the current worktree it can use the refstore from wt->repo, if it is not it needs to open the store for that worktree. I feel I'm struggling to explain this clearly - I find this whole discussion gets confusing because we have "struct worktree" and also a "worktree" member of "struct repository" which means a "struct repository" instance is tied to a specific worktree within the repository. If "struct repository" only had a "commondir" member and no "gitdir" or "worktree" members and we instead used "sturct worktree" to refer to a specific worktree within a repository with functions like worktree_get_oid(wt, "HEAD", &oid); instead of repo_get_oid(repo, "HEAD", &oid); it might be clearer but that would be a very big change. Thanks Phillip >> int lock_reason_valid; /* private */ >> int prune_reason_valid; /* private */ >> }; > > Thanks. ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v3 1/3] worktree: remove "the_repository" from is_current_worktree() 2026-03-27 16:40 ` Phillip Wood @ 2026-03-27 17:07 ` Junio C Hamano 0 siblings, 0 replies; 26+ messages in thread From: Junio C Hamano @ 2026-03-27 17:07 UTC (permalink / raw) To: Phillip Wood; +Cc: git, Patrick Steinhardt, Eric Sunshine Phillip Wood <phillip.wood123@gmail.com> writes: > On 26/03/2026 15:48, Junio C Hamano wrote: >> Phillip Wood <phillip.wood123@gmail.com> writes: >> >>> ... it calls get_workree_git_dir() which also depends on >>> "the_repository". This means that even if "wt->path" matches >>> "wt->repo->worktree" is_current_worktree(wt) will return false when >>> "wt->repo" is not "the_repository". Consequently die_if_checked_out() >>> will fail to skip such a worktree when checking if a branch is already >>> checked out and may die errounously. Fix this by using the worktree's >>> repository instance instead of "the_repository" when comparing gitdirs. >> >> It sounds like the above is something we can write in a test to make >> sure the code with this fix won't regress in the future. Can we add >> one? > > We'd need to create a struct worktree instance with a repository > instance that is not "the_repository" - I'm not sure we can do that in a > script. Ah, so this is more of "futureproofing" than "fix"? That is fine. Thanks for clarifying. >>> - wt->is_current = is_current_worktree(wt); >>> + wt->is_current = true; >>> add_head_info(wt); >>> >>> free(gitdir); >> >> I think I found the semantics of get_worktree_from_repository() >> unclear when we discussed a different patch series, so I may have >> asked the same question already, but what exactly does it mean to >> "get worktree from repository", i.e., this function does? A >> repository can have one or more worktrees attached to it (that was >> the whole point of introducing the worktree mechanism), so "I have >> this repository, give me the worktree for it" is not a sensible >> request. > > A repository can have more that one worktree but a "struct repository" > instance has "gitdir" and "worktree" members that point to a specific > worktree within that repository. For example > > repo_get_oid(repo, "HEAD", &oid); > > reads "HEAD" from a specific worktree within the repository. And...? I _think_ what I am frustrated about is the lack of description on "what it means to be the worktree among many that is pointed by via the .worktree member in the repository struct". Does it correspond to the worktree being "the current worktree the codepath is working on?" >> The function's comment in <worktree.h> talks only at the >> implementation level "construct a worktree struct from repo->gitdir >> and repo->worktree" as if it is so obvious what the resulting >> worktree struct means at a higher layer's point of view, which does >> not help, either. > > That comes from me thinking of a struct repository as referring to a > specific worktree - would calling it > "get_worktree_from_repository_instance" be clearer? It does not change the descriptive value of the name in any meaningful way, so let's not do that. If the answer to my "what frustrates me" comment above is "yeah, we are getting the current worktree", then renaming the function to include "current" in its name would add descriptive value vastly, though. > I feel I'm struggling to explain this clearly - I find this whole > discussion gets confusing because we have "struct worktree" and also a > "worktree" member of "struct repository" which means a "struct > repository" instance is tied to a specific worktree within the > repository. If "struct repository" only had a "commondir" member and no > "gitdir" or "worktree" members and we instead used "sturct worktree" to > refer to a specific worktree within a repository with functions like > > worktree_get_oid(wt, "HEAD", &oid); > > instead of > > repo_get_oid(repo, "HEAD", &oid); > > it might be clearer but that would be a very big change. In short, am I hearing the worktree subsystem is not conceptually clean and it would be a huge undertaking to clean it up? ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v3 2/3] worktree add: stop reading ".git/HEAD" 2026-03-26 14:16 ` [PATCH v3 " Phillip Wood 2026-03-26 14:16 ` [PATCH v3 1/3] worktree: remove "the_repository" from is_current_worktree() Phillip Wood @ 2026-03-26 14:16 ` Phillip Wood 2026-03-26 14:16 ` [PATCH v3 3/3] worktree: reject NULL worktree in get_worktree_git_dir() Phillip Wood 2 siblings, 0 replies; 26+ messages in thread From: Phillip Wood @ 2026-03-26 14:16 UTC (permalink / raw) To: git; +Cc: Patrick Steinhardt, Phillip Wood From: Phillip Wood <phillip.wood@dunelm.org.uk> The function can_use_local_refs() prints a warning if there are no local branches and HEAD is invalid or points to an unborn branch. As part of the warning it prints the contents of ".git/HEAD". In a repository using the reftable backend HEAD is not stored in the filesystem so reading that file is pointless. In a repository using the files backend it is unclear how useful printing it is - it would be better to diagnose the problem for the user. For now, simplify the warning by not printing the file contents and adjust the relevant test case accordingly. Also fixup the test case to use test_grep so that anyone trying to debug a test failure in the future is not met by a wall of silence. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> --- builtin/worktree.c | 21 ++------------------- t/t2400-worktree-add.sh | 28 ++++++++++++---------------- 2 files changed, 14 insertions(+), 35 deletions(-) diff --git a/builtin/worktree.c b/builtin/worktree.c index bc2d0d645ba..9170b2e8981 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -692,25 +692,8 @@ static int can_use_local_refs(const struct add_opts *opts) if (refs_head_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) { return 1; } else if (refs_for_each_branch_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) { - if (!opts->quiet) { - struct strbuf path = STRBUF_INIT; - struct strbuf contents = STRBUF_INIT; - char *wt_gitdir = get_worktree_git_dir(NULL); - - strbuf_add_real_path(&path, wt_gitdir); - strbuf_addstr(&path, "/HEAD"); - strbuf_read_file(&contents, path.buf, 64); - strbuf_stripspace(&contents, NULL); - strbuf_strip_suffix(&contents, "\n"); - - warning(_("HEAD points to an invalid (or orphaned) reference.\n" - "HEAD path: '%s'\n" - "HEAD contents: '%s'"), - path.buf, contents.buf); - strbuf_release(&path); - strbuf_release(&contents); - free(wt_gitdir); - } + if (!opts->quiet) + warning(_("HEAD points to an invalid (or orphaned) reference.\n")); return 1; } return 0; diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh index 023e1301c8e..58b4445cc44 100755 --- a/t/t2400-worktree-add.sh +++ b/t/t2400-worktree-add.sh @@ -987,7 +987,7 @@ test_dwim_orphan () { then test_must_be_empty actual else - grep "$info_text" actual + test_grep "$info_text" actual fi elif [ "$outcome" = "no_infer" ] then @@ -996,39 +996,35 @@ test_dwim_orphan () { then test_must_be_empty actual else - ! grep "$info_text" actual + test_grep ! "$info_text" actual fi elif [ "$outcome" = "fetch_error" ] then test_must_fail git $dashc_args worktree add $args 2>actual && - grep "$fetch_error_text" actual + test_grep "$fetch_error_text" actual elif [ "$outcome" = "fatal_orphan_bad_combo" ] then test_must_fail git $dashc_args worktree add $args 2>actual && if [ $use_quiet -eq 1 ] then - ! grep "$info_text" actual + test_grep ! "$info_text" actual else - grep "$info_text" actual + test_grep "$info_text" actual fi && - grep "$bad_combo_regex" actual + test_grep "$bad_combo_regex" actual elif [ "$outcome" = "warn_bad_head" ] then test_must_fail git $dashc_args worktree add $args 2>actual && if [ $use_quiet -eq 1 ] then - grep "$invalid_ref_regex" actual && - ! grep "$orphan_hint" actual + test_grep "$invalid_ref_regex" actual && + test_grep ! "$orphan_hint" actual else - headpath=$(git $dashc_args rev-parse --path-format=absolute --git-path HEAD) && - headcontents=$(cat "$headpath") && - grep "HEAD points to an invalid (or orphaned) reference" actual && - grep "HEAD path: .$headpath." actual && - grep "HEAD contents: .$headcontents." actual && - grep "$orphan_hint" actual && - ! grep "$info_text" actual + test_grep "HEAD points to an invalid (or orphaned) reference" actual && + test_grep "$orphan_hint" actual && + test_grep ! "$info_text" actual fi && - grep "$invalid_ref_regex" actual + test_grep "$invalid_ref_regex" actual else # Unreachable false -- 2.52.0.362.g884e03848a9.dirty ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v3 3/3] worktree: reject NULL worktree in get_worktree_git_dir() 2026-03-26 14:16 ` [PATCH v3 " Phillip Wood 2026-03-26 14:16 ` [PATCH v3 1/3] worktree: remove "the_repository" from is_current_worktree() Phillip Wood 2026-03-26 14:16 ` [PATCH v3 2/3] worktree add: stop reading ".git/HEAD" Phillip Wood @ 2026-03-26 14:16 ` Phillip Wood 2 siblings, 0 replies; 26+ messages in thread From: Phillip Wood @ 2026-03-26 14:16 UTC (permalink / raw) To: git; +Cc: Patrick Steinhardt, Phillip Wood From: Phillip Wood <phillip.wood@dunelm.org.uk> This removes the final dependence on "the_repository" in get_worktree_git_dir(). The last commit removed only caller that passed a NULL worktree. get_worktree_git_dir() has the following callers: - branch.c:prepare_checked_out_branches() which loops over all worktrees. - builtin/fsck.c:cmd_fsck() which loops over all worktrees. - builtin/receive-pack.c:update_worktree() which is called from update() only when "worktree" is non-NULL. - builtin/worktree.c:validate_no_submodules() which is called from check_clean_worktree() and move_worktree(), both of which supply a non-NULL worktree. - reachable.c:add_rebase_files() which loops over all worktrees. - revision.c:add_index_objects_to_pending() which loops over all worktrees. - worktree.c:is_current_worktree() which expects a non-NULL worktree. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> --- worktree.c | 2 +- worktree.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/worktree.c b/worktree.c index 344ad0c031b..1ed5e8c3cd2 100644 --- a/worktree.c +++ b/worktree.c @@ -227,7 +227,7 @@ struct worktree **get_worktrees_without_reading_head(void) char *get_worktree_git_dir(const struct worktree *wt) { if (!wt) - return xstrdup(repo_get_git_dir(the_repository)); + BUG("%s() called with NULL worktree", __func__); else if (!wt->id) return xstrdup(repo_get_common_dir(wt->repo)); else diff --git a/worktree.h b/worktree.h index 94ae58db973..400b614f133 100644 --- a/worktree.h +++ b/worktree.h @@ -51,7 +51,6 @@ int submodule_uses_worktrees(const char *path); /* * Return git dir of the worktree. Note that the path may be relative. - * If wt is NULL, git dir of current worktree is returned. */ char *get_worktree_git_dir(const struct worktree *wt); -- 2.52.0.362.g884e03848a9.dirty ^ permalink raw reply related [flat|nested] 26+ messages in thread
end of thread, other threads:[~2026-03-27 17:07 UTC | newest] Thread overview: 26+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-03-13 14:19 [PATCH 0/3] worktree: stop using "the_repository" in is_current_worktree() Phillip Wood 2026-03-13 14:19 ` [PATCH 1/3] worktree: remove "the_repository" from is_current_worktree() Phillip Wood 2026-03-13 14:19 ` [PATCH 2/3] worktree add: stop reading ".git/HEAD" Phillip Wood 2026-03-13 21:41 ` Junio C Hamano 2026-03-13 14:19 ` [PATCH 3/3] worktree: reject NULL worktree in get_worktree_git_dir() Phillip Wood 2026-03-13 21:42 ` Junio C Hamano 2026-03-14 20:09 ` Phillip Wood 2026-03-15 16:18 ` [PATCH v2 0/3] worktree: stop using "the_repository" in is_current_worktree() Phillip Wood 2026-03-15 16:18 ` [PATCH v2 1/3] worktree: remove "the_repository" from is_current_worktree() Phillip Wood 2026-03-16 7:38 ` Patrick Steinhardt 2026-03-16 16:22 ` Phillip Wood 2026-03-17 10:24 ` Phillip Wood 2026-03-23 9:41 ` Shreyansh Paliwal 2026-03-23 14:37 ` Phillip Wood 2026-03-23 17:05 ` Shreyansh Paliwal 2026-03-15 16:18 ` [PATCH v2 2/3] worktree add: stop reading ".git/HEAD" Phillip Wood 2026-03-16 7:39 ` Patrick Steinhardt 2026-03-15 16:18 ` [PATCH v2 3/3] worktree: reject NULL worktree in get_worktree_git_dir() Phillip Wood 2026-03-15 21:17 ` [PATCH v2 0/3] worktree: stop using "the_repository" in is_current_worktree() Junio C Hamano 2026-03-26 14:16 ` [PATCH v3 " Phillip Wood 2026-03-26 14:16 ` [PATCH v3 1/3] worktree: remove "the_repository" from is_current_worktree() Phillip Wood 2026-03-26 15:48 ` Junio C Hamano 2026-03-27 16:40 ` Phillip Wood 2026-03-27 17:07 ` Junio C Hamano 2026-03-26 14:16 ` [PATCH v3 2/3] worktree add: stop reading ".git/HEAD" Phillip Wood 2026-03-26 14:16 ` [PATCH v3 3/3] worktree: reject NULL worktree in get_worktree_git_dir() Phillip Wood
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox