import type { GitBranchCompareResult, GitCommitCompareResult, GitDiffResult } from '../../shared/git-diff-compare-types' import { assertGitDiffWithinTransportBudget } from '../../shared/git-diff-transport-budget' import { getRemoteCommitUrl, getRemoteFileUrl } from '../git/repo' import { getBranchCompare, getBranchDiff, getCommitCompare, getCommitDiff, getDiff } from '../git/status' import { awaitWindowsHostGitEnvironmentReady } from '../git/runner' import type { GitAdmissionTier } from '../git/command-runner/git-exec-options' import { normalizeRuntimeRelativePath } from './runtime-relative-paths' import { localGitOptionsForTarget, normalizeRuntimeGitRelativePath, requireRuntimeGitProvider, type RuntimeGitCommandHost } from './runtime-git-command-target' export class RuntimeGitDiffCommands { constructor(private readonly host: RuntimeGitCommandHost) {} // Why: cap both local and forwarded SSH payloads after execution-host dispatch. async getRuntimeGitDiff( worktreeSelector: string, filePath: string, staged: boolean, compareAgainstHead?: boolean, maxContentBytes?: number ): Promise { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const relativePath = normalizeRuntimeGitRelativePath(filePath) const provider = requireRuntimeGitProvider(target) if (provider) { return assertGitDiffWithinTransportBudget( await provider.getDiff(target.worktree.path, relativePath, staged, compareAgainstHead), maxContentBytes ) } return assertGitDiffWithinTransportBudget( await getDiff(target.worktree.path, relativePath, staged, compareAgainstHead, { ...localGitOptionsForTarget(target), admissionTier: 'interactive' }), maxContentBytes ) } async getRuntimeGitBranchCompare( worktreeSelector: string, baseRef: string, admissionTier: GitAdmissionTier = 'interactive' ): Promise { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const provider = requireRuntimeGitProvider(target) if (provider) { return provider.getBranchCompare(target.worktree.path, baseRef, { admissionTier }) } return getBranchCompare(target.worktree.path, baseRef, { ...localGitOptionsForTarget(target), admissionTier }) } async getRuntimeGitCommitCompare( worktreeSelector: string, commitId: string ): Promise { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const provider = requireRuntimeGitProvider(target) if (provider) { return provider.getCommitCompare(target.worktree.path, commitId) } return getCommitCompare(target.worktree.path, commitId, { ...localGitOptionsForTarget(target), admissionTier: 'interactive' }) } async getRuntimeGitBranchDiff( worktreeSelector: string, compare: { mergeBase: string; headOid: string }, filePath: string, oldPath?: string, maxContentBytes?: number ): Promise { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const relativePath = normalizeRuntimeGitRelativePath(filePath) const oldRelativePath = oldPath ? normalizeRuntimeGitRelativePath(oldPath) : undefined const provider = requireRuntimeGitProvider(target) if (provider) { const results = await provider.getBranchDiff(target.worktree.path, compare.mergeBase, { includePatch: true, headOid: compare.headOid, filePath: relativePath, oldPath: oldRelativePath }) return assertGitDiffWithinTransportBudget( results[0] ?? { kind: 'text', originalContent: '', modifiedContent: '', originalIsBinary: false, modifiedIsBinary: false }, maxContentBytes ) } return assertGitDiffWithinTransportBudget( await getBranchDiff( target.worktree.path, { mergeBase: compare.mergeBase, headOid: compare.headOid, filePath: relativePath, oldPath: oldRelativePath }, { ...localGitOptionsForTarget(target), admissionTier: 'interactive' } ), maxContentBytes ) } async getRuntimeGitCommitDiff( worktreeSelector: string, args: { commitOid: string; parentOid?: string | null; filePath: string; oldPath?: string }, maxContentBytes?: number ): Promise { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const relativePath = normalizeRuntimeRelativePath(args.filePath) const oldRelativePath = args.oldPath ? normalizeRuntimeRelativePath(args.oldPath) : undefined const provider = requireRuntimeGitProvider(target) if (provider) { return assertGitDiffWithinTransportBudget( await provider.getCommitDiff(target.worktree.path, { commitOid: args.commitOid, parentOid: args.parentOid, filePath: relativePath, oldPath: oldRelativePath }), maxContentBytes ) } return assertGitDiffWithinTransportBudget( await getCommitDiff( target.worktree.path, { commitOid: args.commitOid, parentOid: args.parentOid, filePath: relativePath, oldPath: oldRelativePath }, { ...localGitOptionsForTarget(target), admissionTier: 'interactive' } ), maxContentBytes ) } async getRuntimeGitRemoteFileUrl( worktreeSelector: string, relativePath: string, line: number ): Promise { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const normalizedRelativePath = normalizeRuntimeGitRelativePath(relativePath) const provider = requireRuntimeGitProvider(target) if (provider) { return provider.getRemoteFileUrl(target.worktree.path, normalizedRelativePath, line) } await awaitWindowsHostGitEnvironmentReady({ cwd: target.worktree.path }) return getRemoteFileUrl(target.worktree.path, normalizedRelativePath, line) } async getRuntimeGitRemoteCommitUrl( worktreeSelector: string, sha: string ): Promise { const target = await this.host.resolveRuntimeGitTarget(worktreeSelector) const provider = requireRuntimeGitProvider(target) if (provider) { return provider.getRemoteCommitUrl(target.worktree.path, sha) } await awaitWindowsHostGitEnvironmentReady({ cwd: target.worktree.path }) return getRemoteCommitUrl(target.worktree.path, sha) } }