Previously, in Part 4, you split ClinicApp's
timeline into parallel branches and merged add-notes-field back into
master — and Git did it with a quiet little "fast-forward," no drama at all.
Today we deal with the drama. Merge conflicts are the number one reason people whisper "I'm scared of Git" — so we're going to cause one on purpose, in a safe playground, and discover it's about as dangerous as a sticky note. By the end of this part, a conflict will feel like Git politely asking for your opinion. Because that's exactly what it is.
Most merges are automatic
Here's the secret nobody tells beginners: the vast majority of merges involve no conflict
at all. If your branch changed Patient.cs and another branch changed
Home.razor, Git merges them without asking you anything. Even two changes in the
same file merge cleanly, as long as they touched different lines.
How? Git performs a three-way merge. It looks at three snapshots: the base (the common ancestor commit where the two branches parted ways), yours (the tip of the branch you're on), and theirs (the tip of the branch you're merging in). For every line, Git asks: "who changed this compared to the base?" If only one side changed it, that change wins automatically. Easy.
Notice the new commit at the top of the figure: a merge commit. Unlike every
commit you've made so far, it has two parents — one on each timeline. That's how Git
records "these two histories became one here." Compare that with Part 4's fast-forward, where
master simply slid its label forward along an existing line of commits and no new
commit was needed. A fast-forward is Git taking a shortcut; a three-way merge is Git tying a
knot.
Let's break something on purpose
A conflict happens in exactly one situation: both sides changed the same lines since the base. Git has no way to know which version you want — it's a tool, not a mind reader. So let's manufacture that situation in your clone of the workshop repo.
Remember the last commit on master, "Polish the home page welcome text"? We're
going to have two branches polish it again, in two different directions. First branch:
git switch master
git switch -c change-welcome-a
Open ClinicApp/Components/Pages/Home.razor, find the welcome line, and make it
warmer — something like:
<p class="lead">Welcome to ClinicApp — caring for you, one visit at a time.</p>
Save and commit:
git add .
git commit -m "Make the welcome line warmer"
Now hop back to master and create a second branch — crucially, it starts
from the old master, so it has never seen your warm version:
git switch master
git switch -c change-welcome-b
Edit the very same welcome line, but make it snappy instead:
<p class="lead">Welcome to ClinicApp! Book, browse, and breathe easy.</p>
git add .
git commit -m "Make the welcome line snappier"
Two timelines, one line, two opinions. Time to merge. The first one is uneventful — since
master hasn't moved, Git just fast-forwards, exactly like Part 4:
git switch master
git merge change-welcome-a
Now merge the second branch. Type it and watch closely:
git merge change-welcome-b
Auto-merging ClinicApp/Components/Pages/Home.razor
CONFLICT (content): Merge conflict in ClinicApp/Components/Pages/Home.razor
Automatic merge failed; fix conflicts and then commit the result.
There it is. The message that makes grown developers spill their coffee. Take a breath —
nothing is broken, nothing is lost, and every commit you've ever made is perfectly safe.
Run git status and Git even tells you what it needs: the file is listed under
"both modified," and the instructions say fix it and commit.
Reading the conflict, calmly
Open Home.razor. The welcome line has grown some strange decorations:
<<<<<<< HEAD
<p class="lead">Welcome to ClinicApp — caring for you, one visit at a time.</p>
=======
<p class="lead">Welcome to ClinicApp! Book, browse, and breathe easy.</p>
>>>>>>> change-welcome-b
Let's translate each region into plain words:
<<<<<<< HEAD— "everything from here down to the divider is what your current branch says."HEADis Git's name for wherever you're standing right now —master, which already absorbed the warm version.=======— the divider. Yours above, theirs below. That's all it is.>>>>>>> change-welcome-b— "everything from the divider up to here is what the incoming branch says," and it's even labeled with that branch's name.
That's the entire anatomy of a conflict. Git isn't broken, and it isn't angry. It merged everything it could, then stopped at the one spot where two humans disagreed and asked the only reasonable question: "You're the editor-in-chief. Which version goes to print?"
Resolving it: pick, blend, or rewrite
Your job is simply to make the file look the way it should look — and delete the marker lines. You can keep yours, keep theirs, or do what a good editor does: blend them. Let's blend:
<p class="lead">Welcome to ClinicApp — book, browse, and breathe easy.</p>
Make sure all three marker lines (<<<<<<<,
=======, >>>>>>>) are gone, save the file, then
tell Git you've made your editorial decision:
git add ClinicApp/Components/Pages/Home.razor
git commit -m "Merge change-welcome-b, blending both welcome lines"
Done. Run git log --oneline --graph and admire the little diamond shape: two
timelines splitting apart and joining again at your brand-new merge commit — the one with two
parents. You just resolved your first merge conflict, on purpose, with no casualties.
The no-shame exit
One more command for your back pocket. If you're ever mid-conflict and feel in over your head — the markers are confusing, you edited the wrong thing, whatever — you can simply walk away:
git merge --abort
This rewinds everything to the moment before you typed git merge, as if it never
happened. No harm, no lost work, no shame. Take a walk, come back, and try again when you're
ready. Knowing the exit exists is half of what makes conflicts stop being scary.
Tip: The best conflict strategy is prevention through rhythm: merge small and merge often. Two branches that drift apart for a week collide like freight trains; two branches that sync up daily bump like shopping carts. Small frequent merges mean small, easy conflicts — often none at all.
Your ClinicApp history now has branches, fast-forwards, a real merge commit, and one
successfully negotiated disagreement. But it all still lives on your machine alone — one
spilled coffee away from oblivion, and invisible to the world. In
Part 6, we put your photo album in the
cloud: GitHub, remotes, and your very first git push.