My git changelog showed every pull request twice — a commit range isn't a change list
Last release cycle I generated notes for our own service the naive way: git log v0.9.0..v0.10.0 , format the subjects, done. The output listed 12 commits. We had merged 6 pull requests. Every feature appeared twice — once as Merge pull request #41 from feature/retry-queue and once as the actual commit subject buried inside that branch. Why: on a merge-commit repo, base..target walks both parents.…
During the latest release cycle, I produced notes for our internal service using a simple git log command: git log v0.9.0..v0.10.0. This generated 12 commit subjects. However, we had merged 6 pull requests, and every feature showed up twice - once as "Merge pull request #41 from feature/retry-queue" and once as the commit subject inside that branch. This happened because, in a merge-commit repository, the command walked both the base and target commits, including the merge commit and the branch's own commits.
To address this issue, I tried two different approaches. The first was to use the --no-merges flag, which produced cleaner results but lost the PR number and title from the notes. The second approach used --first-parent, which collapsed each PR into a single entry representing the merge commit. This worked well until a teammate made a direct push to main with two separate commits. In this case, the merge commit didn't exist, so the notes mixed polished PR entries with raw commits.
The solution that worked best was to treat merge commits as boundaries rather than entries. By walking through the first-parent commits and describing each merge as one change, I could handle the mixed merge culture without relying on a single flag. For repositories with squash-merge conventions, the naive diff already provided the perfect result, with one commit per PR.
However, I learned that a commit range doesn't necessarily produce a change list. Depending on the team's merge conventions, it could result in 2x entries, N+1 entries, or a clean list. Therefore, any changelog tooling needs to be aware of the specific convention or normalize across all three possibilities. To solve this problem permanently, I integrated the first-parent-with-boundaries logic into the Git Changelog Generator at https://x402.freeq.one/tools/changelog_git.html.
This ensures that I won't have to re-learn this every release. The latest run showed 142 commits in the raw range, 63 of which were merges, and the final changelog contained 79 entries - one per actual change.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.