On a Tuesday morning in February two thousand twenty-six, a developer in Jämtland, Sweden, asked an AI to review his Git setup. Not a large company. Not a team of fifty. One person, working from a house in a village called Kall, population somewhere south of three hundred, running about thirty personal projects across a VPS in Paris, a Raspberry Pi in his living room, and a MacBook on his kitchen table.
The AI found thirty-two Git repositories. Twenty-six had GitHub remotes. Four had no remote at all, meaning no offsite backup, meaning one disk failure away from gone. Two were completely empty, initialised and then abandoned, digital ghost towns with a dot-git directory and nothing else. Fourteen repos had uncommitted changes sitting in their working trees. That is almost half. Half of all the projects had work that existed only on one machine, in one directory, with no safety net.
This is not a story about a careless developer. This is a story about how most solo developers actually use Git, and what happens when you are honest about it. Because the gap between how Git is taught and how Git is used in the wild, especially by people working alone, is enormous. And it took an AI audit to make that visible.
The first thing the audit surfaced was dead weight. A directory called hugo-test with zero commits. A git init had been run, a Hugo theme had been added as a submodule, and then nothing. No commit, no remote, no content. Just a six point seven megabyte dot-git directory from the submodule fetch, sitting there like an unfinished sentence.
There was another one. Deep in a directory path with Swedish characters in the folder names, a repo called PartyPär. Zero commits in GitHub's records, but locally it had eight commits of a working FastAPI rental management system. A gift from a friend on a train, built with Claude Code, fully functional, pink-themed, with a humorous notification engine. And absolutely no offsite backup. Eight commits of actual work, protected by nothing but the health of one solid state drive.
Then there was the scoreboard repo. One hundred and twenty-eight megabytes. Nine hundred and thirty-eight commits. From an upstream roller derby project last touched in two thousand sixteen. It had been cloned years ago to grab some reference data for building a different tool, and then it just stayed. One hundred and twenty-eight megabytes of someone else's frozen history, sitting in the tools directory like furniture from a previous tenant.
These are not unusual situations. Every developer who has worked for more than a year has directories like this. The abandoned experiment. The cloned reference that outlived its purpose. The real project with no backup. But most developers never audit. They never look at the full picture. They interact with one project at a time, and the dead weight accumulates like sediment.
The audit found seven cases where the local folder name did not match the GitHub repository name. The folder was called radio, but the repo was called are-radio. The folder was called slides, the repo was parslides. The folder was live, the repo was arebladet-live. Utilities mapped to parceptionUTIL, which is the kind of name you give something at two in the morning when GitHub says your first three choices are taken.
None of this breaks anything. Git does not care what the folder is called. The remote URL is what matters, and that is stored inside the dot-git directory. But it creates a kind of cognitive friction that accumulates over time. You are on GitHub looking for a project and the name does not match what is on your machine. You are in a terminal and you cannot remember if this folder's repo is called the same thing or something else. It is the digital equivalent of a filing cabinet where the labels on the drawers do not match the labels on the folders inside.
The fix is simple in principle. Rename the repos, or rename the folders. In practice, nobody ever does it, because it works fine as is, and there is always something more urgent. This is a pattern that repeats across the entire audit. The difference between "works" and "works well" is maintenance, and maintenance is the thing that solo developers skip first.
Here is a genuinely confusing situation the audit uncovered. Two directories, one called arebladet underscore manager and the other called arebladet underscore manager space v two, both pointed to the exact same GitHub remote. The same URL. Two local repositories, with different commit histories, pushing and pulling from the same place.
What happened was straightforward in retrospect. The project had gone through a major rewrite. Rather than branch within the same repo, the developer had copied the directory, given it a new name, and started fresh. The old directory kept its git history. The new directory got its own git init and its own commits. But both were configured to talk to the same GitHub repo. Only one could be the "real" upstream at any given time.
This is the kind of thing that works until it does not. Push from the wrong directory and you overwrite the other version's history. Forget which one is current and you waste an hour figuring out why the code on GitHub does not match what you are looking at locally. The fix was simple: figure out which one is the latest, delete the other. The v two directory, despite its name, turned out to be the older snapshot. The one without a version number had continued evolving through v three and beyond with security hardening, database migrations, and integration tests. Names lie. Commit history does not.
The Raspberry Pi in the living room ran two services. PinkRemind, an SMS reminder system that sends messages through a cellular router. And PinkThunder, a weather station receiver that intercepts data from a sensor array on the roof and sends alerts when temperatures drop dangerously low. Two different services, two different git repos, both running on the same device, both deployed the same way, both maintained by the same person.
They even lived in the same parent directory already. There was a folder called pinkserver with a CLAUDE dot md file, some documentation, and two subdirectories, pinkremind and pinkthunder, each with their own dot-git. The parent directory had useful files that belonged to neither child repo. Two repos in one folder, with shared documentation floating outside both of them.
The merge was mechanical. Push any uncommitted work. Remove the individual dot-git directories. Create a new repo at the parent level. Write a gitignore. Commit everything. Create a new GitHub repo called pinkserver and push. Archive the old individual repos on GitHub so they still exist for reference but are clearly marked as superseded.
The result: one repo, one remote, one place to commit, one place to check status. The pinkremind directory and the pinkthunder directory still exist as subdirectories, keeping their code separate, but they share a single git history and a single backup. The same thing happened with the hotel projects. A booking dashboard and a bar inventory system for the same hotel, in the same directory, in separate repos, merged into one called kallgarden.
There was also a tiny SMS gateway called brevduvan. Two commits. Its own GitHub repo. It lived physically inside the utilities directory, which was itself a git repo that explicitly gitignored brevduvan to avoid tracking it twice. The solution was obvious: remove brevduvan's dot-git, remove it from the parent's gitignore, and let it be what it always functionally was, a subdirectory in the utilities project. Five old GitHub repos archived. Three new combined repos created. The total repo count dropped, and every project now has exactly one home.
After the cleanup, there was one more task. Seventeen of twenty-six GitHub repos had no description. Just a name, often a cryptic one, and nothing else. From the GitHub dashboard, you could see parceptionUTIL and have no idea what it was. You could see are-radio and guess, but not know. You could see ttpanotis and not have the faintest clue.
Adding descriptions took two minutes. A single command per repo. But the act of writing them forced a kind of accounting. What is this project, in one sentence? Some were easy. "Voice interface for Claude while driving" for carvoice. "Roller derby lineup management for bench coaches" for bench. Others required thought. "EU political ad transparency tool with QR codes" for ttpanotis encodes not just what the tool does but why it exists, a regulation with a number, a compliance requirement, a specific mechanism.
The descriptions are mostly invisible. These are private repos. Nobody browses them. But descriptions serve the future self, the version of you six months from now who has forgotten which of your thirty projects does what. They serve the AI tools that will help you work on these projects and need context. And they serve as a forcing function for clarity. If you cannot describe your project in one sentence, you might not know what it is anymore.
Here is what the audit really revealed, underneath the naming mismatches and missing remotes and uncommitted work. A solo developer using Git as a backup and deploy mechanism, not as version control in the traditional sense.
Most of these projects had between one and ten commits. Not because they were small or inactive, but because the development pattern was: open Claude Code, describe what you want, watch it build the thing, test it, deploy it, move on. Come back weeks later, describe what you want changed, watch it get rebuilt, deploy, move on. The granular commit history that Git was designed for, the incremental record of how code evolved decision by decision, does not happen naturally when an AI writes your code in large coherent chunks.
This is not necessarily wrong. If your workflow is "AI builds the whole feature in one session," then a single commit per session is an accurate representation of what happened. There was one state, then there was a new state. The diff between them might be hundreds of lines, but there was no human journey through those lines. There were no intermediate decisions to record. The meaningful unit of work is the session, not the line change.
But it does mean that the safety net is thinner. With ten commits spread over two months, you have ten restore points. If the third session introduced a subtle bug that you do not notice until the seventh session, you have limited ability to bisect and find it. Compare that to a traditional development workflow with hundreds of small commits, where git bisect can pinpoint the exact change that broke things.
The practical recommendation is simple. Commit at the end of every session, even if it is just one big commit. Push immediately after. This gives you a restore point per session and an offsite backup. It is not the granular history that Git tutorials describe, but it is honest about the actual workflow, and honest version control is better than aspirational version control that never happens.
The most interesting thing about auditing someone's Git setup is what it reveals about how they actually work, as opposed to how they think they work or how best practices say they should work.
This setup had thirty projects running across three machines, two of them headless servers accessed only by SSH. Fifteen services deployed to production, serving real users. A TTS podcast pipeline that turns markdown into audio. A weather station that intercepts radio signals. A Slack bot fine-tuned on nineties Swedish absurdist writing. An EU regulatory compliance tool. A rental management system themed in pink. A voice interface for talking to an AI while driving.
None of these projects have tests in the traditional sense. They are tested by running them, by using them, by seeing if the weather report sounds right or if the booking dashboard shows the correct numbers. The version control is a safety net, not a development methodology. The deploys are rsync commands aliased to memorable names. The architecture is "FastAPI plus whatever database makes sense for this one."
And it works. Thirty projects, most of them running, most of them useful, built by someone who describes themselves as not a coder. The Git hygiene was imperfect, genuinely imperfect, with real risks like no backup on active projects. But the imperfection was not ignorance. It was the natural result of someone moving fast between many projects, always building the next thing, always focused on the feature rather than the infrastructure.
The cleanup session fixed the concrete risks. Every project now has a remote. The dead repos are gone. The duplicates are resolved. The combined repos make more structural sense. But the deeper lesson is about what "using Git properly" actually means for a solo developer working with AI. It does not mean feature branches and pull requests and code review. It means backups. It means knowing what you have. It means occasionally stopping to look at the full picture instead of one project at a time.
The fifteen million developers searching Stack Overflow for how to undo a commit are looking for a command. The real answer, for most of them, is not a command. It is a habit. Commit your work. Push it somewhere safe. Know where your code lives. Everything else is optional.
Git shortlog. Run git shortlog dash s dash n in any repository, and you get a list of every contributor sorted by number of commits. It is the quickest way to understand who built something.
Add dash dash since equals one year ago to see recent activity, or pipe in a branch range to compare contributions between releases. For solo developers it is a reality check on how active your projects really are. For team projects it tells you who the experts are. Simple command, surprisingly useful.