Git & Command Line · Lesson 6 of 12
Resolving Merge Conflicts
Understand why Git merge conflicts happen, read conflict markers, resolve them by hand or with tools, and abort or avoid conflicts on a team.
- Intermediate
- 15 min read
- 4 objectives
Before this lessonLesson 5: Branching and Merging
What you will learn
- Explain when and why conflicts happen
- Read conflict markers and diff3 style
- Resolve, continue or abort a merge
- Reduce conflicts on a team
Your Progress
0 of 12 lessons 0%
- Lessons0 / 12
- Completed0
- Est. time left~ 3 hours
Create a free account to keep your progress on every device.
Tip: pressing Next marks this lesson complete automatically.
The branching lesson showed that conflicts exist. This lesson is the hands-on guide for the moment one appears. A conflict is not an error and nothing is broken: Git is simply refusing to guess when two people changed the same lines in different ways. You make the decision, and Git records it.
Why conflicts happen
When merging, Git compares three versions of each file: the base (the last common commit), ours (the branch you are on) and theirs (the branch you are merging in). If only one side changed a region, Git takes that change. If both sides changed the same lines differently, or one side deleted a file the other edited, Git stops and asks you.
Creating a real conflict
Here main and price-update both edit the same line of README.md:
git switch -c price-update
sed -i '' 's/10/12/' README.md && git commit -am "Raise price to 12"
git switch main
sed -i '' 's/10/15/' README.md && git commit -am "Raise price to 15"
git merge price-updateAuto-merging README.md CONFLICT (content): Merge conflict in README.md Automatic merge failed; fix conflicts and then commit the result.
(On Linux, write sed -i without the empty ''.) The merge is now paused. git status tells you exactly where you are and what to do next:
git statusOn 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: README.md no changes added to commit (use "git add" and/or "git commit -a")
Reading the markers
cat README.md# Stackcone <<<<<<< HEAD Price: 15 ======= Price: 12 >>>>>>> price-update
- Between
<<<<<<< HEADand=======is your version (the current branch). - Between
=======and>>>>>>> price-updateis their version. - Everything outside the markers merged cleanly and needs no attention.
Turn on the diff3 style and Git also shows the original base text, which often makes the right answer obvious because you can see what each side intended to change:
git config --global merge.conflictStyle zdiff3<<<<<<< HEAD
Price: 15
||||||| 4aac871
Price: 10
=======
Price: 12
>>>>>>> price-updateResolving: edit, add, commit
Open the file, decide what the final text should be (yours, theirs, or a combination) and delete every marker line. Then tell Git the file is resolved with git add and finish with git commit:
printf "# Stackcone\nPrice: 12\n" > README.md # the agreed price
git add README.md
git commit --no-edit
git log --oneline --graph* d0603b9 Merge branch 'price-update' |\ | * b4bdf9e Raise price to 12 * | 19aa68a Raise price to 15 |/ * 4aac871 Add README
Shortcuts: take one side for a whole file
For generated files such as package-lock.json you often want one side entirely, then regenerate:
git checkout --theirs package-lock.json # take the incoming version
git checkout --ours config.yml # keep our version
npm install # regenerate the lock file
git add package-lock.json config.ymlCareful: during a rebase, "ours" and "theirs" are swapped, because Git is replaying your commits on top of the other branch. "Ours" is the branch you are rebasing onto.
Using a merge tool
Editors show conflicts visually. VS Code highlights each block with buttons: Accept Current, Accept Incoming, Accept Both, and a three-way merge editor. JetBrains IDEs have a similar three-pane view. You can also launch a configured tool from the terminal:
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait --merge $REMOTE $LOCAL $BASE $MERGED'
git mergetoolGetting out: abort
If the conflict is bigger than expected, or you merged the wrong branch, go back to exactly where you were before the merge started. Nothing is lost:
git merge --abort
# the same idea exists for other operations:
git rebase --abort
git cherry-pick --abortFewer conflicts on a team
- Keep branches short-lived and merge or rebase from
mainoften; small drift means small conflicts. - Make focused commits: a formatting change mixed with a logic change touches every line.
- Agree on a formatter (Prettier, Black, gofmt) so whitespace never conflicts.
- Enable
rerere.enabledso Git reuses your earlier resolution if the same conflict reappears. - Talk: if two people are rewriting the same module, a two-minute chat beats an hour of merging.
Recap
- A conflict means both sides changed the same lines; Git asks you to decide.
- Markers show HEAD (yours) and the incoming branch (theirs); zdiff3 also shows the base.
- Resolve by editing, then
git addandgit commit(or--continue). --ours/--theirspick a whole side; they swap meaning during rebase.git merge --abortalways gets you back safely.
# Write your solution here
Finished reading? Mark this lesson complete to track your progress.
