mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 08:37:49 +00:00
* perf(git): cache sparse-checkout annotation on worktree listing `git worktree list` never reports sparse-checkout state, so every listing paid a per-worktree fs.stat + config read to detect it -- measured at ~9x the cost of the `git worktree list` call it decorates on a 1000-worktree repo. Cache the result per worktree path, invalidated by the existing worktree-change invalidator registry plus explicit remove/move hooks, with a 5-minute reconcile window bounding the one unwitnessed edge case (external `git sparse-checkout` toggle with extensions.worktreeConfig off), matching the precedent already accepted in readRepoWorktreeAdminFingerprint. * perf(git): normalize/scope sparse-checkout cache keys, add SWR Address independent-review follow-ups on the sparse-checkout annotation cache (#17859): - Extract canonicalWorktreePath() from areWorktreePathsEqual and key/invalidate the cache through it on both read and write, closing the disclosed path-spelling P2 outright instead of leaving it as a residual risk. - Scope cache entries and clears by repo path (derived from the invalidator registry's repoId via a store lookup, falling back to a full clear when the repo can't be resolved), so churn in one repo no longer evicts a sibling repo's warm cache. - Replace the hard 5-minute cutoff with stale-while-revalidate: past the window, callers get the cached value immediately while a deduplicated background probe corrects it and, on a flip, drives the existing worktrees-changed notification -- collapsing visible staleness from the full window to one refresh cycle at zero added listing latency. Also corrects a stale claim in the original PR description: newer Git does emit a `sparse` porcelain line (which annotateSparseCheckoutStatus already skips), but Orca's Git 2.25 compatibility baseline predates it, so the fallback detection this caches remains necessary. * fix(git): stop background sparse-checkout revalidation resurrecting invalidated entries Readiness-loop finding: a stale-while-revalidate probe in flight when a worktree is removed/moved (or a repo's cache is cleared) would still write its result back afterward, resurrecting an entry that was deliberately dropped. Guard the write with a presence check so an invalidated key stays absent until the next real read. * fix(git): identity-check the sparse-checkout SWR write-back guard The has()/presence guard from the previous commit only proved some entry existed at the key, not that it was the one this revalidation started from. A worktree removed and re-created at the same path while a background re-detect was in flight would repopulate the key with a fresh cold read, and the stale in-flight result would then overwrite it -- exactly the race greptile (P1) and pullfrog both flagged as still open. Compare the map's current entry by reference to the entry captured when the revalidation began; a mismatch means something else (invalidate, clear, or a fresh cold read) replaced it, and the stale result must not be written back. Added a regression test that fails against the old has() guard and passes with the identity check: invalidate and repopulate the key with a different value mid-flight, then let the stale revalidation settle and assert the fresh value survives.
31 lines
1.2 KiB
TypeScript
31 lines
1.2 KiB
TypeScript
import { gitExecFileAsync } from './runner'
|
|
import { runWithGitReadCacheInvalidation } from './status'
|
|
import { invalidateWslLinkedWorktreeGitRouting } from './wsl-linked-worktree-git-routing'
|
|
import { bumpWorktreeScanGeneration } from './worktree-scan-cache'
|
|
import { invalidateSparseCheckoutState } from './worktree-sparse-checkout-cache'
|
|
|
|
/**
|
|
* Move a worktree with `git worktree move` (not `fs.rename`, which corrupts the
|
|
* `.git` file and the `.git/worktrees/<name>/gitdir` back-pointer). Local-only,
|
|
* so there is no relay parity handler. Caller owns migrating Orca's
|
|
* path-derived worktree identity and pre-checks that the destination is free.
|
|
*/
|
|
export async function moveWorktree(
|
|
repoPath: string,
|
|
oldPath: string,
|
|
newPath: string
|
|
): Promise<void> {
|
|
try {
|
|
await runWithGitReadCacheInvalidation(() =>
|
|
gitExecFileAsync(['worktree', 'move', oldPath, newPath], { cwd: repoPath })
|
|
)
|
|
} finally {
|
|
// A failed move can still have rewritten one `.git` marker, so re-probe both paths.
|
|
invalidateWslLinkedWorktreeGitRouting(oldPath)
|
|
invalidateWslLinkedWorktreeGitRouting(newPath)
|
|
invalidateSparseCheckoutState(repoPath, oldPath)
|
|
invalidateSparseCheckoutState(repoPath, newPath)
|
|
bumpWorktreeScanGeneration(repoPath)
|
|
}
|
|
}
|