import type { GitForkSyncExpectedUpstream, GitForkSyncResult } from '../../shared/git-fork-sync' import type { GitUpstreamStatus } from '../../shared/git-status-types' import type { GitPushTarget } from '../../shared/worktree/types' import { gitSyncForkDefaultBranch } from '../git/fork-sync' import { gitFastForward, gitFetch, gitPull, gitPullRebaseFromBase, gitPush } from '../git/remote' import { abortMerge, abortRebase, commitChanges } from '../git/status' import { getUpstreamStatus } from '../git/upstream' import { getSshGitProvider, SSH_GIT_PROVIDER_UNAVAILABLE_MESSAGE } from '../providers/ssh-git-dispatch' import { localGitOptionsForTarget, type RuntimeGitCommandHost } from './runtime-git-command-target' export class RuntimeGitSyncCommands { constructor(private readonly host: RuntimeGitCommandHost) {} async abortRuntimeGitMerge(worktreeSelector: string): Promise<{ ok: true }> { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const provider = target.connectionId ? getSshGitProvider(target.connectionId) : null if (target.connectionId) { if (!provider) { throw new Error(SSH_GIT_PROVIDER_UNAVAILABLE_MESSAGE) } await provider.abortMerge(target.worktree.path) return { ok: true } } await abortMerge(target.worktree.path, { ...localGitOptionsForTarget(target), admissionTier: 'interactive' }) return { ok: true } } async abortRuntimeGitRebase(worktreeSelector: string): Promise<{ ok: true }> { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const provider = target.connectionId ? getSshGitProvider(target.connectionId) : null if (target.connectionId) { if (!provider) { throw new Error(SSH_GIT_PROVIDER_UNAVAILABLE_MESSAGE) } await provider.abortRebase(target.worktree.path) return { ok: true } } await abortRebase(target.worktree.path, { ...localGitOptionsForTarget(target), admissionTier: 'interactive' }) return { ok: true } } async getRuntimeGitUpstreamStatus( worktreeSelector: string, pushTarget?: GitPushTarget ): Promise { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const provider = target.connectionId ? getSshGitProvider(target.connectionId) : null if (target.connectionId) { if (!provider) { throw new Error(SSH_GIT_PROVIDER_UNAVAILABLE_MESSAGE) } return provider.getUpstreamStatus(target.worktree.path, pushTarget) } return getUpstreamStatus(target.worktree.path, pushTarget, localGitOptionsForTarget(target)) } async fetchRuntimeGit( worktreeSelector: string, pushTarget?: GitPushTarget ): Promise<{ ok: true }> { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const provider = target.connectionId ? getSshGitProvider(target.connectionId) : null if (target.connectionId) { if (!provider) { throw new Error(SSH_GIT_PROVIDER_UNAVAILABLE_MESSAGE) } await provider.fetchRemote(target.worktree.path, pushTarget) return { ok: true } } await gitFetch(target.worktree.path, pushTarget, { ...localGitOptionsForTarget(target), admissionTier: 'interactive' }) return { ok: true } } async syncRuntimeGitForkDefaultBranch( worktreeSelector: string, expectedUpstream: GitForkSyncExpectedUpstream ): Promise { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const provider = target.connectionId ? getSshGitProvider(target.connectionId) : null if (target.connectionId) { if (!provider) { throw new Error(SSH_GIT_PROVIDER_UNAVAILABLE_MESSAGE) } return provider.syncForkDefaultBranch(target.worktree.path, expectedUpstream) } return gitSyncForkDefaultBranch(target.worktree.path, expectedUpstream, { ...localGitOptionsForTarget(target), admissionTier: 'interactive' }) } async pullRuntimeGit( worktreeSelector: string, pushTarget?: GitPushTarget ): Promise<{ ok: true }> { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const provider = target.connectionId ? getSshGitProvider(target.connectionId) : null if (target.connectionId) { if (!provider) { throw new Error(SSH_GIT_PROVIDER_UNAVAILABLE_MESSAGE) } await provider.pullBranch(target.worktree.path, pushTarget) return { ok: true } } await gitPull(target.worktree.path, pushTarget, { ...localGitOptionsForTarget(target), admissionTier: 'interactive' }) return { ok: true } } async fastForwardRuntimeGit( worktreeSelector: string, pushTarget?: GitPushTarget ): Promise<{ ok: true }> { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const provider = target.connectionId ? getSshGitProvider(target.connectionId) : null if (target.connectionId) { if (!provider) { throw new Error(SSH_GIT_PROVIDER_UNAVAILABLE_MESSAGE) } await provider.fastForwardBranch(target.worktree.path, pushTarget) return { ok: true } } await gitFastForward(target.worktree.path, pushTarget, { ...localGitOptionsForTarget(target), admissionTier: 'interactive' }) return { ok: true } } async rebaseRuntimeGitFromBase(worktreeSelector: string, baseRef: string): Promise<{ ok: true }> { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const provider = target.connectionId ? getSshGitProvider(target.connectionId) : null if (target.connectionId) { if (!provider) { throw new Error(SSH_GIT_PROVIDER_UNAVAILABLE_MESSAGE) } await provider.rebaseFromBase(target.worktree.path, baseRef) return { ok: true } } await gitPullRebaseFromBase(target.worktree.path, baseRef, { ...localGitOptionsForTarget(target), admissionTier: 'interactive' }) return { ok: true } } async pushRuntimeGit( worktreeSelector: string, publish?: boolean, pushTarget?: GitPushTarget, forceWithLease?: boolean ): Promise<{ ok: true }> { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const provider = target.connectionId ? getSshGitProvider(target.connectionId) : null if (target.connectionId) { if (!provider) { throw new Error(SSH_GIT_PROVIDER_UNAVAILABLE_MESSAGE) } await provider.pushBranch(target.worktree.path, publish === true, pushTarget, { forceWithLease: forceWithLease === true }) return { ok: true } } await gitPush(target.worktree.path, publish === true, pushTarget, { forceWithLease: forceWithLease === true, ...localGitOptionsForTarget(target), admissionTier: 'interactive' }) return { ok: true } } async commitRuntimeGit( worktreeSelector: string, message: string ): Promise<{ success: boolean; error?: string }> { if (message.trim().length === 0) { throw new Error('Commit message is required') } const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const provider = target.connectionId ? getSshGitProvider(target.connectionId) : null if (target.connectionId) { if (!provider) { throw new Error(SSH_GIT_PROVIDER_UNAVAILABLE_MESSAGE) } return provider.commit(target.worktree.path, message) } return commitChanges(target.worktree.path, message, { ...localGitOptionsForTarget(target), admissionTier: 'interactive' }) } }