Conflict Resolution¶
A conflict happens when two branches change the same lines and Git cannot decide which to keep. Interviewers test this because "how do you resolve a merge conflict" separates people who understand the three-way model from people who guess at the markers.
Track: Core · Interview weight: High
Must-Know Facts¶
| Fact | Value | Verify with |
|---|---|---|
| Conflict | Both sides changed the same lines, so Git cannot merge them automatically | git status |
| Markers | <<<<<<< HEAD, =======, >>>>>>> <branch> wrap the two versions | grep -n '^<<<<<<<' <file> |
| Ours | The side above =======; HEAD during a merge | git checkout --ours <file> |
| Theirs | The side below =======; the merged-in branch during a merge | git checkout --theirs <file> |
| Status code | UU means both modified and unmerged | git status -s |
| Index stages | git ls-files -u lists stage 1 base, 2 ours, 3 theirs | git ls-files -u |
| Mark resolved | git add <file> after editing removes the conflict | git status |
| Finish | git commit for a merge, git rebase --continue for a rebase | git log --graph |
| Abort | git merge --abort or git rebase --abort | git status |
| Reuse | git config rerere.enabled true records and replays resolutions | git rerere status |
Anatomy of a Conflict¶
When a merge cannot combine two edits, it stops and writes both versions into the file, wrapped in markers.
git merge --no-edit feature/tls
Output:
Auto-merging config.yml
CONFLICT (content): Merge conflict in config.yml
Automatic merge failed; fix conflicts and then commit the result.
cat config.yml
Output:
<<<<<<< HEAD
port: 8080
=======
port: 443
>>>>>>> feature/tls
host: localhost
The section above ======= is the current branch (HEAD); the section below is the branch being merged. host: localhost is unmarked because both branches left that line alone.
git status -s
Output:
UU config.yml
UU means both sides modified the file and it is unmerged. Under the surface, Git keeps three versions of the file in the index.
git ls-files -u
Output:
100644 5ab6017ddae91fae7dc714a9b89732b7710cb77d 1 config.yml
100644 4613bb33d5f023a45bf31101269c95b75f6ab318 2 config.yml
100644 64dc71e58e09598699bb66b75c397a7c62d3dd04 3 config.yml
Stage 1 is the merge base, stage 2 is ours, stage 3 is theirs. Git resolves the conflict when the working file is staged and these unmerged entries are replaced by a single stage-0 entry.
Resolving a Conflict¶
Resolution means editing the file to the intended final content, removing all markers, then staging and committing.
git diff
Output:
diff --cc config.yml
index 4613bb3,64dc71e..0000000
--- a/config.yml
+++ b/config.yml
@@@ -1,2 -1,2 +1,6 @@@
++<<<<<<< HEAD
+port: 8080
++=======
+ port: 443
++>>>>>>> feature/tls
host: localhost
The combined diff (diff --cc) shows both sides at once, with two columns of change markers. After editing config.yml to the wanted line and deleting the markers, stage and commit.
git add config.yml
git commit --no-edit
Output:
[main 974b4c5] Merge branch 'feature/tls'
git add on the edited file is what tells Git the conflict is resolved. The merge commit records both parents, so the history still shows where the branches joined.
A committed conflict marker is a real bug
Nothing stops git add on a file that still contains <<<<<<<. Search for leftover markers before committing (git diff --check reports them), because a committed marker breaks the file at runtime.
Taking One Whole Side¶
When one branch's version of a file is entirely correct, git checkout --ours or --theirs replaces the file with that side instead of hand-editing.
git checkout --theirs gen.txt
git add gen.txt
Output:
Updated 1 path from the index
--ours keeps the current branch's version; --theirs takes the merged-in branch's version.
Ours and theirs are reversed during a rebase
In a merge, --ours is your branch and --theirs is the branch you are merging. In a rebase, Git replays your commits onto the other branch, so --ours is the branch you are rebasing onto and --theirs is your own commit. Check which operation is running before choosing a side.
Aborting¶
If a resolution is going wrong, back out completely rather than committing a half-merge.
git merge --abort
git merge --abort restores the working tree and index to the pre-merge commit; git rebase --abort does the same for a rebase. Neither leaves partial resolutions behind.
Reusing a Resolution with rerere¶
rerere (reuse recorded resolution) remembers how a conflict was resolved and replays it automatically the next time the same conflict appears, which is common during a long rebase or repeated merges.
git config rerere.enabled true
git merge --no-edit topic
The first time, the merge conflicts and Git records the conflict's preimage.
Output:
Auto-merging f.txt
CONFLICT (content): Merge conflict in f.txt
Recorded preimage for 'f.txt'
Automatic merge failed; fix conflicts and then commit the result.
After you resolve, stage and commit, Git records the resolution against that preimage.
git commit --no-edit
Output:
Recorded resolution for 'f.txt'.
[main 62da126] Merge branch 'topic'
When the same conflict recurs, Git applies the stored resolution.
git merge --no-edit topic
Output:
Auto-merging f.txt
CONFLICT (content): Merge conflict in f.txt
Resolved 'f.txt' using previous resolution.
Automatic merge failed; fix conflicts and then commit the result.
The file is already resolved; it still needs staging and committing, but the manual edit is not repeated.
Common Errors¶
error: Committing is not possible because you have unmerged files.¶
Cause: git commit was run while a file still has unmerged stage entries, because it was edited but not staged.
Fix: git add <file> each resolved file, then commit; git status lists what is still unmerged.
fatal: 'gen.txt' is not in the working tree when using --theirs¶
Cause: git checkout --theirs was run on a path that is not currently conflicted, so there is no stage-3 version to take.
Fix: confirm the conflict with git status; the path must show as unmerged for --ours and --theirs to apply.
Interview Checkpoints¶
L1: What causes a merge conflict?
Say first: a conflict occurs when both branches change the same lines of a file (or one edits a file the other deleted), so Git cannot decide which change to keep.
Proof: git status shows the file as UU (both modified); the file gains <<<<<<<, ======= and >>>>>>> markers.
Follow-up: In the markers, which side is above the =======?
L1: How do you resolve a conflict once Git has stopped?
Say first: edit each conflicted file to the intended content, remove all markers, git add the file, then commit the merge (or git rebase --continue).
Proof: git add replaces the three unmerged index stages with one resolved entry; git status then shows the file as staged.
Follow-up: What does git add actually change in the index when it marks a file resolved?
L2: Keep the incoming branch's entire version of a generated file during a merge.
Say first: git checkout --theirs <file> then git add <file>.
Proof:
git checkout --theirs gen.txt
git add gen.txt
Follow-up: What would --ours and --theirs mean if this were a rebase instead of a merge?
L2: Show both sides of a conflict at once without opening the file in an editor.
Say first: git diff during a conflict prints a combined diff (diff --cc) with both branches' changes.
Proof: the output marks lines unique to each side with two columns of +, around the conflict markers.
Follow-up: How can you list the base, ours and theirs versions from the index?
L3: A merge stopped on conflicts and you are unsure which side is correct, so you want to start over.
Say first: abort the merge to return to the exact pre-merge state, then re-merge deliberately once the correct resolution is known.
Proof: git merge --abort followed by git status shows a clean tree at the original commit.
Follow-up: How would you avoid resolving the same conflict by hand every time during a long rebase?
L3: The same conflict keeps reappearing across a rebase of many commits, and resolving it each time is error-prone.
Say first: enable rerere so Git records the resolution once and replays it on each recurrence.
Proof: git config rerere.enabled true; later merges print Resolved '<file>' using previous resolution.
Follow-up: Does rerere finish the merge for you, or is there still a step left?
L4: What are the three index stages during a conflict, and how does staging resolve it?
Say first: Git holds stage 1 (merge base), stage 2 (ours) and stage 3 (theirs) for the conflicted path; git add writes the working file as a single stage-0 entry and drops the three, which is what marks it resolved.
Proof: git ls-files -u lists the numbered stages before resolution; after git add, the path leaves that list.
Don't say: "Git picks the newer of the two changes automatically."
Related¶
- Merging: the operation that raises most conflicts
- Rebasing: conflicts resolved one commit at a time
- Branches: the two pointers whose edits collide
- Cherry-Pick: conflicts when copying a single commit
Captured on macOS 26 with git 2.50.1 (throwaway local repositories), 2026-09.