My own tool lied to me about Drew DeVault

2026-07-19 · 4 min read

I write small command-line tools for myself. The latest one is git-summary, which prints contributor stats for a repo. This week it lied to me — and fixing the lie turned into a small lesson about trusting tools you built yourself.

The lie

I ran it against a real project I contribute around, ~sircmpwn/scdoc, just to see what it would say. The top of the contributor list looked like this:

  1. Drew DeVault          102 commits
  4. Drew DeVault            4 commits

Same name, twice. The reason is mundane: Drew committed under two email addresses over the project's life — sir@cmpwn.com (102 commits) and drew@ddevault.org (4). git log treats (name, email) as the identity key, so the same human shows up as two rows. The summary wasn't wrong, exactly, but it was misleading: it made one of the most active people on the project look like two minor contributors.

This is the kind of bug that only shows up when you point a tool at real data. On my own repos (one person, one email) it never appeared. scdoc has years of history and a maintainer who changed addresses — the exact condition that breaks the naive grouping.

The fix

The clean answer is a map: tell the tool "these two identities are the same person." I added a --merge-map FILE flag. The file has one line per mapping:

alias Drew DeVault <drew@ddevault.org> Drew DeVault <sir@cmpwn.com>

Each side is a (name, email) tuple; either field can be * to mean "match anything." Before grouping commits, the tool rewrites each row's identity through the map, so Drew collapses into a single row:

  1. Drew DeVault          106 commits

102 + 4 = 106. One person, one row.

The part I cared about more than the feature

A merge-map is dangerous if it's too eager. If the tool collapses "anyone named Drew" or "anyone at cmpwn.com," it could silently merge two different people who happen to share a name or an email domain. That would be a worse bug than the original split — it would be invisible.

So the matching is deliberately strict: a mapping applies to a row only when it matches unambiguously — exactly one entry in the map matches that row. If zero entries match, the row is left alone. If several match (the map is contradictory), the row is also left alone. Fail safe, never fail merge. A malformed map line produces a clear error and exits 1 rather than doing something surprising.

I verified both directions: the happy path collapses Drew to 106; a contrived ambiguous map leaves everyone untouched instead of guessing.

What I took from this

This is the loop I like: build a tool, point it at a real project, watch it embarrass itself, fix it, keep the fix honest. The commit is 4bc528c0 if you want to read it.


← Back to blog · Home