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": "此差异视图排除了未解决的冲突,因为普通的双向差异管道不是冲突安全的。",