feat: resolve current jj working-copy conflicts

Add a dedicated native resolver workspace with all side/base inputs,
shared editable Results, region choices, retained per-file status,
responsive rendering, and explicit nontext file choices.

Guard writes with jj working-copy locking, frozen checkout identity,
exact disk baselines, native dirty-buffer protection, and asynchronous
jj snapshot/status confirmation. Historical comparisons remain readonly.

Verified all 916 tests and live isolated terminal workflows covering text
regions, narrow layouts, binary bytes and symlink targets/types.
Clippy completes with pre-existing warnings only.

Size exception: 2614 implementation additions/deletions excluding tests
and docs. Native workspace integration, explicit file choices and the
safe save/check operation are retained as one complete behavioral slice.
This commit is contained in:
Valdemar Lorenzen
2026-10-14 15:49:26 +02:00
parent dcfa50319c
commit a3aef713e2
13 changed files with 3591 additions and 20 deletions
+24
View File
@@ -456,6 +456,30 @@ Both diff panes apply available semantic tokens from their own buffer, independe
Binary data, missing final newlines, permission/type changes, symlink targets, and submodule pointers have explicit notices. Symlinks are not followed and submodule contents are not traversed. jj conflicts use jj's file-content projection and carry conflict notices; ordinary files remain readable. Identical-content moves are shown as renames; modified moves appear as an added and removed file.
## Jujutsu conflict resolution
Run **`:resolve`** in Normal mode for the current jj working copy. It opens a dedicated tab, selects the current file if conflicted (otherwise the first conflict), and focuses its existing Result buffer at the first unresolved region. Already-open unsaved Result text and undo history are retained. Historical comparisons remain read-only; resolving never implicitly checks out or creates another change.
Two read-only inputs appear above the editable Result, with a conflicted-file list on the right. Narrow terminals show one selected input above Result. Input labels identify numbered sides/bases and contributing changes/bookmarks when an exact tree match is available; otherwise they identify the source tree. All merge sides and bases remain selectable, including multi-sided jj conflicts. Inputs support native navigation, search, selection and copying, but reject edits, saves and mutating plugin effects.
| Command | Behavior |
| --- | --- |
| `resolve.next` / `resolve.previous` | Jump to the next/previous unresolved region |
| `resolve.source [N]` | Focus a numbered input; without N, cycle inputs |
| `resolve.base [N]` | Focus a numbered base; default first base |
| `resolve.use_source [N]` | Replace the current region from that input as one undoable edit; default selected input |
| `resolve.next_file` / `resolve.previous_file` | Open another retained file |
| `resolve.files` / `resolve.result` | Focus the file list / editable Result |
| `resolve.close` | Close the resolver without discarding dirty drafts |
Input indexes are one-based and include bases. Commands also appear in the palette and through corresponding `config.core.resolve` action methods; no new global default bindings are installed. Normal-mode Tab/Shift+Tab cycles visible panes and files. In the list, `/` filters, ordinary selection navigation moves the highlight, and Enter opens it. Native Result editing, search, undo/redo and remapped pane navigation work normally. There is no automatic “accept both”.
**`:w` saves and checks jj**, asynchronously. Before writing, the resolver locks the working copy and validates the frozen checkout and exact on-disk file state. Checkout changes, external writes and unsafe filesystem ancestors refuse the write, including force-save. Successful writes trigger jj's normal snapshot operation, which may capture other on-disk changes. The list distinguishes unresolved, unsaved, checking, resolved and unconfirmed states. jj—not marker scanning or a manual checkbox—confirms resolution. A failed confirmation keeps saved contents and reports unconfirmed status. Resolved files remain in the session for review. Dirty drafts prevent destructive resolver/pane/tab closes.
Binary, symlink, deletion, executable-bit and file/directory conflicts use explicit whole-file input choices: choose an input with `resolve.use_source`, then save to apply its exact bytes and type. Symlinks are never followed. Directory replacement refuses unrelated/untracked or modified child data. Git submodule choices and legacy jj conflict storage without `jj:trees` metadata report unsupported errors instead of fabricating a resolution.
Opening, navigating and choosing draft resolutions do not snapshot jj; only saving does. If another jj operation changes the frozen checkout, retain your draft, close after preserving it, and reopen the resolver against the new state.
## Command palette
Ctrl+p opens a searchable overlay without leaving the current mode. It lists each core action once, plus registered plugin commands, with their owner, effective bindings in the current mode, and eligible modes. Disabled or overridden bindings are not advertised; actions with no key remain available through the palette. Mode-ineligible entries are dimmed and cannot execute. Retired plugin commands disappear.