mirror of
https://github.com/stablyai/orca.git
synced 2026-11-01 01:27:35 +00:00
* fix(git): reduce queries and preserve data across execution hosts * fix(ci): exercise pinned Git and serialize mobile dependency entrypoints * fix(relay): preserve fresh diff retries after hung shared reads * test(git): wait for fetch barrier before canceling preparation * fix(i18n): describe index-preserving discard in every locale * fix(git): retain clone diagnostics and allow WSL policy startup * test(git): refresh default-base and branch-safety fixtures
111 lines
3.9 KiB
TypeScript
111 lines
3.9 KiB
TypeScript
import type { GitUpstreamStatus } from '../../shared/git-status-types'
|
|
import type { GitPushTarget } from '../../shared/worktree/types'
|
|
import { upstreamOnlyCommitsArePatchEquivalent } from '../../shared/git-upstream-status'
|
|
import { isNoUpstreamError, normalizeGitErrorMessage } from '../../shared/git-remote-error'
|
|
import { getEffectiveGitUpstreamStatus } from '../../shared/git-effective-upstream'
|
|
import { getPublishTargetStatus } from '../../shared/git-publish-target-status'
|
|
import { gitExecFileAsync } from './runner'
|
|
import { validateGitPushTarget } from './push-target-validation'
|
|
import { nativeAndWslGitUpstreamStatusReadOwner } from './git-upstream-status-read-owner'
|
|
import type { GitAdmissionTier } from './command-runner/git-exec-options'
|
|
|
|
type GitExecOptions = {
|
|
wslDistro?: string
|
|
admissionTier?: GitAdmissionTier
|
|
}
|
|
|
|
export function invalidateGitUpstreamStatusReads(): void {
|
|
nativeAndWslGitUpstreamStatusReadOwner.invalidate()
|
|
}
|
|
|
|
function gitExecOptions(
|
|
cwd: string,
|
|
options: GitExecOptions = {}
|
|
): { cwd: string; wslDistro?: string; admissionTier?: GitAdmissionTier } {
|
|
return {
|
|
cwd,
|
|
...(options.wslDistro ? { wslDistro: options.wslDistro } : {}),
|
|
...(options.admissionTier ? { admissionTier: options.admissionTier } : {})
|
|
}
|
|
}
|
|
|
|
async function getBehindCommitsArePatchEquivalent(
|
|
worktreePath: string,
|
|
upstreamName: string,
|
|
options: GitExecOptions = {}
|
|
): Promise<boolean> {
|
|
try {
|
|
const { stdout } = await gitExecFileAsync(
|
|
[
|
|
'log',
|
|
'--no-show-signature',
|
|
'--no-color',
|
|
'--oneline',
|
|
'--cherry-mark',
|
|
'--right-only',
|
|
`HEAD...${upstreamName}`,
|
|
'--'
|
|
],
|
|
gitExecOptions(worktreePath, options)
|
|
)
|
|
return upstreamOnlyCommitsArePatchEquivalent(stdout)
|
|
} catch {
|
|
// Why: patch-equivalence is an optimization for the rebase case. If the
|
|
// probe fails, keep the conservative pull-first behavior.
|
|
return false
|
|
}
|
|
}
|
|
|
|
async function readUpstreamStatus(
|
|
worktreePath: string,
|
|
pushTarget?: GitPushTarget,
|
|
options: GitExecOptions = {}
|
|
): Promise<GitUpstreamStatus> {
|
|
try {
|
|
if (pushTarget) {
|
|
const target = await validateGitPushTarget(worktreePath, pushTarget, options)
|
|
return await getPublishTargetStatus(
|
|
(args) => gitExecFileAsync(args, gitExecOptions(worktreePath, options)),
|
|
target,
|
|
(upstreamName) => getBehindCommitsArePatchEquivalent(worktreePath, upstreamName, options)
|
|
)
|
|
}
|
|
return await getEffectiveGitUpstreamStatus(
|
|
(args) => gitExecFileAsync(args, gitExecOptions(worktreePath, options)),
|
|
(upstreamName) => getBehindCommitsArePatchEquivalent(worktreePath, upstreamName, options)
|
|
)
|
|
} catch (error) {
|
|
// Why: we only swallow clearly-no-upstream signals — that's an expected
|
|
// state, not a failure. Other errors (auth, corruption, "not a git
|
|
// repository", sparse-checkout) should surface to the user so they can
|
|
// act on them. The shared isNoUpstreamError helper intentionally omits
|
|
// broad phrases like "no such branch" to avoid masking real errors.
|
|
if (isNoUpstreamError(error)) {
|
|
return {
|
|
hasUpstream: false,
|
|
ahead: 0,
|
|
behind: 0
|
|
}
|
|
}
|
|
// Why: parity with gitPush/gitPull/gitFetch — normalize before crossing
|
|
// the IPC boundary so renderers don't see execFile stderr preambles or local paths.
|
|
throw new Error(normalizeGitErrorMessage(error, 'upstream'))
|
|
}
|
|
}
|
|
|
|
export function getUpstreamStatus(
|
|
worktreePath: string,
|
|
pushTarget?: GitPushTarget,
|
|
options: GitExecOptions = {}
|
|
): Promise<GitUpstreamStatus> {
|
|
const executionIdentity = options.wslDistro
|
|
? ({ kind: 'wsl', distro: options.wslDistro } as const)
|
|
: ({ kind: 'native' } as const)
|
|
return nativeAndWslGitUpstreamStatusReadOwner.read(
|
|
executionIdentity,
|
|
worktreePath,
|
|
pushTarget,
|
|
() => readUpstreamStatus(worktreePath, pushTarget, options)
|
|
)
|
|
}
|