[package-deps-hash] Fix build cache failures in git linked worktrees caused by GIT_DIR set by pre-commit hooks#5815
Conversation
7b20309 to
7bfddde
Compare
|
@microsoft-github-policy-service agree company="Squarespace" |
… calls to fix build cache in linked worktrees
When a git pre-commit hook runs in a linked worktree, git sets GIT_DIR to the
per-worktree metadata directory (.git/worktrees/{name}) without setting
GIT_WORK_TREE. With GIT_DIR set this way, `git rev-parse --show-toplevel`
returns the CWD (e.g. the rushJsonFolder subdirectory) instead of the actual
worktree root, causing all subsequent git calls to use the wrong root directory.
This makes `git status -u` miss the top-level .gitignore, surfacing node_modules
symlinks as untracked files, which then causes `git hash-object` to fail on
symlink-to-directory entries and ultimately breaks the build cache.
Fix: strip GIT_DIR and GIT_WORK_TREE from the environment in getRepoRoot,
spawnGitAsync, and getRepoChanges so git auto-discovers the correct repo root
from the working directory regardless of hook-injected env vars.
7bfddde to
ef318f0
Compare
| { | ||
| currentWorkingDirectory | ||
| currentWorkingDirectory, | ||
| environment: getCleanGitEnvironment() |
There was a problem hiding this comment.
Should we strip those env vars iff they point to nonexistent locations/locations without a rush.json?
Also note that AFAIK rush.json doesn't have to be at the repo root.
…ee-hook_2026-06-03-00-00.json Co-authored-by: Ian Clanton-Thuon <iclanton@users.noreply.github.com>
Co-authored-by: Ian Clanton-Thuon <iclanton@users.noreply.github.com>
Co-authored-by: Ian Clanton-Thuon <iclanton@users.noreply.github.com>
| "changes": [ | ||
| { | ||
| "packageName": "@microsoft/rush", | ||
| "comment": "No-op change to trigger changeset for rush publish for package-deps-hash changes.", |
There was a problem hiding this comment.
Not sure if we need something more descriptive here, if it ends up in a changelog or anything.
There was a problem hiding this comment.
It does end up in the changelog. Can you describe the fix from a Rush user's perspective here?
There was a problem hiding this comment.
Actually, the text you have in the @rushstack/package-deps-hash changefile would be better here. That changefile should be updated to describe what will change for consumers of that package (without independent of Rush).
There was a problem hiding this comment.
I've updated this changefile to use what was previously the description in the package-deps-hash changefile, and updated the other changefile's description to now read Strip GIT_DIR and GIT_WORK_TREE Node env variables to fix issues with miscalculating the git repo root when working in a linked worktree.
How's that read to you?
Summary
Fixes build cache failures when Rush commands that trigger incremental build analysis run inside a git linked worktree via a pre-commit hook.
Fixes #5479
When git invokes a pre-commit hook in a linked worktree, it sets
GIT_DIRto the per-worktree metadata directory (e.g..git/worktrees/{name}) but does not setGIT_WORK_TREE. Child processes inherit this environment.getRepoRoot()callsgit rev-parse --show-topleveland inherits thisGIT_DIR, causing git to return thecurrentWorkingDirectoryargument (e.g. therushJsonFoldersubdirectory) instead of the actual worktree root. All subsequent git calls use this wrong root, causinggit status -uto miss the top-level.gitignore, surfacingnode_modules/symlinks as untracked files, whichgit hash-objectcannot hash — ultimately reported as "Build cache is only supported if running in a Git repository."Details
The fix strips
GIT_DIRandGIT_WORK_TREEfrom the environment before spawning any git subprocess ingetRepoState.ts. This lets git auto-discover the correct repo root by scanning up fromcurrentWorkingDirectory, which correctly resolves to the linked worktree root regardless of the hook-injected environment. Three call sites are patched:getRepoRoot,spawnGitAsync(used bygetDetailedRepoStateAsyncandhashFilesAsync), andgetRepoChanges.This regression was introduced in #5500, which switched from
git ls-tree -r HEAD(reads committed objects, never surfacesnode_modules/) togit ls-files --cached+git status -u(scans the work tree, exposing the broken.gitignorecontext).The diff also includes prettier reformatting the
WINDOWS_RESERVED_BASENAMESarray from a compact multi-line form to one-entry-per-line — this is an unrelated side effect of the project's pre-commit hook. Happy to add a// prettier-ignorecomment to suppress it if preferred.How it was tested
Added a unit test to
getRepoDeps.test.tsthat setsGIT_DIRto a nonexistent path (simulating hook interference) and verifies thatgetRepoRootstill returns the correct repo root. Without the fix,git rev-parseexits 128 ("not a git repository") and the function throws; with the fix,GIT_DIRis stripped and git auto-discovers the root correctly.Also manually reproduced the original failure by running a Rush build command from within a git linked worktree via a pre-commit hook and confirmed the build cache error no longer occurs with this fix applied.
Reproduction
The bug can be reproduced with this shell script: https://gist.github.com/istateside/e8b0c5f694424a423ae29fe9203ec895
The script bootstraps a Rush monorepo with all of the requisite contributing factors to recreate the bug:
rush.jsonis not in the root directory of the git repo)In that environment, the bug is triggered if you are in a linked worktree and make a commit, to trigger the pre-commit rush command.