Conflicts without panic
The interactive lesson needs JavaScript. The text below is the complete lesson.
After this lesson you can
- explain why Git stops with a conflict when the same lines changed differently on both sides understand
- read
git statusand the conflict markers during a merge in progress understand - resolve a conflict by deciding the intended content, mark it with
git addand finish withgit commitapply - safely abort a merge in progress with
git merge --abortapply
Understand: A question only you can answer
Both branches changed the same line in README.md. On main it says Welcome, new students. On feature it says Welcome to the Git course. The base had just Welcome.
Git cannot know which text you want. So the merge stops halfway, and the file is marked as a conflict. The staging area now holds three versions of the file: the base, ours and theirs. No merge commit has been made yet.
Git has written conflict markers into the file. From <<<<<<< HEAD to ======= is your version, from the branch you are on. From ======= to >>>>>>> feature is the version you are merging in.
You resolve the conflict by deciding what the file should actually say. Often the answer is a mix of both. Write the final text, and remove all the markers.
git add tells Git the conflict is resolved. The new text replaces the three versions in the staging area. Git does not check whether the resolution is good, so read the file before you stage it.
git commit completes the merge commit with two parents.
Watch: Stop, read, decide
We are on main, and both branches changed the first line of README.md. We merge. The terminal says CONFLICT, and the command exits with an error code.
$ git merge feature
When something unexpected happens, read git status first. It says we are in the middle of a merge, that README.md was modified on both sides, and how we can finish or abort.
$ git status
Here are the markers. At the top is our version from HEAD. Below the line is the version from feature.
$ cat README.md
First we practise the emergency exit. We started clean, so git merge --abort takes us back to exactly where we were before the merge. Look at the file areas: README.md is back to the version from main.
$ git merge --abort
Now we start the merge again. This time we resolve the conflict.
$ git merge feature
We decide that the greeting should include both parts. We write the final text, without markers. Look at the working tree: the file now has one line.
$ echo "Welcome to the Git course, new students" > README.md
Now we tell Git the conflict is resolved. Look at the staging area: the conflict mark disappears, and the new text is ready to commit.
$ git add README.md
Finally we finish. Git has already suggested the message Merge branch feature, so we save it. Watch the graph: the merge commit has two parents.
$ git commit
Tasks
- Start a merge of
featureintomain. - Ask Git what the state is now.
- Abort the merge, and get back to where you were before.
- Write the final text in README.md: both greetings in one line, without markers.
- Mark the conflict in README.md as resolved.
- Finish the merge.
Practise: Transfer: the canteen menu
You are on main in the canteen's repository. The branches lunch and discount changed files that main also changed. No demo now — use what you have seen.
Tasks
- Merge
lunchintomain. The menu should include both salad and soup. Finish the merge. - Merge
discount. Management has decided that the student price of 79 applies. Resolve the conflict accordingly, and finish the merge.
Check yourself: Recall it
You edited the conflicted file and ran git add. What has Git checked?
- That the resolution is correct and the program works
- Nothing. Git has only recorded the content you chose as the resolution.
- That both versions are in the file
You are on main and merging feature. What is between <<<<<<< HEAD and =======?
- The version from
feature - The version from
main, where you are - The version from the merge base
You are in the middle of a conflict and would rather start over. What is safest?
- Delete the folder and clone again
git merge --abortgit add .andgit commit, so the conflict goes away
What does a merge conflict mean?
- That someone did something wrong
- That Git cannot combine the changes from both sides by itself, and needs your decision
- That both branches changed the same file
Review: In the middle of the conflict you spot a typo in another file that has nothing to do with the merge. What is best?
- Fix it now, so it goes into the merge commit
- Finish the merge with only the conflict resolution, and fix the typo in a commit of its own afterwards
- Wait until you have collected more small things, and commit them all together later
Common misconceptions
- “A conflict means something is broken.” It is a question Git cannot answer alone. The history is untouched.
- “
git addchecks that the conflict is resolved correctly.” It only records the content you chose, even if the markers are still there. - “I must pick either my version or yours.” The resolution is what you actually want. Often it is a mix.
Read more
What Git stopped on
A conflict happens when both sides changed the same region of a file differently since the merge base. There are other kinds too, and git status --short labels each one:
| Code | Meaning |
|---|---|
UU |
both modified the same lines (a content conflict) |
AA |
both added a file with the same name but different content |
DU / UD |
one side deleted a file the other side modified |
The merge exits with status 1 and tells you which files need you:
$ git merge feature
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.
Every file without a conflict has already been merged and staged. Only the listed files wait for you.
Read the state before you touch anything
$ git status
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: README.md
git status always names the operation in progress and the commands that finish or abort it. That matters, because rebase and cherry-pick conflicts look the same but are finished differently.
Three versions in the staging area
During a conflict the staging area (the index) holds up to three versions of the file, called stages:
$ git ls-files -u
100644 ba4c5142cc… 1 README.md
100644 b489365621… 2 README.md
100644 4b854ebe86… 3 README.md
Stage 1 is the base, 2 is ours (HEAD) and 3 is theirs (the branch being merged). You can print each one, for example git show :1:README.md for the base. git add README.md replaces the three stages with one ordinary entry. That is all "marking as resolved" means: Git does not look at what you wrote.
Seeing the base in the markers
By default the markers show only ours and theirs. Turn on the base too:
git config --global merge.conflictStyle diff3
The next conflict shows what the line looked like before either side changed it:
<<<<<<< HEAD
Welcome, new students
||||||| 0483acd
Welcome
=======
Welcome to the Git course
>>>>>>> feature
Now the intent is clear: both sides extended "Welcome", so the answer is probably to keep both additions. zdiff3 is a more compact variant of the same style.
Taking one whole side
When you really want one version of a whole file, you do not have to edit by hand:
git checkout --ours README.mdorgit checkout --theirs README.mdwrites that side into the working tree.git restore --oursandgit restore --theirsdo the same.git checkout -m README.mdbrings the conflict markers back if you made a mess and want to start the file over.
Then stage the file as usual. For -X ours and -X theirs, which decide every conflict in advance, see the three-way merge lesson.
Check before you commit
git add happily stages a file that still contains markers. Two safety nets:
$ git diff --cached --check
README.md:1: leftover conflict marker
README.md:3: leftover conflict marker
README.md:5: leftover conflict marker
And git commit refuses while any file is still unmerged:
error: Committing is not possible because you have unmerged files.
Neither checks that the result is right. Read the file, build, and run the tests.
Finish or go back
git commitorgit merge --continuecompletes the merge commit with two parents. They do the same thing.git merge --abortreturns to the state before the merge. It works reliably only if you started clean. If you had uncommitted changes, Git may not be able to restore them. That is why Git refuses to start a merge that would overwrite local changes: "Please commit your changes or stash them before you merge."
Merge tools
git mergetool opens a three-way merge tool, graphical or in the terminal, such as Meld, KDiff3, vimdiff or VS Code, for each conflicted file, with base, ours and theirs side by side. git mergetool --tool-help lists the tools Git finds on your machine. Some tools leave *.orig backup files behind; git config --global mergetool.keepBackup false turns that off. The browser sandbox has no merge tools, but on your own machine they help with large conflicts.
Conflicts in rebase and cherry-pick
The markers and stages are the same, but you finish with the command that started the operation:
$ git rebase main
CONFLICT (content): Merge conflict in README.md
error: could not apply 01d2650... Name the course in the greeting
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
Use git rebase --continue, git rebase --skip or git rebase --abort, and git cherry-pick --continue or git cherry-pick --abort. Beware that during a rebase the sides swap: "ours" is the branch you are rebasing onto, and "theirs" is your own commit being replayed.
Resolving the same conflict again: rerere
If you meet the same conflict repeatedly, for example when rebasing a long-lived branch many times, turn on "reuse recorded resolution":
git config --global rerere.enabled true
Git records how you resolved each conflict and applies the same resolution next time:
Recorded resolution for 'README.md'.
...
Resolved 'README.md' using previous resolution.
The file is updated but not staged. Check it, then git add as usual.
Fewer and smaller conflicts
- Merge often. A day of divergence gives small conflicts; a month gives big ones.
- Keep commits focused. Do not mix reformatting of a whole file with real changes.
- Agree on a formatter, so whitespace and style do not collide.
- Talk to the person whose change you are combining with. The right resolution is a question about intent, not about text.
Per-Arne's Git Good!