Files
orca/src/main/git/command-runner/git-process-env.ts
T
Neil d3501f7ad6 fix(worktrees): stop a failed worktree scan from being recorded as an authoritative empty listing (#18456)
* fix(worktrees): stop a failed worktree scan from pruning as an authoritative empty listing

A `git worktree list` that could not run at all — a WSL distro that stopped
resolving, a hung mount, a git binary that errored — was softened to `[]` by the
lenient listing path, so the detected scan published it as
`authoritative: true, worktrees: []`. That disables the #1158 retention guard,
drops every persisted tab for the repo, and the pruned session is written back to
disk on the next launch. The loss is permanent, not a transient glitch.

Route the detected scan through a strict listing that still reports the two
genuinely empty states (repo path gone, not a Git repo) as `[]`. Everything else
rejects, so the existing catch answers `authoritative: false` and the destructive
halves (`rememberLocalWorktreeRoots`, `pruneLineageForMissingRepoWorktrees`)
never see a failed scan.

Also make the failure readable: wsl.exe reports its own launch failures as exit
0xFFFFFFFF with an EMPTY stderr and the `Wsl/Service/WSL_E_*` line on stdout as
UTF-16LE, which is why the field bundle carried a git error with no text. Set
WSL_UTF8 for WSL-routed git (matching the wsl runner, #9010) and attach that
stdout diagnostic to the error so `git.exec` spans name the cause.

* fix(worktrees): surface a failed worktree scan's cause on the repo header with a retry

A failed scan now travels with its reason (optional unavailableReason on
DetectedWorktreeListResult), the repo header shows it with click-to-retry,
and the WSL deleted-guest-directory shape measured on a real Windows host is
pinned as retained-not-pruned.
2026-09-04 15:23:09 -07:00

80 lines
2.9 KiB
TypeScript

import { addWslEnvKeys } from '../../wsl-env'
import {
appendGitConfigEnv,
gitCredentialPromptGuardEnv
} from '../../../shared/git-credential-prompt-env'
import { UNTRANSLATED_GIT_OUTPUT_ENV } from '../../../shared/git-output-locale'
export function gitOptionalLocksDisabledEnv(
env: NodeJS.ProcessEnv = process.env
): NodeJS.ProcessEnv {
return {
...env,
GIT_OPTIONAL_LOCKS: '0'
}
}
/**
* Append git config via the GIT_CONFIG_COUNT/KEY_n/VALUE_n env protocol (git >= 2.31),
* composing with any count already in `env` so we never clobber a caller's config.
*/
export { appendGitConfigEnv }
/**
* Pin Orca-spawned git to untranslated English output so stderr/progress parsers
* work under any user locale (issue #7808). Terminal git is untouched.
*/
export function untranslatedGitOutputEnv(env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {
return { ...env, ...UNTRANSLATED_GIT_OUTPUT_ENV }
}
export function promptGuardGitEnv(
env: NodeJS.ProcessEnv = process.env,
platform: NodeJS.Platform = process.platform
): NodeJS.ProcessEnv {
return gitCredentialPromptGuardEnv(untranslatedGitOutputEnv(env), platform)
}
/**
* Credential-prompt guard for a general-purpose shell (PTYs, hook scripts):
* like promptGuardGitEnv but without the issue-7808 locale pins, which would
* change the locale of every child process, not just git's.
*/
export function promptGuardShellEnv(
env: NodeJS.ProcessEnv = process.env,
platform: NodeJS.Platform = process.platform
): NodeJS.ProcessEnv {
return gitCredentialPromptGuardEnv(env, platform)
}
/**
* Force git non-interactive so it fails fast instead of hanging on a prompt with
* no terminal to answer it; on headless `serve` those stuck calls wedge every
* client (issue #5308).
*
* - GIT_TERMINAL_PROMPT=0: git errors instead of prompting for credentials.
* - GIT_ASKPASS / SSH_ASKPASS: emptied when unset so no GUI helper blocks; a
* caller-provided askpass is preserved (custom setups serve creds non-interactively).
* - GIT_SSH_COMMAND BatchMode=yes: SSH errors instead of prompting (doesn't change
* host trust); only added when the caller hasn't set its own.
*/
export function nonInteractiveGitEnv(
env: NodeJS.ProcessEnv = process.env,
platform: NodeJS.Platform = process.platform
): NodeJS.ProcessEnv {
const next = promptGuardGitEnv(env, platform)
if (platform === 'win32') {
// Why: without it wsl.exe writes its OWN failures ("no distribution with the supplied name") as
// UTF-16LE (#9010), which is how a dead distro reached telemetry as an error with no text.
next.WSL_UTF8 = '1'
}
if (!next.GIT_SSH_COMMAND) {
next.GIT_SSH_COMMAND = 'ssh -o BatchMode=yes'
if (platform === 'win32') {
// Why: forward GIT_SSH_COMMAND to WSL only when we set it — a caller's Windows-specific value must not leak into Linux git.
addWslEnvKeys(next, ['GIT_SSH_COMMAND'])
}
}
return next
}