Why conflicts happen
In Module 4, Git combined two branches on its own. It can do that because it merges line by line. It compares each branch with the commit where they split, and takes every change from whichever side made it. Changes to different files combine automatically, and so do changes to different parts of the same file.
Sometimes that is not enough. A merge conflict happens when both branches changed the same lines in different ways, or when one branch deleted a file that the other changed. Git cannot know which version is right: perhaps one is, perhaps you need parts of both, perhaps neither. So it does as much of the merge as it can, stops, and asks you to decide the rest.
A conflict is not an error, and it does not mean that anything is broken or lost. Both versions are safe, and Git shows you exactly where they disagree. Conflicts are a normal part of working with other people, and of working on several branches yourself. This module shows you how to read one, how to resolve it, how to back out when you are not ready, and how to keep conflicts rare and small.
Merging is not the only way to meet a conflict. Reverting an old commit (Module 3), pulling other people’s work (Module 6) and rebasing (Module 8) combine changes in the same way, and can stop in the same way. The markers look the same, and you resolve each file the same way: edit it, then git add it. Only the command that finishes or abandons the operation differs, such as git revert --continue instead of git commit, and git status tells you which one to use.
Two editors mark up the same manuscript. One rewrites a sentence in chapter 2, the other fixes a typo in chapter 9, and both sets of changes go in. But if both rewrite the same sentence in different ways, someone has to decide which wording the book keeps.
When Git merges on its own
First, a merge that succeeds although both branches changed the same file. The recipe starts with 200 g of flour and a 30-minute rest. On a branch called resting-time, the rest is shortened to 20 minutes; meanwhile, on main, the flour goes up to 250 g. The two changes are several lines apart.
To follow along, first give your recipe a - 50 g sugar line and the method line Mix everything and leave the batter to rest for 30 minutes., as in the cat output below, and commit on main. Then create resting-time, change the resting time and commit; switch back to main, change the flour and commit; and merge resting-time.
git merge resting-time
Auto-merging pancakes.md
Merge made by the 'ort' strategy.
pancakes.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
cat pancakes.md
# Pancakes
- 250 g flour
- 2 eggs
- 300 ml milk
- 50 g sugar
Mix everything and leave the batter to rest for 20 minutes.
Auto-merging pancakes.md says that Git had to combine changes inside the file, and the next line says that it succeeded and made a merge commit. cat shows the result: 250 g of flour from main, and 20 minutes from resting-time.
Changes on neighbouring lines can still conflict, even when they do not touch the same line. Git needs at least one unchanged line between two changes to tell them apart; without one, it treats them as a single change made differently on each side.
Reading and resolving a conflict
This time the two branches change the same line. The recipe starts with 50 g of sugar. On the branch less-sugar, someone changes it to 30 g (“Use less sugar”); meanwhile, on main, someone changes it to 40 g (“Reduce the sugar a little”). Both are reasonable, and they disagree, so Git cannot merge them alone.
In the session, you are on main and merge less-sugar. The merge stops with a conflict, and you resolve it step by step: look at the state with git status, read the file, edit it into the version you want, mark it resolved, and commit. To follow along, create less-sugar, change the sugar to 30 g and commit, switch back to main, change it to 40 g and commit, then merge less-sugar.
git merge less-sugar
Auto-merging pancakes.md
CONFLICT (content): Merge conflict in pancakes.md
Automatic merge failed; fix conflicts and then commit the result.
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: pancakes.md
no changes added to commit (use "git add" and/or "git commit -a")
cat pancakes.md
# Pancakes
- 200 g flour
- 2 eggs
- 300 ml milk
<<<<<<< HEAD
- 40 g sugar
=======
- 30 g sugar
>>>>>>> less-sugar
Mix everything and leave the batter to rest for 30 minutes.
pancakes.md so that it contains:# Pancakes
- 200 g flour
- 2 eggs
- 300 ml milk
- 35 g sugar
Mix everything and leave the batter to rest for 30 minutes.
git add pancakes.md
git status
On branch main
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
modified: pancakes.md
git commit
[main a4d11e9] Merge branch 'less-sugar'
* a4d11e9 (HEAD -> main) Merge branch 'less-sugar' |\ | * 7d5360a (less-sugar) Use less sugar * | 3c34e74 Reduce the sugar a little |/ * f49b1d8 Add a pancake recipe
git log --oneline --graph
* a4d11e9 Merge branch 'less-sugar'
|\
| * 7d5360a Use less sugar
* | 3c34e74 Reduce the sugar a little
|/
* f49b1d8 Add a pancake recipe
The merge reports CONFLICT (content): Merge conflict in pancakes.md and stops, as its last line says: Automatic merge failed; fix conflicts and then commit the result. The merge is now in progress, and it stays that way until you finish it or abort it. git status confirms this with You have unmerged paths, lists pancakes.md as both modified, and even suggests the next commands.
cat shows where the branches disagree. The conflicting part sits between three conflict markers:
- From
<<<<<<< HEADto=======is the version on your current branch,main: 40 g. - From
=======to>>>>>>> less-sugaris the version from the branch you are merging: 30 g.
Everything outside the markers merged cleanly and needs nothing from you.
When several files conflict, git status lists each of them under Unmerged paths. Resolve and git add them one at a time, in any order; git status shows which ones are left.
To resolve the conflict, edit the file into the version you want, and delete all three marker lines. You can keep one side, keep both, or write something new; here the cooks settle on 35 g. Then git add pancakes.md marks the file as resolved, and git status says All conflicts fixed but you are still merging. Finally, git commit concludes the merge. Git opens your editor with the message Merge branch 'less-sugar'; save and close it to accept, as the session does. The graph shows the finished merge: a merge commit with both sugar commits as its parents.
A resolution is your own edit, and Git trusts it completely: whatever the file contains when you run git add goes into the merge commit. Read the whole file once more before you add it, and make sure the result still makes sense, not just that the markers are gone.
Editors such as Visual Studio Code highlight conflicts in colour and offer buttons above each one: Accept Current Change (your branch), Accept Incoming Change (the branch you are merging) and Accept Both Changes. They only edit the file for you. You still finish with git add and git commit.
Backing out of a merge
Sometimes you start a merge and realise that you cannot resolve it now: you need to ask a colleague which version is right, or the conflict is bigger than you expected. git merge --abort puts everything back as it was before the merge started, as if you had never run it. In the session, the sugar conflict happens again, and this time you back out. In your own repository less-sugar is already merged, so to try this, make a fresh conflict: for example, change the eggs line one way on a new branch and another way on main, then merge that branch.
git merge less-sugar
Auto-merging pancakes.md
CONFLICT (content): Merge conflict in pancakes.md
Automatic merge failed; fix conflicts and then commit the result.
git merge --abort
git status
On branch main
nothing to commit, working tree clean
cat pancakes.md
# Pancakes
- 200 g flour
- 2 eggs
- 300 ml milk
- 40 g sugar
Mix everything and leave the batter to rest for 30 minutes.
After the abort, git status reports a clean working tree, and cat shows that pancakes.md still has main’s 40 g, with no markers. You can merge again whenever you are ready.
Aborting works only while the merge is in progress. Once you have committed the merge, there is nothing left to abort: the merge commit is an ordinary part of your history.
If you had uncommitted changes when the merge started, --abort may not be able to restore them. So commit your own changes before you merge.
Keeping conflicts small
You cannot avoid conflicts entirely, but a few habits keep them rare, and small when they do happen:
- Merge
maininto your branch often. You then meet other people’s changes early, while they are small and fresh in everyone’s mind, instead of all at once at the end. Module 8 shows rebasing as an alternative. - Keep branches short-lived and commits focused. A branch that lives for a day collects fewer surprises than one that lives for a month, and a commit that does one thing is easier to merge, and to understand when it conflicts.
- Talk to each other. Agree who works on which files, and speak up before two people rework the same part.
- Do not reformat whole files on a feature branch. Re-indenting a file, or changing its line endings, touches every line, so it conflicts with every other change to that file. If a file needs reformatting, do it in a commit of its own, agreed with everyone, and merge it quickly.
- Check before committing a resolution. A forgotten marker line is easy to miss in a long file. Run
git diff --checkafter editing and beforegit add: it reports leftover conflict markers. If you have already staged the file, usegit diff --staged --check. Exercise 6 shows the check at work.
A conflict is Git asking a question, not reporting a failure. Read the markers, decide what the file should say, then git add and git commit. When you are not ready to answer, git merge --abort takes you back to where you started.
Exercises
Will it conflict? Say whether each merge stops with a conflict.
- Branch A edits
soup.md; branch B editsbread.md. - Both edit
pancakes.md: A the first ingredient, B the last line of the method. - Both change the sugar line of
pancakes.md, to different amounts. - Branch A deletes
cake.md; branch B edits it.
Show solution
- No. Different files merge automatically.
- No, as long as unchanged lines separate the two changes, as in Section 2.
- Yes. Both changed the same line differently.
- Yes. Git cannot both delete the file and keep the edit, so it asks you to choose.
Read the markers. A conflicted file contains:
<<<<<<< HEAD
- 40 g sugar
=======
- 30 g sugar
>>>>>>> less-sugar
Which version is from the branch you are on, and which from the branch you are merging? What must the file contain when you have resolved the conflict?
Show solution
- 40 g sugar, between <<<<<<< HEAD and =======, is from the branch you are on. - 30 g sugar, between ======= and >>>>>>> less-sugar, is from less-sugar. When resolved, the file contains only the line you choose (one of them, both, or something new), and none of the three marker lines.
Keep both changes. On one branch you added chocolate chips to the pancakes; on main someone added a banana, in the same place. Merge the branch and keep both ingredients.
Show solution
The merge stops, because both changes are on the same line of the list. Keep both lines and remove the markers:
cat pancakes.md
# Pancakes
- 200 g flour
- 2 eggs
- 300 ml milk
- 50 g sugar
<<<<<<< HEAD
- 1 banana, mashed
=======
- 50 g chocolate chips
>>>>>>> chocolate
Mix everything and leave the batter to rest for 30 minutes.
pancakes.md so that it contains:# Pancakes
- 200 g flour
- 2 eggs
- 300 ml milk
- 50 g sugar
- 1 banana, mashed
- 50 g chocolate chips
Mix everything and leave the batter to rest for 30 minutes.
git add pancakes.md
git commit
[main a653ada] Merge branch 'chocolate'
git log --oneline --graph
* a653ada Merge branch 'chocolate'
|\
| * 2fb1669 Add chocolate chips
* | 6dc884d Add a banana
|/
* f49b1d8 Add a pancake recipe
A resolution does not have to pick a side: here the right answer is both.
Committing too early. You removed the markers from pancakes.md and ran git commit, but Git refused, saying that committing is not possible because you have unmerged files. Why?
Show solution
You skipped git add pancakes.md. Until you stage the file, Git still counts it as unmerged, whatever its content. Run git add pancakes.md, then git commit.
Prevent it. Two cooks changed the sugar line at the same time, on different branches. What could they have done to avoid the conflict?
Show solution
Any of these: talk first and let one person make the change; keep the branches short, so that one change is merged before the other begins; merge main into each branch often, so that the second cook sees the first change before editing the same line.
A leftover marker. Someone resolved the sugar conflict in a hurry and committed the file with the markers still in it. Find them and fix the file. To try it, make a conflict as in Section 3, then git add and commit the file without removing the markers.
Show solution
git diff --check reports leftover conflict markers, and fails when it finds any; comparing with HEAD~1 checks what the last commit changed:
git diff --check HEAD~1
pancakes.md:6: leftover conflict marker
pancakes.md:8: leftover conflict marker
pancakes.md:10: leftover conflict marker
pancakes.md so that it contains:# Pancakes
- 200 g flour
- 2 eggs
- 300 ml milk
- 35 g sugar
Mix everything and leave the batter to rest for 30 minutes.
git add pancakes.md
git commit -m "Remove leftover conflict markers"
[main 79d79ee] Remove leftover conflict markers
1 file changed, 1 insertion(+), 5 deletions(-)
Run git diff --check before you git add a resolution (or git diff --staged --check after it), and you will not need this repair.
Self-check quiz
<<<<<<< HEAD and =======?======= comes from the other branch.git commit then concludes the merge.git diff --check warn about?Further reading
- Scott Chacon and Ben Straub, Pro Git, section 3.2: Basic Branching and Merging, part “Basic Merge Conflicts”.
- The reference page for git merge, section “How conflicts are presented”.
- GitHub Docs, Resolving a merge conflict using the command line.