Lesson 4 • Intermediate
Resolving Merge Conflicts 🌿
By the end of this lesson you'll be able to read Git's conflict markers, resolve a clash by hand, finish the merge with git add and git commit, bail out safely with git merge --abort, and structure your work so conflicts rarely happen at all.
What You'll Learn
- Explain exactly what makes Git raise a merge conflict
- Read the three conflict markers (<<<<<<<, =======, >>>>>>>)
- Resolve a conflict by editing the file and removing all markers
- Finish the merge with git add and git commit
- Bail out of a bad merge instantly with git merge --abort
- Prevent most conflicts with small, frequent merges and communication
💡 Real-World Analogy
A merge conflict is like two people editing the same sentence of a shared document at the same time. When their edits come together, the document can't show both, so it asks: "Alice wrote blue but Bob wrote green — which should I keep?" Git is exactly that careful editor. It happily combines edits to different sentences on its own, but when two edits land on the same line it stops and lets you, the human, make the final call. A conflict isn't an error or a bug — it's Git refusing to silently throw away someone's work.
1️⃣ Why Conflicts Happen
Git is very good at merging changes automatically. A conflict happens only when two branches modify the exact same lines of the same file. Different files, or different lines of the same file? Git merges them for you with no fuss. Read this worked example, then run a merge and watch Git stop.
# A conflict happens when two branches change the SAME lines of the SAME
# file, so Git can't decide which version is correct. It refuses to guess
# and hands the decision to you.
#
# main had: color: blue;
# you made: color: red; (on branch main)
# teammate made: color: green; (on branch feature-branch)
# -> both touch line 5 -> CONFLICT
#
# What does NOT conflict (Git merges these for you, no markers):
# - different files were modified
# - the same file, but different lines
# - one branch adds new content, the other leaves that area alone
#
# Try to merge a branch that changed the same line and Git stops:
git merge feature-branchAuto-merging style.css
CONFLICT (content): Merge conflict in style.css
Automatic merge failed; fix conflicts and then commit the result.CONFLICT line is Git asking for your help, not a crash.2️⃣ Reading the Conflict Markers
When a conflict occurs, Git edits the file and wraps the clashing lines in three markers. Everything between <<<<<<< HEAD and ======= is your version (the branch you're on, called HEAD); everything between ======= and >>>>>>> is theirs (the branch coming in). Lines outside the markers merged cleanly.
# When a conflict happens, Git edits the file IN PLACE and inserts three
# markers around the clashing section. Open style.css and you'll see:
body {
font-size: 16px;
<<<<<<< HEAD
color: red; # YOUR version (the branch you are ON, called HEAD)
=======
color: green; # THEIR version (the branch you are merging IN)
>>>>>>> feature-branch
margin: 0;
}
# Read the markers like this:
# <<<<<<< HEAD start of YOUR version (everything above =======)
# ======= the dividing line between the two versions
# >>>>>>> feature-branch end of THEIR version (everything below =======)
#
# Note: "font-size" and "margin" are OUTSIDE the markers — Git merged those
# fine. Only the lines Git couldn't reconcile are wrapped.3️⃣ Resolving by Hand
Resolving means making a decision and cleaning up. You edit the file to the version you actually want — keep yours, keep theirs, combine both, or write something new — and then delete all three markers. The conflict is "resolved" the moment no markers remain. Here's the same file from above, finished.
# To resolve, you EDIT the file by hand: pick the version you want (or
# combine them, or write something new), then DELETE all three markers.
# Here you decide "green" is correct, so the finished file reads:
body {
font-size: 16px;
color: green; # kept THEIR version; HEAD line + all 3 markers gone
margin: 0;
}
# A conflict is "resolved" when NO markers remain. Git doesn't care which
# side you keep — it only checks that <<<<<<<, =======, and >>>>>>> are gone.<<<<<<<, =======, or >>>>>>> lines are left.Your turn. Below is a conflict in greeting.txt. You've decided the page should greet returning visitors. Replace the ___ with the one line that should remain — and remember, no markers belong in the finished file.
# 🎯 YOUR TURN — a conflict in greeting.txt. You decide the page should
# say "Welcome back!" (THEIR version). Write the file with NO markers left.
# The conflicted file currently looks like this:
# <<<<<<< HEAD
# Hello!
# =======
# Welcome back!
# >>>>>>> feature-greeting
# Replace ___ with the single correct resolved line:
___ # 👉 the one line that should remain (no markers!)
# ✅ Expected resolved greeting.txt:
# Welcome back!4️⃣ Finishing the Merge
Editing the file isn't quite the end. You have to tell Git the conflict is fixed by staging the file with git add, then complete the merge with git commit. Use git status at any point to see which files are still "both modified".
# The full resolve-a-conflict routine, start to finish.
# 1. A merge triggers a conflict
git merge feature-auth
# 2. See exactly which files are conflicted ("both modified")
git status
# 3. Open each file, choose/combine the changes, and remove every
# <<<<<<<, =======, and >>>>>>> marker. (VS Code shows clickable
# "Accept Current / Incoming / Both Changes" buttons above the markers.)
code src/App.js
# 4. Stage the resolved file — this is how you tell Git "I fixed it"
git add src/App.js
# For several conflicted files: resolve each, then git add .
# 5. Finish the merge with a commit (Git pre-fills a merge message)
git commit -m "Merge feature-auth"Auto-merging src/App.js
CONFLICT (content): Merge conflict in src/App.js
Automatic merge failed; fix conflicts and then commit the result.
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: src/App.js
[main abc1234] Merge feature-authgit add is what flips a file from "conflicted" to "resolved".Now you finish a merge. You've already edited config.json and removed every marker — only the two final commands remain. Fill in the blanks.
# 🎯 YOUR TURN — you've already edited config.json and deleted every
# conflict marker. Two commands are left to FINISH the merge.
# 1) Tell Git the conflict in config.json is resolved
___ config.json # 👉 the command that stages a resolved file
# 2) Complete the merge by recording a commit
git ___ -m "Merge feature-config" # 👉 the verb that records a commit
# ✅ Expected: git status afterwards shows "working tree clean"
# and the merge appears in git log --oneline5️⃣ The Escape Hatch: git merge --abort
Sometimes you start a merge and realise it's the wrong branch, or the conflicts are bigger than expected. Don't panic and start deleting markers at random. One command rewinds you to exactly how things were before the merge — like it never happened.
# Made a mess, or merged the wrong branch? Don't panic and don't start
# deleting markers blindly. One command rewinds you to BEFORE the merge,
# as if it never happened — no commits, no half-resolved files:
git merge --abort
# After aborting, your working tree is clean again:
git statusOn branch main
nothing to commit, working tree cleangit merge --abort only works while a merge is unfinished (before you commit it). It's completely safe — nothing you'd lose has been saved yet.6️⃣ Resolution Tools
You never have to manage markers in a bare text editor. VS Code recognises conflicts and shows clickable Accept Current Change, Accept Incoming Change, and Accept Both Changes buttons right above each clash, plus a side-by-side comparison. Most editors and IDEs (JetBrains, Sublime) have the same. Git also ships git mergetool, which opens a dedicated three-way merge view. Whatever tool you use, the end state is identical: the right content, and no markers left behind.
7️⃣ Preventing Conflicts
The best conflict is one that never happens. Merge from main often so your branch doesn't drift, keep branches small and focused, talk to your team so two people aren't rewriting the same file, and use .gitignore so generated files never collide.
# The best conflict is one that never happens. Prevention strategies:
# - Merge/pull often so your branch never drifts far from main.
# - Keep branches small and focused (fewer changes = fewer clashes).
# - Communicate so two people don't rewrite the same file at once.
# - Don't mix a big refactor and a new feature in one branch/PR.
# - Use .gitignore so generated files never cause fake conflicts.
# A simple "stay in sync" routine — pull main into your feature branch:
git checkout main
git pull origin main
git checkout my-feature
git merge main
# Create a .gitignore so build artefacts are never tracked (and so they
# never collide on merge):
cat > .gitignore <<'EOF'
node_modules/
dist/
.env
.DS_Store
*.log
coverage/
EOF
git add .gitignore
git commit -m "Add .gitignore"Already up to date.
Updating a1b2c3d..e4f5g6h
Fast-forward
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
[my-feature 7h8i9j0] Add .gitignore
1 file changed, 6 insertions(+)
create mode 100644 .gitignoremain in little and often means each merge has far less to clash over.Common Errors (and the fix)
- Committing the markers by accident — leaving
<<<<<<</=======/>>>>>>>in a file breaks your code. Rungit diff --checkbefore every merge commit — it lists any leftover markers so you can delete them. - Panicking and hacking at a bad merge — if a merge is going wrong, stop.
git merge --abortrewinds to before the merge cleanly. That's almost always safer than trying to manually undo a half-resolved mess. - Resolving the wrong side — blindly clicking "Accept Current" (or always keeping "mine") can silently throw away a teammate's fix. Read both versions, then choose — or combine them.
- "fatal: There is no merge to abort" — you've already committed the merge (or there isn't one). To undo a finished merge instead, use
git reset --hard HEAD~1on a local-only merge (with care). - "error: Committing is not possible because you have unmerged files" — you tried to commit before resolving. Fix the markers,
git addeach file, then commit.
Pro Tips
- 💡 Search before you commit:
git diff --check(or grep your project for<<<<<<<) catches markers you missed. - 💡 Resolve, don't rush: a conflict is Git protecting two people's work. Read both sides before choosing.
- 💡 Abort is free:
git merge --abortcosts you nothing — practise with it until merges stop feeling scary. - 💡 Small and often beats big and rare: frequent small merges create tiny, easy conflicts instead of giant ones.
📋 Quick Reference
| Command | Purpose |
|---|---|
| git merge <branch> | Start a merge (may raise a conflict) |
| git status | See which files are conflicted |
| git add <file> | Mark a resolved file as fixed |
| git commit | Complete the merge |
| git merge --abort | Cancel an in-progress merge |
| git diff --check | Warn about leftover markers |
| git mergetool | Open a visual 3-way merge tool |
Frequently Asked Questions
Q: What do the <<<<<<<, =======, and >>>>>>> conflict markers mean?
Git inserts them around code it could not merge. Everything between <<<<<<< HEAD and ======= is your version (the branch you are on). Everything between ======= and >>>>>>> branch-name is their version (the branch you are merging in). To resolve, edit the section to what you want and delete all three marker lines.
Q: I committed the conflict markers by mistake — how do I fix it?
Edit the file to remove the leftover <<<<<<<, =======, and >>>>>>> lines, then make a new commit. To catch this before committing, run git diff --check, which flags any leftover markers. Many teams also add a CI check or pre-commit hook that fails if markers are found.
Q: What does git merge --abort do?
It cancels the in-progress merge and rewinds your repository to exactly how it was before you ran git merge — no merge commit, no half-resolved files. It is the safe escape hatch whenever a merge goes wrong or you merged the wrong branch. It only works while a merge is unfinished (before you have committed the merge).
Q: How do I finish a merge once I've resolved the conflicts?
Stage each resolved file with git add <file> (this marks the conflict as resolved), then run git commit. Git pre-fills a merge commit message for you, so you usually just save and close. After that, git status should report a clean working tree.
Q: How can I avoid merge conflicts in the first place?
Pull or merge from main frequently so your branch never drifts far, keep branches small and focused, and communicate so two people don't edit the same file at once. Avoid mixing big refactors with feature work, and use .gitignore so generated files (like node_modules or build output) never collide.
Mini-Challenge: Resolve a Real Conflict
No blanks this time — just a brief and an outline. Reproduce a real conflict in your own terminal and take it all the way to a clean merge. This is the exact loop you'll run on real projects.
# 🎯 MINI-CHALLENGE: resolve a real conflict end to end.
# In your own terminal, reproduce and fix a conflict:
#
# 1. Make a repo and an initial commit on main with a file note.txt
# containing one line: Draft v1
# 2. Create a branch "edit-a", change the line to Final A, commit.
# 3. Switch back to main, change the SAME line to Final B, commit.
# 4. Merge edit-a into main -> Git reports a CONFLICT in note.txt.
# 5. Open note.txt, decide the line should read Final B,
# delete all three markers, then stage and commit to finish.
#
# ✅ Expected: after the merge commit, git status is clean and note.txt
# contains exactly one line: Final B
# (Tip: if you get stuck, git merge --abort rewinds and you can retry.)
# your commands heregit merge --abort rewinds so you can try again.🎉 Lesson Complete
- ✅ A conflict means two branches changed the same lines of the same file
- ✅
<<<<<<< HEAD…=======…>>>>>>>wrap your version and theirs - ✅ Resolve by editing to the content you want and deleting all three markers
- ✅ Finish with
git addthengit commit; check withgit status - ✅
git merge --abortsafely rewinds a merge that's going wrong - ✅ Small, frequent merges plus good communication prevent most conflicts
- ✅ Next lesson: Git Workflows and Best Practices — how real teams branch, review, and ship
Sign up for free to track which lessons you've completed and get learning reminders.