Fix remote runtime branch diffs (#6942)
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
2c0ac2a75f
commit
8a52bec8ff
|
|
@ -456,6 +456,39 @@ describe('createEditorSlice openDiff', () => {
|
|||
])
|
||||
})
|
||||
|
||||
it('derives a runtime owner for source-control diffs from the worktree host', () => {
|
||||
const store = createEditorStore()
|
||||
store.setState({
|
||||
repos: [{ id: 'repo-1', executionHostId: 'runtime:env-1' }] as unknown as AppState['repos'],
|
||||
worktreesByRepo: {
|
||||
'repo-1': [
|
||||
{
|
||||
id: 'repo-1::/srv/repo/worktree',
|
||||
repoId: 'repo-1',
|
||||
hostId: 'runtime:env-1'
|
||||
}
|
||||
]
|
||||
} as unknown as AppState['worktreesByRepo']
|
||||
})
|
||||
|
||||
store
|
||||
.getState()
|
||||
.openDiff(
|
||||
'repo-1::/srv/repo/worktree',
|
||||
'/srv/repo/worktree/src/file.ts',
|
||||
'src/file.ts',
|
||||
'typescript',
|
||||
false
|
||||
)
|
||||
|
||||
expect(store.getState().openFiles[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
id: 'editor-diff:repo-1%3A%3A%2Fsrv%2Frepo%2Fworktree:env-1:unstaged:src%2Ffile.ts',
|
||||
runtimeEnvironmentId: 'env-1'
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('repairs an existing diff tab entry to the correct mode and staged state', () => {
|
||||
const store = createEditorStore()
|
||||
|
||||
|
|
@ -2769,6 +2802,46 @@ describe('createEditorSlice combined diff exclusions', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('createEditorSlice openBranchDiff', () => {
|
||||
it('derives a runtime owner for branch diffs from the worktree host', () => {
|
||||
const store = createEditorStore()
|
||||
const worktreeId = 'repo-1::/srv/repo/worktree'
|
||||
const branchSummary: GitBranchCompareSummary = {
|
||||
baseRef: 'main',
|
||||
baseOid: 'base-oid',
|
||||
compareRef: 'HEAD',
|
||||
headOid: 'head-oid',
|
||||
mergeBase: 'merge-base-oid',
|
||||
changedFiles: 1,
|
||||
status: 'ready'
|
||||
}
|
||||
store.setState({
|
||||
repos: [{ id: 'repo-1', executionHostId: 'runtime:env-1' }] as unknown as AppState['repos'],
|
||||
worktreesByRepo: {
|
||||
'repo-1': [{ id: worktreeId, repoId: 'repo-1', hostId: 'runtime:env-1' }]
|
||||
} as unknown as AppState['worktreesByRepo']
|
||||
})
|
||||
|
||||
store
|
||||
.getState()
|
||||
.openBranchDiff(
|
||||
worktreeId,
|
||||
'/srv/repo/worktree',
|
||||
{ path: 'src/file.ts', status: 'modified' },
|
||||
branchSummary,
|
||||
'typescript'
|
||||
)
|
||||
|
||||
expect(store.getState().openFiles[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
diffSource: 'branch',
|
||||
filePath: '/srv/repo/worktree/src/file.ts',
|
||||
runtimeEnvironmentId: 'env-1'
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('createEditorSlice remote branch actions', () => {
|
||||
const gitStatusMock = vi.fn()
|
||||
const gitUpstreamStatusMock = vi.fn()
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ import {
|
|||
import { settingsForRuntimeOwner } from '@/runtime/runtime-rpc-client'
|
||||
import { notifyHostOfMirroredEditorClose } from '@/runtime/close-mirrored-editor-tab'
|
||||
import { findWorktreeById, getRepoIdFromWorktreeId } from './worktree-helpers'
|
||||
import { getRuntimeEnvironmentIdForWorktree } from '@/lib/worktree-runtime-owner'
|
||||
import { createUntitledMarkdownFileWithTemplateSelection } from '@/lib/create-untitled-markdown'
|
||||
import { extractIpcErrorMessage } from '@/lib/ipc-error'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
|
@ -299,6 +300,19 @@ type GitRuntimeOperationOptions = {
|
|||
applyUpstreamStatus?: boolean
|
||||
}
|
||||
|
||||
function resolveDiffRuntimeEnvironmentId(
|
||||
state: AppState,
|
||||
worktreeId: string,
|
||||
explicitRuntimeEnvironmentId: string | null | undefined
|
||||
): string | null | undefined {
|
||||
if (explicitRuntimeEnvironmentId !== undefined) {
|
||||
return explicitRuntimeEnvironmentId
|
||||
}
|
||||
// Why: Source Control callers often know only the worktree. Runtime-host
|
||||
// diffs still need their owner stamped so content loads through runtime RPC.
|
||||
return getRuntimeEnvironmentIdForWorktree(state, worktreeId) ?? undefined
|
||||
}
|
||||
|
||||
export type PendingEditorReveal = {
|
||||
filePath: string
|
||||
fileId?: string
|
||||
|
|
@ -2415,10 +2429,14 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
|
||||
openDiff: (worktreeId, filePath, relativePath, language, staged, options) => {
|
||||
const isPreview = options?.preview ?? false
|
||||
const runtimeEnvironmentId = options?.runtimeEnvironmentId
|
||||
let editorItemTargetGroupId = options?.targetGroupId
|
||||
let editorItemFileId = ''
|
||||
set((s) => {
|
||||
const runtimeEnvironmentId = resolveDiffRuntimeEnvironmentId(
|
||||
s,
|
||||
worktreeId,
|
||||
options?.runtimeEnvironmentId
|
||||
)
|
||||
const diffSource: DiffSource = staged ? 'staged' : 'unstaged'
|
||||
const id = buildDiffEditorFileId(worktreeId, diffSource, relativePath, runtimeEnvironmentId)
|
||||
editorItemFileId = id
|
||||
|
|
@ -2459,7 +2477,7 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
skippedConflicts: undefined,
|
||||
conflictReview: undefined,
|
||||
isPreview: isPreview || undefined,
|
||||
runtimeEnvironmentId: options?.runtimeEnvironmentId
|
||||
runtimeEnvironmentId
|
||||
}
|
||||
if (isPreview) {
|
||||
const replaceablePreviewId = getReplaceablePreviewFileId(s, worktreeId, targetGroupId)
|
||||
|
|
@ -2507,6 +2525,11 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
const targetGroupId =
|
||||
resolveEditorOpenTargetGroupId(s, worktreeId, options?.targetGroupId) ?? undefined
|
||||
editorItemTargetGroupId = targetGroupId
|
||||
const runtimeEnvironmentId = resolveDiffRuntimeEnvironmentId(
|
||||
s,
|
||||
worktreeId,
|
||||
options?.runtimeEnvironmentId
|
||||
)
|
||||
const existing = s.openFiles.find((f) => f.id === id)
|
||||
if (existing) {
|
||||
const updatedPreview = isPreview ? existing.isPreview : false
|
||||
|
|
@ -2519,7 +2542,8 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
conflict: undefined,
|
||||
skippedConflicts: undefined,
|
||||
conflictReview: undefined,
|
||||
isPreview: updatedPreview
|
||||
isPreview: updatedPreview,
|
||||
runtimeEnvironmentId
|
||||
})
|
||||
return {
|
||||
openFiles: s.openFiles.map((f) => (f.id === id ? reopenedDiff : f)),
|
||||
|
|
@ -2543,7 +2567,8 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
conflict: undefined,
|
||||
skippedConflicts: undefined,
|
||||
conflictReview: undefined,
|
||||
isPreview: isPreview || undefined
|
||||
isPreview: isPreview || undefined,
|
||||
runtimeEnvironmentId
|
||||
}
|
||||
if (isPreview) {
|
||||
const replaceablePreviewId = getReplaceablePreviewFileId(s, worktreeId, targetGroupId)
|
||||
|
|
@ -2591,6 +2616,11 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
const targetGroupId =
|
||||
resolveEditorOpenTargetGroupId(s, worktreeId, options?.targetGroupId) ?? undefined
|
||||
editorItemTargetGroupId = targetGroupId
|
||||
const runtimeEnvironmentId = resolveDiffRuntimeEnvironmentId(
|
||||
s,
|
||||
worktreeId,
|
||||
options?.runtimeEnvironmentId
|
||||
)
|
||||
const existing = s.openFiles.find((f) => f.id === id)
|
||||
if (existing) {
|
||||
const updatedPreview = isPreview ? existing.isPreview : false
|
||||
|
|
@ -2603,7 +2633,8 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
conflict: undefined,
|
||||
skippedConflicts: undefined,
|
||||
conflictReview: undefined,
|
||||
isPreview: updatedPreview
|
||||
isPreview: updatedPreview,
|
||||
runtimeEnvironmentId
|
||||
})
|
||||
return {
|
||||
openFiles: s.openFiles.map((f) => (f.id === id ? reopenedDiff : f)),
|
||||
|
|
@ -2627,7 +2658,8 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
conflict: undefined,
|
||||
skippedConflicts: undefined,
|
||||
conflictReview: undefined,
|
||||
isPreview: isPreview || undefined
|
||||
isPreview: isPreview || undefined,
|
||||
runtimeEnvironmentId
|
||||
}
|
||||
if (isPreview) {
|
||||
const replaceablePreviewId = getReplaceablePreviewFileId(s, worktreeId, targetGroupId)
|
||||
|
|
@ -2708,6 +2740,7 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
areaFilter
|
||||
] ?? 'All Changes')
|
||||
: 'All Changes'
|
||||
const runtimeEnvironmentId = resolveDiffRuntimeEnvironmentId(s, worktreeId, undefined)
|
||||
const existing = s.openFiles.find((f) => f.id === id)
|
||||
if (existing) {
|
||||
return {
|
||||
|
|
@ -2723,7 +2756,8 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
combinedAreaFilter: areaFilter,
|
||||
skippedConflicts,
|
||||
conflictReview: undefined,
|
||||
conflict: undefined
|
||||
conflict: undefined,
|
||||
runtimeEnvironmentId
|
||||
}
|
||||
: f
|
||||
),
|
||||
|
|
@ -2749,7 +2783,8 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
combinedAreaFilter: areaFilter,
|
||||
skippedConflicts,
|
||||
conflictReview: undefined,
|
||||
conflict: undefined
|
||||
conflict: undefined,
|
||||
runtimeEnvironmentId
|
||||
}
|
||||
return {
|
||||
openFiles: [...s.openFiles, newFile],
|
||||
|
|
@ -3181,6 +3216,7 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
const branchCompare = toBranchCompareSnapshot(compare)
|
||||
const id = `${worktreeId}::all-diffs::branch::${compare.baseRef}::${branchCompare.compareVersion}`
|
||||
set((s) => {
|
||||
const runtimeEnvironmentId = resolveDiffRuntimeEnvironmentId(s, worktreeId, undefined)
|
||||
const branchEntriesSnapshot = s.gitBranchChangesByWorktree[worktreeId] ?? []
|
||||
const existing = s.openFiles.find((f) => f.id === id)
|
||||
if (existing) {
|
||||
|
|
@ -3194,7 +3230,8 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
combinedAlternate: alternate,
|
||||
conflict: undefined,
|
||||
skippedConflicts: undefined,
|
||||
conflictReview: undefined
|
||||
conflictReview: undefined,
|
||||
runtimeEnvironmentId
|
||||
}
|
||||
: f
|
||||
),
|
||||
|
|
@ -3218,7 +3255,8 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
combinedAlternate: alternate,
|
||||
conflict: undefined,
|
||||
skippedConflicts: undefined,
|
||||
conflictReview: undefined
|
||||
conflictReview: undefined,
|
||||
runtimeEnvironmentId
|
||||
}
|
||||
return {
|
||||
openFiles: [...s.openFiles, newFile],
|
||||
|
|
@ -3244,6 +3282,7 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
? `Commit ${commitCompare.compareRef}: ${subject}`
|
||||
: `Commit ${commitCompare.compareRef}`
|
||||
set((s) => {
|
||||
const runtimeEnvironmentId = resolveDiffRuntimeEnvironmentId(s, worktreeId, undefined)
|
||||
const existing = s.openFiles.find((f) => f.id === id)
|
||||
if (existing) {
|
||||
return {
|
||||
|
|
@ -3256,7 +3295,8 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
commitEntriesSnapshot: entries,
|
||||
conflict: undefined,
|
||||
skippedConflicts: undefined,
|
||||
conflictReview: undefined
|
||||
conflictReview: undefined,
|
||||
runtimeEnvironmentId
|
||||
}
|
||||
: f
|
||||
),
|
||||
|
|
@ -3280,7 +3320,8 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
|
|||
commitEntriesSnapshot: entries,
|
||||
conflict: undefined,
|
||||
skippedConflicts: undefined,
|
||||
conflictReview: undefined
|
||||
conflictReview: undefined,
|
||||
runtimeEnvironmentId
|
||||
}
|
||||
return {
|
||||
openFiles: [...s.openFiles, newFile],
|
||||
|
|
|
|||
Loading…
Reference in New Issue