From 39b50adaeb0973205b4ea0741ac3f7d6f5e4133a Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Sun, 30 Aug 2026 15:12:02 -0700 Subject: [PATCH] fix(combined-diff): re-resolve sections by key to handle list rebuilds The section list can rebuild while a write is pending (due to rebase, file changes, etc.); re-resolve by key instead of stale index to apply updates to the correct section. - Convert skipped conflicts message to structured i18n plural forms - Add oldPath field to git status signature for rename tracking --- .../combined-diff-git-status-signature.ts | 1 + .../combined-diff-skipped-conflicts.tsx | 11 ++++++----- .../use-combined-diff-section-actions.ts | 14 +++++++++++--- src/renderer/src/i18n/locales/en.json | 5 +++-- src/renderer/src/i18n/locales/es.json | 5 +++-- src/renderer/src/i18n/locales/ja.json | 5 +++-- src/renderer/src/i18n/locales/ko.json | 5 +++-- src/renderer/src/i18n/locales/zh.json | 5 +++-- 8 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/renderer/src/components/editor/combined-diff/resolve-changes/combined-diff-git-status-signature.ts b/src/renderer/src/components/editor/combined-diff/resolve-changes/combined-diff-git-status-signature.ts index b443f8a557e..20c266d04c3 100644 --- a/src/renderer/src/components/editor/combined-diff/resolve-changes/combined-diff-git-status-signature.ts +++ b/src/renderer/src/components/editor/combined-diff/resolve-changes/combined-diff-git-status-signature.ts @@ -10,6 +10,7 @@ export function buildCombinedGitStatusSignature( return JSON.stringify( matching.map((entry) => ({ path: entry.path, + oldPath: entry.oldPath ?? null, area: entry.area, status: entry.status, added: entry.added ?? null, diff --git a/src/renderer/src/components/editor/combined-diff/review-controls/combined-diff-skipped-conflicts.tsx b/src/renderer/src/components/editor/combined-diff/review-controls/combined-diff-skipped-conflicts.tsx index 0328752f53c..745cbc0a576 100644 --- a/src/renderer/src/components/editor/combined-diff/review-controls/combined-diff-skipped-conflicts.tsx +++ b/src/renderer/src/components/editor/combined-diff/review-controls/combined-diff-skipped-conflicts.tsx @@ -79,12 +79,13 @@ export function CombinedDiffSkippedConflictNotice({ )}
- {skippedConflicts.length}{' '} - {translate('auto.components.editor.CombinedDiffViewer.689b99f8ad', 'unresolved conflict')} - {skippedConflicts.length === 1 ? '' : 's'}{' '} {translate( - 'auto.components.editor.CombinedDiffViewer.39e73e7181', - 'were excluded from this diff view.' + 'auto.components.editor.CombinedDiffViewer.skippedConflictsExcluded', + '{{count}} unresolved conflicts were excluded from this diff view.', + { + count: skippedConflicts.length, + defaultValue_one: '{{count}} unresolved conflict was excluded from this diff view.' + } )}
diff --git a/src/renderer/src/components/editor/combined-diff/review-controls/use-combined-diff-section-actions.ts b/src/renderer/src/components/editor/combined-diff/review-controls/use-combined-diff-section-actions.ts index 50648747de1..0fa71dbf136 100644 --- a/src/renderer/src/components/editor/combined-diff/review-controls/use-combined-diff-section-actions.ts +++ b/src/renderer/src/components/editor/combined-diff/review-controls/use-combined-diff-section-actions.ts @@ -163,6 +163,7 @@ export function useCombinedDiffSectionActions({ return } + const sectionKey = section.key const content = modifiedEditor?.getValue() ?? section.modifiedContent const absolutePath = joinPath(file.filePath, section.path) try { @@ -183,10 +184,16 @@ export function useCombinedDiffSectionActions({ absolutePath, content ) - setSectionHeights((prev) => removeDiffSectionMeasuredHeight(prev, index)) + // Why: the section list can be rebuilt while the write is pending, so re-resolve + // by key — the captured index may now point at a different file. + const savedIndex = sectionsRef.current.findIndex((s) => s.key === sectionKey) + if (savedIndex === -1) { + return + } + setSectionHeights((prev) => removeDiffSectionMeasuredHeight(prev, savedIndex)) setSections((prev) => - prev.map((s, i) => { - if (i !== index) { + prev.map((s) => { + if (s.key !== sectionKey) { return s } @@ -226,6 +233,7 @@ export function useCombinedDiffSectionActions({ file.runtimeEnvironmentId, file.worktreeId, sections, + sectionsRef, setSectionHeights, setSections ] diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index 0519b661cce..5c866f20d40 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -14233,8 +14233,9 @@ "7e7ca60816": "changed files", "b6c3b84476": "Show file tree", "39f8007549": "Review conflicts", - "39e73e7181": "were excluded from this diff view.", - "689b99f8ad": "unresolved conflict", + "skippedConflictsExcluded": "{{count}} unresolved conflicts were excluded from this diff view.", + "skippedConflictsExcluded_one": "{{count}} unresolved conflict was excluded from this diff view.", + "skippedConflictsExcluded_other": "{{count}} unresolved conflicts were excluded from this diff view.", "820ec01f24": "Conflicted files are reviewed separately", "fd8892b120": "No changes to display", "eb5f40e49c": "This diff view excludes unresolved conflicts because the normal two-way diff pipeline is not conflict-safe.", diff --git a/src/renderer/src/i18n/locales/es.json b/src/renderer/src/i18n/locales/es.json index 8e73672554c..91e9dc49dcc 100644 --- a/src/renderer/src/i18n/locales/es.json +++ b/src/renderer/src/i18n/locales/es.json @@ -12767,8 +12767,9 @@ "7e7ca60816": "archivos cambiados", "b6c3b84476": "Mostrar árbol de archivos", "39f8007549": "Revisar conflictos", - "39e73e7181": "se excluyeron de esta vista de diff.", - "689b99f8ad": "conflicto no resuelto", + "skippedConflictsExcluded": "Se excluyeron {{count}} conflictos no resueltos de esta vista de diff.", + "skippedConflictsExcluded_one": "Se excluyó {{count}} conflicto no resuelto de esta vista de diff.", + "skippedConflictsExcluded_other": "Se excluyeron {{count}} conflictos no resueltos de esta vista de diff.", "820ec01f24": "Los archivos en conflicto se revisan por separado", "fd8892b120": "No hay cambios para mostrar", "eb5f40e49c": "Esta vista de diff excluye los conflictos sin resolver porque el pipeline normal de diff de dos vías no es seguro para conflictos.", diff --git a/src/renderer/src/i18n/locales/ja.json b/src/renderer/src/i18n/locales/ja.json index c9ec69f8e46..493dfce6bda 100644 --- a/src/renderer/src/i18n/locales/ja.json +++ b/src/renderer/src/i18n/locales/ja.json @@ -12767,8 +12767,9 @@ "7e7ca60816": "変更されたファイル", "b6c3b84476": "ファイルツリーを表示", "39f8007549": "競合をレビューする", - "39e73e7181": "はこの差分ビューから除外されました。", - "689b99f8ad": "未解決の競合", + "skippedConflictsExcluded": "未解決の競合 {{count}} 件がこの差分ビューから除外されました。", + "skippedConflictsExcluded_one": "未解決の競合 {{count}} 件がこの差分ビューから除外されました。", + "skippedConflictsExcluded_other": "未解決の競合 {{count}} 件がこの差分ビューから除外されました。", "820ec01f24": "競合したファイルは個別にレビューされます", "fd8892b120": "表示に変更はありません", "eb5f40e49c": "通常の双方向 diff パイプラインは競合に対して安全ではないため、この diff ビューでは未解決の競合が除外されます。", diff --git a/src/renderer/src/i18n/locales/ko.json b/src/renderer/src/i18n/locales/ko.json index bf60596e599..583b6ba2da6 100644 --- a/src/renderer/src/i18n/locales/ko.json +++ b/src/renderer/src/i18n/locales/ko.json @@ -12799,8 +12799,9 @@ "7e7ca60816": "변경된 파일", "b6c3b84476": "파일 트리 표시", "39f8007549": "충돌 리뷰", - "39e73e7181": "이 차이점 보기에서 제외되었습니다.", - "689b99f8ad": "미해결 충돌", + "skippedConflictsExcluded": "미해결 충돌 {{count}}개가 이 차이점 보기에서 제외되었습니다.", + "skippedConflictsExcluded_one": "미해결 충돌 {{count}}개가 이 차이점 보기에서 제외되었습니다.", + "skippedConflictsExcluded_other": "미해결 충돌 {{count}}개가 이 차이점 보기에서 제외되었습니다.", "820ec01f24": "충돌하는 파일은 별도로 리뷰됩니다.", "fd8892b120": "표시할 변경사항이 없습니다.", "eb5f40e49c": "일반적인 양방향 diff 파이프라인은 충돌로부터 안전하지 않기 때문에 이 diff 보기는 해결되지 않은 충돌을 제외합니다.", diff --git a/src/renderer/src/i18n/locales/zh.json b/src/renderer/src/i18n/locales/zh.json index 3f2a1e2d1ed..dc0cf670765 100644 --- a/src/renderer/src/i18n/locales/zh.json +++ b/src/renderer/src/i18n/locales/zh.json @@ -12810,8 +12810,9 @@ "7e7ca60816": "更改的文件", "b6c3b84476": "显示文件树", "39f8007549": "评审冲突", - "39e73e7181": "被排除在此差异视图之外。", - "689b99f8ad": "未解决的冲突", + "skippedConflictsExcluded": "{{count}} 个未解决的冲突已被排除在此差异视图之外。", + "skippedConflictsExcluded_one": "{{count}} 个未解决的冲突已被排除在此差异视图之外。", + "skippedConflictsExcluded_other": "{{count}} 个未解决的冲突已被排除在此差异视图之外。", "820ec01f24": "冲突文件单独评审", "fd8892b120": "没有显示任何变化", "eb5f40e49c": "此差异视图排除了未解决的冲突,因为普通的双向差异管道不是冲突安全的。",