Writing 5 min read

Repo Vibe Check Skill: Turning Git Log into a Visual Survey

Repo Vibe Check Skill: Turning Git Log into a Visual Survey

In the agency and contractor world, you get thrown into a loooooot of code bases. Onboarding and getting your bearings, I find, can often be the part with the most friction. Granted, thanks to AI, it's gotten a bit smoother. I recently saw a blog post from Ally Piechowski making the rounds on the internet. I took his process, combined it with what I was already doing, and turned it into an AI skill that produces a visual history. I want to reiterate: the commands running in the background are 90% from his blog post.

View the skill on Github

Project Breakdown

Every legacy codebase comes with a maintenance log. Nobody ever reads it, because it's not a binder on a shelf, it's buried in git log and everyone would rather just start clicking through files and vibing. That's how you end up three days into a new repo confidently refactoring the one file that's been on fire for two years, and nobody mentioned it because nobody looked.

Scoping first

Before collecting anything, the skill locates the primary source directory ( src/ , app/ , lib/) and scopes every churn and bug query to it. Run those queries against the repo root instead, and lockfiles, changelogs, and generated code dominate every list. Obviously the package lock files will have some of the highest activity, we don’t want that noise.

The commands

Vitals: age, size, and who's around

git log --reverse --format='%ad' --date=short | head -1   # first commit
git log -1 --format='%ad' --date=short                     # last commit
git rev-list --count HEAD                                  # total commits
git ls-files | wc -l                                        # tracked files
git shortlog -sn --no-merges HEAD | wc -l                   # all-time contributors
git shortlog -sn --no-merges --since='1 year ago' HEAD | wc -l  # active contributors

Every later verdict gets calibrated against this. "High churn" means something different on a 200-commit side project than on a 20,000-commit monster that's been running since Obama's first term.

Language mix: extensions, counted and ranked:

git ls-files | sed -n 's/.*\.\([A-Za-z0-9_]\{1,10\}\)$/\1/p' | sort | uniq -c | sort -nr

One line, and it already tells you more than the README does. Free signal for what the repo actually is before you've read a single import statement.

Churn, two windows: most-touched files, last year vs. last two months:

git log --format=format: --name-only --since="1 year ago" -- "$SRC" | sed '/^$/d' | sort | uniq -c | sort -nr
git log --format=format: --name-only --since="2 months ago" -- "$SRC" | sed '/^$/d' | sort | uniq -c | sort -nr

One window isn't enough, because churn by itself doesn't necessarily mean anything. All the stoic bros will tell you, change is morally neutral, it’s just perception. A file that churned hard over the year but has gone quiet the last two months has settled down. A file churning hard in both windows is either evolving fast or actively bleeding, and churn alone can't tell you which. That's what the next command is for

Bug-fix clusters: same shape, filtered to commits that smell like an apology:

git log -i -E --grep="fix|bug|broken" --name-only --format='' -- "$SRC" | sed '/^$/d' | sort | uniq -c | sort -nr

Cross-reference this against the churn list and you get the Danger Zone: files that change constantly and keep breaking. You know, the one that has the DO NOT TOUCH THIS comment that everyone is afraid of.

Contributors: all-time vs. six months

git shortlog -sn --no-merges HEAD
git shortlog -sn --no-merges --since="6 months ago" HEAD

Diffing these two commands answers the bus factor question. How is the spread of the knowledge of the codebase and are those legends still around.

Monthly velocity: commits bucketed by month, full history

git log --format='%ad' --date=format:'%Y-%m' | sort | uniq -c

Charted, this stops being a number and becomes a shape. Steady rhythm: healthy. A sustained ≥40% drop: someone probably left and nobody backfilled. Spikes then silence: batch releases, not decline… don't panic over that one, it's just how some teams ship. (NOTE: Some code bases reach maturity and serve their purpose without much other than package updates… nothing wrong there)

Firefighting log: reverts, hotfixes, rollbacks, last year, with dates

git log --format='%ad %s' --date=short --since="1 year ago" | grep -iE 'revert|hotfix|emergency|rollback'

Deliberately a commit-message grep, not a git flag. What parts of the code base have had devs ugly crying on a Friday evening because the PM said deployment on Friday morning was low risk.

TODO/FIXME register:

git grep -c -iE 'TODO|FIXME|HACK|XXX' -- "$SRC" | sort -t: -k2 -nr

git grep stays fast on a big repo and respects .gitignore without being asked twice. Where this list overlaps with the churn and bug-cluster lists, that's known-broken code someone already flagged and nobody ever circled back for. The confession was in the codebase the whole time.

Lockfile staleness: last-touched date, whichever manifest actually exists

git log -1 --format='%ad (%ar)' --date=short -- package-lock.json

We’ve all been there, legacy dependency hell. Is the repo on top of it or not?

Commit timing: hour and weekday distribution, last year

git log --format='%ad' --date=format:'%H' --since="1 year ago" | sort | uniq -c
git log --format='%ad' --date=format:'%u %a' --since="1 year ago" | sort | uniq -c

Cross-referenced with the firefighting log, this is what separates "this team just likes late nights" from "this team is up at 2am because production is on fire again." Same data point, very different Tuesday.

The Visual Output

I’m currently building a personal React Native app, so I ran this on an example repo I’ve been studying:

// 06.contact

Bring your vision into focus.

From initial market strategy to deep code implementation, together we design and build full-lifecycle digital ecosystems. Drop a brief, a deck, or an open problem, and let Branden Builds do his thing.

Start a project

Let's get to work.