Lesson 7 of 30
git merge does one of two completely different things depending on history shape — a fast-forward that just slides a pointer, or a true merge that creates a new commit with two parents.
14 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
git merge <branch> brings another branch's commits into the one you're on — but the mechanism it uses depends entirely on where the two branches' histories stand relative to each other, and that distinction matters more than most people realize.
A fast-forward merge happens when the branch you're merging in is simply ahead of the branch you're merging into, with no divergence — your current branch's commit is a direct ancestor of the other branch's tip. In that case there's nothing to actually combine: Git just slides your current branch's pointer forward to match. No new commit is created. History stays perfectly linear, as if you'd been committing directly on the branch you're now on.
A true three-way merge happens when both branches have new commits the other doesn't have — real divergence. Git can't just move a pointer here, because there are two different sets of changes to reconcile. Instead it looks at three points: the tip of your branch, the tip of the other branch, and their common ancestor (the "merge base"), diffs each tip against that base, combines the changes, and writes a brand-new merge commit with two parent hashes instead of one. That merge commit is what makes git log --graph show a diamond shape instead of a straight line.
Fast-forward first — feature is simply ahead of main, nothing to reconcile:
$ git log --oneline --graph --all
* 9f8e7d6 (feature) add login stub
* a1b2c3d (main) initial commit
$ git switch main
$ git merge feature
Updating a1b2c3d..9f8e7d6
Fast-forward
login.js | 1 +
1 file changed, 1 insertion(+)
$ git log --oneline --graph --all
* 9f8e7d6 (HEAD -> main, feature) add login stub
* a1b2c3d initial commit
main and feature now point at the identical commit. No merge commit exists — Git literally just moved the main ref forward. Now the divergent case, where both branches gained commits after splitting:
$ git log --oneline --graph --all
* c3d4e5f (feature) tweak login copy
| * b2c3d4e (main) fix header typo
|/
* a1b2c3d initial commit
$ git switch main
$ git merge feature
Merge made by the 'ort' strategy.
login.js | 1 +
1 file changed, 1 insertion(+)
$ git log --oneline --graph --all
* f1e2d3c (HEAD -> main) Merge branch 'feature'
|\
| * c3d4e5f (feature) tweak login copy
* | b2c3d4e fix header typo
|/
* a1b2c3d initial commit
f1e2d3c is a real new commit with two parents (c3d4e5f and b2c3d4e). Confirm it directly:
$ git cat-file -p f1e2d3c
tree 7a8b9c0d...
parent b2c3d4e
parent c3d4e5f
author Adesh <adesh@example.com> 1710003600 +0530
Merge branch 'feature'
Two parent lines — that's the actual, structural difference from a normal commit, not just a naming convention.
git merge --no-ff.-m to pick a parent) or a reset — genuinely different operations.git log --graph with lots of diamonds isn't automatically bad, but it does make git bisect and history review harder to read. This is the core reason some teams prefer rebasing feature branches before merging — flattening the diamonds into a line — while others accept the diamonds as an honest record of what happened.Everything here follows from the previous lesson: because a branch is just a pointer, a fast-forward merge is nothing but moving that pointer, and a true merge is nothing but writing one new commit object with two parent hashes instead of one. Nothing new is invented at the storage level — merging is nowhere as magical as it feels once you already know commits form a graph, not a straight line.