# Three-way merges and conflicts

> Git compares both sides with their common ancestor; a conflict is a line both sides changed.

Concept · Merging & history · https://levelcode.org/concepts/three-way-merge

Comparing only the two branch tips can't tell you who changed what. If a line differs, did *you* change it, or did *they*? A **three-way merge** adds a third version, the **merge base**: the most recent commit both branches share.

```text
         ┌─ ours   (main)
base ────┤
         └─ theirs (feature)
```

For each region of each file, Git compares both sides with the base:

| Ours vs base | Theirs vs base | Result |
|---|---|---|
| unchanged | unchanged | keep |
| changed | unchanged | take ours |
| unchanged | changed | take theirs |
| changed | changed, **identically** | take it |
| changed | changed, **differently** | **conflict** |

So most merges resolve automatically. A conflict means one specific thing: **both sides changed the same region relative to the base, in different ways.** Git can't know which one you want, or whether you want both.

## Reading conflict markers

Turn on the base section. It shows what the code looked like *before* either change, which is often the key to resolving correctly:

```bash
git config --global merge.conflictStyle zdiff3
```

```text
<<<<<<< HEAD
const timeout = 5_000
||||||| base
const timeout = 3_000
=======
const timeoutMs = 3_000
>>>>>>> feature
```

Here one side changed the value and the other renamed the variable. Neither side's version is right on its own. The correct resolution keeps both changes: `const timeoutMs = 5_000`.

## Resolving

1. `git status` lists the conflicted files.
2. Edit each one to the correct combined result, and remove the markers.
3. **Build and run the tests.** A merge without conflicts can still be wrong. If one side renamed a function and the other added a call to the old name, both changes apply cleanly and the result doesn't compile.
4. `git add` the files, then `git commit` (or `git rebase --continue`).

`git merge --abort` gets you back to the state before the merge. `git rerere` remembers how you resolved a conflict and reapplies it if the same conflict comes back, which helps with long-running branches.

## Fewer conflicts

Conflicts grow with **how long branches diverge** and **how many people edit the same lines**. Small, short-lived branches merged often ([trunk-based development](https://levelcode.org/concepts/trunk-based-development)) avoid most of them. Moving code and changing it in the same commit is a reliable way to create them.

See [fast-forward vs merge commits](https://levelcode.org/concepts/fast-forward-merge) for what's recorded after the merge.
