datarekha

Merging

How to fold a finished feature branch back into main safely, and what makes fast-forward merges different from true merge commits.

8 min read Intermediate Git Lesson 8 of 15

What you'll learn

  • git merge feature: run it from the target branch, not the feature branch
  • Fast-forward vs 3-way merge: what history each strategy produces and when Git chooses each
  • How to force a merge commit with --no-ff and clean up merged branches

Before you start

Setting the scene

You have been working on a feature branch. main exists in parallel. At some point the two branches diverge — or they don’t. That single fact determines which of two very different merge strategies Git uses.

Fast-forward merge

A fast-forward merge happens when the target branch (the one you want to merge into) has not received any new commits since the feature branch was created. In other words, the feature branch is simply ahead of main on a straight line.

Git has nothing to reconcile. It moves the main pointer forward to the tip of feature. No new commit is created. The word “fast-forward” is Git telling you: “I just slid the label along — I didn’t have to think.”

# You are on main, which has not moved since feature branched off
git checkout main
git merge feature
Updating 3f1a2b4..c7e9d01
Fast-forward
 src/auth.ts | 42 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

Notice the word Fast-forward in Git’s output — that is your confirmation.

3-way merge (true merge)

A 3-way merge is required when both branches have advanced since they diverged. Main has new commits; the feature branch has new commits. They share a common ancestor — the last commit that both branches have in common — but they have gone different directions since then.

Git uses three snapshots to work out what the combined result should look like:

  1. The common ancestor (the fork point)
  2. The tip of main
  3. The tip of feature

Because no single pointer move can represent both lines of work, Git creates a brand-new merge commit. A merge commit is special: it has two parents — one pointing back into main’s history and one pointing back into feature’s history. It is the only commit type that stitches two lines together.

# Both branches have new commits since they diverged
git checkout main
git merge feature
Merge made by the 'ort' strategy.
 src/payment.ts | 27 +++++++++++++++++++
 1 file changed, 27 insertions(+)

Git will open your editor so you can write (or accept the default) merge commit message. The result looks like the diagram below.

Before and after a 3-way merge

BEFOREA (ancestor)shared baseB1main tipF1feature tipAFTER git merge featureA (ancestor)shared baseB1main tipF1feature tipM (merge commit)two parents: B1 + F1← main (HEAD)

Before the merge, B1 (main) and F1 (feature) diverged from a common ancestor A. After git merge feature, Git creates merge commit M whose two parents are B1 and F1.

Forcing a merge commit with --no-ff

If the branches have not diverged, Git will fast-forward by default. Sometimes that is not what you want — you may want a permanent record in history that a feature branch existed and was merged as a unit.

--no-ff (no fast-forward) tells Git to always create a merge commit, even when a fast-forward would be possible:

git checkout main
git merge --no-ff feature
Merge branch 'feature' into main
# (editor opens for commit message)

Many teams enable this as policy so every feature shows up as a distinct bubble in the commit graph, making history easier to audit.

Cleaning up after a merge

Once the merge is done and pushed, the feature branch has served its purpose. Delete it to keep the repository tidy:

# Delete the local branch (safe — Git refuses if unmerged changes exist)
git branch -d feature

# Verify which branches are already merged into main
git branch --merged main
* main
  feature

Any branch listed by git branch --merged is safe to delete — its commits are already reachable from main. A branch that is not listed has work that has not been merged yet; -d will refuse to delete it, protecting you from accidental data loss.

Quick reference

ScenarioCommandResult
Merge feature into current branchgit merge featureFast-forward or 3-way
Always create a merge commitgit merge --no-ff featureMerge commit regardless
Delete merged branchgit branch -d featureRemoves local branch
List merged branchesgit branch --mergedSafe-to-delete list

Quick check

0/3
Q1You run `git merge feature` and Git prints `Fast-forward`. What does this tell you?
Q2What is the defining characteristic of a merge commit?
Q3Your team uses `git merge --no-ff` for every feature merge, even when fast-forward would be possible. A new teammate asks why. What is the main benefit?

Practice this in an interview

All questions

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Explore further

Cheat sheets

Related lessons

Skip to content