What's Actually Happening

You ran git merge to combine branches, but Git stopped mid-merge with conflicts. Files contain conflict markers (<<<<<<<, =======, >>>>>>>) showing conflicting changes. The merge won't complete until you resolve all conflicts. Some conflicts are complex, involving binary files, renames, or multiple conflicting sections.

Merge conflicts occur when Git can't automatically combine changes from different branches because they modify the same lines in different ways. Git pauses the merge and marks the conflicts for you to resolve manually.

The Error You'll See

Git merge conflicts manifest in various ways:

```bash # Merge conflict during branch merge $ git merge feature-branch Auto-merging src/app.js CONFLICT (content): Merge conflict in src/app.js Automatic merge failed; fix conflicts and then commit the result.

# List of conflicting files $ 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: src/app.js both modified: src/utils.js deleted by them: src/old-file.js

# Conflict markers in file $ cat src/app.js function greet() { <<<<<<< HEAD console.log("Hello from main"); ======= console.log("Hello from feature"); >>>>>>> feature-branch }

# Multiple conflicts in one file $ cat config.json { <<<<<<< HEAD "version": "1.0.0", "name": "my-app" ======= "version": "2.0.0", "description": "Updated app" >>>>>>> feature }

# Binary file conflict $ git status Unmerged paths: both modified: images/logo.png

# Rename conflict CONFLICT (rename/rename): src/old.js renamed to src/new.js in HEAD and to src/other.js in feature. CONFLICT (content): Merge conflict in src/new.js

# Delete/modify conflict CONFLICT (modify/delete): src/deleted.js deleted in feature and modified in HEAD.

# Merge with conflicts preventing commit $ git commit error: Committing is not possible because you have unmerged files. hint: Fix them up in the work tree, and then use 'git add/rm <file>' hint: as appropriate to mark resolution and make a commit. fatal: Exiting because of an unresolved merge conflict.

# Large number of conflicts $ git diff --name-only --diff-filter=U | wc -l 42

# Rebase conflict $ git rebase main CONFLICT (content): Merge conflict in src/app.js error: could not apply commit abc123... feat: new feature Resolve all conflicts manually, mark them as resolved with "git add/rm <conflicted_files>" ```

Additional symptoms: - Files show <<<<<, =====, >>>>> markers - git status shows "both modified" or "unmerged paths" - Can't commit until conflicts resolved - IDE shows red conflict indicators - Branch shows as "merging" or "rebasing" - Some files work while others have conflicts - Cherry-pick shows same conflict patterns

Why This Happens

  1. 1.Same Lines Modified Differently: Both branches changed the same lines of code in different ways. Git can't determine which version is correct and requires manual intervention.
  2. 2.Adjacent Lines Changed: Changes in both branches are close enough that Git can't merge them cleanly, even if they don't overlap exactly.
  3. 3.One Branch Deleted, Other Modified: One branch deleted a file while another modified it. Git needs you to decide whether to keep the modifications or respect the deletion.
  4. 4.Rename Conflicts: Both branches renamed the same file to different names, or one renamed while the other modified.
  5. 5.Binary File Conflicts: Binary files (images, compiled files) can't be merged textually. Git needs you to choose which version to keep.
  6. 6.Large Refactoring Conflicts: Major code restructuring in one branch causes widespread conflicts with another branch's changes.
  7. 7.Long-lived Branch Divergence: Branches have been separated for a long time, accumulating many conflicting changes.
  8. 8.Merge Algorithm Limitations: Git's three-way merge algorithm has limitations with complex changes like moved code blocks or restructured functions.

Step 1: Identify All Conflicted Files

Find all files with conflicts to understand the scope.

```bash # List all conflicted files git status

# Or more specifically: git diff --name-only --diff-filter=U

# Count conflicts: git diff --name-only --diff-filter=U | wc -l

# Show conflict details: git diff --check

# See which files have conflicts and their types: git status --porcelain | grep "^UU|^AA|^DD"

# UU = both modified # AA = both added # DD = both deleted

# List files with conflict count: git diff --name-only --diff-filter=U | while read file; do count=$(grep -c "<<<<<<< HEAD" "$file" 2>/dev/null || echo 0) echo "$count conflicts in $file" done

# See what branches are being merged: cat .git/MERGE_HEAD 2>/dev/null || echo "Not in merge" cat .git/rebase-merge/head-name 2>/dev/null || echo "Not in rebase"

# Check current merge state: git status | head -10

# Show summary of conflicts: git diff --stat ```

Step 2: Understand the Conflict

Examine the specific conflict before resolving.

```bash # View conflict in file: cat src/app.js

# Or use git show with conflict markers: git show :2:src/app.js # Their version (MERGE_HEAD) git show :3:src/app.js # Our version (HEAD)

# See both versions side by side: git diff HEAD MERGE_HEAD -- src/app.js

# View just our changes: git diff --ours src/app.js

# View just their changes: git diff --theirs src/app.js

# See base version (common ancestor): git show :1:src/app.js

# Use git merge-file to see three-way diff: git merge-file -p src/app.js .merge_file_ours .merge_file_theirs

# For understanding rename conflicts: git diff --name-status HEAD MERGE_HEAD

# Check log for context: git log --oneline -5 HEAD git log --oneline -5 MERGE_HEAD

# See who made conflicting changes: git log --follow -p src/app.js | head -100

# Use git blame to see line history: git blame src/app.js ```

