skip to content

In Git, what does the first parent of a merge commit mean for git log --first-parent?

level: seniorimportance: should knowfreq 40%

answer

  1. Parent order encodes which branch received the merge
  2. One edge is followed, the rest are skipped
  3. Gives one line per landed change
  4. Same number used when reverting a merge
  5. Breaks down if merges run both directions

basics

~20 s

A merge commit's first parent is the branch that received the merge. git log --first-parent follows only that edge, so it shows one entry per merge and hides the individual commits from merged branches — the integration history of the receiving branch.

solid answer

~50 s

When `git merge` runs, the commit you had checked out becomes parent 1 and the incoming branch becomes parent 2. `git log --first-parent` walks only parent-1 edges, so on an integration branch it reads as a list of merges — one line per change that landed — instead of every commit from every contributor interleaved by date. That makes it the practical way to answer "what landed on `main`, in what order", and `git rev-list --count --first-parent main` gives a monotonically increasing count you can use as a build number. The same convention drives `git revert -m 1 <merge>`, where the mainline number says which parent's history to keep. The catch is that first-parent order is only meaningful if merges consistently run in one direction; merging `main` back into a feature branch inverts the roles for that commit.

code

bash · 4 lines
bash
git log --first-parent --oneline main
git rev-list --count --first-parent main
git rev-parse --short 'main^1' 'main^2'
git show -m HEAD

go deeper

for a junior

Know that a merge commit has two parents and that the first one is the branch you were on when you merged. The command detail can wait.

for a middle

Explain what the traversal does — follow parent 1 only — and why that turns a noisy log into one entry per merge on an integration branch.

for a senior

Demonstrate operating with it: reconstructing what landed on a release branch, choosing the mainline number when reverting a merge, and diagnosing why a first-parent log became useless.

for a principal

Own the merge-direction convention that makes first-parent history trustworthy, and decide what your organisation derives from it — build numbers, release notes, audit trails.

## Parent order is data, not decoration A merge commit lists its parents in a defined order. Parent 1 is whatever `HEAD` pointed at when the merge was created — the branch doing the receiving. Parent 2 (and 3, 4… in an octopus merge) is each branch being merged in. Because that order is part of the commit's content, it is hashed into the commit ID and is stable forever. This is the one piece of branch identity the DAG actually preserves. Branch *names* are ephemeral refs that can be deleted or renamed, and commits do not record them. But "this commit was merged into that line of development" survives, encoded as an edge position. ## What --first-parent does `git log --first-parent` restricts the traversal: at each merge it follows only parent 1 and never descends into the merged branch. On a repository where work lands via merges into `main`, the result is a compact integration log — one entry per merge — rather than the default view, which interleaves every commit from every side. Related uses of the same walk: - `git rev-list --count --first-parent main` counts integration steps. Unlike a plain commit count, it does not jump by the size of whichever branch merged last, which is why it is a usable build counter. - `git log --first-parent -p` shows the *net* change each merge introduced to the receiving branch, which is often what a reviewer of `main` wants. - `git blame --first-parent` attributes lines to the merge that brought them in, rather than to the original author's commit on a side branch. ## Where it matters in production The question "what actually changed on the release branch between Tuesday and Thursday" is unanswerable from a default log of a busy repository, because hundreds of side-branch commits are mixed in by date order — and dates are unverified metadata, so even the ordering can lie. The first-parent view answers it directly. It also underpins reverting merges. `git revert -m 1 <merge>` undoes the merge by treating parent 1 as the mainline to keep — that is, it removes what the second parent brought in. Getting the mainline number wrong reverts the wrong half. Resolving the parents with `git rev-parse <merge>^1` and `^2` before choosing is the safe habit. ## The precondition: consistent merge direction All of this depends on a discipline the DAG cannot enforce. If someone regularly merges `main` *into* a long-lived feature branch, then on those merge commits the feature branch is parent 1 and `main` is parent 2 — and when that branch finally merges into `main`, the first-parent chain of `main` walks into feature-branch history. The compact integration log degrades accordingly. That is why teams that care about first-parent readability standardise on "changes always merge into the integration branch, never the other way", and update feature branches by rebasing or by merging in a way that keeps the integration branch as the receiving side. ## Common misreadings First-parent is not "the older parent" or "the bigger branch" — it is purely the branch that was checked out. It is also not a filter that hides merge commits; that is `--no-merges`, which does the opposite thing. And a fast-forward update creates no merge commit at all, so it contributes nothing to the first-parent story: the receiving branch simply moves to the incoming tip, and the incoming commits become part of the first-parent chain themselves.

  • Why can git rev-list --count --first-parent make a better build number than a plain commit count?
    It increments once per integration step instead of by however many commits the merged branch happened to contain, so the number rises smoothly and predictably. It is still only monotonic on a branch whose history is not rewritten and whose merges consistently run in one direction.
  • What breaks the usefulness of a first-parent log?
    Merging the integration branch into a feature branch. On those merges the feature branch becomes parent 1, so the first-parent chain of the integration branch eventually walks into side-branch history and the compact one-line-per-change view disappears.
  • How is --first-parent different from --no-merges?
    `--first-parent` keeps merge commits and hides what they brought in, giving an integration summary. `--no-merges` drops the merge commits themselves and shows the individual contributions instead. They answer opposite questions and are rarely useful together.

It is the trunk-only view of a river: you follow the main channel downstream and ignore every tributary's internal course, while still seeing where each one joined.

saying these in an interview costs you the question

  • Thinks first parent means the older or larger branch
  • Believes --first-parent hides merge commits
  • Assumes parent order is arbitrary or by date
  • Reverts a merge without checking the mainline number
  • Expects a fast-forward update to create a merge commit

context