Add integrated Git conflict resolution workflow

Introduce a three-pane UIMergeView with an editable shared result
document, conflict navigation and acceptance actions, live diff
highlighting, synchronized scrolling, and contextual pane labels.

Read conflict stages directly from Git's index, detect paused merge
operations, restore conflict sessions, and support resolving, staging,
recreating, continuing, committing, and aborting operations.

Add repository-aware conflict state handling, safe asynchronous
lifetimes, merge operation UI, configurable key modifiers, i18n
strings, and focused merge-view and Git conflict tests.
This commit is contained in:
Martín Lucas Golini
2026-08-27 22:18:58 -03:00
parent 7d322a035f
commit 66d22ea5bb
29 changed files with 2604 additions and 107 deletions
+41 -1
View File
@@ -407,7 +407,47 @@ Stash benennen (optional):</string>
<string name="git_stash_save">Stash speichern</string>
<string name="git_stashes">Stashes</string>
<string name="git_tags">Tags</string>
<string name="git_unmerged">Unvereinigt</string>
<string name="git_conflicts">Konflikte</string>
<string name="git_conflict_missing_result">Bei diesem Konflikt muss die Datei behalten oder gelöscht werden.</string>
<string name="git_save_and_stage">Speichern und stagen</string>
<string name="git_previous_conflict_file">Vorherige Datei</string>
<string name="git_next_conflict_file">Nächste Datei</string>
<string name="git_continue_operation">Fortfahren</string>
<string name="git_commit_merge">Committen</string>
<string name="git_abort_operation">Abbrechen</string>
<string name="git_no_continuable_operation">Es ist kein fortsetzbarer Git-Vorgang aktiv.</string>
<string name="git_no_abortable_operation">Es ist kein abbrechbarer Git-Vorgang aktiv.</string>
<string name="git_conflict_state_failed">Der Git-Konfliktstatus konnte nicht gelesen werden.</string>
<string name="git_merge_tab">Zusammenführen: %s</string>
<string name="git_merge_base">Basis</string>
<string name="git_merge_result">Ergebnis</string>
<string name="git_merge_current_branch">Aktueller Branch (unsere Version)</string>
<string name="git_merge_incoming_branch">Eingehender Branch (deren Version)</string>
<string name="git_merge_upstream">Upstream</string>
<string name="git_merge_replayed_commit">Wiederholter Commit (Ihre Änderung)</string>
<string name="git_merge_ours_stage">Unsere Version (Stufe 2)</string>
<string name="git_merge_theirs_stage">Deren Version (Stufe 3)</string>
<string name="git_merge_not_present">Nicht vorhanden</string>
<string name="git_conflict_save_failed">Das Konfliktergebnis konnte nicht gespeichert werden.</string>
<string name="git_conflict_markers_remain">Das gespeicherte Ergebnis enthält noch erkennbare Konfliktmarkierungen.</string>
<string name="git_conflict_still_unmerged">Git meldet diesen Pfad weiterhin als nicht zusammengeführt.</string>
<string name="git_conflict_recreate_failed">Der Konflikt konnte im Git-Index nicht wiederhergestellt werden.</string>
<string name="git_conflict_recreate_save_failed">Der Konflikt wurde im Git-Index wiederhergestellt, aber seine Markierungen konnten nicht gespeichert werden.</string>
<string name="git_resolve_before_continue">Alle Konflikte müssen vor dem Fortfahren gelöst und gestaged werden.</string>
<string name="git_confirm_abort_operation">Den aktuellen Git-Vorgang abbrechen?</string>
<string name="git_resolve_conflict">Konflikt lösen</string>
<string name="git_accept_ours">Unsere Version übernehmen</string>
<string name="git_accept_theirs">Deren Version übernehmen</string>
<string name="git_conflict_no_longer_unmerged">Git meldet diesen Pfad nicht mehr als nicht zusammengeführt.</string>
<string name="git_operation_merge">Zusammenführung läuft</string>
<string name="git_operation_rebase">Rebase läuft</string>
<string name="git_operation_cherry_pick">Cherry-Pick läuft</string>
<string name="git_operation_revert">Rücknahme läuft</string>
<string name="git_operation_stash_apply">Stash wird angewendet</string>
<string name="git_operation_ready">%s · Bereit zum Fortfahren</string>
<string name="git_operation_ready_commit">%s · Bereit zum Committen</string>
<string name="git_operation_conflicts">%s · %d Konflikte</string>
<string name="git_operation_conflict">%s · %d Konflikt</string>
<string name="git_unstage">Entstagen</string>
<string name="git_unstage_all">Alles entstagen</string>
<string name="git_untracked">Unverfolgt</string>
+41 -1
View File
@@ -391,7 +391,47 @@ Name your stash (optional):</string>
<string name="git_stash_save">Save Stash</string>
<string name="git_stashes">Stashes</string>
<string name="git_tags">Tags</string>
<string name="git_unmerged">Unmerged</string>
<string name="git_conflicts">Conflicts</string>
<string name="git_conflict_missing_result">This conflict requires choosing whether to keep or delete the file.</string>
<string name="git_save_and_stage">Save &amp; Stage</string>
<string name="git_previous_conflict_file">Previous File</string>
<string name="git_next_conflict_file">Next File</string>
<string name="git_continue_operation">Continue</string>
<string name="git_commit_merge">Commit</string>
<string name="git_abort_operation">Abort</string>
<string name="git_no_continuable_operation">No continuable Git operation is active.</string>
<string name="git_no_abortable_operation">No abortable Git operation is active.</string>
<string name="git_conflict_state_failed">Unable to read Git's conflict state.</string>
<string name="git_merge_tab">Merge: %s</string>
<string name="git_merge_base">Base</string>
<string name="git_merge_result">Result</string>
<string name="git_merge_current_branch">Current branch (ours)</string>
<string name="git_merge_incoming_branch">Incoming branch (theirs)</string>
<string name="git_merge_upstream">Upstream</string>
<string name="git_merge_replayed_commit">Replayed commit (your change)</string>
<string name="git_merge_ours_stage">Ours (stage 2)</string>
<string name="git_merge_theirs_stage">Theirs (stage 3)</string>
<string name="git_merge_not_present">Not present</string>
<string name="git_conflict_save_failed">Unable to save the conflict result.</string>
<string name="git_conflict_markers_remain">The saved result still contains recognizable conflict markers.</string>
<string name="git_conflict_still_unmerged">Git still reports this path as unmerged.</string>
<string name="git_conflict_recreate_failed">Unable to restore the conflict in Git's index.</string>
<string name="git_conflict_recreate_save_failed">The conflict was restored in Git's index, but its marker text could not be saved to disk.</string>
<string name="git_resolve_before_continue">Resolve and stage all conflicts before continuing.</string>
<string name="git_confirm_abort_operation">Abort the current Git operation?</string>
<string name="git_resolve_conflict">Resolve Conflict</string>
<string name="git_accept_ours">Accept Ours</string>
<string name="git_accept_theirs">Accept Theirs</string>
<string name="git_conflict_no_longer_unmerged">Git no longer reports this path as unmerged.</string>
<string name="git_operation_merge">Merging</string>
<string name="git_operation_rebase">Rebasing</string>
<string name="git_operation_cherry_pick">Cherry-picking</string>
<string name="git_operation_revert">Reverting</string>
<string name="git_operation_stash_apply">Applying stash</string>
<string name="git_operation_ready">%s · Ready to continue</string>
<string name="git_operation_ready_commit">%s · Ready to commit</string>
<string name="git_operation_conflicts">%s · %d conflicts</string>
<string name="git_operation_conflict">%s · %d conflict</string>
<string name="git_unstage">Unstage</string>
<string name="git_unstage_all">Unstage All</string>
<string name="git_untracked">Untracked</string>
+41 -1
View File
@@ -390,7 +390,47 @@ Nommez votre stash (optionnel):</string>
<string name="git_stash_save">Sauvegarder le stash</string>
<string name="git_stashes">Stashes</string>
<string name="git_tags">Tags</string>
<string name="git_unmerged">Non fusionné</string>
<string name="git_conflicts">Conflits</string>
<string name="git_conflict_missing_result">Ce conflit nécessite de choisir de conserver ou de supprimer le fichier.</string>
<string name="git_save_and_stage">Enregistrer et indexer</string>
<string name="git_previous_conflict_file">Fichier précédent</string>
<string name="git_next_conflict_file">Fichier suivant</string>
<string name="git_continue_operation">Continuer</string>
<string name="git_commit_merge">Valider</string>
<string name="git_abort_operation">Abandonner</string>
<string name="git_no_continuable_operation">Aucune opération Git pouvant être poursuivie n’est active.</string>
<string name="git_no_abortable_operation">Aucune opération Git pouvant être abandonnée n’est active.</string>
<string name="git_conflict_state_failed">Impossible de lire l’état des conflits Git.</string>
<string name="git_merge_tab">Fusion : %s</string>
<string name="git_merge_base">Base</string>
<string name="git_merge_result">Résultat</string>
<string name="git_merge_current_branch">Branche actuelle (la nôtre)</string>
<string name="git_merge_incoming_branch">Branche entrante (la leur)</string>
<string name="git_merge_upstream">Branche amont</string>
<string name="git_merge_replayed_commit">Commit rejoué (votre modification)</string>
<string name="git_merge_ours_stage">Notre version (étape 2)</string>
<string name="git_merge_theirs_stage">Leur version (étape 3)</string>
<string name="git_merge_not_present">Absent</string>
<string name="git_conflict_save_failed">Impossible d’enregistrer le résultat du conflit.</string>
<string name="git_conflict_markers_remain">Le résultat enregistré contient encore des marqueurs de conflit reconnaissables.</string>
<string name="git_conflict_still_unmerged">Git signale toujours ce chemin comme non fusionné.</string>
<string name="git_conflict_recreate_failed">Impossible de restaurer le conflit dans l’index Git.</string>
<string name="git_conflict_recreate_save_failed">Le conflit a été restauré dans l’index Git, mais ses marqueurs n’ont pas pu être enregistrés.</string>
<string name="git_resolve_before_continue">Résolvez et indexez tous les conflits avant de continuer.</string>
<string name="git_confirm_abort_operation">Abandonner l’opération Git en cours ?</string>
<string name="git_resolve_conflict">Résoudre le conflit</string>
<string name="git_accept_ours">Accepter la nôtre</string>
<string name="git_accept_theirs">Accepter la leur</string>
<string name="git_conflict_no_longer_unmerged">Git ne signale plus ce chemin comme non fusionné.</string>
<string name="git_operation_merge">Fusion en cours</string>
<string name="git_operation_rebase">Rebasage en cours</string>
<string name="git_operation_cherry_pick">Picorage en cours</string>
<string name="git_operation_revert">Annulation en cours</string>
<string name="git_operation_stash_apply">Application du stash</string>
<string name="git_operation_ready">%s · Prêt à continuer</string>
<string name="git_operation_ready_commit">%s · Prêt à valider</string>
<string name="git_operation_conflicts">%s · %d conflits</string>
<string name="git_operation_conflict">%s · %d conflit</string>
<string name="git_unstage">Déstocker</string>
<string name="git_unstage_all">Tout déstocker</string>
<string name="git_untracked">Non suivi</string>
+41 -1
View File
@@ -308,7 +308,47 @@ Name your stash (optional):</string>
<string name="git_stash_save">Save Stash</string>
<string name="git_stashes">Stashes</string>
<string name="git_tags">标签</string>
<string name="git_unmerged">未合并</string>
<string name="git_conflicts">冲突</string>
<string name="git_conflict_missing_result">此冲突需要选择保留或删除文件。</string>
<string name="git_save_and_stage">保存并暂存</string>
<string name="git_previous_conflict_file">上一个文件</string>
<string name="git_next_conflict_file">下一个文件</string>
<string name="git_continue_operation">继续</string>
<string name="git_commit_merge">提交</string>
<string name="git_abort_operation">中止</string>
<string name="git_no_continuable_operation">没有可继续的 Git 操作。</string>
<string name="git_no_abortable_operation">没有可中止的 Git 操作。</string>
<string name="git_conflict_state_failed">无法读取 Git 冲突状态。</string>
<string name="git_merge_tab">合并:%s</string>
<string name="git_merge_base">基础版本</string>
<string name="git_merge_result">结果</string>
<string name="git_merge_current_branch">当前分支(我们的版本)</string>
<string name="git_merge_incoming_branch">传入分支(他们的版本)</string>
<string name="git_merge_upstream">上游</string>
<string name="git_merge_replayed_commit">重放的提交(您的更改)</string>
<string name="git_merge_ours_stage">我们的版本(阶段 2)</string>
<string name="git_merge_theirs_stage">他们的版本(阶段 3)</string>
<string name="git_merge_not_present">不存在</string>
<string name="git_conflict_save_failed">无法保存冲突解决结果。</string>
<string name="git_conflict_markers_remain">保存的结果中仍包含可识别的冲突标记。</string>
<string name="git_conflict_still_unmerged">Git 仍将此路径报告为未合并。</string>
<string name="git_conflict_recreate_failed">无法在 Git 索引中恢复冲突。</string>
<string name="git_conflict_recreate_save_failed">冲突已在 Git 索引中恢复,但无法将冲突标记保存到磁盘。</string>
<string name="git_resolve_before_continue">继续之前请解决并暂存所有冲突。</string>
<string name="git_confirm_abort_operation">中止当前 Git 操作?</string>
<string name="git_resolve_conflict">解决冲突</string>
<string name="git_accept_ours">接受我们的版本</string>
<string name="git_accept_theirs">接受他们的版本</string>
<string name="git_conflict_no_longer_unmerged">Git 不再将此路径报告为未合并。</string>
<string name="git_operation_merge">正在合并</string>
<string name="git_operation_rebase">正在变基</string>
<string name="git_operation_cherry_pick">正在拣选</string>
<string name="git_operation_revert">正在还原</string>
<string name="git_operation_stash_apply">正在应用储藏</string>
<string name="git_operation_ready">%s · 可以继续</string>
<string name="git_operation_ready_commit">%s · 可以提交</string>
<string name="git_operation_conflicts">%s · %d 个冲突</string>
<string name="git_operation_conflict">%s · %d 个冲突</string>
<string name="git_unstage">Unstage</string>
<string name="git_untracked">Untracked</string>
<string name="global_search">全局搜索</string>