mirror of
https://github.com/stablyai/orca.git
synced 2026-09-28 00:02:41 +00:00
The relay carried its own copies of the worktree-list and unmerged-entry porcelain parsers, and both had drifted from the desktop originals: the relay copy had no `sparse` branch, so SSH sparse checkouts were never marked, and it never C-quote-decoded a conflict path, so a conflicted file with a space or non-ASCII byte was published under its raw quoted name and probed as missing. Move both parsers into src/shared and delete the relay copies, so there is one implementation each. Type the relay's worktree-list plumbing on GitWorktreeInfo instead of Record<string, unknown> so a field-copying step can no longer silently drop a newly parsed field. `isSparse` is a new optional field on the git.listWorktrees result (remote-wire-compatibility Rule 1); Git <2.28 omits the porcelain line and the field stays absent. No new git subcommand or option. Closes #18280
77 lines
2.9 KiB
TypeScript
77 lines
2.9 KiB
TypeScript
import type { LocalBaseRefRefreshResult } from '../../shared/worktree/base-ref-drift-types'
|
|
import { gitExecFileAsync, translateWslOutputPaths } from './runner'
|
|
import {
|
|
evaluateLocalBaseRefRefreshability,
|
|
getLocalBaseRefUpdateSuggestionForWorktreeCreate
|
|
} from './worktree-base-refresh-analysis'
|
|
import { parseWorktreeList } from '../../shared/git-worktree-porcelain-parser'
|
|
import type { AddWorktreeOptions, GitWorktreeExecOptions } from './worktree-operation-options'
|
|
import { gitExecOptions } from './worktree-operation-options'
|
|
|
|
export { getLocalBaseRefUpdateSuggestionForWorktreeCreate }
|
|
|
|
export async function refreshLocalBaseRefForWorktreeCreate(
|
|
repoPath: string,
|
|
baseBranch: string,
|
|
remoteTrackingRef: string,
|
|
remoteTrackingBase?: AddWorktreeOptions['remoteTrackingBase'],
|
|
options: GitWorktreeExecOptions = {}
|
|
): Promise<LocalBaseRefRefreshResult | undefined> {
|
|
const evaluation = await evaluateLocalBaseRefRefreshability(
|
|
repoPath,
|
|
baseBranch,
|
|
remoteTrackingRef,
|
|
remoteTrackingBase,
|
|
options
|
|
)
|
|
if (!evaluation) {
|
|
return undefined
|
|
}
|
|
if (!evaluation.refreshable) {
|
|
return evaluation.result
|
|
}
|
|
|
|
const resultBase = { baseRef: evaluation.baseRef, localBranch: evaluation.localBranch }
|
|
try {
|
|
if (evaluation.ownerWorktreePath) {
|
|
const { stdout: worktreeListOutput } = await gitExecFileAsync(
|
|
['worktree', 'list', '--porcelain'],
|
|
gitExecOptions(repoPath, options)
|
|
)
|
|
const worktrees = parseWorktreeList(
|
|
translateWslOutputPaths(worktreeListOutput, repoPath, options)
|
|
)
|
|
const currentOwner = worktrees.find((wt) => wt.branch === evaluation.fullRef)
|
|
if (!currentOwner || currentOwner.path !== evaluation.ownerWorktreePath) {
|
|
return { ...resultBase, status: 'skipped_error' }
|
|
}
|
|
const { stdout: status } = await gitExecFileAsync(
|
|
['status', '--porcelain', '--untracked-files=no'],
|
|
gitExecOptions(currentOwner.path, options)
|
|
)
|
|
if (status.trim()) {
|
|
return {
|
|
...resultBase,
|
|
status: 'skipped_dirty_worktree',
|
|
ownerWorktreePath: currentOwner.path
|
|
}
|
|
}
|
|
await gitExecFileAsync(
|
|
['reset', '--hard', evaluation.remoteOid],
|
|
gitExecOptions(currentOwner.path, options)
|
|
)
|
|
return { ...resultBase, status: 'updated', ownerWorktreePath: currentOwner.path }
|
|
}
|
|
|
|
// Why: no owner worktree — fast-forward the bare ref; the expected-old-OID form is a no-op-safe CAS if the ref moved since evaluation.
|
|
await gitExecFileAsync(
|
|
['update-ref', evaluation.fullRef, evaluation.remoteOid, evaluation.localOid],
|
|
gitExecOptions(repoPath, options)
|
|
)
|
|
return { ...resultBase, status: 'updated' }
|
|
} catch {
|
|
// update-ref/reset can fail on locked refs or odd worktree states; worktree creation should still proceed.
|
|
return { ...resultBase, status: 'skipped_error' }
|
|
}
|
|
}
|