DEV Community

Cover image for My First Git Merge Conflict (On Purpose)
IriAndr
IriAndr

Posted on

My First Git Merge Conflict (On Purpose)

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
CONFLICT (content): Merge conflict in greeting.txt
Automatic merge failed; fix conflicts and then commit the result.
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)