Files
orca/src/main/worktree-prunable-git-file.ts
T
Neil 41e42beab4 fix(worktrees): safely remove prunable git-file registrations (#20617)
Preserve checkout files and the named branch when removing a positively attested malformed Git-file registration. Reject file/symlink targets in deferred directory deletion.

Verified exact head with 75 focused tests including actual Git malformation, preserved marker/file bytes and branch HEAD. Independent review and complete product CI passed. WSL routing is covered by unit tests; direct SSH fails safely without local recovery.

Fixes #17316
2026-09-14 14:52:45 -07:00

42 lines
1.5 KiB
TypeScript

import { isENOENT } from './ipc/filesystem-path-containment'
import type { GitWorktreeInfo } from '../shared/worktree/types'
import type { LocalWorktreeFilesystemOptions } from './local-worktree-filesystem'
import { getLocalWorktreePathAccess, toLocalWorktreeRuntimePath } from './local-worktree-filesystem'
/** Registration cleanup must never reinterpret a malformed .git row as its parent checkout. */
export async function isPrunableGitFileWorktree(
worktree: GitWorktreeInfo,
options: LocalWorktreeFilesystemOptions = {}
): Promise<boolean> {
if (
worktree.prunable !== true ||
worktree.isMainWorktree ||
worktree.isBare ||
worktree.locked ||
!worktree.branch.startsWith('refs/heads/') ||
worktree.branch === 'refs/heads/' ||
!worktree.head ||
worktree.path.split(/[\\/]/).at(-1) !== '.git'
) {
return false
}
const access = getLocalWorktreePathAccess(options)
const entry = await access
.statPath(toLocalWorktreeRuntimePath(worktree.path, options))
.catch((error: unknown) => {
// A vanished marker leaves missing-path recovery to its existing stricter gate.
if (isENOENT(error)) {
return null
}
throw error
})
if (!entry || typeof entry !== 'object') {
return false
}
// WSL returns the owning guest's lstat-equivalent type; native lstat rejects symlinks too.
return (
('type' in entry && entry.type === 'file') ||
('isFile' in entry && typeof entry.isFile === 'function' && entry.isFile() === true)
)
}