By admin , 28 April 2026

Introduction

A merge combines two lines of development. The most common case is bringing a feature branch back into main. This page walks through a clean merge, a fast-forward, and a conflicted merge.

Setting up

git switch -c feature/greeting
echo "hello world" > greeting.txt
git add greeting.txt
git commit -m "Add greeting"
git switch main
git merge feature/greeting

If main has not moved since the branch was created, Git fast-forwards: it simply moves main's pointer to feature/greeting's tip. No merge commit is created.

True three-way merges

If both branches have new commits, Git creates a merge commit with two parents:

git switch main
echo "main change" >> notes.txt
git commit -am "Update notes on main"
git merge feature/greeting
# Merge made by the 'ort' strategy.

The merge commit's first parent is the previous HEAD of main; its second parent is the tip of the merged branch.

Forcing a merge commit

To preserve history visibly even when fast-forward is possible:

git merge --no-ff feature/greeting -m "Merge feature/greeting"

Conflicts

If both sides edited the same lines, Git stops and writes conflict markers:

<<<<<<< HEAD
line from main
=======
line from feature
>>>>>>> feature/greeting

Resolve by editing the file, then:

git add notes.txt
git status
git commit               # uses the prepared merge message

Aborting

If you change your mind mid-merge:

git merge --abort

This restores the working tree to the pre-merge state.

Inspecting the result

git log --oneline --graph --decorate -n 10

The graph shows the diamond shape created by the merge.

Inspecting a merge

Merge commits have two parents, so diffs need explicit handling. git show on a merge defaults to a "combined diff" that highlights only changes that conflict with both parents:

git show HEAD                       # combined diff
git show -m HEAD                    # diff against each parent separately
git show --first-parent HEAD        # against the first parent only
git log --merges --oneline          # only merge commits
git log --no-merges --oneline       # excluding merge commits

For code archaeology, --first-parent on a long-running branch reveals the integration story: when each feature landed, in chronological trunk order.

Common mistakes

Editing files during a merge but forgetting to git add them, so git commit still complains about unmerged paths. Always run git status after editing. Resolving by deleting the conflict markers but leaving stale text from one side; build and run tests before committing the merge. Using --no-ff on every merge in a fast-moving repo, polluting history with empty merge commits. Finally, panicking at the prompt and running git reset --hard mid-merge; git merge --abort is safer because it knows about ORIG_HEAD.