Introduction
Git merge conflicts occur when two branches have made changes to the same lines in a file, and Git cannot automatically determine which change to keep. Merge conflicts are a normal part of collaborative development but can become complex when involving binary files, large refactors, or long-lived feature branches. Resolution errors occur when conflicts are resolved incorrectly, leading to compilation failures, lost changes, or broken functionality. Git uses a three-way merge algorithm comparing the common ancestor (merge base) with both branch tips. Common causes include concurrent edits to the same file lines, file moves or renames in one branch, encoding or line-ending changes, binary file modifications, cherry-pick operations across diverged branches, and rebase operations replaying commits onto changed code. The fix requires understanding Git's merge algorithm, using mergetool interfaces, applying systematic resolution strategies, and implementing conflict prevention workflows. This guide provides production-proven techniques for resolving Git merge conflicts across feature branch merges, rebase operations, and cherry-pick scenarios.
Symptoms
CONFLICT (content): Merge conflict in filename.extduring merge/rebaseAutomatic merge failed; fix conflicts and then commit- File shows conflict markers:
<<<<<<<,=======,>>>>>>> - Git status shows
both modifiedfor files error: could not apply <commit>during rebase with conflictsfatal: resolving a merge conflict requires a commit- Binary file shows
CONFLICT (binary)with no resolution markers merge is taking too longorToo many files to merge- Rebase stuck with message
Resolve all conflicts first - Git diff shows both versions concatenated incorrectly
Common Causes
- Same lines modified in both branches being merged
- File deleted in one branch but modified in another
- Whitespace or line-ending changes conflicting with content changes
- Code formatting tool applied in one branch but not the other
- Long-running feature branch diverged significantly from main
- Multiple developers working on same file without communication
- Cherry-pick applying commit to different code context
- Rebase replaying commits onto changed base
Step-by-Step Fix
### 1. Understand Git merge conflict markers
Git uses standard conflict markers to show both versions:
<<<<<<< HEAD (or current branch name)
Your changes in the current branch
These lines come from the branch you're merging INTO
=======
Changes from the branch being merged
These lines come from the branch being merged FROM
>>>>>>> feature-branch (or incoming branch name)
Example conflict:
python
<<<<<<< HEAD
def calculate_total(items):
total = sum(item.price for item in items)
return total * 1.1 # Added 10% tax
=======
def calculate_total(items):
total = sum(item.price for item in items)
return total * 1.08 # Added 8% tax per new policy
>>>>>>> feature-tax-update
Resolve by editing to keep desired version:
python
def calculate_total(items):
total = sum(item.price for item in items)
return total * 1.08 # Keep new tax rate
Then mark as resolved:
```bash # After editing conflict markers git add filename.py
# Or use git mergetool git mergetool
# Continue merge/rebase git commit # For merge git rebase --continue # For rebase ```
### 2. Configure merge tool and diff viewer
Set up visual merge tools:
```bash # Configure default merge tool git config --global merge.tool vscode git config --global mergetool.vscode.cmd "code --wait \$MERGED"
# Popular merge tools: git config --global merge.tool meld # Linux/Windows git config --global merge.tool p4merge # Cross-platform git config --global merge.tool kdiff3 # Cross-platform git config --global merge.tool opendiff # macOS (FileMerge)
# Install and configure p4merge (recommended) # Download from: https://www.perforce.com/products/helix-visual-client-p4merge git config --global merge.tool p4merge git config --global mergetool.p4merge.cmd "p4merge \$LOCAL \$REMOTE \$MERGED" git config --global mergetool.prompt false
# Configure diff tool git config --global diff.tool vscode git config --global difftool.vscode.cmd "code --wait --diff \$LOCAL \$REMOTE"
# Use during conflicts git mergetool # Launch configured merge tool git difftool HEAD~1 # Compare with previous commit ```
VS Code merge resolution:
```bash # VS Code built-in merge editor (GitLens or built-in) # When conflict detected, VS Code shows: # - Current Change (HEAD) # - Incoming Change (feature branch) # - Accept Both, Accept Current, Accept Incoming buttons
# Configure VS Code as default git config --global core.editor "code --wait" git config --global merge.tool vscode ```
### 3. Use systematic conflict resolution workflow
Step-by-step resolution process:
```bash # 1. Identify all conflicted files git status
# Output: # Unmerged paths: # both modified: src/calculator.py # both modified: tests/test_calculator.py # deleted by them: src/legacy.py
# 2. Review each conflict git diff --name-only --diff-filter=U # List conflicted files git diff --diff-filter=U # Show all conflicts
# 3. For each file, understand the context git log --oneline --left-right --cherry-pick HEAD...MERGE_HEAD -- src/calculator.py # Shows which commits modified this file in each branch
# 4. View conflict with context git diff --diff-filter=U src/calculator.py
# 5. Resolve conflict # Edit file to remove conflict markers # Or use mergetool git mergetool src/calculator.py
# 6. Mark as resolved git add src/calculator.py
# 7. Verify resolution git diff --cached src/calculator.py # Should show clean diff git diff HEAD src/calculator.py # Show final result
# 8. Continue operation git commit # For merge git rebase --continue # For rebase ```
### 4. Handle rebase conflicts
Rebase replays commits onto new base, may cause conflicts at each step:
```bash # Start rebase git rebase main
# Conflict occurs: # CONFLICT (content): Merge conflict in src/file.py # error: could not apply abc1234 - Feature commit
# Rebase is now paused # git status shows: # rebase in progress; onto main # You are currently rebasing branch 'feature' on 'main' # (all conflicts fixed: run "git rebase --continue") # Unmerged paths: # both modified: src/file.py
# Resolve conflict # Edit file to fix conflict markers git add src/file.py
# Continue to next commit git rebase --continue
# If another conflict, repeat # If no more conflicts, rebase completes
# Abort rebase if needed git rebase --abort # Return to pre-rebase state
# Skip current commit (use carefully) git rebase --skip
# Check rebase progress git status # Shows "currently on commit abc1234" git rebase --show-current-pick ```
Interactive rebase for conflict management:
```bash # Edit commits before replaying (reorder, squash, edit) git rebase -i main
# Editor opens with todo list: # pick abc1234 First feature commit # pick def5678 Second feature commit # pick ghi9012 Third feature commit
# Change 'pick' to 'edit' for commits likely to conflict: # pick abc1234 First feature commit # edit def5678 Second feature commit # Will stop here for manual review # pick ghi9012 Third feature commit
# Rebase stops at 'edit' commit # Make changes, then: git add . git commit --amend # Modify commit if needed git rebase --continue
# Squash commits to reduce conflict points # pick abc1234 First feature commit # squash def5678 Second feature commit # Combines with previous # squash ghi9012 Third feature commit # Combines with previous ```
### 5. Use git rerere for automatic resolution
rerere (reuse recorded resolution) remembers how you resolved conflicts:
```bash # Enable rerere git config --global rerere.enabled true
# For shared repositories, track resolutions git config --global rerere.autoupdate true
# rerere automatically: # 1. Records conflict state when conflict occurs # 2. Records resolution when you commit # 3. Applies same resolution when same conflict seen again
# Check recorded resolutions git rerere status
# View recorded conflicts ls .git/rr-cache/
# Replay a previous resolution manually git rerere
# Share rerere database with team # Commit .git/rr-cache to repository (optional) # Or export resolutions: git rerere export-resolution
# Clear rerere cache git rerere gc # Garbage collect old entries git rerere forget <file> # Forget resolution for specific file ```
rerere workflow example:
```bash # First conflict resolution git merge feature-branch # CONFLICT in src/config.py
# Resolve manually # Edit file to fix conflict git add src/config.py git commit
# rerere recorded this resolution
# Later, same conflict occurs (cherry-pick or different merge) git cherry-pick abc1234 # CONFLICT in src/config.py (same conflict as before)
# rerere automatically resolves # git status shows: # resolved by rerere: src/config.py
# Verify resolution is correct git diff --cached src/config.py git commit # Complete cherry-pick ```
### 6. Handle binary file conflicts
Binary files cannot be resolved with text markers:
```bash # Binary file conflict # CONFLICT (binary): image.png
# Options for resolution:
# Option 1: Keep one version entirely git checkout --ours image.png # Keep current branch version git checkout --theirs image.png # Keep incoming branch version git add image.png
# Option 2: Use external tool git mergetool image.png # May open image editor
# Option 3: Manual combination (for some binary formats) # E.g., for PowerPoint: extract XML, merge, repackage
# Configure merge strategy for specific file types # .gitattributes *.png merge=ours # Always keep current version for PNGs *.jpg merge=ours *.lock merge=ours # For lock files package.json merge=union # Try to combine (use carefully)
# For files that should always take incoming *.md merge=theirs # Documentation takes incoming changes ```
Prevent binary conflicts:
```bash # Use Git LFS for large binary files git lfs install git lfs track "*.psd" git lfs track "*.ai"
# Binary files generate conflict on any change # Consider using cloud storage links instead # Or establish ownership rules (only one person edits) ```
### 7. Handle complex multi-file conflicts
When many files conflict, work systematically:
```bash # List all conflicted files git diff --name-only --diff-filter=U | tee /tmp/conflicts.txt
# Count conflicts wc -l /tmp/conflicts.txt
# Group by directory git diff --name-only --diff-filter=U | xargs dirname | sort | uniq -c
# Process conflicts by priority: # 1. Configuration files first (build may fail otherwise) # 2. Core library/utility files # 3. Feature-specific files # 4. Test files
# For large merges, consider partial commits git add src/core/ tests/core/ git commit -m "Resolve core conflicts"
git add src/features/ git commit -m "Resolve feature conflicts"
# Or complete all then verify git mergetool # Process all files with merge tool
# After all resolved git status # Verify no remaining conflicts git diff --cached # Review all changes git commit ```
Conflict analysis before merge:
```bash # Predict conflicts before merging git diff --name-only main...feature-branch
# Files modified in both branches (likely conflicts) comm -12 <(git diff --name-only main...feature-branch | sort) \ <(git diff --name-only feature-branch...main | sort)
# See line-level overlap git diff main...feature-branch --word-diff
# Use git conflict-analysis tools # Install: npm install -g git-conflict-analyzer git conflict-analyzer main feature-branch ```
### 8. Handle file rename/move conflicts
Git may not detect renames during merge:
```bash # Scenario: File moved in one branch, edited in another
# Branch A: file moved git mv src/old/file.py src/new/file.py
# Branch B: file edited # Edit src/old/file.py
# Merge may show: # CONFLICT: src/old/file.py (deleted by A, modified by B) # CONFLICT: src/new/file.py (added by A)
# Resolution: Combine changes # 1. Get content from modified version git checkout --theirs src/old/file.py > /tmp/modified.py
# 2. Move to new location mv /tmp/modified.py src/new/file.py
# 3. Stage both changes git add src/new/file.py git rm src/old/file.py
# Help Git detect renames in future git config --global diff.renames true git config --global merge.renames true ```
### 9. Cherry-pick with conflicts
Cherry-pick applies specific commit, may conflict:
```bash # Cherry-pick single commit git cherry-pick abc1234
# Conflict occurs: # CONFLICT (content): Merge conflict in src/file.py # error: could not apply abc1234 - Commit message
# Resolve conflict git add src/file.py
# Continue to next (if cherry-picking range) git cherry-pick --continue
# Or complete single cherry-pick git commit
# Cherry-pick range git cherry-pick abc1234^..def5678 # Range inclusive
# Cherry-pick with strategy options git cherry-pick --strategy=recursive abc1234 git cherry-pick --strategy-option=ours abc1234 # Prefer current git cherry-pick --strategy-option=theirs abc1234 # Prefer incoming
# Abort cherry-pick git cherry-pick --abort
# Check status git status # Shows cherry-pick in progress ```
### 10. Prevent merge conflicts with better workflows
Team coordination strategies:
```bash # Regular rebasing to stay current # Feature branch workflow: git checkout feature-branch git fetch origin git rebase origin/main # Replay feature on latest main # Resolve conflicts incrementally (easier than big merge) git push --force-with-lease
# Trunk-based development (smaller, frequent merges) # Merge to main multiple times per day # Reduces conflict likelihood
# Use Git worktrees for parallel work git worktree add ../hotfix-branch hotfix-branch # Work on hotfix while feature branch waits
# Establish code ownership # CODEOWNERS file for review assignment # Reduce concurrent edits to same files
# Feature flags instead of long branches # Merge incomplete features behind flags # Avoid long-running divergent branches ```
Automated conflict detection:
```yaml # GitHub Actions - detect conflicts before merge name: Check Merge Conflicts on: pull_request: types: [synchronize, opened, reopened]
jobs: check-conflicts: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0
- name: Check for conflicts
- run: |
- git fetch origin main
- git diff --name-only --diff-filter=U origin/main...HEAD > conflicts.txt || true
- if [ -s conflicts.txt ]; then
- echo "Conflicts detected in:"
- cat conflicts.txt
- exit 1
- fi
`
Prevention
- Rebase feature branches regularly on latest main
- Merge small changes frequently rather than large batches
- Communicate with team about files being modified
- Use feature flags instead of long-lived branches
- Establish code ownership for critical files
- Configure rerere to remember common resolutions
- Use Git worktrees for parallel feature development
- Run CI checks before merging to catch integration issues
Related Errors
- **fatal: Not possible to fast-forward**: Branch divergence requiring merge
- **error: could not apply <commit>**: Cherry-pick or rebase conflict
- **CONFLICT (delete/modify)**: File deleted in one branch, modified in other
- **CONFLICT (add/add)**: Same file added independently in both branches
- **CONFLICT (renamed/renamed)**: File renamed differently in both branches