DEV Community

Cover image for My git changelog generator learned that merge commits hide the truth — parents are a graph, not a line
InApp
InApp

Posted on Originally published at imapp.blogspot.com

My git changelog generator learned that merge commits hide the truth — parents are a graph, not a line

I built a git changelog generator that diffs two commits and emits structured release notes. First version walked git log A..B and treated every commit as a straight line. Clean, deterministic, wrong.

The bug: on a repo with a normal PR workflow, git log A..B on the main branch shows merge commits but NOT the individual feature commits they brought in. My changelog would say "Merge pull request #42" and nothing else. Humans reading the release notes got zero signal about what actually shipped.

The fix wasn't a bigger diff. It was changing what I walk. I switched to git log --first-parent A..B for the branch-level summary, then for each merge commit, git log ^parent1 parent2 to enumerate the commits that merge actually introduced. The changelog now shows: the merge as a structural event, then the real commits underneath it, deduped against anything already on the base.

The deeper lesson: git history is a DAG, not a list. Any tool that assumes linearity is lying to you, and the lie is invisible until someone reads your release notes and asks "wait, what changed in v2.3?" The graph is the source of truth. --first-parent is a view. Walking both and reconciling them is the only honest answer.

I also learned that git log A..B is symmetric in a way that tricks you — it's "reachable from B but not A", which on a graph includes commits from branches that were merged and later reverted. If you want "what shipped", you need reachability from B, minus reachability from A, intersected with first-parent edges, plus the side-branch commits each merge dragged in. That's not a one-liner. It's a small graph traversal.

Shipped as a deterministic diff at https://x402.freeq.one/tools/changelog_git.html — no LLM rewriting your history, just the graph doing what the graph does.

Top comments (0)