Skip to content

The conflicts list in front of the three-way merge - #55

Merged
mindaugaskasp merged 2 commits into
mainfrom
feat/merge-conflicts-list
Aug 11, 2026
Merged

The conflicts list in front of the three-way merge#55
mindaugaskasp merged 2 commits into
mainfrom
feat/merge-conflicts-list

Conversation

@mindaugaskasp

Copy link
Copy Markdown
Owner

git mergetool walks conflicted files one launch at a time, and Diff Bro showed only the file git happened to be asking about — a chip reading 3 of 7 was the whole picture.

It now opens on the list: every unmerged path, what is left and what is done, and a whole-file Ours / Theirs per row that answers a file straight out of the index without opening an editor. Files can be resolved in any order; saving returns to the list, and closing it ends nothing — the walk chip, a toolbar chip and View ▸ Merge conflicts… (⌘⇧K) all bring it back.

The take control moves out of Monaco's glyph margin onto real buttons on each pane's inner edge, against the result. A glyph margin only exists on an editor's left edge, so "take ours" sat two panes from where its text lands — and being a CSS mask, it could not be reached from the keyboard at all.

The write fence

This widens the one place the app writes files it did not create, so it is fenced the same way rather than differently:

  • the renderer addresses a row by index and never by path — a row carries a display name and a directory label
  • every path is recomputed in main from the repository root, through isRepoRelative
  • verification happens at write time, not list time: each write re-runs git diff --name-only --diff-filter=U and refuses a row that is no longer in it
  • a file answered out of order is short-circuited before the marker guard on its later launch and released with a written sentinel — refused there instead, it would hang the terminal for the launcher's full two hours

Audited

Two agents were run over the finished change. They reproduced every gate and found eight further defects, all fixed here — including a data-loss path (a launch outside any repo wrote the previous repo's file) and a hang on the dialog's primary button. Full table in specs/2026-08-11-merge-conflicts-list/plan.md.

A shortcut collision the audit caught (⌘⇧M was already Tools ▸ XML) is now guarded by a uniqueness test over buildMenus, proven red against the collision.

Verified

gate result
npm run check exit 0 — 3365 tests, coverage 95.32 / 88.15 / 95.77 / 96.43
make e2e exit 0 — 485 passed
make theme-sweep SWEEP_ONLY=conflict-list exit 0 — 120 measurements × 20 themes, tightest 4.61 vs 4.5

🤖 Generated with Claude Code

mindaugaskasp and others added 2 commits August 11, 2026 16:19
`git mergetool` walks conflicted files one launch at a time, and the app
showed only the file git happened to be asking about. It now opens on the
LIST: every unmerged path, what is left and what is done, and a whole-file
Ours / Theirs per row that answers a file straight out of the index without
opening an editor. Files can be resolved in any order — saving returns to the
list, and closing it ends nothing.

The take control moves out of Monaco's glyph margin onto real buttons on each
pane's inner edge, against the result. A glyph margin only exists on an
editor's LEFT edge, so "take ours" sat two panes from where its text lands,
and being a CSS mask it could not be reached from the keyboard at all.

Main keeps custody of the list. The renderer addresses a row by INDEX and
never by path; main recomputes every path from the repository root and
re-verifies it against `git diff --diff-filter=U` at WRITE time, so a row that
went stale since the dialog opened fails closed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`locate()` derived the path from `path.relative` over realpath'd ends, which
needs both in the same form — and they are not. Windows hands out an 8.3 temp
path where git answers with the long one, so the result was a `..`-laden path
`isRepoRelative` rejected: the conflicts list never opened on Windows at all,
silently falling back to the single-file view.

`ls-files --full-name -z` asks git for its own spelling. The computation stays
as a fallback for a file git does not track.

Caught by the Windows leg of the check matrix; the macOS host and the Linux
container both missed it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mindaugaskasp
mindaugaskasp merged commit 3d61835 into main Aug 11, 2026
6 checks passed
@mindaugaskasp
mindaugaskasp deleted the feat/merge-conflicts-list branch August 11, 2026 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant