Skip to content

[package-deps-hash] RepoStateCache misses edits that keep size and mtime when the repo sets core.checkStat=minimal / core.trustctime=false #6143

Open

Description

Versions: @rushstack/package-deps-hash / rush-lib 5.181.0, main @ 9f5a087 (RepoStateCache.ts unchanged since 1e18b16); git 2.51.1, Linux.

Summary

RepoStateCache refreshes a private copy of the index with git status on every snapshot, using PRIVATE_INDEX_STATUS_OPTIONS. Those options don't pin Git's stat checks, so the repository's config applies. Large repos often set core.checkStat=minimal and core.trustctime=false for speed. Then Git compares only the whole-second mtime and the size, so once the private index has recorded a file, an edit that keeps the size and restores the mtime goes unnoticed. Such edits come from touch -d/touch -r, cp -p, tar x and rsync -t. The snapshot then keeps reporting the old blob hash, so the build cache can restore stale outputs and watch mode skips the rebuild. RepoStateCache's own ctime-based FileStamp guards only the files it hashes itself (shrinkwrap files, modified files), not Git's view of tracked files.

Minimal repro (plain git; RepoStateCache runs the same git status on its private index)

git init -q r && cd r
git config core.checkStat minimal && git config core.trustctime false
echo aaaa > f.txt && touch -d '-1 hour' f.txt && git add f.txt && git commit -qm init
sleep 2
touch -r f.txt ../keep && echo bbbb > f.txt && touch -r ../keep f.txt
git status --porcelain      # prints nothing: the edit is missed
git -c core.checkStat=default -c core.trustctime=true status --porcelain   # " M f.txt"

Expected: a modified file is reported, whatever the user's stat settings.
Actual: it is reported as unchanged. I reproduced the same with a standalone RepoStateCache: it kept returning the HEAD blob for the file while the file on disk differed.

Code pointer

libraries/package-deps-hash/src/RepoStateCache.ts:75-87 (PRIVATE_INDEX_STATUS_OPTIONS), used at :474-480.

Suggested fix

Add -c core.checkStat=default -c core.trustctime=true to PRIVATE_INDEX_STATUS_OPTIONS. The private index is not the user's, so this doesn't change the user's own Git behavior.

Cost: the first status after the index is copied re-hashes the files whose ctime differs from the copied index. That happens once per copy (about 25 s warm on a ~241k-file repo whose files had all been re-created since the index was written), and later snapshots are unaffected.

Related: the uncached path (getDetailedRepoStateAsync on the real index with --no-optional-locks) has the same gap under these settings, once the user's own Git commands have refreshed the index after the file's mtime. It is just less likely to be triggered.

Filed with help from GitHub Copilot (AI); reviewed by the reporter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    • Status
      Needs triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions