Today I did something I'd only read about before: I created a Git merge
conflict on purpose, and then resolved it by hand. I wanted to understand
exactly what happens when two branches change the same line differently —
not just memorize the commands, but actually see the process.
Branches basics
A branch in Git is just a separate line of work. It lets you make changes
without touching the main codebase until you're ready to merge them back.
To create a new branch and switch to it in one command:
git checkout -b feature/add-greeting
The -b flag tells Git to create the branch first, then switch to it.
You can confirm which branch you're on with:
git branch
The current branch is marked with an asterisk.
One thing that confused me at first: why checkout? It sounds like
something you'd do at a library, not something related to branches.
Turns out that's exactly the idea — checkout comes from a "check out
a book" metaphor. You're telling Git "give me this version of the files."
Git even introduced a newer, clearer command for this — git switch —
because checkout historically does too many different things and
confuses beginners. But checkout -b is still what most tutorials and
teams use, so it's worth knowing first.
First merge — the easy one
Once my branch had a commit, I switched back to main and merged it in:
git checkout main
git merge feature/add-greeting
I expected this to be more dramatic. Instead, Git just said "Fast-forward"
and moved on. No conflict, no drama.
Here's why: a conflict only happens when two branches diverge from the
same point and change the same content independently. My feature branch
was just sitting on top of main with nothing new happening on main in the
meantime — so Git could simply "fast-forward" main to match the branch,
no merging required.
That's when I realized: to actually see a conflict, I'd need to make main
and my branch disagree about something, on purpose.
Creating a real conflict on purpose
My first attempt to force a conflict didn't work either. I edited
greeting.txt on main, committed it, then created a new branch and
edited the same file there too:
git checkout -b feature/change-greeting
Still no conflict — another fast-forward. The reason: I'd created this
branch after the change on main, so there was still no real divergence,
just a straight line of commits.
To actually diverge, my branch needed to start from a point before the
latest change on main. I checked the commit history:
git log --oneline
and created a new branch from an earlier commit, not the latest one:
git checkout -b feature/real-conflict a0ae0d3
echo "Hello from a truly diverging feature!" > greeting.txt
git add greeting.txt
git commit -m "Diverging change from feature"
Now main and feature/real-conflict had both changed the same line of
greeting.txt, independently, from the same starting point. Merging them
finally produced what I was looking for:
git checkout main
git merge feature/real-conflict
CONFLICT (content): Merge conflict in greeting.txt
Automatic merge failed; fix conflicts and then commit the result.
Resolving the conflict
Opening the conflicted file showed something like this:
<<<<<<< HEAD
Hello from main - second edit!
=======
Hello from a truly diverging feature!
>>>>>>> feature/real-conflict
Git can't decide which version is "correct" — both are valid changes, made
independently — so it inserts both versions directly into the file,
wrapped in markers, and leaves the decision to you.
Here's what each marker means:
-
<<<<<<< HEAD— start of the version on your current branch -
=======— separator between the two versions -
>>>>>>> feature/real-conflict— end of the version from the branch you're merging in
The markers themselves aren't part of your code — they're temporary, and
Git expects you to remove them before committing. I opened the file in
nano (a simpler alternative to vim for anyone just starting out),
deleted the marker lines, decided what the final content should be, and
saved.
Then it's just a normal commit:
git add greeting.txt
git commit -m "Resolve merge conflict in greeting.txt"
Running git log --oneline --graph afterward made the whole story visible
at once — the branches splitting apart and merging back together at the
conflict resolution commit.
Pull Request workflow
Resolving conflicts locally is one thing, but in real teams, changes
usually go through a Pull Request first — a place for review before
anything reaches main. I wanted to go through that flow too, even
working solo.
After cleaning up my practice branches, I created a fresh one for an
actual feature:
git checkout -b feature/add-notes
echo "This repo is my Git practice playground." > notes.md
git add notes.md
git commit -m "Add notes about this repo"
git push origin feature/add-notes
Pushing for the first time meant dealing with authentication — GitHub
no longer accepts a regular account password for Git operations. Instead,
you need a Personal Access Token, generated from GitHub's developer
settings and used in place of a password. Once I set
credential.helper store, Git remembered it for future pushes.
The push output included a direct link to open a Pull Request. From
there, the GitHub interface shows exactly what a reviewer would see:
a diff of changed files, commit history, and a merge button — in my case,
with "No conflicts with base branch," since this change didn't overlap
with anything else.
Clicking "Merge pull request" combined the branch into main, and
GitHub offered to delete the now-unused branch. Back in the terminal,
one command brought my local copy up to date:
git checkout main
git pull origin main
What I learned
Before today, "merge conflict" was one of those phrases that sounded
scarier than it turned out to be. Once I saw what it actually looks
like — two versions of the same line, wrapped in plain markers, waiting
for a decision — it stopped being abstract.
A few things:
- Conflicts only happen when branches genuinely diverge, not just because you're working on separate branches
- The conflict markers are just text — nothing to be careful around, just something to read and clean up
- The PR workflow exists for a reason: even solo, going through it made the whole process feel less like typing commands and more like actually working the way teams do
Top comments (0)