Complete Guide to Resolving Git Merge Conflicts

What Are Merge Conflicts and Why Do They Happen?

A merge conflict occurs when Git cannot automatically combine changes from different branches. This happens when two or more developers modify the same part of a file in different ways, and Git can't determine which version to keep.

Simple Analogy: Imagine two editors working on the same document. One changes "color" to "colour" (British spelling) while another changes the same word to "color" (American spelling). Without knowing the intent, an automated system can't decide which change is correct. This is exactly what happens with merge conflicts in Git.

Common Causes of Merge Conflicts

  • Simultaneous editing: Two developers modify the same lines in a file
  • Divergent branches: Branches that haven't been merged for a long time
  • Conflicting deletions: One developer deletes a file that another has modified
  • Binary files: Git can't merge binary files like images or compiled code
  • Structural changes: Major refactoring that changes file organization

Important: Merge conflicts are a natural part of collaborative development. They're not failures or mistakes – they're simply situations where human judgment is needed. The key is knowing how to resolve them efficiently.

Understanding Conflict Markers

When Git encounters a conflict, it inserts special markers into the affected files to show you what needs resolution. Understanding these markers is the first step to solving conflicts.

<<<<<<< HEAD
// Current branch changes
function calculateTotal(price, quantity) {
  return price * quantity * 1.08; // 8% tax
}
=======
// Incoming branch changes
function calculateTotal(price, quantity) {
  return price * quantity * 1.10; // 10% tax
}
>>>>>>> feature/tax-update

