Author Identity Consistency¶
The same person can show up under several different names/emails across their commit history — this affects how accurately reports reflect real contributor and team activity, and it's fixable.
The problem¶
Git records whatever user.name/user.email was configured locally at
commit time, with no validation and no link back to a single "identity"
for a person. It's common for the same person to end up with several
distinct combinations in the same repo's history:
- A personal email on one machine, a work email on another
- A name typo, a shortened/incomplete name (
J Smithvs.Jane Smith), or different capitalization - A config that was never updated after a name change (e.g. after marriage, or switching teams/orgs)
Git treats each distinct (name, email) pair as a separate identity —
there's nothing under the hood that groups them back together on its
own.
How it affects reports¶
eng-reports groups commit activity by (lowercased) author_email, and
rolls up --team-map assignments the same way. Both are direct
consequences of this:
- Split individual stats — if the same person committed under two different email addresses, they show up as two separate rows in the "By Author (commits)" table, each with a fraction of their real commit/line counts, instead of one accurate total.
- Team rollups silently miscategorize people — if your
--team-mapCSV listsjane@work.combut some of Jane's commits usedjane.smith@gmail.com, those commits land in "Unmapped" instead of Jane's real team, even though the CSV itself is correct.
Neither of these produces an error — they just quietly under-report or misattribute real activity, which is what makes this worth checking for rather than assuming it isn't happening.
Fixing it going forward: .mailmap¶
.mailmap is git's own native mechanism for this — a file at the root
of a repo mapping inconsistent identities to one canonical one:
Jane Smith <jane@work.com> <jane.smith@gmail.com>
Jane Smith <jane@work.com> J Smith <jane@work.com>
Each line is <canonical name> <canonical email> [<messy name>]
<messy email> — the bracketed name is only needed if the messy commit
used a different name, not just a different email. Add one line per
alias.
git ls-stats/git ls-tags (the tools git-processor uses to import
commit/tag data) resolve author_name/author_email and
tagger_name/tagger_email against a repo's .mailmap automatically,
the same way git log/git shortlog already do — there's no flag to
opt in or out.
You don't have to commit it to test it
.mailmap just needs to exist in the repo's working directory when
git-processor runs — it doesn't need to be committed first. For a
real, shared fix (and so plain git log/git shortlog show the
same resolved identities for everyone), commit it like any other
file.
Reprocessing already-imported data¶
Adding a .mailmap doesn't retroactively fix commits git-processor
already imported, at least not on its own. A normal run only re-resolves
the single most-recently-imported commit (git ls-stats re-fetches its
own start point inclusively every time) — everything older than that
stays as originally imported.
Run git_processor.py with --force-full-reimport against the repo to
fully re-resolve every commit's identity:
docker compose run --rm -v /path/to/local/clone:/repo --entrypoint python3 \
git-processor git_processor.py /repo --force-full-reimport
(You need a local clone of the repo to bind-mount in — git-processor's
normal queue-worker mode clones and discards a scratch copy per repo, it
doesn't keep one around for this.)
No extra setup needed for the bind mount itself — earlier versions of
this image required a manual git config --add safe.directory step to
work around a host/container UID-ownership check; that's now baked into
the image itself as long as you bind-mount to /repo (the path shown
above), so it works out of the box.
Tags don't need this step — git ls-tags output is fully reconciled
against the database on every normal run (not just full reimports),
so a tag's tagger_name/tagger_email picks up a new .mailmap
automatically the next time git-processor runs.
One thing .mailmap/reprocessing does not touch:
Co-authored-by: trailers in commit messages. Those are extracted as
plain text, not resolved as git identities (the same way git itself
doesn't apply mailmap to them) — a coauthor's name/email there stays
exactly as written in the commit message.
Next: Generating Reports to confirm the fix actually collapsed those split rows.