Merge conflicts

Resolve Git merge conflicts with the result in view.

A merge conflict is Git asking you to decide. Nori’s resolver puts your version and the incoming one side by side, with the finished file underneath, so you choose what stays and see it before you commit. It works the same way for a merge, a rebase and a cherry-pick.

What a merge conflict is

Git merges by comparing three versions of each file: yours, the incoming one, and their common ancestor, the last version both sides shared. Where only one side changed a region, Git takes that change. Where both sides changed the same lines in different ways, Git cannot know which one you want, so it stops and marks the region in the file:

<<<<<<< HEAD
const timeout = 30
=======
const timeout = 60
>>>>>>> feature/retry

Everything between <<<<<<< and ======= is your side; everything down to >>>>>>> is the incoming one. Until every marker is gone and the file is staged, the merge, rebase or cherry-pick stays paused.

Resolving it in the terminal

The usual sequence, for a merge:

git status                 # lists the files "both modified"
# edit each file, remove the markers
git add src/config.ts      # mark it resolved
git merge --continue       # or: git merge --abort

During a rebase the last step is git rebase --continue, and Git may stop again on the next commit it replays. During a cherry-pick it is git cherry-pick --continue. The hard part is not the commands: it is reading two versions of a block interleaved with markers and being sure the file you save is the one you meant.

How Nori resolves a conflict

When Git stops on a conflict, Nori names the files left to resolve and offers to open the resolver, which opens each file on its first conflict. Your side and the incoming side sit next to each other, side by side or inline as you prefer, and the result is an editable file below them.

  • Take a line, a block or a whole side. Click a conflicted line to add or remove it from the result, use the checkbox on a conflict to take that side for the whole block, or the column header to take one side for the entire file. A second click gives it back.
  • Walk the conflicts from the keyboard. ⌥↑ and ⌥↓ move between conflict hunks; ⌃⌘1 and ⌃⌘2 take yours or the incoming side. On Windows and Linux they are Alt+↑, Alt+↓, Ctrl+Shift+1 and Ctrl+Shift+2.
  • Edit the result directly when neither side is right and the answer is a mix of both.
  • Mark resolved sits in the resolver’s header. On the last file of a merge the button reads Mark resolved and commit merge, because that is what it does.

The resolver names each side by where it comes from: during a merge, yours and incoming. Continue and abort stay in reach at every step, and Continue says what it will do for the operation in progress.

Nori’s resolver on Workspace.swift: your version and the incoming one side by side, the result underneath, and ✦ Suggest resolution, applied only on Use this.

A suggestion, if you want one

On any conflict hunk, ✦ Suggest resolution asks the agent CLI you already have installed and signed in, such as Claude Code or Codex, for a proposal built from both sides and their common ancestor. Nori runs the agent in an empty private folder with its tools off, and needs no API key.

The suggestion is only a proposal. Nothing reaches your file until you choose Use this, and the action can be switched off in Settings. Without an agent CLI on the computer, the resolver works exactly the same, without the ✦ button.

Merges, rebases and cherry-picks

Conflicts do not only come from git merge. A rebase replays your commits one by one and can stop on several of them; a cherry-pick or a revert can stop too. Nori uses the same resolver for all of them and keeps the operation’s state from Git itself, so a rebase you started in a terminal opens in Nori where Git left it.

Nori also recognises a conflict when Git speaks another language than English: since Nori 1.14 it opens the resolver instead of reporting a failed command.

What the resolver does not do

  • It resolves text files. Nori has no dedicated support for Git LFS, and no submodule interface.
  • It does not choose for you. Even with ✦ Suggest resolution, every change to the result is one you accept.