← back to latest

Count who has actually committed to a repository, and how much

git shortlog -sne --no-merges HEAD

anatomy

git shortlog
Walk the history and group commits by author instead of listing them in date order. It was built to write release announcements, so by default it prints each author's name followed by every one of their commit subjects. That is useful once a release and noise the rest of the time, which is why the next three letters exist.
-s
Summary. Drop the commit subjects and print only a count per author. This turns a page-long changelog into a few lines, one per person.
-n
Sort by the number of commits, highest first. Without it the list is alphabetical by name, which answers who exists but not who matters.
-e
Show the email address next to each name. Shortlog groups by name alone unless you ask for this, and with -e it groups by name plus email. That sounds like a cosmetic difference and is not: one person committing from a work laptop and a personal one becomes two rows with -e and one row without it, so the flag is how you find out a count is split.
--no-merges
Skip commits with more than one parent. Whoever clicks the merge button on a hosted platform gets a merge commit for every pull request they land, which inflates the person doing review and release work rather than the people who wrote the changes. Drop this flag if merging is the work you want to count.
HEAD
The revision to walk from. It looks redundant and is the most important word in the command. When shortlog receives no revision and its standard input is not a terminal, it stops reading the repository and waits to summarize git log output piped into it instead. In a script, a cron job, or a CI step, that means it prints nothing and exits 0. Naming HEAD, a branch, or a range like v1.0..v2.0 removes the ambiguity.

Sample output

    41	Ada Okafor <ada@payments.dev>
    23	Marco Reyes <marco@payments.dev>
    17	Sam Levine <sam@payments.dev>
     9	Ada Okafor <ada.okafor@gmail.com>
     2	Priya Nair <priya@payments.dev>

Ada appears twice, once per email address, so her real total is 50 and the repository has one dominant author rather than a close race. Run the same command without --no-merges and Sam jumps to 29, because twelve of those commits are pull request merges rather than code.

When you would reach for it

You have inherited a repository and need to know who to ask about it. You are writing release notes and want the list of contributors for a tag range. You suspect a project has a bus factor of one and want a number instead of a feeling. Shortlog answers all three in one line, and unlike counting surviving lines with git blame, it measures who has done the work over time rather than who touched the code last.

Gotchas

  • Without a revision, shortlog reads from standard input whenever stdin is not a terminal. In a pipeline, a $(...) substitution, or a CI job, git shortlog -sn prints nothing and exits 0, which looks exactly like an empty repository. Always pass HEAD or a range.
  • The same person under two emails shows up as two rows with -e, and two different people who share a name merge into one row without it. Neither view is correct on its own. A .mailmap file at the repository root fixes it: the line Ada Okafor <ada@payments.dev> <ada.okafor@gmail.com> folds the second address into the first, and shortlog, git log, and git blame all read it.
  • Commit counts measure commits, not effort. One person who squashes every branch into a single commit looks less active than someone who commits every save. Use the count to find people, not to rank them.
  • Co-authors listed in Co-authored-by: trailers are invisible by default, since shortlog counts the author header only. --group=trailer:co-authored-by (git 2.29 or later) counts them as well.

Variants

$ git shortlog -sn --no-merges v1.4.0..v1.5.0

Contributors to one release, the use shortlog was originally written for

$ git shortlog -sn --no-merges --since="6 months ago" HEAD -- src/billing/

Who has been active in one directory recently, which is usually who to ask

$ git shortlog -sn --no-merges --group=author --group=trailer:co-authored-by HEAD

Credit pair-programmed and co-authored commits to everyone named on them

lineage

By most accounts git shortlog started life as a Perl script that Jeff Garzik wrote in 2005 to produce the per-author summaries attached to Linux kernel release announcements. The script automated a format kernel developers already knew: names in alphabetical order, each followed by an indented list of commit subjects. Johannes Schindelin rewrote it in C as a git built-in in 2006, and it still reads git log output on standard input for exactly that reason, since the original workflow piped one into the other. The .mailmap file came out of the same kernel bookkeeping: with thousands of contributors committing under shifting names and addresses, shortlog needed a way to fold them back into one person, and the mapping file it read was eventually honored across git log, git blame, and the rest of the toolset.