Repository navigation
[medium] fix(git): stop respawning git outside repos and across session cwds - #631
Merged
Merged
Conversation
Two cases defeat the persistent git cache and make every render spawn git again, forever: - Outside any repository, getGitRepoMetadata finds no git dir, so the persistent cache is never read or written and `rev-parse --is-inside-work-tree` (plus the remote lookups of the origin/upstream widgets) run on every render. runGitArgs now returns null without spawning when git is certain to fail: no GIT_DIR/GIT_WORK_TREE/ GIT_COMMON_DIR in the environment and no `.git` entry or `HEAD` on the physical path from cwd up to the root. Every command runGit issues needs a repository, so the result is the same null git's exit 128 gave. - The cache file was keyed by git dir but stored a single file-level cwd, so two sessions in one repo at different cwds (root vs a subdirectory) discarded each other's entries on every render. The file is now keyed by (git dir, cwd). Some outputs are cwd-scoped (`ls-files --unmerged`), so sharing entries across cwds would not be correct either. Existing per-repo cache files are simply no longer read. Measured (git spawns per render, heavy config, ttl 0): non-repo cwd 4 -> 0; root/subdir sessions alternating 14 -> 0 once warm. bench2, 20 interleaved passes, load 42-53 on 6 cores, CPU median (user+sys incl. children): git17 root 1642 -> 1422 ms (-13%), git17 subdir 1711 -> 1389 ms (-19%), heavy root 2102 -> 1837 ms (-13%). stdout is byte-identical. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This was referenced Sep 30, 2026
whycantfindaname
pushed a commit
to whycantfindaname/ccstatusline
that referenced
this pull request
Oct 1, 2026
Merged from the jason/beta4-local-fixes Trellis build (compat-repair base): bounded stdin reads in shared hooks (sirmalloc#590), research dispatch may write the task research dir (sirmalloc#634), scoped archive commits (sirmalloc#622, sirmalloc#630), list filter traversal (sirmalloc#631), remove-subtask link check (sirmalloc#632), hooks.local.json ignore (sirmalloc#633).
This was referenced Oct 10, 2026
Closed
This was referenced Oct 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
BLUF
rev-parse --is-inside-work-treeand the origin/upstream remote lookups used to spawn git on every render, because with no git dir there was nothing to cache against. They now returnnullwithout spawning, but only when git is certain to fail. Spawns per render (heavy config): 4 → 0.Details
(a) No repository.
getGitRepoMetadatareturnsnullwhen no.gitis found.readPersistentCacheEntryandwritePersistentCacheEntrythen return early, so a statusline in a non-repo cwd spawns git on every render forever.runGitArgsnow skips the spawn only when all of these hold:GIT_DIR,GIT_WORK_TREEandGIT_COMMON_DIRare all unset, since any of them can point git elsewhere..gitentry of any kind (dir, gitfile, malformed gitfile, symlink) and noHEAD(a bare repo, or a cwd inside a git dir) exists anywhere on the physical path (realpath) from cwd up to the filesystem root. Git discovers fromgetcwd()after thechdir, so a logical path through a symlink could miss a repo that git would find.GIT_CEILING_DIRECTORIESand filesystem boundaries only make git stop earlier, so walking all the way to the root is the conservative direction.Every command that
runGit/runGitArgsissues (rev-parse,status,diff,rev-list,ls-files,remote,symbolic-ref) needs a repository. Where the skip applies, git would exit 128 and the function would returnnullanyway.(b) Cache keyed per cwd. The cache file was named after the git dir but stored one file-level
cwd. A read with a different cwd missed, and the next write replaced the whole file. Hashing(gitDir, cwd)into the file name gives each session its own file. This also removes the read-modify-write race between them. Sharing entries across cwds would be wrong anyway, because some outputs depend on the cwd:ls-files --unmergedonly lists conflicts under it. The existing per-repo files become one orphan per repo and are no longer read.Measurements
Spawn counts (strace,
heavy-ttl0, i.e. TTL 0 so freshness is decided by HEAD/index mtimes only):bench2: round-robin fork/exec, 20 interleaved passes. CPU is user+sys including reaped children. The host was heavily loaded, so compare ratios rather than absolute values.
Run 1:
git17-ttl0(19 git widgets), load1 42-53 on 6 cores.node -e 0Run 2:
heavy-ttl0(heavy config with a transcript, so the git share is smaller), load1 44-64.In the non-repo case the CPU difference is within noise, because a git process that exits immediately with 128 costs little CPU. The gain there is the spawn count and latency.
Checks
bun run lint: the full run flagged only two unnecessary casts in the new test, now removed. After the fix,tsc --noEmitis clean and eslint is clean on the changed files.bun run build: OK.bun test: the new and existing git tests pass (59 ingit.test.ts). Red before the fix: the two new behaviour tests fail onmain. The new tests hide any stray.git/HEADabove the temp fixture, so they do not depend on the host's directory layout.mainas on this branch:fetchUsageData error handling, custom-command capture, and several TUI menu tests such asColorMenu, which also fails in isolation onmain. None of them touch git.git.ts(render-scoped cache I/O), which will need a trivial rebase whichever lands first.Overlap
src/utils/git.tsas well; the two branches conflict there. Whichever merges first, I will rebase the other.🤖 Generated with Claude Code