Every month, I write a newsletter which (among other things) discusses some of the technical projects I’ve been working on. It’s a useful exercise — partly as a record for other people, but mostly as a way for me to remember what I’ve actually done. Because, as I’m sure you’ve noticed, it’s very easy to […]
The post Summarising a Month of Git Activity with Perl (and a Little Help from AI) first appeared on Perl Hacks.
Every month, I write a newsletter which (among other things) discusses some of the technical projects I’ve been working on. It’s a useful exercise — partly as a record for other people, but mostly as a way for me to remember what I’ve actually done.
Because, as I’m sure you’ve noticed, it’s very easy to forget.
So this month, I decided to automate it.
(And, if you’re interested in the end result, this is also a good excuse to mention that the newsletter exists. Two birds, one stone.)
All of my Git repositories live somewhere under /home/dave/git. Over time, that’s become… less organised than it might be. Some repos are directly under that directory, others are buried a couple of levels down, and I’m fairly sure there are a few I’ve completely forgotten about.
What I wanted was:
The first three are straightforward enough. The last one is where things get interesting.
The first step is walking the directory tree and finding .git directories. This is a classic Perl task — File::Find still does exactly what you need.
(There are, of course, other ways to do this — you could shell out to fd or find, for example — but keeping it in Perl keeps everything nicely self-contained.)
For each repo, we can run git log with appropriate date filters.
sub commits_for_month ($repo, $since, $until) { my $cmd = sprintf( q{git -C %s log --since="%s" --until="%s" --pretty=format:"%%s"}, $repo, $since, $until ); my @commits = `$cmd`; chomp @commits; return @commits; } |
Where $since and $until define the month we’re interested in. I’ve been using something like:
It turns out I have a few repositories where I never got around to making a first commit. In that case, git log helpfully explodes with:
fatal: your current branch ‘master’ does not have any commits yet
Which is fair enough — but not helpful in a script that’s supposed to quietly churn through dozens of repositories.
The fix is simply to ignore failures:
This is one of those little bits of defensive programming that makes the difference between a script you run once and a script you’re happy to run every month.
Once we have a list of commit messages, we can summarise them.
And this is where I cheated slightly.
I used OpenAPI::Client::OpenAI to feed the commit messages into an LLM and ask it to produce a short summary.
Something along these lines:
Could I have written some heuristics to group and summarise commit messages? Possibly.
Would it have been as much fun? Definitely not.
And in practice, it works remarkably well. Even messy, inconsistent commit messages tend to turn into something that looks like a coherent summary of work.
For each repo:
The output looks something like:
my-project ----------- Refactored database layer, added caching, and fixed several edge-case bugs. another-project --------------- Initial scaffolding, basic API endpoints, and deployment configuration. |
Which is already a pretty good starting point for a newsletter.
One unexpected benefit of this approach is that it surfaces projects I’d forgotten about.
Because the script walks the entire directory tree, it finds everything — including half-finished experiments, abandoned ideas, and repos I created at 11pm and never touched again.
Sometimes that’s useful. Sometimes it’s mildly embarrassing.
But it’s always interesting.
This is very much a first draft.
It works, but it’s currently a script glued together with shell commands and assumptions about my directory structure. The obvious next step is to:
At that point, it becomes something other people might actually want to use — not just a personal tool with hard-coded paths and questionable date handling.
One idea I particularly like is to run this automatically using GitHub Actions.
For example:
Over time, that would build up a permanent, browsable record of what I’ve been working on.
It’s a nice combination of:
Which is either a fascinating historical archive…
…or a slightly alarming reminder of how many half-finished projects I have.
This started as a small piece of automation to help me write a newsletter. But it’s turned into a nice example of what Perl is still very good at:
And, occasionally, outsourcing the hard thinking to a machine.
The code (such as it is currently is) is on GitHub at https://github.com/davorg/git-month-summary.
If you’re interested in the kind of projects this helps summarise, you can find my monthly newsletter over on Substack.
And if I get round to turning this into a CPAN module, I’ll let you know – well, if you’re subscribed to the newsletter!
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | How One Pull Request Took App::HTTPThis to Version 1.0 | 0 | 8.06 | 05-07-2026 |
| 2 | Perl School Goes Paperback | 0 | 10.09 | 11-07-2026 |
| 3 | Teaching AI About the British Monarchy with MCP | 0 | 9.14 | 30-05-2026 |
| 4 | Writing a TOON Module for Perl | 0 | 7.96 | 29-03-2026 |
| 5 | Turn one giant AI-generated pull request to a reviewable stack | 0 | 9.34 | 04-08-2026 |
| 6 | Automating cross-repo documentation with GitHub Agentic Workflows | 0 | 14.07 | 08-07-2026 |
| 7 | Daily Hacker News for 2026-07-23 | 0 | 10.96 | 24-07-2026 |
| 8 | Trivial anti-crawler with Caddy | 0 | 8.42 | 23-06-2026 |
| 9 | Anthropic recommends a git worktree per agent. Your runtime infra makes that a problem. | 0 | 12.63 | 06-08-2026 |
| 10 | (Выходит на сцену с кружкой кофе, на которой написано «Не ... | 0 | 6.81 | 12-08-2026 |