← Blog

Guide · · 5 min read

Generate a git commit report from the command line

The git log invocations that produce a usable weekly report: date windows, per-author tallies, commit-type counts, and CSV output, with the flags that silently return nothing.

DevSummary Team

You do not need a tool to get a commit report out of a repository. git log will do most of it, and the git log documentation covers every flag below. What follows is the small set of invocations that actually produce something readable, plus the three that quietly return the wrong thing.

Everything here runs against a local clone with no dependencies beyond git and standard shell tools.

The weekly list

The baseline report. One line per commit for the last seven days, newest first.

git log --since="7 days ago" --no-merges \
        --date=short --pretty=format:"%ad  %an  %s"
2026-08-29  Ada Lovelace  fix: serve the demo video with byte ranges
2026-08-28  Ada Lovelace  chore: ignore raw mp4 files
2026-08-27  Grace Hopper  feat: consent mode v2 and a cookie card

Three flags carry the weight. --since takes human dates, so "7 days ago", "last monday" and 2026-08-01 all work, and --until closes the window at the other end. --no-merges drops merge commits, which contain no changes of their own and will otherwise inflate every count you produce. --pretty=format: shapes the line: %ad is the author date, %an the author name, %s the subject, %h the short hash.

Pair --since with --until when you want a fixed period rather than a rolling one, which matters if you are producing the same report every Monday and want the windows to tile without gaps or overlaps.

Who did what

git shortlog -sn --no-merges --since="30 days ago" HEAD
    96  Ada Lovelace
    41  Grace Hopper

The HEAD at the end is not optional. git shortlog reads from standard input when it is not given a revision, so leaving it off gives you an empty result or a hung terminal rather than an error. This is the single most common way this command appears broken.

Drop the -s to get the commit subjects grouped under each author, which is far more useful than the count:

git shortlog --no-merges --since="30 days ago" HEAD

Treat the counts as a description of activity, not of contribution. A hundred small commits and four large ones are indistinguishable here, and any report that ranks people by commit count will change how people commit within about two weeks.

Activity by week

git log --since="90 days ago" --no-merges \
        --date=format:'%Y-W%V' --pretty=format:'%ad' \
  | sort | uniq -c
   3 2026-W23
  13 2026-W24
   2 2026-W29
  12 2026-W30

--date=format: accepts strftime specifiers, so %V gives the ISO week number. The gaps are the interesting part. Two commits in W29 against thirteen in W24 is a question worth asking, and it is invisible in any total.

Commit types at a glance

If the team uses conventional prefixes, tally them:

git log --since="365 days ago" --no-merges --pretty=format:'%s' \
  | grep -oE '^[a-z]+' | sort | uniq -c | sort -rn
  25  feat
  15  fix
   4  chore
   2  docs

This one is only as honest as the team’s commit discipline. Prefixes are a convention, not a rule, and any subject that does not start with one silently drops out of the tally. Run it once and look at the total against git log --oneline | wc -l before you trust the proportions.

Volume of change

git log --since="30 days ago" --no-merges --shortstat --pretty=format:"%h %s"
42d3574 fix: SEO and accessibility pass
 24 files changed, 566 insertions(+), 159 deletions(-)

Useful for spotting the one commit that touched two hundred files. Not useful as a productivity measure, for the reason every engineer will tell you: deleting four hundred lines of dead code is usually the better day’s work.

Exporting for a spreadsheet

git log --since="90 days ago" --no-merges \
        --date=short --pretty=format:'%ad|%an|%s' > report.psv

Use a pipe or a tab, not a comma. Commit subjects contain commas constantly, and a comma-delimited export will split fix: SEO, performance and a11y pass into two columns and shift every field after it. If you need real CSV, quote the subject field explicitly rather than hoping.

Three ways this goes wrong

A shallow clone returns almost nothing. In CI, actions/checkout fetches a single commit by default, so --since finds nothing to filter. Set fetch-depth: 0. This is the usual reason a scheduled digest posts an empty message, covered further in sending a weekly GitHub digest to Slack.

Author date and commit date are not the same. %ad is when the work was written, %cd when the commit last landed in its current form. A rebase rewrites the second and preserves the first, so a rebase-heavy team can produce reports where work appears in the week it was authored rather than the week it shipped. Pick one and use it consistently.

You are only seeing one branch. git log reports on your current checkout. Run it on the branch you actually ship from, and remember that a local clone only knows what it has fetched. git fetch --all before generating anything you intend to send to another person.

What this will not give you

The output above is complete and accurate, and it is still a list of commit subjects written by developers for developers. It answers what was committed. It cannot answer what changed for customers, because that information is in the diff rather than in the message. What a GitHub commit actually is covers why that gap exists.

It also runs on your laptop, which means it only produces a report when a person remembers to run it. For anything recurring, the scheduling and scoping decisions matter more than the flags, and those are in the guide to GitHub reporting for non-technical stakeholders.

DevSummary reads the diffs rather than the subjects, which is the part git log structurally cannot do.

For an engineer summarising their own quarter, the commands above are the whole job. For a report that goes to someone else, they are the easy quarter of it.

More posts