mirror of
https://github.com/stablyai/orca.git
synced 2026-09-30 16:02:56 +00:00
* fix(worktrees): stop surfacing prunable git worktrees as live workspaces A worktree still registered in git but whose directory was deleted (git's `prunable` state) was enumerated as a normal workspace, producing repeated pty:spawn DaemonProtocolError / fs:readDir ENOENT loops and a blank pane. - Parse the `prunable` porcelain field (Git >= 2.36) in both the main and relay worktree-list parsers. - For Git < 2.36 (no `prunable` field), probe each linked worktree path for existence on the fallback line-block path, skipping locked registrations to mirror git's own prunable rules. - Omit prunable worktrees from the detected-workspace enumeration only; removal/cleanup flows keep seeing them. - Extend the real-binary compatibility contract with the 2.36 `prunable` boundary. Fixes #8389 Claude-Session: https://claude.ai/code/session_018Rg1Bpq4GGwmz613hq6RSD * fix(worktrees): pin the prunable/locked porcelain annotations to their real Git 2.31 boundary The prunable and locked annotations landed in Git 2.31, five releases before `worktree list -z` (2.36); only -z defines the capability fallback boundary. Correct the compatibility contract so a future matrix entry in the 2.31-2.35 range passes, and reword the fallback comments: on 2.31-2.35 the annotations still parse and the existence probe is a backstop; only Git <2.31 relies on it outright. * fix(worktrees): omit prunable registrations from the Space scan A prunable registration has no directory to size or reclaim, so Space rendered it as a dead "Missing" row whose checkbox stayed disabled with no prune/remove affordance (reported on macOS after a reboot cleared /private/tmp under 16 registrations). Skip prunable entries in the scan, matching the workspace enumeration; removal flows list worktrees separately and still see them. --------- Co-authored-by: kaynan <kaynan.camargo@terceiro-sky.com.br> Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
50 lines
1.9 KiB
TypeScript
50 lines
1.9 KiB
TypeScript
import { mkdtemp, rm } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import * as path from 'node:path'
|
|
import { afterEach, describe, expect, it } from 'vitest'
|
|
import { annotatePrunableWorktreesByExistence } from './git-handler-worktree-list'
|
|
|
|
const tempRoots: string[] = []
|
|
|
|
async function createTempDir(): Promise<string> {
|
|
const root = await mkdtemp(path.join(tmpdir(), 'orca-relay-prunable-'))
|
|
tempRoots.push(root)
|
|
return root
|
|
}
|
|
|
|
afterEach(async () => {
|
|
await Promise.all(tempRoots.splice(0).map((root) => rm(root, { recursive: true, force: true })))
|
|
})
|
|
|
|
describe('annotatePrunableWorktreesByExistence', () => {
|
|
it('marks linked worktrees with missing directories as prunable', async () => {
|
|
const liveDir = await createTempDir()
|
|
const missingDir = path.join(liveDir, 'deleted-worktree')
|
|
|
|
const annotated = await annotatePrunableWorktreesByExistence([
|
|
{ path: liveDir, isMainWorktree: true },
|
|
{ path: path.join(liveDir, 'also-missing-main'), isMainWorktree: true },
|
|
{ path: liveDir, isMainWorktree: false },
|
|
{ path: missingDir, isMainWorktree: false }
|
|
])
|
|
|
|
expect(annotated[0]?.prunable).toBeUndefined()
|
|
// Git never marks the main worktree prunable; repo-level handling owns it.
|
|
expect(annotated[1]?.prunable).toBeUndefined()
|
|
expect(annotated[2]?.prunable).toBeUndefined()
|
|
expect(annotated[3]).toMatchObject({ path: missingDir, prunable: true })
|
|
})
|
|
|
|
it('shields locked registrations, mirroring git prunable semantics', async () => {
|
|
const liveDir = await createTempDir()
|
|
const missingDir = path.join(liveDir, 'deleted-locked-worktree')
|
|
|
|
const annotated = await annotatePrunableWorktreesByExistence([
|
|
{ path: liveDir, isMainWorktree: true },
|
|
{ path: missingDir, isMainWorktree: false, locked: true, lockReason: 'agent session' }
|
|
])
|
|
|
|
expect(annotated[1]?.prunable).toBeUndefined()
|
|
})
|
|
})
|