Lesson 4 of 7 in Branching & Merging
Merge conflicts
Story: Two timelines rewrote the same moment, and only you can say what really happened. Juno will show you how.
A merge combines what each branch changed since they split. Changes to different files combine by themselves, and so do changes to one file with an unchanged line or more between them. But when both branches changed the same part of a file in different ways, with no unchanged line between their changes, git can't know which version is right, or whether both should stay. That's a merge conflict, and git asks you to decide.
The merge stops halfway
Say main and contact both added a line at the end of index.html:
$ git merge contact
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
git has merged what it could, but it hasn't made the merge commit. The branch you're on hasn't moved, so the Live Graph shows nothing new yet. The merge is in progress, waiting for you.
git status during a merge
git status says that a merge is in progress, and which files have a conflict:
$ git status
On 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: index.html
no changes added to commit (use "git add" and/or "git commit -a")
both modified means both branches changed the file. A file the merge brought in without a conflict is already staged, and git status lists it under Changes to be committed.
If you have changes you haven't committed, git only starts a merge that isn't a fast-forward when they aren't staged and are in files the merge doesn't need to change. Otherwise it refuses and names the files in the way, and your files, the staging area and your branch stay as they were. During the merge, git status lists such changes as usual, under Changes not staged for commit. A fast-forward can't stop on a conflict, and it's less strict: it keeps staged changes too, as long as they're in files it doesn't change.
Conflict markers
In each file with a conflict, git writes both versions, between conflict markers:
<!doctype html>
<title>Home</title>
<h1>Hello</h1>
<p>Welcome to my website.</p>
<<<<<<< HEAD
<p>Open Monday to Friday.</p>
=======
<p>Write to me at hello@example.com.</p>
>>>>>>> contact
- Between
<<<<<<< HEADand=======is your side: the version on the branch you're on. - Between
=======and>>>>>>> contactis the other side: the version on the branch you're merging. - Lines outside the markers are ones git merged by itself: lines both sides agree on, and changes only one side made.
Resolving a conflict
Open the file with edit and make it what it should be: your side, the other side, or a mix of both. Then delete the three marker lines. Here, both new lines should stay, so you only delete the markers.
Next, stage the file. For a file with a conflict, git add means "this one is resolved":
$ git add index.html
$ git status
On branch main
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
modified: index.html
git doesn't look inside the file when you stage it. If the markers are still there, they go into the merge commit, so check the file first.
Once every file with a conflict is staged, git commit -m "Merge contact" makes the merge commit, with two parents, as in the last Lesson. That ends the merge. On your own computer, git commit without -m opens your editor with a ready-made message, Merge branch 'contact'. Here, give the message with -m.
Until then, git won't commit: while a file still has a conflict, git commit stops with error: Committing is not possible because you have unmerged files. And while the merge is in progress, git won't start another one.
Staging and committing in one go
git commit -a -m "<message>" stages every change to the files git tracks, then commits, as you saw in Seeing changes. When the files you've fixed are your only changes, it finishes the merge in one step. But it stages every file just as it is: a file you haven't fixed yet, markers and all, or a change of your own that isn't ready. If you have one of those, git add the fixed files by name instead.
Backing out: git merge --abort
Not ready to resolve it? git merge --abort stops the merge. It puts the files the merge changed, and the staging area, back as they were before you started. It prints nothing. The branch never moved, so nothing is lost, and you can merge again when you're ready. Changes you hadn't committed, in files the merge didn't touch, stay as they are.
--abort works until you commit. After that, the merge is done.
| Command | What it does |
|---|---|
git status |
During a merge, lists each file with a conflict under Unmerged paths. |
git add <file> |
Marks the conflict in <file> as resolved, once you've fixed the file. |
git commit -m "<message>" |
Once every conflict is resolved, makes the merge commit and ends the merge. |
git merge --abort |
Stops the merge and puts things back as they were before it. |
New commands
git merge --abort
Explainer: When both branches change the same place
Graph as text
project: your repo, you are here
- HEAD → main at 3df7ae7 "Add opening hours"
Branches and tags
- contact at 0b34fac "Add a contact line"
- main at 3df7ae7 "Add opening hours"
Commits, newest first
- 3df7ae7 "Add opening hours"; parent a4b8d97; refs: HEAD → main
- 0b34fac "Add a contact line"; parent a4b8d97; refs: contact
- a4b8d97 "Style the heading"; parent ab89288
- ab89288 "Add the home page"; parent 37d7cf7
- 37d7cf7 "Add a README"; no parent
Challenge 1 of 3
Story: Two reports of the same afternoon, Agent, and each one adds a line the other lacks. History keeps both.
Merge contact into main. Both branches added a line at the end of index.html, so the merge stops with a conflict. Keep both lines.
You've also started a change to README.md that isn't finished. Leave it out of the merge commit, as it is. You're on main.
Unlocks after Basics.
Terminal
Unlocks after Basics.
This Lesson can be played once Basics is complete. Its text and Explainer are open to everyone.