I built a little script to count my own commits per day, because I wanted an honest number for “how much did I actually ship.” The first day I ran it, the tool told me I made 134 commits across 13 repositories. That felt great for about ten seconds, until I looked at the per-repo breakdown and realized the number was almost entirely a lie. Not a rounding error. The real figure was under 25 unique commits, and the gap was hiding in two places I had stopped thinking of as duplication at all.

Here is the breakdown the script spat out:

legacy-site-repo 13
renamed-site-repo 13
local clone 10
local clone 2 10
local clone 3 10
local clone 4 10
local clone 5 10
local clone 6 10
local clone 8 10
local clone 9 10
local clone 10 10
local clone 7 9
site-archive 9

Two things jump out if you stare at it. The two 13s are suspiciously equal. And there is a column of nines and tens that is obviously one number wearing ten hats.

The mirror: two names, one history

legacy-site-repo and renamed-site-repo were the same project. One was the original repository name and the other the migration target; during the transition both directories and remotes existed on disk. I had genuinely stopped seeing them as the same thing because they live in different folders with different names, and my brain treats a folder name as an identity.

Git does not. When I pulled the commit list out of each, the SHAs were byte-identical. Not “similar commits made around the same time.” The exact same hashes: a7e6cb5, 6f89781, 01368a5, 26b6797, and the rest, present in both repos. A commit ID identifies the same commit object and history position in both views. If two repositories show the same SHA, it is not two pieces of work. It is one commit that two directories can both see.

So those 13 commits in renamed-site-repo are the same 13 commits in legacy-site-repo. My script counted every one of them twice. Thirteen real commits became 26 in the total.

The clones: one fix, seen ten times

The site block is the same disease with a bigger blast radius. I run the sports SaaS on classic ASP under IIS, and my local dev setup is ten clones of the same repo, local clone through local clone 10, each bound to its own local site so I can have several branches live in browsers at once without a build dance. They are not ten projects. They are ten working copies of one project, and they fetch from the same origin.

So when the day’s ten commits land, every clone that has fetched develop sees those same ten SHAs. The script dutifully reported “10 commits in local clone, 10 in local clone 2, 10 in local clone 3,” all the way down. That is not 100 commits. It is ten commits observed ten times. (local clone 7 and site-archive showed nine because they had fetched slightly stale, which is its own tell: when “identical” repos disagree by one, the difference is fetch timing, not work.)

Add it up. Thirteen mirrored commits, ten unique site commits multiplied across eleven clone directories, and the inflated 134 collapses to roughly 23 unique commits once you dedupe. The metric I built to make myself honest was off by nearly 6x in the flattering direction.

Why a directory is the wrong unit

The bug is conceptual, not arithmetic. I was counting commits per repository directory, and I was implicitly assuming one directory equals one stream of work. In a normal layout that assumption holds well enough to be invisible. In any workflow with mirrors, clones, or worktrees it is just wrong, and it is wrong in the direction you least want a self-measurement to be wrong: it tells you you did more than you did.

The clone-heavy setup makes it acute, but you do not need ten clones to get bitten. Any of these will do it:

  • A repo mid-rename or mid-migration with both old and new directories on disk.
  • A mirror clone you keep for backup or for a second remote.
  • Git worktrees, where one repository legitimately checks out across several directories.
  • A fork you cloned next to the upstream you also cloned.
  • A monorepo you also vendored into another project as a subtree, which shares commit history.

Every one of these puts the same SHAs in more than one place, and any tool that sums “commits per directory” will multiply your output by however many copies you happen to have lying around.

The fix is one line of intent

The fix is embarrassingly small once you name the unit correctly. The unit of work is the commit, identified by its SHA, not the directory it happens to be visible from. So you gather every commit SHA from every repo, throw them in a set, and count the set.

Terminal window
# Naive: counts each clone's view separately, multiplies by copy count
for d in */; do
git -C "$d" log --since=midnight --oneline | wc -l
done | paste -sd+ | bc
# Honest: union of unique SHAs across every repo, counted once
for d in */; do
git -C "$d" log --since=midnight --author="you" --format='%H'
done | sort -u | wc -l

The difference between those two snippets is sort -u, and that one detail is the entire post. The first answer flatters you in proportion to how many copies of your repos you keep. The second answer is the same no matter how many clones, mirrors, or worktrees are on disk, because a set does not care how many times you hand it the same element.

If you want the number to mean something, dedupe before you aggregate, and key the dedupe on the SHA. The same logic extends to anything you derive from git: lines changed, files touched, “active days,” tickets referenced in commit messages. Sum them per directory and your mirrors and clones inflate every figure. Union them by commit hash and you get the real number.

The wider point: self-instrumentation lies in the flattering direction

What stuck with me afterward was not the off-by-6x. It was the direction. I built this to keep myself honest, and the default implementation lied in exactly the way that would have felt good to believe. A metric that overcounts your output is a metric you are not motivated to audit, which is precisely why it survives.

The general rule I took from it: when you instrument yourself, assume the first version is optimistic and go looking for the double-count before you trust the trend line. The check is cheap. Pick one suspiciously round or suspiciously high day and verify it by hand against the underlying objects. If the per-directory numbers come in pairs of equal values, or in a long flat run of the same count, you are looking at copies, not work.

Commit hashes are content addresses. Treat them as the identity of the work, not the folders they live in, and your numbers stop lying to you.