What's Actually Happening

Git merge conflicts occur when changes in different branches modify the same lines in a file. Git cannot automatically merge these changes and requires manual resolution before the merge can complete.

The Error You'll See

Merge conflict message:

```bash $ git merge feature-branch

Auto-merging src/main.js CONFLICT (content): Merge conflict in src/main.js Automatic merge failed; fix conflicts and then commit the result. ```

Status showing conflicts:

```bash $ git status

Unmerged paths: (use "git add <file>..." to mark resolution) both modified: src/main.js

no changes added to commit ```

Conflict markers in file:

javascript
<<<<<<< HEAD
const value = 100;
=======
const value = 200;
>>>>>>> feature-branch

Why This Happens

  1. 1.Same line changed - Both branches modified same lines
  2. 2.Deleted vs modified - One branch deleted, other modified
  3. 3.Binary file conflict - Binary files cannot be merged
  4. 4.Renamed file conflict - File renamed in one branch
  5. 5.Large refactor - Significant changes in both branches
  6. 6.Long-running branch - Diverged significantly from main

Step 1: View Conflict Status

```bash # Check which files have conflicts git status

# List only conflicted files git diff --name-only --diff-filter=U

# Show conflict details git diff --check

# View conflict statistics git diff --stat

# See what branches are involved git log --merge --oneline

# View both versions git show :2:src/main.js > ours.js # Our version git show :3:src/main.js > theirs.js # Their version

# Check merge state cat .git/MERGE_MSG ```

Step 2: Understand Conflict Markers

```javascript // Example conflict: <<<<<<< HEAD const apiUrl = "https://api.example.com/v1"; ======= const apiUrl = "https://api.staging.com/v2"; >>>>>>> feature-branch

// Breakdown: // <<<<<<< HEAD - Start of our changes (current branch) // ... - Our code // ======= - Separator // ... - Their code (merging branch) // >>>>>>> branch - End of their changes

// Extended conflict: <<<<<<< HEAD if (condition) { doSomething(); } ======= if (otherCondition) { doSomethingElse(); } >>>>>>> feature-branch

// Multiple conflicts in one file are common ```

Step 3: Resolve Conflicts Manually

```bash # Open file with conflicts nano src/main.js # Or vim src/main.js # Or use IDE (VS Code, IntelliJ have merge tools)

# Remove conflict markers and keep desired code: # BEFORE (with conflict): <<<<<<< HEAD const apiUrl = "https://api.example.com/v1"; ======= const apiUrl = "https://api.staging.com/v2"; >>>>>>> feature-branch

# AFTER (resolved - chose theirs): const apiUrl = "https://api.staging.com/v2";

# Or combine both: # AFTER (resolved - combined): const apiUrl = process.env.NODE_ENV === 'production' ? "https://api.example.com/v1" : "https://api.staging.com/v2";

# Save file after editing ```

Step 4: Use Git Merge Tools

```bash # Use configured merge tool git mergetool

# Use specific merge tool git mergetool --tool=vimdiff git mergetool --tool=meld git mergetool --tool=code

# Configure default merge tool git config --global merge.tool code

# For VS Code: git config --global mergetool.code.cmd 'code --wait $MERGED'

# For vimdiff: git config --global mergetool.vimdiff.cmd 'vimdiff $LOCAL $REMOTE $MERGED'

# Merge tool shows: # - LOCAL: Our version (HEAD) # - REMOTE: Their version (merging branch) # - MERGED: Working copy with markers # - BASE: Common ancestor

# After resolving in merge tool, save and exit ```

Step 5: Resolve Using Git Commands

```bash # Accept our version (keep current branch) git checkout --ours src/main.js

# Accept their version (use merging branch) git checkout --theirs src/main.js

# For specific file patterns: # Accept ours for all conflicts git checkout --ours .

# Accept theirs for all conflicts git checkout --theirs .

# Restore conflict markers (if needed) git checkout --conflict=merge src/main.js

# Use git restore (newer syntax) git restore --ours src/main.js git restore --theirs src/main.js ```

Step 6: Stage Resolved Files

```bash # Mark file as resolved by staging git add src/main.js

# Stage all resolved files git add .

# Or use git restore with --staged git restore --staged src/main.js

# Check if all conflicts resolved git status # Should show: "all conflicts fixed"

# Verify staged changes git diff --cached

# Check for remaining conflict markers grep -r "<<<<<<" src/ grep -r "======" src/ grep -r ">>>>>>" src/ ```

Step 7: Complete the Merge

```bash # After resolving all conflicts and staging:

# Complete merge with default message git commit

# Or with custom message git commit -m "Merge feature-branch: resolve conflicts in main.js"

# View merge commit git log -1 --stat

# Push merge to remote git push origin main

# If merge already in progress: git merge --continue

# View merge history git log --oneline --graph --all ```

Step 8: Abort or Undo Merge

```bash # If merge too complex, abort it git merge --abort

# Or reset to state before merge git reset --hard HEAD

# If already committed, undo merge git reset --hard HEAD~1

# Or use revert (safer for pushed commits) git revert -m 1 HEAD

# For ongoing rebase conflict: git rebase --abort

# View reflog to find state before merge git reflog # Find commit before merge, then: git reset --hard <commit-sha> ```

Step 9: Resolve Binary File Conflicts

```bash # Binary files cannot be auto-merged # Check which binary has conflict git status | grep "both modified"

# Accept our binary git checkout --ours images/logo.png git add images/logo.png

# Accept their binary git checkout --theirs images/logo.png git add images/logo.png

# Or use new version cp /path/to/new-logo.png images/logo.png git add images/logo.png

# Check for binary files in merge git diff --stat --diff-filter=U ```

Step 10: Prevent Future Conflicts

```bash # Pull frequently to stay up to date git pull --rebase origin main

# Use feature branches with short lifespan # Avoid long-running branches

# Communicate with team about file ownership # Use CODEOWNERS file

# Use git rerere (reuse recorded resolution) git config --global rerere.enabled true

# This remembers how you resolved conflicts # And applies same resolution automatically

# Use smaller, focused commits # Easier to resolve when changes are minimal

# Before merge, see potential conflicts: git checkout main git pull git checkout feature-branch git merge main --no-commit --no-ff # See conflicts without committing git merge --abort ```

Merge Conflict Resolution Checklist

StepCommandPurpose
Check statusgit statusSee conflicted files
View markerscat fileUnderstand conflict
Edit filemanual or mergetoolResolve conflict
Stage filegit add fileMark resolved
Complete mergegit commitFinish merge
Pushgit pushShare with team

Verify the Fix

```bash # After resolving conflicts and committing

# 1. Check for remaining conflicts git status # Should show clean working tree

# 2. Verify no conflict markers grep -r "<<<<<<" . --include="*.js" --include="*.ts" # Should return nothing

# 3. Check merge commit git log -1 # Should show merge commit

# 4. Run tests npm test # Ensure code works

# 5. Push to remote git push origin main # Verify push succeeds

# 6. Check history git log --oneline --graph -10 # Should show merge commit connecting branches ```

  • [Fix Git Merge Conflict Abort Failed](/articles/fix-git-merge-conflict-abort-failed)
  • [Fix Git Rebase Conflict](/articles/fix-git-rebase-conflict)
  • [Fix Git Merge Divergent Branches](/articles/fix-git-merge-divergent-branches)