Marker Breakdown

  • <<<<<<< HEAD – Start of current branch changes (your branch)
  • ======= – Separator between conflicting changes
  • >>>>>>> branch-name – End of incoming branch changes (the branch you're merging)

Step-by-Step Conflict Resolution Guide

Step 1: Identify the Conflict

When you attempt to merge and encounter a conflict, Git will clearly tell you:

git merge feature/tax-update

# Output:
Auto-merging calculator.js
CONFLICT (content): Merge conflict in calculator.js
Automatic merge failed; fix conflicts and then commit the result.

Use git status to see all conflicted files:

git status

# Output will show:
Unmerged paths:
  (use "git add <file>..." to mark resolution)
  both modified: calculator.js

Step 2: Examine the Conflicted Files

Open each conflicted file in your editor. Look for the conflict markers we discussed earlier. Some tools can help:

# See conflicts in all files
git diff

# See conflicts in a specific file
git diff calculator.js

Pro Tip: Use git mergetool to launch a visual merge tool if you have one configured. This provides a side-by-side comparison of changes.

Step 3: Resolve Each Conflict

For each conflict marker in each file, decide how to resolve it. You have several options:

Option A: Keep Your Changes

Delete the incoming changes and conflict markers, keeping only your version:

<<<<<<< HEAD
function calculateTotal(price, quantity) {
  return price * quantity * 1.08; // 8% tax
}
=======
function calculateTotal(price, quantity) {
  return price * quantity * 1.10; // 10% tax
}
>>>>>>> feature/tax-update

Option B: Keep Their Changes

Delete your changes and conflict markers, keeping only the incoming version:

<<<<<<< HEAD
function calculateTotal(price, quantity) {
  return price * quantity * 1.08; // 8% tax
}
=======

function calculateTotal(price, quantity) {
  return price * quantity * 1.10; // 10% tax
}
>>>>>>> feature/tax-update

Option C: Create a Combined Solution

Manually edit to create a new version that incorporates the best of both changes:

<<<<<<< HEAD
function calculateTotal(price, quantity) {
  return price * quantity * 1.08; // 8% tax
}
=======
function calculateTotal(price, quantity) {
  return price * quantity * 1.10; // 10% tax
}
>>>>>>> feature/tax-update
// Combined solution with configurable tax rate
function calculateTotal(price, quantity, taxRate = 0.09) {
  return price * quantity * (1 + taxRate);
}

Option D: Use a Merge Tool

Many IDEs and dedicated tools provide visual interfaces for conflict resolution. We'll cover these in detail later.

Step 4: Mark Files as Resolved

After editing each conflicted file and removing all conflict markers, stage the files to mark them as resolved:

# Stage a specific resolved file
git add calculator.js

# Stage all resolved files at once
git add .

Check your status again – conflicted files should now show as "changes to be committed":

git status

# Output should show:
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Step 5: Complete the Merge

Once all conflicts are resolved and files are staged, commit the merge:

# Git will provide a default merge message
git commit

# Or provide your own message
git commit -m "Merge feature/tax-update with tax rate fix"

Best Practice: Write clear commit messages that explain what was merged and how conflicts were resolved. This helps team members understand the history.

Step 6: Verify the Merge

After committing, verify that everything works correctly:

# Check merge was successful
git log --oneline --graph -5

# Test your code
npm test # or your project's test command

Advanced Conflict Resolution Techniques

1. Aborting a Merge

If you're not ready to resolve conflicts, you can abort the merge and return to the previous state:

# Abort the merge completely
git merge --abort

# Alternative command (does the same thing)
git reset --merge

When to use: When you realize you need more information, want to consult with a teammate, or need to run tests before proceeding with the merge.

2. Using "Ours" and "Theirs" Strategies

For simple cases where you want to automatically choose one side:

# Keep our version in case of any conflict
git merge -X ours feature/branch

# Keep their version in case of any conflict
git merge -X theirs feature/branch

Warning: These strategies apply to ALL conflicts in the merge. Use only when you're certain about which version to keep for every possible conflict.

3. Checking Out Specific Versions

During a conflict, you can check out specific versions of a file:

# Keep the version from our branch
git checkout --ours conflicted-file.js

# Keep the version from their branch
git checkout --theirs conflicted-file.js

# Restore file to pre-merge state (abandon both)
git checkout -m conflicted-file.js

Merge Conflict Resolution Tools

Several tools can make conflict resolution easier, especially for complex cases:

Git Built-in Mergetool

Launch with git mergetool. Configurable to use various diff tools like vimdiff, kdiff3, or meld.

✓ Built-in, no installation needed
✓ Works in terminal environments

✗ Can be complex for beginners
✗ Limited visual interface

GitHub/GitLab Web Interface

Resolve conflicts directly in pull/merge requests before merging.

✓ No local setup required
✓ Great for simple conflicts
✓ Collaborative resolution

✗ Limited to supported file types
✗ Can't run tests locally

VS Code

Built-in Git integration with visual conflict resolution interface.

✓ Excellent visual interface
✓ Accept/combine changes with clicks
✓ Integrated with editor

✗ Requires VS Code installation
✗ Less powerful for binary files

Meld / KDiff3

Dedicated visual diff and merge tools for complex scenarios.

✓ Powerful three-way merging
✓ Handles binary files well
✓ Advanced comparison features

✗ Separate application
✗ Steeper learning curve

Setting Up VS Code for Conflict Resolution

VS Code provides one of the best conflict resolution experiences:

  1. Open a file with merge conflicts in VS Code
  2. Conflict sections are highlighted with "Current Change" and "Incoming Change" buttons
  3. Click buttons to accept one side or combine changes
  4. Use the "Merge Editor" for complex conflicts (Enable with git.mergeEditor setting)
# VS Code commands for Git conflicts
// Accept Current Change
Cmd+Shift+. (Mac) / Ctrl+Shift+. (Win)
// Accept Incoming Change
Cmd+Shift+, (Mac) / Ctrl+Shift+, (Win)
// Accept Both Changes
Cmd+Shift+/ (Mac) / Ctrl+Shift+/ (Win)

Preventing Merge Conflicts

While you can't eliminate conflicts entirely, these strategies can reduce their frequency and severity:

1. Communicate with Your Team

Regular sync-ups and clear assignment of files/modules prevent simultaneous editing.

Team Practice: Use project management tools to track who's working on what. Announce when you start modifying shared files.

2. Keep Branches Short-Lived

The longer a branch exists without merging, the more likely conflicts become.

# Regularly merge main into your feature branch
git checkout feature/my-feature
git merge main

3. Make Small, Focused Commits

Small changes are easier to merge than large, sweeping modifications.

Guideline: Commit at logical breaking points. Each commit should represent one complete thought or change.

4. Use Consistent Code Style

Automated formatting (Prettier, ESLint) reduces "false conflicts" from formatting differences.

# Set up pre-commit hooks for formatting
# In .git/hooks/pre-commit:
npx prettier --write .

5. Consider Alternative Workflows

Rebase instead of merge, or use trunk-based development with feature flags.

# Rebase your feature branch onto main
git checkout feature/my-feature
git rebase main

Note: Rebasing rewrites history, which can cause issues on shared branches. Use only on your local branches.

Special Cases and Edge Scenarios

Binary File Conflicts

Git can't merge binary files (images, PDFs, compiled code). You must choose one version:

# Check out our version of the image
git checkout --ours logo.png

# OR check out their version
git checkout --theirs logo.png

# Then mark as resolved
git add logo.png

Deleted/Modified Conflicts

When one branch deletes a file that another modifies:

git status

# Output might show:
deleted by us: old-file.js
deleted by them: old-file.js

# Decide: keep modified file or accept deletion
git add old-file.js # to keep
# OR
git rm old-file.js # to delete

Submodule Conflicts

When Git submodules point to different commits:

# Update submodule to specific commit
git submodule update --init

# Manually resolve in .gitmodules file
# Then commit the resolved state
git add .gitmodules
git add path/to/submodule

Practical Command Reference

Conflict Detection and Status

# Check for unmerged paths (conflicts)
git status

# See conflict differences
git diff --name-only --diff-filter=U

# List all files with conflicts
git ls-files -u

Conflict Resolution

# Launch visual merge tool
git mergetool

# Keep our version of all conflicts
git checkout --ours .

# Keep their version of all conflicts
git checkout --theirs .

# Mark all conflicts as resolved
git add .

Merge Control

# Abort merge attempt
git merge --abort

# Continue after resolving conflicts
git merge --continue

# Skip this commit (cherry-pick/rebase)
git rebase --skip
Test Your Understanding: What command shows you which files have merge conflicts?
  • git status
  • git merge --status
  • git conflict list
  • git show conflicts

Frequently Asked Questions

How do I know if I've resolved all conflicts correctly?

After resolving conflicts and staging files, run git status. If you see:

  • "All conflicts fixed but you are still merging" – Good! Ready to commit
  • "Unmerged paths" – Still have conflicts to resolve
  • No mention of merging – You might have exited the merge state

Additional checks:

  1. Search for conflict markers: grep -r '<<<<<<<' .
  2. Run your test suite to ensure functionality
  3. Build your project to catch compilation errors
  4. Manually test affected features
What's the difference between git merge --abort and git reset --merge?

Both commands abort a merge in progress, but with subtle differences:

  • git merge --abort: Specifically designed to abort merges. It attempts to reconstruct the pre-merge state exactly.
  • git reset --merge: A more general command that resets to HEAD while preserving uncommitted changes. Works for merges but also other operations.

Practical advice: Use git merge --abort for merge-specific aborting. It's clearer and handles edge cases better. Use git reset --merge when you need more control or are aborting other operations.

Both will leave your working directory in a state where you can start over with the merge or choose a different approach.

Can I prevent certain files from ever causing merge conflicts?

Yes, for certain types of files:

  1. Generated files: Add build artifacts, compiled code, and auto-generated files to .gitignore
  2. Config files: Use templates (e.g., config.template.js) and ignore the actual config files
  3. Lock files: For package managers, consider whether to commit lock files based on team policy
  4. Personal IDE files: Add .vscode/, .idea/ to .gitignore

For unavoidable shared files:

  • Use structured formats (JSON, YAML) over unstructured
  • Implement conflict-free data types where possible
  • Break large files into smaller, modular files
  • Use "ours" merge strategy for auto-generated files: git merge -X ours
How should teams handle conflict resolution in code reviews?

Effective conflict resolution in teams requires process:

  1. Pre-review synchronization: Require developers to merge main into their branch before review
  2. Reviewer guidance: Reviewers should suggest specific resolutions for conflicts they foresee
  3. Pair resolution: For complex conflicts, pair the author with a reviewer to resolve together
  4. Document decisions: Record why certain resolutions were chosen in PR descriptions or comments
  5. Post-merge verification: After merging conflicted PRs, run extended tests to catch issues

Tool recommendations:

  • Use GitHub/GitLab's web-based conflict resolution for simple cases
  • Set up CI to detect and warn about potential conflicts early
  • Use "require up-to-date branches" setting in repository settings
What should I do if I can't decide how to resolve a conflict?

When you're unsure how to resolve a conflict:

  1. Abort and investigate: Use git merge --abort to return to a clean state
  2. Consult the authors: Talk to the developers who made the conflicting changes
  3. Check git blame: Use git blame file.js to see who wrote each line and why
  4. Examine commit history: Look at the full context of both changes with git log --oneline -10 file.js
  5. Test both versions: Check out each version separately and test which works better
  6. Schedule a team discussion: For architectural conflicts, involve relevant team members
  7. Consider a third option: Sometimes the best solution is neither original version

Remember: It's better to take time for a good resolution than to rush a bad one. Complex conflicts often reveal underlying design issues that should be addressed.

Are there tools that can automatically resolve simple conflicts?

Yes, several tools can help with automatic or assisted resolution:

  • git rerere (Reuse Recorded Resolution): Learns from your conflict resolutions and reuses them
  • Prettier/Formatters: Auto-format code to eliminate formatting-only conflicts
  • Semantic merge tools: Understand code structure (e.g., SemanticMerge, Plastic SCM)
  • CI/CD automation: Scripts that detect and attempt simple resolutions

Setting up git rerere:

# Enable rerere globally
git config --global rerere.enabled true

# Automatically stage resolutions
git config --global rerere.autoupdate true

# View recorded resolutions
git rerere diff

Warning: Automatic tools work best for trivial conflicts (whitespace, imports). Always review automatic resolutions before committing.

Previous: Git Merging Next: Git Rebasing