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:
<<<<<<< HEAD
const value = 100;
=======
const value = 200;
>>>>>>> feature-branchWhy This Happens
- 1.Same line changed - Both branches modified same lines
- 2.Deleted vs modified - One branch deleted, other modified
- 3.Binary file conflict - Binary files cannot be merged
- 4.Renamed file conflict - File renamed in one branch
- 5.Large refactor - Significant changes in both branches
- 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
| Step | Command | Purpose |
|---|---|---|
| Check status | git status | See conflicted files |
| View markers | cat file | Understand conflict |
| Edit file | manual or mergetool | Resolve conflict |
| Stage file | git add file | Mark resolved |
| Complete merge | git commit | Finish merge |
| Push | git push | Share 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 ```
Related Issues
- [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)