Files
orca/src/shared/git-rebase-source.ts
T
Jinjing 4e7ad768d2 Fix rebase race by fetching to private ref with timeout
Concurrent fetches can interfere with remote-tracking refs between
fetch and rebase. Use a unique private ref and 60-second timeout to
isolate each rebase operation and prevent hangs on stalled remotes.
Extract gitPullRebaseFromBase to a dedicated module.
2026-08-24 11:35:13 -07:00

54 lines
1.5 KiB
TypeScript

// Why: a stalled remote must fail the rebase fetch, not hang the rebase; client and relay share one bound.
export const REBASE_SOURCE_FETCH_TIMEOUT_MS = 60_000
export type GitCommandRunner = (args: string[]) => Promise<{ stdout: string }>
export type GitRemoteRebaseSource = {
remoteName: string
branchName: string
displayName: string
}
function normalizeBaseRef(baseRef: string): string {
const trimmed = baseRef.trim()
if (!trimmed || trimmed.startsWith('-')) {
throw new Error('Choose a remote base branch to rebase from.')
}
if (trimmed.startsWith('refs/remotes/')) {
return trimmed.slice('refs/remotes/'.length)
}
if (trimmed.startsWith('remotes/')) {
return trimmed.slice('remotes/'.length)
}
return trimmed
}
export async function resolveGitRemoteRebaseSource(
runGit: GitCommandRunner,
baseRef: string
): Promise<GitRemoteRebaseSource> {
const normalizedBaseRef = normalizeBaseRef(baseRef)
const { stdout } = await runGit(['remote'])
const remotes = stdout
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean)
.sort((a, b) => b.length - a.length)
const remoteName = remotes.find(
(remote) => normalizedBaseRef !== remote && normalizedBaseRef.startsWith(`${remote}/`)
)
if (!remoteName) {
throw new Error('Choose a remote base branch to rebase from.')
}
const branchName = normalizedBaseRef.slice(remoteName.length + 1)
await runGit(['check-ref-format', '--branch', branchName])
return {
remoteName,
branchName,
displayName: `${remoteName}/${branchName}`
}
}