| |

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:

MarkerMeaning
<<<<<<< HEADStart of changes from your current branch
=======Separator between conflicting changes
>>>>>>> branch-nameEnd 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:

  1. Open the conflicted file in your editor
  2. Examine the markers and decide what the final code should be
  3. Edit the file — remove the markers and leave the desired result
  4. Stage the resolved file with git add
  5. 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 (--theirs or --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

CommandPurpose
git statusShows conflicted files
git diffShows differences in conflicted files
git checkout --theirs FILEAccept the incoming branch’s version
git checkout --ours FILEKeep the current branch’s version
git mergetoolOpen a visual merge tool
git reset --mergeAbort the merge

Each has a specific role in the flow:

  • git status is the first thing to run — it lists the files Git couldn’t merge automatically
  • git diff shows you what’s different, including the conflict regions
  • git checkout --theirs FILE and git checkout --ours FILE let you pick a side automatically — fast when the right choice is obvious
  • git mergetool opens a three-way merge interface in editors like VS Code or a dedicated tool
  • git reset --merge aborts the in-progress merge and returns to the pre-merge state

Why --theirs and --ours are 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

MarkerMeaning
<<<<<<< HEADStart of current branch’s version
=======Separator
>>>>>>> branch-nameEnd of incoming branch’s version

Resolution Steps

StepCommand
1. Find conflictsgit status
2. Inspectgit diff
3. Edit file(editor)
4. Stagegit add FILE
5. Commitgit commit

Conflict Commands

CommandPurpose
git statusList conflicted files
git diffShow differences
git checkout --ours FILEKeep current branch
git checkout --theirs FILEAccept incoming branch
git mergetoolVisual merge tool
git reset --mergeAbort the merge
git merge --abortAbort merge

Merge Abort Options

CommandResult
git merge --abortReturn to pre-merge state
git reset --mergeSimilar — resets index and working tree
git reset --hardDiscards everything — last resort

Checking Merge State

CommandShows
git statusUnmerged paths
git diff --name-only --diff-filter=UJust conflicted files
git log --mergeCommits involved

Test the Resolution

CommandPurpose
git diffVerify resolved file matches expected
TestsRun your test suite
BuildEnsure 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

PitfallProblemSolution
Committing with markersBroken codeVerify no <<<<<<< remains
Wrong side chosenLost valid changesReview git diff first
Forgot to stageMerge can’t completegit add each file
Merge stuckCan’t proceedResolve all conflicts, then commit
Ambiguous --ours / --theirsReversed during rebaseCheck context
No testingSilent breakageRun tests after
Aborting blindlyLost workUnderstand 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

ConceptMeaning
Merge conflictGit can’t auto-merge
Conflict markers<<<<<<<, =======, >>>>>>>
HEAD sideCurrent branch
Incoming sideBranch being merged
ResolutionEdit, add, commit
Abortgit merge --abort
--oursCurrent branch version
--theirsIncoming branch version
git mergetoolVisual 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 — <<<<<<< HEAD for current, >>>>>>> branch for incoming
  • Resolve by editing the file, removing the markers, and leaving the correct final code
  • Stage the resolved file with git add and complete with git commit
  • Use git status to find conflicts, git diff to inspect them
  • git checkout --ours FILE keeps your branch’s version; --theirs FILE accepts the incoming one
  • git mergetool opens a visual three-way merge interface
  • git merge --abort returns 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!