Your GitHub graph says 30 commits this month. Does it mean 30 days of real work?
I build Vigilante, a free browser game where verified GitHub pushes open the next day of the story. To make that fair, I had to answer one question: what counts as a real commit? Some commits are noise. Merges, empty commits and lock-file-only changes are the usual ones. Vigilante filters those out.
You don't need my game to check this. Here is a small script that sorts your own history. I tested it on a throwaway repo.
Three kinds of noise
Merge commits. They have two parents. Git creates them when you join branches. The work was done in the branch commits, so the merge adds no new work.
Empty commits. git commit --allow-empty makes a commit with no file changes at all. It takes two seconds and keeps a green square alive. It also proves nothing.
Lock-file-only commits. A dependency bump can touch only package-lock.json or yarn.lock. That is machine output, not your thinking.
The script
Save this as junk.sh and run it inside any repo:
#!/usr/bin/env bash # Sort the last N commits into "real" and "noise" N=${1:-30} real=0; noise=0 for c in $(git rev-list --max-count="$N" HEAD); do # 1 word = root commit, 2 = normal, 3+ = merge parents=$(git rev-list --parents -n 1 "$c" | wc -w) files=$(git diff-tree --root --no-commit-id --name-only -r -m --first-parent "$c") if [ "$parents" -gt 2 ]; then kind="merge" elif [ -z "$files" ]; then kind="empty" elif ! echo "$files" | grep -qvE '(^|/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|Cargo\.lock|poetry\.lock|go\.sum)$'; then kind="lockfile-only" else kind="real"; fi printf '%-14s %s\n' "$kind" "$(git log -1 --format='%h %s' "$c")" [ "$kind" = real ] && real=$((real+1)) || noise=$((noise+1)) done echo "real: $real noise: $noise" Run it:
bash junk.sh 50 How it works
-
git rev-list --parents -n 1 <commit>prints the commit and its parents. Counting the words tells us if it is a merge. -
git diff-tree --name-onlylists the files the commit touched. No files means an empty commit. The--rootflag makes the very first commit of a repo show its files too. Without it, the first commit of a repo would be reported as empty. - The
grep -qvEline asks one question: is there any file that is not a lock file? If not, the commit is lock-file-only.
A test you can repeat
I made a repo with five commits: one real change, one empty commit, a lock-file-only commit, a feature on a branch, and the merge of that branch.
merge 5d58535 Merge f lockfile-only a64ba80 lock real bf53283 feat empty 0ac05eb keep streak real 5700773 real work real: 2 noise: 3 Five commits on the graph. Two days of actual work. That gap is the whole point.
What the script can't know
Be honest about the limits.
- It cannot tell a thoughtful commit from
fix typorepeated daily. Judging that is hard, and I won't pretend a regex can. - It treats config files as real. If you want to filter
.github/or formatting-only commits, add patterns to the lock-file list. - Squash merges look like normal commits, which is correct, since they carry real changes.
So treat the output as a prompt for a question, not as a grade.
Why I care about this
A streak is only motivating if it is true. If an empty commit can keep it alive, the streak measures whether you opened a terminal, not whether you built anything. That is why a verified day in Vigilante comes from real pushes, and why buying something in the game never counts as one.
If you want a quick look at your own last 30 days without running anything, Vigilante has a free GitHub check-up for public activity. Full disclosure, I built it. The script above is enough if you prefer to stay in your terminal.
Try it on yourself
Run the script on a repo you have worked on for a month. Count the noise. Then ask one thing: on how many of those days did I actually change something that matters?
I'm curious what ratio you get. Drop it in the comments, and tell me which kind of noise shows up most in your history: merges, empty commits or lock files.