Step 3: Resolve Simple Text Conflicts

Fix straightforward conflicts by editing files directly.

```bash # Open conflicted file in editor: nano src/app.js # Or: code src/app.js vim src/app.js

# Conflict looks like: <<<<<<< HEAD console.log("Version A"); ======= console.log("Version B"); >>>>>>> feature-branch

# Resolution options: # 1. Keep HEAD (our version): console.log("Version A");

# 2. Keep MERGE_HEAD (their version): console.log("Version B");

# 3. Combine both: console.log("Version A"); console.log("Version B");

# 4. Write new version: console.log("Resolved version");

# After editing, mark as resolved: git add src/app.js

# Verify resolution: git diff --cached src/app.js

# For multiple conflicts in one file, find all: grep -n "<<<<<<< HEAD" src/app.js

# Navigate to each conflict and resolve

# After all conflicts in file resolved: git add src/app.js

# Check if any conflicts remain: git diff --name-only --diff-filter=U ```

Step 4: Use Git Commands for Quick Resolution

Resolve conflicts with git commands for common scenarios.

```bash # Accept our version for specific file: git checkout --ours src/app.js git add src/app.js

# Accept their version for specific file: git checkout --theirs src/app.js git add src/app.js

# Accept our version for all conflicts: git checkout --ours . git add .

# Accept their version for all conflicts: git checkout --theirs . git add .

# For specific file patterns: git checkout --ours -- "src/*.js" git add "src/*.js"

# Remove deleted file (accept deletion): git rm src/deleted-file.js

# Keep deleted file (reject deletion): git add src/deleted-file.js

# For binary files: # Keep our version: git checkout --ours images/logo.png git add images/logo.png

# Keep their version: git checkout --theirs images/logo.png git add images/logo.png

# For rename conflicts: # Keep our rename: git checkout --ours src/new-name.js git add src/new-name.js git rm src/old-name.js # if needed

# Use git restore for newer Git versions: git restore --ours src/app.js git restore --theirs src/app.js ```

Step 5: Use Merge Tools for Complex Conflicts

Use visual merge tools for difficult conflicts.

```bash # Configure merge tool: git config --global merge.tool vscode

# For VSCode: git config --global mergetool.vscode.cmd 'code --wait $MERGED'

# For vimdiff: git config --global merge.tool vimdiff

# For meld (Linux): git config --global merge.tool meld

# For Kaleidoscope (macOS): git config --global merge.tool kaleidoscope

# For Beyond Compare: git config --global merge.tool bc

# Launch merge tool for all conflicts: git mergetool

# Launch for specific file: git mergetool src/app.js

# Without prompting: git mergetool --no-prompt

# List available merge tools: git mergetool --tool-help

# Use specific tool once: git mergetool --tool=vscode

# Keep backup of original files: git config --global mergetool.keepBackup true

# After using mergetool: git add src/app.js

# Check remaining conflicts: git diff --name-only --diff-filter=U ```

Step 6: Handle Binary File Conflicts

Resolve conflicts in non-text files.

```bash # List binary files with conflicts: git diff --name-only --diff-filter=U | while read f; do if file "$f" | grep -q "text"; then : else echo "Binary: $f" fi done

# Or check directly: file images/logo.png

# View binary file info: git ls-files -s images/logo.png

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

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

# View binary content (if image): # On Linux: xdg-open images/logo.png

# On macOS: open images/logo.png

# On Windows: start images/logo.png

# Or use git show: git show HEAD:images/logo.png > /tmp/our-logo.png git show MERGE_HEAD:images/logo.png > /tmp/their-logo.png

# Compare visually: diff <(xxd /tmp/our-logo.png) <(xxd /tmp/their-logo.png)

# Replace with completely new binary: cp /path/to/new-logo.png images/logo.png git add images/logo.png ```

Step 7: Resolve Delete/Modify Conflicts

Handle conflicts where one side deleted and other modified.

```bash # Check delete/modify conflicts: git status | grep "deleted by"

# Example: # deleted by them: src/old-file.js # Means: we modified it, they deleted it

# Option 1: Keep the file (our modification): git add src/old-file.js

# Option 2: Accept deletion (their change): git rm src/old-file.js

# For "deleted by us": # We deleted it, they modified it

# Option 1: Accept deletion (our change): git rm src/old-file.js

# Option 2: Keep the file (their modification): git checkout --theirs src/old-file.js git add src/old-file.js

# Restore file that was deleted in both but we want: git checkout HEAD -- src/deleted-file.js git add src/deleted-file.js

# Check before committing: git status git diff --cached ```

Step 8: Resolve Rename Conflicts

Handle conflicts involving file renames.

