Skip to content

feat(workspace): add merge-conflict resolution UI#166

Open
liam-russell wants to merge 1 commit into
mainfrom
feat/merge-conflict-ui
Open

feat(workspace): add merge-conflict resolution UI#166
liam-russell wants to merge 1 commit into
mainfrom
feat/merge-conflict-ui

Conversation

@liam-russell

@liam-russell liam-russell commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Type

  • feat — new feature or user-visible capability

Summary

  • Added a Conflicts tab, sidebar badge, and workspace banner that surface any worktree with unresolved merge/rebase conflicts.
  • Added a per-file conflict resolution view with ours/theirs/result Monaco panes and per-block "Accept Ours" / "Accept Theirs" / "Accept Both" actions, plus a "Mark Resolved" action that stages the file once no conflict markers remain.
  • Fixed a pre-existing bug where a lone unstaged file modification could be silently misread as staged, corrupting its reported path by one character.

Why

Closes #91 — today, resolving a merge conflict means dropping to a terminal or an external merge tool. This adds a minimal but real in-app resolution flow on top of the existing Monaco-based file editor (#57).

Screenshots / recordings

Captured via a WebdriverIO screenshot against the real Electron app (same mechanism as hero-screenshots.spec.ts), but this session's browser tooling for uploading it inline to the PR isn't available right now. Description of the captured view instead:

  • Sidebar: the active worktree row shows a warning-triangle badge with the conflict count instead of the normal change-count badge.
  • Tab bar: a "Conflicts" tab appears next to Files, with a count badge (e.g. "Conflicts 1").
  • Workspace banner: an amber banner reading "N file(s) with unresolved merge conflicts" with a "Resolve Conflicts" button, shown on every tab except Conflicts itself.
  • Conflicts tab: a left-hand list of conflicted files; selecting one opens the resolution view.
  • Resolution view: a header with the file path, a "1 conflict remaining" / "All conflicts resolved" pill, Save and Mark Resolved buttons; below that, a per-block toolbar ("Conflict 1" with Accept Ours / Accept Theirs / Accept Both); below that, three Monaco panes side by side — Ours (read-only), Theirs (read-only), Result (editable, live-updates as blocks are accepted).

I'll attach the actual screenshot as a follow-up comment once browser tooling is available, or on request.

Breaking changes

None — adds a new IPC channel (git:conflictContent) and a conflicted field on StatusFileEntry; both are additive.

Test plan

  • Added packages/git/src/__tests__/conflicts.test.ts — real merge conflicts (UU, both-added/AA) verifying conflicted detection and base/ours/theirs blob reads via index stages.
  • Added app/src/renderer/workspace/__tests__/conflict-blocks.test.ts — conflict-marker parsing/resolution, including diff3-style markers and multi-block files.
  • Added e2e/specs/merge-conflict-workflow.spec.ts — scripts a real merge conflict in a managed worktree, then drives the full UI flow (banner → Conflicts tab → accept-ours → mark resolved → banner disappears), asserting the on-disk result and that git no longer reports the path as unmerged.
  • pnpm lint && pnpm typecheck && pnpm test all pass (362 unit tests + 15/15 E2E specs green, run 3x for flake-checking).

Copilot AI review requested due to automatic review settings July 7, 2026 10:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an in-app merge-conflict resolution flow in the Workspace UI, backed by a new IPC endpoint that reads base/ours/theirs content from git index stages, and extends worktree status parsing to flag conflicted paths (while also addressing a porcelain parsing edge-case).

Changes:

  • Introduces a new Conflicts tab (banner + sidebar badges) and a three-pane Monaco-based conflict resolution panel with per-block accept actions and “Mark Resolved”.
  • Adds git:conflictContent IPC + git library support to read conflict stage blobs from the index (stages 1/2/3).
  • Updates status parsing/types to expose StatusFileEntry.conflicted and fixes a porcelain parsing misclassification for unstaged-only entries.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/types/src/ipc.ts Adds IPC constant and IpcMap entry for git:conflictContent.
packages/types/src/git.ts Extends status entries with conflicted and defines conflict stage/content result types.
packages/git/src/staging.ts Adjusts porcelain status retrieval/parsing and flags conflicted XY codes.
packages/git/src/index.ts Re-exports the new conflicts helper from the git package entrypoint.
packages/git/src/conflicts.ts Implements index-stage reads for base/ours/theirs conflict content.
packages/git/src/tests/conflicts.test.ts Adds unit tests for conflict detection and stage-blob reads.
packages/git/package.json Exports the new ./conflicts module entry.
e2e/specs/merge-conflict-workflow.spec.ts Adds E2E coverage for banner → Conflicts tab → resolve → mark resolved.
app/src/renderer/workspace/WorktreeSidebar.tsx Shows conflict icon/badge per worktree and updates tooltip text.
app/src/renderer/workspace/ConflictResolutionPanel.tsx Adds the conflict resolution UI (three panes + per-block actions + mark resolved).
app/src/renderer/workspace/ConflictedFilesPanel.tsx Adds conflicted-file list panel for the Conflicts tab.
app/src/renderer/workspace/conflict-blocks.ts Adds parsing/resolution logic for conflict markers (incl. diff3 tolerance).
app/src/renderer/workspace/tests/conflict-blocks.test.ts Adds unit tests for conflict parsing and per-block resolution.
app/src/renderer/stores/workspace-store.ts Adds conflicts to persisted workspace tab state.
app/src/renderer/routes/workspace.tsx Wires up the banner/tab/panels and hooks for conflict counts/status.
app/src/renderer/queries.ts Adds worktree conflict counts and a per-worktree status query hook.
app/src/preload/index.ts Exposes getConflictFileContent to the renderer via preload API.
app/src/main/ipc/git.ts Registers IPC handler for git:conflictContent with path assertion.
Comments suppressed due to low confidence (1)

packages/git/src/staging.ts:121

  • parsePorcelainStatus trims the extracted file path (line.slice(3).trim()), which can silently corrupt valid filenames that begin/end with whitespace. Since porcelain v1 already has a fixed XY␠ prefix, it’s safer to take the substring as-is and only strip a trailing \r (for CRLF) if present.
      const indexStatus = line[0] ?? ' ';
      const worktreeStatus = line[1] ?? ' ';
      const filePath = line.slice(3).trim();

Comment thread app/src/renderer/workspace/WorktreeSidebar.tsx Outdated
Comment thread app/src/renderer/workspace/ConflictResolutionPanel.tsx
Comment thread app/src/renderer/workspace/ConflictResolutionPanel.tsx
Comment thread packages/git/src/conflicts.ts
@liam-russell liam-russell force-pushed the feat/merge-conflict-ui branch 3 times, most recently from 081260f to 0eef5af Compare July 7, 2026 11:38
Detects unmerged paths via git status porcelain codes and surfaces them as
a sidebar badge, workspace banner, and dedicated Conflicts tab. Conflicted
files get a three-pane ours/theirs/result view (Monaco-based) with per-block
accept-ours/accept-theirs/accept-both actions and a resolve action that
stages the file once no markers remain.

Also fixes a pre-existing bug in getWorktreeStatus: gitForPath's
`trimmed: true` config trimmed the whole porcelain status string, silently
eating the leading space of a lone unstaged-only entry and misreading it as
staged. Fixed by forcing a `##` branch header as the guaranteed first line.

Closes #91
@liam-russell liam-russell force-pushed the feat/merge-conflict-ui branch from 0eef5af to a8e932f Compare July 8, 2026 09:07
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.

Merge-conflict resolution UI

2 participants