Keeping Git Fast at Scale
Big repos get slow because git walks more objects. A blobless partial clone (--filter=blob:none), cone-mode sparse-checkout, the commit-graph and fsmonitor cut the walking, so most commands stay fast as the repo grows.
Keeping git fast at scale is mostly about doing less work per command, not buying a faster disk. Four features carry the load: a partial clone that skips file contents, cone-mode sparse-checkout that keeps your working tree small, the commit-graph that caches history walks, and fsmonitor that stops git status scanning every file. None of them are new, and most people never turn them on.
The topic came up again after Cursor published Git at any scale in August 2026. Their post is about the hosting side, a write-ahead log in S3 behind their Origin service hitting 120 pushes a second on S3 Standard and scaling reads across a hundred replicas. Impressive, and not your problem. What follows is the client side, the part you can change today without rebuilding version control.
Why git slows down as a repo grows
Git is fast on small things and gets slower as three numbers climb: the number of objects it has to walk, the size of the working tree it has to stat, and the amount of history it has to parse. A monorepo pushes all three. A git status on a working tree with 200,000 files spends its time asking the filesystem about files you never touch. A git log on a decade of history parses commit objects one at a time. That is the real shape of git at scale, and each fix below attacks one of those numbers.
Clone less: partial clone
A full clone downloads every version of every file that ever existed. You need almost none of it. A blobless clone skips file contents and fetches them on demand:
git clone --filter=blob:none https://example.com/big/repo.git
You get the full commit and tree history, so git log and git blame work, but blobs arrive only when a checkout or diff actually needs them. On a repo with a heavy binary history this turns a coffee-break clone into something that finishes while you’re reading the URL.
Check out less: sparse-checkout
If you only work in a couple of directories, don’t materialise the whole tree. Cone-mode sparse-checkout, stable since Git 2.25, writes only the paths you name:
git sparse-checkout init --cone
git sparse-checkout set services/api libs/shared
Now your working tree holds services/api and libs/shared and their parents, nothing else. Every command that stats files, status, checkout, commit, has less to look at. Combine it with --filter=blob:none and you download less and check out less at once.
Walk less: the commit-graph
History operations traverse the commit DAG, and parsing raw commit objects to do it is slow once there are hundreds of thousands. The commit-graph precomputes that structure into one file:
git commit-graph write --reachable
git log --graph, git merge-base, and the ahead/behind counts your prompt shows all get faster because git reads the cache instead of the objects. It’s just a cache, so it’s safe to regenerate, and modern git writes it during gc and fetch when fetch.writeCommitGraph is on.
Stat less: fsmonitor
The last one is the biggest single win on a large working tree. git status normally stats every tracked file to see what changed. fsmonitor, built into git since 2.37, runs a daemon that watches the filesystem and tells git which files moved, so status skips the full scan:
git config core.fsmonitor true
git config core.untrackedcache true
On a tree with a few hundred thousand files this drops git status from seconds to under a hundred milliseconds. The untracked-cache line saves the other half, caching the scan for untracked files too.
Turn on all four and the repo stops feeling like it’s fighting you. The fix for git at scale was in the box the whole time.