Files
orca/src/relay/git-handler-worktree-change-operations.ts
T
Neil 91a35b9257 Split relay Git handler responsibilities (#17217)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Split CDP bridge responsibilities

* Split relay Git handler responsibilities

* Track moved relay Git fetch audit

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:19:09 -07:00

135 lines
4.4 KiB
TypeScript

import { GitHandlerOperationContext, GIT_BULK_CHUNK_SIZE } from './git-handler-operation-context'
import { commitChangesRelay } from './git-handler-worktree-ops'
const BULK_CHUNK_SIZE = GIT_BULK_CHUNK_SIZE
export class GitHandlerWorktreeChangeOperations extends GitHandlerOperationContext {
async stage(params: Record<string, unknown>) {
this.clearGitMutationReadCaches()
const worktreePath = params.worktreePath as string
const filePath = params.filePath as string
try {
await this.git(['add', '--', this.literalPathspec(filePath)], worktreePath)
} finally {
this.clearGitMutationReadCaches()
}
}
async commit(params: Record<string, unknown>): Promise<{ success: boolean; error?: string }> {
this.clearGitMutationReadCaches()
const worktreePath = params.worktreePath as string
const message = params.message as string
try {
return await commitChangesRelay(this.git.bind(this), worktreePath, message)
} finally {
this.clearGitMutationReadCaches()
}
}
async unstage(params: Record<string, unknown>) {
this.clearGitMutationReadCaches()
const worktreePath = params.worktreePath as string
const filePath = params.filePath as string
try {
await this.git(['restore', '--staged', '--', this.literalPathspec(filePath)], worktreePath)
} finally {
this.clearGitMutationReadCaches()
}
}
async bulkStage(params: Record<string, unknown>) {
this.clearGitMutationReadCaches()
const worktreePath = params.worktreePath as string
const filePaths = params.filePaths as string[]
try {
for (let i = 0; i < filePaths.length; i += BULK_CHUNK_SIZE) {
const chunk = filePaths.slice(i, i + BULK_CHUNK_SIZE)
await this.git(
['add', '--', ...chunk.map((filePath) => this.literalPathspec(filePath))],
worktreePath
)
}
} finally {
this.clearGitMutationReadCaches()
}
}
async bulkUnstage(params: Record<string, unknown>) {
this.clearGitMutationReadCaches()
const worktreePath = params.worktreePath as string
const filePaths = params.filePaths as string[]
try {
for (let i = 0; i < filePaths.length; i += BULK_CHUNK_SIZE) {
const chunk = filePaths.slice(i, i + BULK_CHUNK_SIZE)
await this.git(
['restore', '--staged', '--', ...chunk.map((filePath) => this.literalPathspec(filePath))],
worktreePath
)
}
} finally {
this.clearGitMutationReadCaches()
}
}
async abortMerge(params: Record<string, unknown>) {
this.clearGitMutationReadCaches()
const worktreePath = params.worktreePath as string
try {
await this.git(['merge', '--abort'], worktreePath)
} finally {
this.clearGitMutationReadCaches()
}
}
async abortRebase(params: Record<string, unknown>) {
this.clearGitMutationReadCaches()
const worktreePath = params.worktreePath as string
try {
await this.git(['rebase', '--abort'], worktreePath)
} finally {
this.clearGitMutationReadCaches()
}
}
async checkout(params: Record<string, unknown>) {
this.clearGitMutationReadCaches()
const worktreePath = params.worktreePath as string
const branch = params.branch as string
// Defense-in-depth: reject `-`-prefixed branch tokens to block flag injection (this relay entrypoint is reachable independently of the RPC schema).
if (typeof branch !== 'string' || branch.length === 0 || branch.startsWith('-')) {
throw new Error('invalid_branch_name')
}
try {
await this.git(['checkout', branch, '--'], worktreePath)
return { ok: true as const, branch }
} finally {
this.clearGitMutationReadCaches()
}
}
async localBranches(params: Record<string, unknown>) {
const worktreePath = params.worktreePath as string
const { stdout } = await this.git(
['for-each-ref', '--format=%(HEAD)%09%(refname:short)', 'refs/heads/'],
worktreePath
)
let current: string | null = null
const branches: string[] = []
for (const line of stdout.split('\n')) {
if (line.length === 0) {
continue
}
const [marker, name] = line.split('\t')
if (!name) {
continue
}
if (marker === '*') {
current = name
}
branches.push(name)
}
branches.sort((a, b) => (a === current ? -1 : b === current ? 1 : 0))
return { current, branches }
}
}