Resolving Merge Conflicts
A merge conflict happens when two branches changed the same part of the same file in different ways, and Git cannot know which version is right. It is not an error and nothing is broken. Git simply stops halfway through the merge, marks the disputed lines, and asks a human to decide.
Conflicts scare beginners mostly because the output looks alarming. In this lesson you will create one on purpose, read every part of it, resolve it, and learn the shortcuts and settings that make conflicts easier.
How a conflict happens
On a branch called new-prices, Priya raised the price of kaju katli to ₹1,200. Meanwhile, on main, she renamed the same menu line to say "(sugar-free)". Both commits changed line 10 of menu.html. Now she merges:
git merge new-pricesAuto-merging menu.html CONFLICT (content): Merge conflict in menu.html Automatic merge failed; fix conflicts and then commit the result.
Git merged everything it could and stopped on the one file it could not. git status explains the situation and the way out:
git statusOn 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: menu.html no changes added to commit (use "git add" and/or "git commit -a")
"both modified" means both branches edited this file. In git status -s the same file shows as UU (unmerged on both sides).
Reading conflict markers
Open menu.html in your editor. Git has written both versions into the file, separated by conflict markers:
<h1>Our Menu</h1>
<ul>
<<<<<<< HEAD
<li>Kaju katli (sugar-free): ₹1,100 per kg</li>
=======
<li>Kaju katli: ₹1,200 per kg</li>
>>>>>>> new-prices
<li>Gulab jamun: ₹480 per kg</li>
<li>Rasgulla: ₹420 per kg</li>- Between
<<<<<<< HEADand=======is your side: the branch you are on (main). - Between
=======and>>>>>>> new-pricesis their side: the branch you are merging in. - Everything outside the markers merged cleanly and needs no attention.
Resolving the conflict
Resolving means editing the file so it contains exactly what you want, and deleting all three marker lines. You can keep one side, the other, both, or write something new. Priya talks to the shop owner and learns that the plain kaju katli goes up to ₹1,200 and the sugar-free one is a separate item at ₹1,250. She edits the lines to:
<h1>Our Menu</h1>
<ul>
<li>Kaju katli: ₹1,200 per kg</li>
<li>Kaju katli (sugar-free): ₹1,250 per kg</li>
<li>Gulab jamun: ₹480 per kg</li>
<li>Rasgulla: ₹420 per kg</li>Then she tells Git the file is resolved by staging it, and finishes the merge with a commit. --no-edit keeps the default message "Merge branch 'new-prices'":
git add menu.html
git status
git commit --no-editOn branch main All conflicts fixed but you are still merging. (use "git commit" to conclude merge) Changes to be committed: modified: menu.html [main 8bfdef1] Merge branch 'new-prices'
Done. The merge commit records her decision, and the history graph shows the two branches joining just like a normal merge.
Changing your mind: git merge --abort
If the conflict is bigger than you expected, or you merged the wrong branch, you can cancel the whole merge and return to exactly where you were before it started:
git merge --abort
git status -sThe empty status shows that the working tree is clean again, as if git merge had never run. You can try again later, perhaps after talking to whoever wrote the other branch.
Seeing the original text with zdiff3
The two sides often do not tell the whole story. Did main add "(sugar-free)", or did new-prices remove it? Git can also show the base, the text before either branch changed it. Turn this on once, globally:
git config set --global merge.conflictStyle zdiff3The same conflict now has a third section, after |||||||, showing the merge base:
<<<<<<< HEAD
<li>Kaju katli (sugar-free): ₹1,100 per kg</li>
||||||| d89c43a
<li>Kaju katli: ₹1,100 per kg</li>
=======
<li>Kaju katli: ₹1,200 per kg</li>
>>>>>>> new-pricesNow the story is clear: the original said ₹1,100; main added "(sugar-free)" and the other branch changed the price. Many experienced developers consider zdiff3 the most useful Git setting there is.
Taking one whole side
For some files, such as a generated file or an image, you do not want to mix lines; you want one complete version. git restore can pick a side for a conflicted file:
git restore --theirs package-lock.json
git restore --ours logo.png
git add package-lock.json logo.png--ours is the branch you are on and --theirs is the branch being merged in. If you change your mind, git restore --merge <file> puts the conflict markers back. (Older guides use git checkout --ours <file>, which does the same.)
Tools that help
You do not have to edit markers by hand. VS Code highlights conflicts with buttons such as "Accept Current Change", "Accept Incoming Change" and "Accept Both Changes", and has a three-pane merge editor. JetBrains IDEs have a similar tool. git mergetool opens whichever merge program you have configured. Whatever tool you use, the rules are the same: decide what the final text should be, remove the markers, stage, commit.
Avoiding painful conflicts
- Merge or pull
maininto long-running branches often, so differences stay small. - Keep branches short-lived; a branch that lives for a month will conflict with everything.
- Do not mix big formatting changes (re-indenting a whole file) with real changes in the same branch.
- Talk to your team when two people need to change the same file.
Common mistakes
- Committing conflict markers. Search the file for
<<<<<<<before staging, or usegit add --resolved, which checks for you. - Keeping both sides without thinking. Two
<li>lines for the same item, or two definitions of the same function, will compile badly or confuse users. - Not testing after resolving. A merge can be textually clean and still break the program. Run the site or the tests before committing.
- Forgetting that the merge is still in progress. Until you commit (or abort), Git is mid-merge;
git statuswill remind you.
Key points
- A conflict means both branches changed the same lines; Git stops and marks them for you.
<<<<<<< HEADto=======is your side;=======to>>>>>>> branchis theirs.- Resolve by editing the file and removing all markers, then
git addandgit commit. git merge --abortcancels the merge;git restore --ours/--theirstakes one whole side.merge.conflictStyle zdiff3shows the original base text, and Git 2.56'sgit add --resolvedrefuses files that still contain markers.
Exercise
Create a conflict on purpose. In a practice repository, make a file team.txt with the line Captain: Rohan and commit it. On a new branch, change it to Captain: Meera; on main, change it to Captain: Farhan. Merge the branch into main, read the markers, resolve it so the file says Captain: Meera and Vice-captain: Farhan on two lines, and finish the merge.
Show solution
git switch -c new-captain
git commit -am "Make Meera captain"
git switch main
git commit -am "Make Farhan captain"
git merge new-captain
git add --resolved
git commit --no-editBefore git add --resolved, edit team.txt so it contains only Captain: Meera and Vice-captain: Farhan, with the <<<<<<<, ======= and >>>>>>> lines deleted. If you are on a Git older than 2.56, use git add team.txt instead.
Quick Quiz
1. In a conflict, which text is between <<<<<<< HEAD and =======?
HEAD is your current branch; the incoming branch's text comes after =======.
2. How do you tell Git that you have resolved a conflicted file?
Staging a conflicted file marks it as resolved; then commit to finish the merge.
3. What does git merge --abort do?
It is the safe way out when you are not ready to resolve a conflict.
4. What extra information does the zdiff3 conflict style show?
The ||||||| section shows the merge base, which explains what each side changed.
Learn Git with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
