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.
function calculateTotal(price, quantity) {
return price * quantity * 1.08; // 8% tax
}
function calculateTotal(price, quantity) {
return price * quantity * 1.10; // 10% tax
}
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:
# 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:
# 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:
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:
return price * quantity * 1.08; // 8% tax
}
return price * quantity * 1.10; // 10% tax
}
Option B: Keep Their Changes
Delete your changes and conflict markers, keeping only the incoming version:
return price * quantity * 1.08; // 8% tax
}
return price * quantity * 1.10; // 10% tax
}
Option C: Create a Combined Solution
Manually edit to create a new version that incorporates the best of both changes:
return price * quantity * 1.08; // 8% tax
}
return price * quantity * 1.10; // 10% tax
}
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:
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":
# 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 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:
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:
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:
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:
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:
- Open a file with merge conflicts in VS Code
- Conflict sections are highlighted with "Current Change" and "Incoming Change" buttons
- Click buttons to accept one side or combine changes
- Use the "Merge Editor" for complex conflicts (Enable with
git.mergeEditorsetting)
// 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.
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.
# In .git/hooks/pre-commit:
npx prettier --write .
5. Consider Alternative Workflows
Rebase instead of merge, or use trunk-based development with feature flags.
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:
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:
# 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:
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
git status
# See conflict differences
git diff --name-only --diff-filter=U
# List all files with conflicts
git ls-files -u
Conflict Resolution
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
git merge --abort
# Continue after resolving conflicts
git merge --continue
# Skip this commit (cherry-pick/rebase)
git rebase --skip
Frequently Asked Questions
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:
- Search for conflict markers:
grep -r '<<<<<<<' . - Run your test suite to ensure functionality
- Build your project to catch compilation errors
- Manually test affected features
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.
Yes, for certain types of files:
- Generated files: Add build artifacts, compiled code, and auto-generated files to
.gitignore - Config files: Use templates (e.g.,
config.template.js) and ignore the actual config files - Lock files: For package managers, consider whether to commit lock files based on team policy
- 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
Effective conflict resolution in teams requires process:
- Pre-review synchronization: Require developers to merge main into their branch before review
- Reviewer guidance: Reviewers should suggest specific resolutions for conflicts they foresee
- Pair resolution: For complex conflicts, pair the author with a reviewer to resolve together
- Document decisions: Record why certain resolutions were chosen in PR descriptions or comments
- 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
When you're unsure how to resolve a conflict:
- Abort and investigate: Use
git merge --abortto return to a clean state - Consult the authors: Talk to the developers who made the conflicting changes
- Check git blame: Use
git blame file.jsto see who wrote each line and why - Examine commit history: Look at the full context of both changes with
git log --oneline -10 file.js - Test both versions: Check out each version separately and test which works better
- Schedule a team discussion: For architectural conflicts, involve relevant team members
- 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.
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:
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.