```bash # Check rename conflicts: git status | grep "rename"

# View rename details: git diff --name-status HEAD MERGE_HEAD

# Typical output: # R100 old-name.js new-name.js (HEAD) # R100 old-name.js other-name.js (MERGE_HEAD)

# Option 1: Keep our rename: git mv new-name.js other-name.js # If needed git checkout --ours new-name.js git add new-name.js git rm old-name.js 2>/dev/null || true

# Option 2: Keep their rename: git checkout --theirs other-name.js git add other-name.js git rm new-name.js 2>/dev/null || true git rm old-name.js 2>/dev/null || true

# Option 3: Keep both versions: git checkout --ours new-name.js git checkout --theirs other-name.js git add new-name.js other-name.js

# Resolve content conflict in renamed file: # Edit the file with conflict markers # Then add: git add renamed-file.js

# For complex renames: git status --porcelain # R old.js -> new.js # R old.js -> other.js

# Check what Git knows about renames: git ls-files -s ```

Step 9: Abort or Complete the Merge

Finalize the merge or abort if needed.

```bash # Check if all conflicts resolved: git diff --name-only --diff-filter=U # Should return nothing

# Stage all resolved files: git add .

# Verify staging: git status

# Complete the merge: git commit -m "Merge branch 'feature-branch' into main"

# Or let Git create default message: git commit # Git will show merge message with conflict info

# If merge message editor appears, save and close

# Verify merge completed: git log --oneline -3 git status

# If you want to abort the merge: git merge --abort

# For rebase: git rebase --abort

# If abort doesn't work (broken state): git reset --hard HEAD

# After abort, verify clean state: git status

# If stuck in merge state: rm -rf .git/MERGE_* rm -rf .git/rebase-merge git reset --hard HEAD

# After completing merge, push: git push origin main ```

Step 10: Prevent and Manage Future Conflicts

Set up workflows to minimize conflicts.

```bash # Merge frequently to reduce conflicts: git checkout main git pull origin main git checkout feature-branch git merge main # Resolve any conflicts git push origin feature-branch

# Use rebase instead of merge for cleaner history: git checkout feature-branch git fetch origin git rebase origin/main

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

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

# View rerere status: git rerere status

# Clear rerere cache: git rerere gc

# Use git rerere to train resolutions: git rerere diff

# Set up merge driver for specific files: # In .gitattributes: ```

bash
*.json merge=json-driver

```bash # In .git/config: [merge "json-driver"] name = JSON merge driver driver = git merge-file -L mine -L base -L theirs %O %A %B

# Use lock files for package managers: # Always commit package-lock.json, yarn.lock, etc.

# Communicate with team about file ownership: # Document which files different teams work on

# Create feature flags instead of long-lived branches

# Use smaller, focused branches: # Instead of one large feature branch, use multiple small ones

# Set up branch protection rules: # Require branches to be up-to-date before merging

# Use git merge --no-commit to preview: git merge --no-commit feature-branch # Review changes git merge --abort # If conflicts are too complex ```

Checklist for Resolving Git Merge Conflicts

StepActionCommandStatus
1Identify all conflicted filesgit diff --name-only --diff-filter=U
2Understand the conflictgit diff HEAD MERGE_HEAD -- file
3Resolve simple text conflictsEdit file, remove markers
4Use git commands for quick resolutiongit checkout --ours/--theirs file
5Use merge tools for complex conflictsgit mergetool
6Handle binary file conflictsChoose version, git add
7Resolve delete/modify conflictsgit add or git rm
8Resolve rename conflictsChoose rename, resolve content
9Complete or abort mergegit commit or git merge --abort
10Prevent future conflictsMerge frequently, use rerere

Verify the Fix

After resolving merge conflicts:

```bash # 1. No unresolved conflicts git diff --name-only --diff-filter=U # Should return empty

# 2. Clean status git status # Should show "all conflicts fixed"

# 3. All files staged git diff --cached --stat # Should show all resolved files

# 4. No conflict markers remain grep -r "<<<<<<< HEAD" . --include="*.js" --include="*.ts" # Should return nothing

# 5. Merge commit created git log --oneline -3 # Should show merge commit

# 6. Code compiles/works npm test # Or equivalent for your project

# 7. Branch is clean git branch -v # No "merging" indicator

# 8. Can push successfully git push origin main # No errors

# 9. Tests pass npm test # All tests pass

# 10. Application works # Run and test the application ```

  • [Fix Git Cherry Pick Failed](/articles/fix-git-cherry-pick-failed) - Cherry-pick conflicts
  • [Fix Git Rebase Conflict](/articles/fix-git-rebase-conflict) - Rebase issues
  • [Fix Git Push Rejected](/articles/fix-git-push-rejected) - Push conflicts
  • [Fix Git Detached HEAD State](/articles/fix-git-detached-head-state) - HEAD issues
  • [Fix Git Stash Conflict](/articles/fix-git-stash-conflict) - Stash conflicts
  • [Fix Git Reset Wrong Commit](/articles/fix-git-reset-wrong-commit) - Reset issues
  • [Fix Git Clone Failed](/articles/fix-git-clone-failed-repository-not-found) - Clone problems