[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).
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.