fix: resolve git origin in linked worktrees (Tia storage key + baseline fetch) - #1820
fix: resolve git origin in linked worktrees (Tia storage key + baseline fetch)#1820dammyammy wants to merge 2 commits into
Conversation
…aseline fetch In a linked git worktree, .git is a file pointing at the real git dir, so reading $projectRoot/.git/config directly fails. Both Storage::rawOriginUrl() and BaselineSync::detectGitHubRepo() bail out silently as a result: - the storage key falls back to a realpath hash, so worktrees of the same repository do not share the documented origin-derived project key, and - --tia --baselined silently skips the CI baseline fetch in every worktree. Keep the fast .git/config file read for regular checkouts and fall back to `git config --get remote.origin.url` (run inside the project root) when .git is not a directory.
|
Before this merges, it seems worth settling whether the folder name belongs in
The docs describe the second:
The code does the first. That is already true today, before this PR. Measured on Two clones with the same folder name share a cache:
Two clones with different folder names do not share a cache:
A main checkout and its worktree do not share a cache either:
In the first two examples, the hash is identical, so sharing by origin identity already works for clones and only the prefix separates them. This PR changes nothing there. In the third example, the hashes differ as well, and that is the bug this PR fixes. The bug: in a linked worktree The fix: when the config can't be read directly, ask git for it with The hashes now match, so the worktree follows the same rule as clones. But What that leaves open is the prefix, and it isn't something this PR introduces: two clones in differently named folders already don't share. Is the folder name meant to be part of the key, or is it a readability aid that ended up in it? If it's intended, the docs are what needs updating. If not, dropping it would make the documented behavior true for clones and worktrees alike. |
Problem
In a linked git worktree (
git worktree add),.gitis a file containing agitdir:pointer, not a directory. Two TIA code paths read<projectRoot>/.git/configdirectly withfile_get_contents, so both silently fail in every linked worktree:Storage::rawOriginUrl()— the project key falls back to a hash of the absolute path instead of the normalized origin URL. The TIA docs promise:In a linked worktree that promise doesn't hold today — every worktree gets an unrelated path-derived key.
BaselineSync::detectGitHubRepo()— returnsnull, so--tia --baselinedsilently skips the CI baseline fetch and falls back to a full local re-record. This bites hardest exactly where the baseline fetch matters most: fresh, short-lived worktree checkouts (CI shards, coding-agent worktrees) that have no local graph to reuse.Reproduction (measured on v5.0.2)
pest --baselineprints the resolved storage directory:To reproduce from scratch:
git worktree add ../repo-wton any repo with anoriginremote,composer installin the worktree, runvendor/bin/pest --baselinein both checkouts and compare the hashes.Fix
.git/configfile read for regular checkouts — behaviour there is unchanged, no process is spawned..gitexists but is not a directory (linked worktree), fall back togit config --get remote.origin.urlexecuted in the project root. Git resolves thegitdir:pointer natively, so worktrees behave like regular clones.nullbehaviour — a worktree without a resolvable origin keeps today's path-hash fallback.BaselineSync::detectGitHubRepo()now derives the URL through the same resolution, so both call sites agree on what the origin is.Related work — checked, not duplicates
Fingerprint::isTrackedByGit(), which breaks in linked worktrees for the same root cause (Symfony Finder doesn't resolve thegitdir:pointer). Different defect, different file — this PR is complementary and touches neitherFingerprint.phpnor the paths changed there.vendor. Neither touches origin resolution.Tests
tests/Unit/Plugins/Tia/Storage.phpandtests/Unit/Plugins/Tia/BaselineSync.phpcreate real temp repositories (plus a linked worktree viagit worktree add) and cover:detectGitHubRepo()resolvesowner/repofrom ssh and https origins, resolves it inside a linked worktree, and returnsnullfor non-GitHub or missing origins.Verified both ways: all 8 pass on this branch, and with
src/Plugins/Tia/{Storage,BaselineSync}.phpreverted to5.xexactly the two worktree tests fail (path hash instead of origin hash;nullinstead offoo/bar) while the six regular-checkout tests keep passing — i.e. behaviour outside worktrees is unchanged.Notes for review
Storage::projectKey()still prefixesslug(basename($projectRoot)), so two worktrees end up with sibling storage dirs (repo-<hash>,wt-<hash>) that share the origin-derived hash, rather than literally one shared directory. If literal sharing (the docs' wording) is preferred, the slug would also need to derive from the origin identity — happy to extend the PR that way. Keeping per-worktree keys does avoid concurrent runs in different worktrees racing on the samegraph.json.