The Day Git Refused My Push

One day, git simply stopped letting me save my work. My push failed with a server error I had never seen before — not a merge conflict, not a permission problem, just a flat refusal from the other end. I had been committing happily for weeks, and now the most routine command in my day would not complete.

The cause was not a bug in git. It was that I had spent those weeks quietly stuffing my repository with thousands of things that had no business being there, and it had finally grown too heavy to lift.

The push that wouldn’t go

At first I assumed it was a fluke — a network hiccup, a server having a bad moment. But it kept happening, reliably, every time. The repository’s accumulated bulk appeared to be the cause: the push kept failing the same way every time, and no amount of retrying was going to fix a problem that was structural rather than transient.

When I finally looked at what the repository actually contained, the answer was embarrassing. The code — the thing a repository is for — was a small minority of it. The overwhelming bulk was data: thousands of generated files, caches, and compiled artifacts, each one faithfully tracked, versioned, and carried along in every operation.

How a repo quietly becomes a warehouse

Nobody decides to put gigabytes of data into version control. It happens one reasonable-seeming step at a time. You generate a few files and they land in your project folder. You run an add command, maybe a little carelessly, and they come along. It works. Nothing complains. So you do it again, and again, and the repository accretes data the way a drawer accretes clutter — invisibly, until the day it will not close.

By the time git refused my push, I was tracking thousands of data files. I had never made the conscious choice to put them under version control. I had simply never made the choice to keep them out.

Data and code want opposite things

The reason mixing them hurts is that they are versioned for completely different reasons. Code is small, meaningful, and changes in ways you want to see line by line — git is built precisely for that. Data is large, opaque, and tends to change wholesale; a line-by-line diff of it tells you almost nothing, and storing every past version of it forever is mostly waste. Git is wonderful at the first job; its standard history is often a poor fit for the second, at least once the data gets large or changes constantly.

Put them together and you get the worst of both. Your history bloats with enormous changes you can never meaningfully read, every clone and push drags all of it around, and the small, precious thing — the actual source — ends up buried in a repository that is mostly ballast.

The fix: draw the boundary you skipped

The repair was conceptually trivial and slightly tedious: tell git, explicitly, what not to track. The generated files, the caches, the compiled artifacts — all of it got excluded, and the repository went back to being what it should always have been: a home for the things I had intentionally authored — code, configuration, docs, tests — rather than bulk generated output. The data moved to where large, regenerable things belong — produced on demand, or stored somewhere built for big files, but never again versioned line by line as though it were source.

The push went through on the first try. The fix was not clever. It was just a boundary I should have drawn on the first day and never had.

The habit underneath: source versus derived

The lasting lesson is not really about git. It is a question I now ask about every file a project produces: did I author this, or did my code generate it? Is it source — the irreplaceable thing a human wrote — or is it derived, an output that can be recreated by running the source again?

Source generally belongs in version control. Large or bulky derived files usually do not — they should be committed only when reproducibility or workflow genuinely calls for it, and ideally remain reproducible from the source at any time. Many of the repositories that rot into an unmanageable mess do so by losing track of that distinction — by treating something derived as though it were precious, and letting the things that can always be regenerated pile up next to the things that can never be replaced.

Git did not break that day. It just finally made me notice that I had stopped keeping source and output in their separate places, and had been asking one tool to do a job it was never built for.

— No signals, no returns, not investment advice.