Files
orca/src/main/ipc/worktree-base-directory-watch-targets.ts
T
mirror 34fa2cd5ca
Skill update round trip / roundtrip (false, macos-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (false, macos-latest, symlink, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (false, ubuntu-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (false, ubuntu-latest, symlink, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (false, ubuntu-latest, symlink, latest) (push) Canceled after 0s
Skill update round trip / roundtrip (false, windows-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (false, windows-latest, symlink, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, macos-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, macos-latest, symlink, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, ubuntu-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, ubuntu-latest, symlink, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, windows-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, windows-latest, symlink, 1.5.17) (push) Canceled after 0s
source snapshot 2026-09-02
2026-09-02 02:03:16 +08:00

246 lines
8.0 KiB
TypeScript

import { normalize } from 'node:path'
import { realpath, stat } from 'node:fs/promises'
import type { Stats } from 'node:fs'
import type { Store } from '../persistence'
import type { FileStat, IFilesystemProvider } from '../providers/types'
import type { GlobalSettings } from '../../shared/global-settings-types'
import type { Repo } from '../../shared/repo-types'
import { getRepoExecutionHostId, LOCAL_EXECUTION_HOST_ID } from '../../shared/execution-host'
import { isFolderRepo } from '../../shared/repo-kind'
import {
isRuntimePathAbsolute,
isWindowsAbsolutePathLike,
getRuntimePathBasename,
normalizeRuntimePathForComparison,
resolveRuntimePath
} from '../../shared/cross-platform-path'
import { isWslUncPath } from '../../shared/wsl-paths'
import { mapWithConcurrency } from '../../shared/map-with-concurrency'
import { getSshFilesystemProvider } from '../providers/ssh-filesystem-dispatch'
import { getWorktreeMirrorDistro } from '../project-runtime-git-options'
import {
computeWorkspaceRoot,
getWorktreePathSettings,
hasRepoWorktreeBasePath
} from './worktree-logic'
import { shouldEmitBoundedWarning } from './bounded-warning-dedupe'
import { resolveWorktreeCommonGitDirectory } from './worktree-common-git-directory'
import type {
WorktreeBaseRepoWatchConfig,
WorktreeBaseWatchKind,
WorktreeBaseWatchTarget
} from './worktree-base-directory-event-filter'
const missingRootWarnings = new Set<string>()
const skippedWslWarnings = new Set<string>()
// Why: match existing worktree probe caps while bounding aggregate SSH filesystem RPC pressure.
export const WORKTREE_BASE_TARGET_RESOLUTION_CONCURRENCY = 8
function normalizeWatchKey(pathValue: string): string {
return normalizeRuntimePathForComparison(normalize(pathValue))
}
async function canonicalizeExistingPath(
pathValue: string,
connectionId: string | undefined
): Promise<string> {
if (connectionId) {
const provider = getSshFilesystemProvider(connectionId)
if (!provider) {
return normalize(pathValue)
}
try {
return await provider.realpath(pathValue)
} catch {
return normalize(pathValue)
}
}
try {
return await realpath(pathValue)
} catch {
return normalize(pathValue)
}
}
function isDirectoryStat(value: Stats | FileStat | undefined): boolean {
if (!value) {
return false
}
return 'type' in value ? value.type === 'directory' : value.isDirectory()
}
async function addTarget(
targets: Map<string, WorktreeBaseWatchTarget>,
kind: WorktreeBaseWatchKind,
pathValue: string,
config: WorktreeBaseRepoWatchConfig,
connectionId?: string
): Promise<void> {
const watchedPath = await canonicalizeExistingPath(pathValue, connectionId)
const key = `${kind}:${connectionId ?? 'local'}:${normalizeWatchKey(watchedPath)}`
const existing = targets.get(key)
if (existing) {
existing.repos.set(config.repoId, config)
return
}
targets.set(key, {
key,
kind,
path: watchedPath,
...(connectionId ? { connectionId } : {}),
repos: new Map([[config.repoId, config]])
})
}
function getRemoteProvider(connectionId: string | undefined): IFilesystemProvider | undefined {
return connectionId ? getSshFilesystemProvider(connectionId) : undefined
}
function isRuntimePathAbsoluteForRepo(repoPath: string, pathValue: string): boolean {
const pathFlavor =
isWindowsAbsolutePathLike(repoPath) || isWindowsAbsolutePathLike(pathValue)
? 'windows'
: 'posix'
return isRuntimePathAbsolute(pathValue, pathFlavor)
}
function getBaseWatchLayout(
repo: Repo,
pathSettings: Pick<GlobalSettings, 'workspaceDir' | 'nestWorkspaces'>,
connectionId: string | undefined
): { workspaceRoot: string; nestWorkspaces: boolean } {
if (
connectionId &&
!hasRepoWorktreeBasePath(repo) &&
isRuntimePathAbsoluteForRepo(repo.path, pathSettings.workspaceDir)
) {
// Why: SSH creates default worktrees beside the remote repo when the
// global workspace dir is a desktop-local absolute path.
return { workspaceRoot: resolveRuntimePath(repo.path, '..'), nestWorkspaces: false }
}
return {
workspaceRoot: computeWorkspaceRoot(repo.path, pathSettings),
nestWorkspaces: pathSettings.nestWorkspaces
}
}
function warnSkippedWslRoot(repoId: string, workspaceRoot: string): void {
if (shouldEmitBoundedWarning(skippedWslWarnings, `${repoId}:${workspaceRoot}`)) {
console.warn(`[worktree-base-watcher] skipping WSL worktree root watcher for ${workspaceRoot}`)
}
}
async function maybeAddBaseTarget(
targets: Map<string, WorktreeBaseWatchTarget>,
repo: Repo,
settings: GlobalSettings,
mirrorDistro: string | undefined,
connectionId?: string
): Promise<void> {
const pathSettings = getWorktreePathSettings(repo, settings, mirrorDistro)
const { workspaceRoot, nestWorkspaces } = getBaseWatchLayout(repo, pathSettings, connectionId)
const config = {
repoId: repo.id,
repoName: getRuntimePathBasename(repo.path).replace(/\.git$/, ''),
nestWorkspaces
}
const remoteProvider = getRemoteProvider(connectionId)
if (connectionId && !remoteProvider) {
return
}
// Why: WSL UNC paths are unreliable for native watching. A repo inside the
// distro has nothing watchable at all; a Windows-drive repo whose worktrees
// are mirrored into the distro still has its gitdir on the Windows side.
if (isWslUncPath(repo.path)) {
warnSkippedWslRoot(repo.id, workspaceRoot)
return
}
if (isWslUncPath(workspaceRoot)) {
warnSkippedWslRoot(repo.id, workspaceRoot)
} else {
try {
const rootStat = remoteProvider
? await remoteProvider.stat(workspaceRoot)
: await stat(workspaceRoot)
if (isDirectoryStat(rootStat)) {
await addTarget(targets, 'base', workspaceRoot, config, connectionId)
}
} catch {
const key = normalizeWatchKey(workspaceRoot)
if (shouldEmitBoundedWarning(missingRootWarnings, key)) {
console.warn(`[worktree-base-watcher] worktree root unavailable: ${workspaceRoot}`)
}
}
}
const commonDir = await resolveWorktreeCommonGitDirectory(
repo,
remoteProvider
? {
stat: (path) => remoteProvider.stat(path),
readFile: async (path) => (await remoteProvider.readFile(path)).content
}
: undefined
)
if (commonDir && !isWslUncPath(commonDir)) {
await addTarget(targets, 'git-common', commonDir, config, connectionId)
}
}
async function resolveRepoTargets(
repo: Repo,
settings: GlobalSettings,
mirrorDistro: string | undefined
): Promise<Map<string, WorktreeBaseWatchTarget>> {
const targets = new Map<string, WorktreeBaseWatchTarget>()
if (isFolderRepo(repo)) {
return targets
}
const executionHostId = getRepoExecutionHostId(repo)
if (executionHostId === LOCAL_EXECUTION_HOST_ID) {
await maybeAddBaseTarget(targets, repo, settings, mirrorDistro)
} else if (repo.connectionId) {
await maybeAddBaseTarget(targets, repo, settings, mirrorDistro, repo.connectionId)
}
return targets
}
function mergeRepoTargets(
targets: Map<string, WorktreeBaseWatchTarget>,
repoTargets: Map<string, WorktreeBaseWatchTarget>
): void {
for (const [key, target] of repoTargets) {
const existing = targets.get(key)
if (!existing) {
targets.set(key, target)
continue
}
for (const [repoId, config] of target.repos) {
existing.repos.set(repoId, config)
}
}
}
export async function buildWorktreeBaseDirectoryWatchTargets(
store: Store
): Promise<Map<string, WorktreeBaseWatchTarget>> {
const settings = store.getSettings()
const resolvedRepoTargets = await mapWithConcurrency(
store.getRepos(),
WORKTREE_BASE_TARGET_RESOLUTION_CONCURRENCY,
(repo) => resolveRepoTargets(repo, settings, getWorktreeMirrorDistro(store, repo))
)
const targets = new Map<string, WorktreeBaseWatchTarget>()
for (const repoTargets of resolvedRepoTargets) {
mergeRepoTargets(targets, repoTargets)
}
return targets
}
export function clearWorktreeBaseDirectoryWatchTargetWarnings(): void {
missingRootWarnings.clear()
skippedWslWarnings.clear()
}