What you'll learn
Quick Answer
A merge conflict happens when two branches change the same lines of a file and Git cannot decide which version to keep. Git marks the file with conflict markers showing your version and theirs. You resolve it by editing the file into the state you actually want, deleting the markers, staging it with git add and committing. If you would rather start over, git merge --abort restores everything to before the merge.
What a Conflict Actually Is
Git merges automatically most of the time. Two people editing different files, or different parts of the same file, merge without you noticing.
A conflict occurs only when the same lines were changed on both branches, or one branch deleted a file the other modified. Git will not guess, so it stops and asks.
$ git merge feature-login
Auto-merging src/app.js
CONFLICT (content): Merge conflict in src/app.js
Automatic merge failed; fix conflicts and then commit the result.Nothing is broken and nothing is lost. Your work is intact, their work is intact, and the repository is paused mid-merge waiting for a decision.
Check the state at any point with:
git statusIt lists files under Unmerged paths — those are the ones needing your attention. Files that merged cleanly are already staged.
Reading the Conflict Markers
Git rewrites the conflicting region of the file with markers:
<<<<<<< HEAD
const greeting = "Hello";
=======
const greeting = "Hi there";
>>>>>>> feature-loginRead it as three parts:
- Between
<<<<<<< HEADand=======— the version on the branch you are currently on, usually yours. - Between
=======and>>>>>>>— the version from the branch being merged in. - The label after
>>>>>>>— which branch or commit that side came from.
One point that confuses people during a git pull: HEAD is your local branch and the incoming side is the remote. During a rebase, however, the labels are reversed — HEAD is the upstream branch and the incoming side is your own commit being replayed. If a rebase conflict looks backwards, that is why.
You can also ask Git to show the common ancestor, which makes it much clearer who changed what:
git checkout --conflict=diff3 <file>
Resolving It Step by Step
The resolution is ordinary text editing. Produce the file you actually want.
1. Open each conflicted file and decide, for each conflict, whether you want your version, their version, both, or something new. Combining is common and perfectly valid:
// After editing — markers removed, both changes kept
const greeting = "Hi there";
const farewell = "Goodbye";2. Delete every marker line. Leaving a <<<<<<< or ======= behind is the classic mistake and produces a syntax error. Search the file for <<<<<<< before moving on.
3. Test the result. Run the code. A merge that compiles is not necessarily a merge that works — you may have kept two halves of incompatible changes.
4. Stage and commit:
git add src/app.js
git status # confirm nothing is still unmerged
git commit # opens with a prepared merge messageIf you want one side wholesale, Git can do it for you rather than editing by hand:
git checkout --ours src/app.js # keep the current branch's version
git checkout --theirs src/app.js # keep the incoming version
git add src/app.jsUse these deliberately — they discard the other side entirely for that file.
Getting Out of a Merge You Do Not Want
Nothing here is irreversible, which is worth knowing before you start experimenting.
git merge --abortThis cancels the merge and restores your branch exactly as it was. Use it whenever the conflict is larger than expected and you want to prepare properly first.
For a rebase, the equivalents are:
git rebase --abort # cancel entirely
git rebase --continue # after resolving the current conflict
git rebase --skip # drop the commit being appliedIf you resolved a file badly and want to start that file again:
git checkout --merge src/app.js # restore the conflict markersAnd if you have already committed a merge and want to undo it:
git reset --hard HEAD~1 # discards the merge commit AND working changesBe careful with that last one — --hard discards uncommitted work. If you are unsure, commit or stash first. git reflog records where every branch has pointed and can recover almost anything, which is the real safety net in Git.
Preventing Conflicts
Conflicts are a workflow symptom more than a Git problem.
- Pull frequently. Two hours of divergence conflicts far less than two weeks. Most painful conflicts are long-lived branches meeting reality.
- Keep branches small and short-lived. A branch touching four files for two days rarely conflicts badly.
- Communicate on shared files. Config files, route tables and dependency manifests are conflict magnets because everyone edits them.
- Commit formatting separately. A commit that reformats a file and also changes logic conflicts with everything. Keep whitespace changes in their own commit.
- Agree on a formatter. Prettier or Black in the project removes the whole category of conflicts caused by differing editor settings.
Two Git features worth turning on. rerere records how you resolved a conflict and replays it if the same one appears again, which is genuinely useful during long rebases:
git config --global rerere.enabled trueAnd a merge tool gives you a three-way visual view instead of raw markers:
git mergetoolVS Code has this built in — conflicted files show Accept Current, Accept Incoming and Accept Both buttons above each conflict, which is considerably less error-prone than deleting markers by hand.
