
Conflicts mean Git found two versions of the same line and needs you to pick one.
Two people edited the same code. Or someone deleted a file another person changed. Git stops and waits for your decision.
You'll fix most conflicts in under two minutes once you know the pattern.
What a conflict looks like
Git marks the file with conflict boundaries:
<<<<<<< HEAD
const API_URL = "https://api.prod.com";
=======
const API_URL = "https://api.staging.com";
>>>>>>> feature-branch
Three sections:
<<<<<<< HEADto=======: your current branch=======to>>>>>>>: the incoming branch
Pick one version, combine both, or write something new. Delete the markers when done.
When conflicts happen
You'll see conflicts during:
git mergegit rebasegit pullgit cherry-pickgit stash pop
Same trigger each time: two branches changed the same lines in different ways.
Step by step resolution
1. See the conflicted files
git status
Conflicted files show as "both modified" or "both added".
Unmerged paths:
both modified: src/config.js
both modified: src/api/client.js
2. Open the file
Find the conflict markers. One file can have multiple conflicts.
function getUser(id) {
<<<<<<< HEAD
return fetch(`/api/users/${id}`);
=======
return axios.get(`/api/users/${id}`);
>>>>>>> feature-branch
}
3. Decide what to keep
Option A: Keep yours (HEAD)
function getUser(id) {
return fetch(`/api/users/${id}`);
}
Option B: Keep theirs (incoming)
function getUser(id) {
return axios.get(`/api/users/${id}`);
}
Option C: Combine both
function getUser(id) {
// Using axios instead of fetch for better error handling
return axios.get(`/api/users/${id}`);
}
Option D: Write something new
function getUser(id) {
return httpClient.get(`/users/${id}`);
}
Delete the markers. The file should compile.
4. Mark as resolved
git add src/config.js
5. Continue
For merge:
git commit
For rebase:
git rebase --continue
For cherry-pick:
git cherry-pick --continue
Tools
VS Code
VS Code shows clickable buttons above each conflict:
- "Accept Current Change"
- "Accept Incoming Change"
- "Accept Both Changes"
- "Compare Changes"
TIP
"Compare Changes" opens a side-by-side diff. Use it for anything beyond a one-line conflict.
Git mergetool
git mergetool
Configure your editor:
- VS Code:
git config --global merge.tool vscode - Vim:
git config --global merge.tool vimdiff - Meld:
git config --global merge.tool meld
Lazygit
Lazygit handles conflicts in a visual interface. Arrow keys to navigate, shortcuts to pick versions. Article 8 covers this.
Conflict patterns
Different values
<<<<<<< HEAD
const timeout = 5000;
=======
const timeout = 10000;
>>>>>>> feature
Pick the right value.
Added vs modified
One branch added lines, another modified nearby code.
function init() {
<<<<<<< HEAD
setupLogging();
setupDatabase();
=======
setupDatabase();
setupCache();
>>>>>>> feature
You want all three:
function init() {
setupLogging();
setupDatabase();
setupCache();
}
Delete vs modify
One branch deleted a file, another modified it.
git status
# deleted by us: src/old-utils.js
Your choices:
- Keep the deletion:
git rm src/old-utils.js - Keep the file:
git add src/old-utils.js
Rename conflicts
Both branches renamed the same file to different names.
git status
# both added: src/utils/helpers.js
# both added: src/lib/helpers.js
Pick one location, delete the other.
Reducing conflicts
Pull often
git pull origin main
Longer divergence means more conflicts.
Keep branches short-lived Merge in days, not weeks.
Talk to your team Two people editing the same file? One waits, or you split the work.
Use feature flags Ship behind flags instead of long-running branches.
NOTE
Some conflicts will happen in any codebase with multiple contributors. That's normal.
Aborting
Want to start over?
# Abort a merge
git merge --abort
# Abort a rebase
git rebase --abort
# Abort a cherry-pick
git cherry-pick --abort
You're back to before you started.
WARNING
After you commit a merge, abort won't work. Use git reset --hard HEAD~1 instead.
Conflicts during rebase
Rebase replays commits one by one. You might hit conflicts multiple times.
git rebase main
# CONFLICT in file.js
# Fix the conflict
git add file.js
git rebase --continue
# Another conflict in the next commit
# Fix again
git add file.js
git rebase --continue
# Done
Too painful? Switch to merge:
git rebase --abort
git merge main
🔍 Why rebase triggers more conflicts
Merge compares two endpoints and combines them once.
Rebase applies your commits one at a time onto the new base. Commit 1 might conflict. You fix it. Commit 2 might conflict with the now-modified file. You fix it again.
For branches with many commits touching the same files, you can end up resolving similar conflicts multiple times. Merge handles it in one pass.
After resolving
Before you commit:
# Check what you're about to commit
git diff --staged
# Run tests
npm test
# Build
npm run build
TIP
A bad resolution can break the build even when it looks correct. Test after every conflict.
Cheat sheet
| Command | What it does |
|---|---|
git status | See conflicted files |
git diff | See conflict details |
git add <file> | Mark as resolved |
git merge --abort | Cancel merge |
git rebase --abort | Cancel rebase |
git rebase --continue | Continue after fixing |
git mergetool | Open visual merge tool |
The takeaway
Git asks for your input when it can't decide. Look at both versions, understand what each change intended, pick one. Done.
The more you resolve, the faster you get. After a few dozen, it's muscle memory.
Next article: exploring history with log, diff, and blame.
This is part 4 of my "Git & GitHub in 10 articles" series.