Git 3c 🧩 Resolving Merge Conflicts
3c Resolving Merge Conflicts
A merge conflict happens when Git can’t automatically combine changes from two branches. The same lines were touched in both, and Git refuses to guess which version should win. It stops the merge, marks the conflict in the affected files, and waits for you to decide.
Key point: A conflict isn’t an error — it’s a handoff. Git has done everything it can automatically and is asking a human to make the choice it can’t make. Resolving conflicts is a routine part of working with branches, not a failure.
Understanding what causes conflicts
Conflicts occur when Git cannot automatically merge changes because the same lines of code were modified in both branches. This happens when:
- Same file, same lines modified — both branches changed the same portion of a file
- Different files with related content — branches modified related functionality that overlaps logically
- Branches that have diverged significantly — many changes across multiple files
Git detects these conflicts automatically and marks them in the affected files. It refuses to merge automatically to avoid data loss or incorrect code behavior.
Why Git stops: Merging is not just concatenation. If both branches changed line 5 to different things, there’s no correct answer — only a human decision. Git stops rather than guessing.
Identifying conflict markers
When Git encounters a conflict, it inserts special markers in the conflicted file:
<<<<<<< HEAD
console.log("Hello World");
var message = "Welcome";
=======
console.log("Hello Universe");
var message = "Greetings";
>>>>>>> branch-name
The markers work like this:
| Marker | Meaning |
|---|---|
<<<<<<< HEAD | Start of changes from your current branch |
======= | Separator between conflicting changes |
>>>>>>> branch-name | End of changes from the branch being merged |
Everything between <<<<<<< and ======= is what your current branch has. Everything between ======= and >>>>>>> is what the incoming branch has.
Why markers are useful: They show you exactly what’s in conflict. The rest of the file is already merged and doesn’t need attention. You edit only the marked regions.
Strategies for resolving merge conflicts
Manual resolution process
The standard way to resolve a conflict:
- Open the conflicted file in your editor
- Examine the markers and decide what the final code should be
- Edit the file — remove the markers and leave the desired result
- Stage the resolved file with
git add - Complete the merge with
git commit
Before:
<<<<<<< HEAD
function greetUser() {
console.log("Hello World");
var message = "Welcome";
return message;
}
=======
function greetUser() {
console.log("Hello Universe");
var message = "Greetings";
return message;
}
>>>>>>> feature-branch
After resolution:
function greetUser() {
console.log("Hello Universe");
var message = "Greetings";
return message;
}
You chose one side. Or you could have combined them, if that made sense — the resolution is whatever the correct final code is.
Why manual resolution is often best: You see both versions and decide with full context. Automated choices (
--theirsor--ours) work when one side is clearly right, but manual is the default for a reason.
Complete example with commands
A full walkthrough of a merge conflict.
1. Create and switch to main branch
git checkout main
git pull origin main
Start from the latest main.
2. Make changes in main branch
// main.js
function calculateTotal(items) {
var total = 0;
for (var i = 0; i < items.length; i++) {
total += items[i].price;
}
return total;
}
git add main.js
git commit -m "Add calculateTotal function"
3. Create and switch to feature branch
git checkout -b feature/payment
4. Make conflicting changes in the feature branch
// main.js
function calculateTotal(items) {
var total = 0;
for (var i = 0; i < items.length; i++) {
total += items[i].price * 1.1; // Added tax calculation
}
return total;
}
git add main.js
git commit -m "Add tax calculation to total"
5. Switch back to main
git checkout main
6. Modify the same function in main
// main.js
function calculateTotal(items) {
var total = 0;
for (var i = 0; i < items.length; i++) {
total += items[i].price;
total += items[i].tax; // Added tax addition
}
return total;
}
git add main.js
git commit -m "Add tax addition to total"
7. Try to merge the feature branch into main
git merge feature/payment
8. Git reports the conflict
Auto-merging main.js
CONFLICT (content): Merge conflict in main.js
Automatic merge failed; fix conflicts and then commit the result.
9. Open the conflicted file
function calculateTotal(items) {
var total = 0;
for (var i = 0; i < items.length; i++) {
<<<<<<< HEAD
total += items[i].price;
total += items[i].tax;
=======
total += items[i].price * 1.1;
>>>>>>> feature/payment
}
return total;
}
10. Resolve the conflict
Decide what the correct final code is. Suppose both ideas are valid — multiply by 1.1 and add tax separately would double-count. Keep the addition:
function calculateTotal(items) {
var total = 0;
for (var i = 0; i < items.length; i++) {
total += items[i].price;
total += items[i].tax;
}
return total;
}
11. Stage and commit the resolution
git add main.js
git commit -m "Resolved merge conflict in calculateTotal function"
The merge commit is created. Both branches are now combined.
Why this pattern: Git stops, you edit, you stage, you commit. Every conflict resolution follows the same five-step shape — no matter how complex the underlying disagreement.
Best practices for merge conflicts
Communicate — if the conflict is with a teammate’s work, talk to them before deciding. You may be missing context.
Test thoroughly — resolved code needs to run correctly. A conflict resolved in the wrong way can silently break behavior.
Keep changes minimal — resolve only what’s actually in conflict. Don’t rewrite surrounding code while you’re in the file.
Use tools — a merge tool or IDE view often makes conflicts easier to reason about than raw text with markers.
Document decisions — when the resolution isn’t obvious, add a comment or mention it in the commit message. Future readers shouldn’t have to re-derive why you chose what you chose.
Common commands for conflict management
| Command | Purpose |
|---|---|
git status | Shows conflicted files |
git diff | Shows differences in conflicted files |
git checkout --theirs FILE | Accept the incoming branch’s version |
git checkout --ours FILE | Keep the current branch’s version |
git mergetool | Open a visual merge tool |
git reset --merge | Abort the merge |
Each has a specific role in the flow:
git statusis the first thing to run — it lists the files Git couldn’t merge automaticallygit diffshows you what’s different, including the conflict regionsgit checkout --theirs FILEandgit checkout --ours FILElet you pick a side automatically — fast when the right choice is obviousgit mergetoolopens a three-way merge interface in editors like VS Code or a dedicated toolgit reset --mergeaborts the in-progress merge and returns to the pre-merge state
Why
--theirsand--oursare confusing: During a merge, “ours” is your current branch; “theirs” is the incoming one. During a rebase, it’s reversed. Remember this and the flags make sense.
Complete Example Session
# ============================================
# PART 1: SETUP
# ============================================
git checkout main
git pull origin main
cat > main.js << 'EOF'
function calculateTotal(items) {
var total = 0;
for (var i = 0; i < items.length; i++) {
total += items[i].price;
}
return total;
}
EOF
git add main.js
git commit -m "Add calculateTotal function"
# ============================================
# PART 2: FEATURE BRANCH
# ============================================
git checkout -b feature/payment
cat > main.js << 'EOF'
function calculateTotal(items) {
var total = 0;
for (var i = 0; i < items.length; i++) {
total += items[i].price * 1.1;
}
return total;
}
EOF
git add main.js
git commit -m "Add tax calculation to total"
# ============================================
# PART 3: CHANGE ON MAIN
# ============================================
git checkout main
cat > main.js << 'EOF'
function calculateTotal(items) {
var total = 0;
for (var i = 0; i < items.length; i++) {
total += items[i].price;
total += items[i].tax;
}
return total;
}
EOF
git add main.js
git commit -m "Add tax addition to total"
# ============================================
# PART 4: MERGE — CONFLICT
# ============================================
git merge feature/payment
# [ Auto-merging main.js ]
# [ CONFLICT (content): Merge conflict in main.js ]
# [ Automatic merge failed; fix conflicts and then commit the result. ]
# ============================================
# PART 5: CHECK STATUS
# ============================================
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: main.js ]
# [ ]
# ============================================
# PART 6: VIEW CONFLICT
# ============================================
cat main.js
# [ function calculateTotal(items) { ]
# [ var total = 0; ]
# [ for (var i = 0; i < items.length; i++) { ]
# [ <<<<<<< HEAD ]
# [ total += items[i].price; ]
# [ total += items[i].tax; ]
# [ ======= ]
# [ total += items[i].price * 1.1; ]
# [ >>>>>>> feature/payment ]
# [ } ]
# [ return total; ]
# [ } ]
# ============================================
# PART 7: RESOLVE
# ============================================
cat > main.js << 'EOF'
function calculateTotal(items) {
var total = 0;
for (var i = 0; i < items.length; i++) {
total += items[i].price;
total += items[i].tax;
}
return total;
}
EOF
git add main.js
git commit -m "Resolved merge conflict in calculateTotal function"
# ============================================
# PART 8: VERIFY
# ============================================
git status
# [ On branch main ]
# [ nothing to commit, working tree clean ]
git log --oneline --graph
# [ * abc1234 Resolved merge conflict in calculateTotal function ]
# [ |\ ]
# [ | * def5678 Add tax calculation to total ]
# [ * | ghi9012 Add tax addition to total ]
# [ |/ ]
# [ * jkl3456 Add calculateTotal function ]
Quick Reference
Conflict Markers
| Marker | Meaning |
|---|---|
<<<<<<< HEAD | Start of current branch’s version |
======= | Separator |
>>>>>>> branch-name | End of incoming branch’s version |
Resolution Steps
| Step | Command |
|---|---|
| 1. Find conflicts | git status |
| 2. Inspect | git diff |
| 3. Edit file | (editor) |
| 4. Stage | git add FILE |
| 5. Commit | git commit |
Conflict Commands
| Command | Purpose |
|---|---|
git status | List conflicted files |
git diff | Show differences |
git checkout --ours FILE | Keep current branch |
git checkout --theirs FILE | Accept incoming branch |
git mergetool | Visual merge tool |
git reset --merge | Abort the merge |
git merge --abort | Abort merge |
Merge Abort Options
| Command | Result |
|---|---|
git merge --abort | Return to pre-merge state |
git reset --merge | Similar — resets index and working tree |
git reset --hard | Discards everything — last resort |
Checking Merge State
| Command | Shows |
|---|---|
git status | Unmerged paths |
git diff --name-only --diff-filter=U | Just conflicted files |
git log --merge | Commits involved |
Test the Resolution
| Command | Purpose |
|---|---|
git diff | Verify resolved file matches expected |
| Tests | Run your test suite |
| Build | Ensure the project compiles |
Best Practices
✅ Do This:
# Check status first
git status # ✅
# Understand the conflict before resolving
git diff # ✅
# Resolve one file at a time
git add main.js # ✅
# Test after resolving
npm test # ✅
# Commit with a descriptive message
git commit -m "Resolved conflict in calculateTotal" # ✅
# Communicate with the other author
# Talk before deciding # ✅
# Use a merge tool for complex conflicts
git mergetool # ✅
❌ Don’t Do This:
# Don't leave conflict markers in committed code
git add main.js && git commit # ❌ if markers remain
# Don't blindly accept theirs or ours
git checkout --theirs main.js # ⚠️ may lose valid changes
# Don't skip testing after resolution
git commit # ⚠️ silent breakage
# Don't resolve conflicts you don't understand
# Ask for help first # ⚠️
# Don't abort without understanding what's lost
git reset --hard # ❌ discards everything
# Don't edit unrelated code while resolving
# Keep changes minimal # ⚠️
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
| Committing with markers | Broken code | Verify no <<<<<<< remains |
| Wrong side chosen | Lost valid changes | Review git diff first |
| Forgot to stage | Merge can’t complete | git add each file |
| Merge stuck | Can’t proceed | Resolve all conflicts, then commit |
Ambiguous --ours / --theirs | Reversed during rebase | Check context |
| No testing | Silent breakage | Run tests after |
| Aborting blindly | Lost work | Understand state first |
Real-World Examples
1. Identify conflicts
git status
Lists conflicted files.
2. Inspect the conflict
git diff main.js
Shows the conflicting regions.
3. Accept incoming
git checkout --theirs main.js
Uses the merging branch’s version.
4. Keep current
git checkout --ours main.js
Uses the current branch’s version.
5. Manual edit
Open the file, remove markers, choose the final code.
6. Stage resolution
git add main.js
Marks it resolved.
7. Complete the merge
git commit
Creates the merge commit.
8. Abort the merge
git merge --abort
Returns to pre-merge state.
9. Visual merge tool
git mergetool
Opens a three-way diff editor.
10. Verify the resolution
git diff
Shows what’s staged — a final review.
11. Test the code
npm test
Catches silent breakage.
12. Find remaining conflicts
git diff --name-only --diff-filter=U
Lists unresolved files.
13. Check the merge tree
git log --oneline --graph
Shows the merge commit with both parents.
14. Force a merge commit for review
git merge --no-ff feature/payment
Preserves the branch’s existence in history.
15. Clean up after
git branch -d feature/payment
Deletes the merged branch.
Visual: Conflict Markers
┌──────────────────────────────────────────────┐
│ <<<<<<< HEAD │
│ console.log("Hello World"); │
│ var message = "Welcome"; │
│ ======= │
│ console.log("Hello Universe"); │
│ var message = "Greetings"; │
│ >>>>>>> feature-branch │
│ │
│ Current branch ──── HEAD section │
│ Incoming branch ─── between ======= and >>> │
│ │
└──────────────────────────────────────────────┘
Visual: Merge Conflict Flow
┌──────────────────────────────────────────────┐
│ git merge feature/payment │
│ │ │
│ ▼ │
│ Git tries to auto-merge │
│ │ │
│ ┌────┴────┐ │
│ success conflict │
│ │ │ │
│ ▼ ▼ │
│ done mark files, stop │
│ │ │
│ ▼ │
│ You edit conflicted files │
│ │ │
│ ▼ │
│ git add FILE │
│ │ │
│ ▼ │
│ git commit │
│ │ │
│ ▼ │
│ Merge complete │
│ │
└──────────────────────────────────────────────┘
Visual: --ours vs --theirs
┌──────────────────────────────────────────────┐
│ During a merge │
│ │
│ --ours → current branch (e.g., main) │
│ --theirs → incoming branch (e.g., feature) │
│ │
├──────────────────────────────────────────────┤
│ During a rebase │
│ │
│ --ours → branch you're rebasing onto │
│ --theirs → branch you're rebasing │
│ │
│ Reversed! Remember which operation. │
│ │
└──────────────────────────────────────────────┘
Visual: Abort vs Continue
┌──────────────────────────────────────────────┐
│ Continue the merge │
│ │
│ 1. Resolve conflicts │
│ 2. git add FILE │
│ 3. git commit │
│ │
│ → Merge commit created │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ Abort the merge │
│ │
│ git merge --abort │
│ │
│ → Back to pre-merge state │
│ → No merge commit created │
│ → All conflict markers removed │
│ │
└──────────────────────────────────────────────┘
Visual: The Five-Step Resolution
┌──────────────────────────────────────────────┐
│ 1. git status │
│ → find conflicted files │
│ │
│ 2. open in editor │
│ → read markers │
│ │
│ 3. edit │
│ → choose final code, remove markers │
│ │
│ 4. git add FILE │
│ → mark as resolved │
│ │
│ 5. git commit │
│ → complete the merge │
│ │
└──────────────────────────────────────────────┘
Summary
| Concept | Meaning |
|---|---|
| Merge conflict | Git can’t auto-merge |
| Conflict markers | <<<<<<<, =======, >>>>>>> |
| HEAD side | Current branch |
| Incoming side | Branch being merged |
| Resolution | Edit, add, commit |
| Abort | git merge --abort |
--ours | Current branch version |
--theirs | Incoming branch version |
git mergetool | Visual merge interface |
Key takeaways:
- A merge conflict happens when both branches change the same lines — Git stops and asks you to decide
- Conflict markers show the conflicting regions —
<<<<<<< HEADfor current,>>>>>>> branchfor incoming - Resolve by editing the file, removing the markers, and leaving the correct final code
- Stage the resolved file with
git addand complete withgit commit - Use
git statusto find conflicts,git diffto inspect them git checkout --ours FILEkeeps your branch’s version;--theirs FILEaccepts the incoming onegit mergetoolopens a visual three-way merge interfacegit merge --abortreturns to the pre-merge state- Never commit with conflict markers still in the file — always verify
- Test the resolved code — a wrong resolution can silently break behavior
- Communicate with the other author if the conflict isn’t obvious
- Resolutions are routine — every conflict follows the same five-step shape
Remember: Conflicts aren’t failures — they’re Git asking for a human decision. Read the markers, understand both sides, choose or combine, stage, commit. Use --ours/--theirs when one side is clearly right. Use a merge tool for complex cases. Always test after resolving. And communicate when the choice matters. Master conflict resolution, and merging becomes a routine step in every workflow.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!