Back to Blog

What Nobody Tells You About Git Maintenance: Automating the Cleanup Your Repo Actually Needs

What Nobody Tells You About Git Maintenance: Automating the Cleanup Your Repo Actually Needs

You run git status and wait. That split-second delay where the cursor hangs is the first sign that your .git folder is becoming a junk drawer. Git is essentially a content-addressable filesystem, and every time you git add or git commit, you're generating loose objects. Eventually, searching through thousands of tiny files makes every command feel like it’s wading through thick molasses.

Most of us were taught to run git gc when things get slow. We run it, wait for the compression bars to finish, and things feel snappy for a week. Then the bloat returns. Manually running garbage collection is a losing game because humans are terrible at remembering to do chores until the house is on fire.

The "New" Way: git maintenance

Introduced around Git 2.29, the git maintenance command is the solution nobody seems to talk about. Instead of you deciding when the repo is too slow, Git sets up a background schedule to clean itself up.

To get started in your current repository, run:

git maintenance register
git maintenance start

That’s it. You’re done.

Well, not quite—you should know what just happened under the hood. When you run register, Git adds the path of your current repository to a global configuration file (usually ~/.gitconfig). When you run start, Git hooks into your operating system’s scheduler (systemd timers on Linux, launchd on macOS, or Task Scheduler on Windows) to run background tasks.

What is Git actually doing while you sleep?

Git maintenance isn't just running git gc over and over. It breaks down optimization into a few specific tasks that run at different intervals.

1. The Commit-Graph (Hourly)

This is the single biggest performance win for large repos. Git has to "walk" the commit graph for almost everything—calculating merges, finding the base of a branch, or even just showing a log. The commit-graph task builds a structured index of your history. Instead of Git parsing raw object files to find a parent commit, it hits this index. It’s the difference between searching a library by looking at every book cover versus using the digital catalog.

2. Prefetch (Hourly)

I love this one. The prefetch task downloads the latest objects from your remotes every hour but *doesn't* update your local branches. It puts them into a special refs/prefetch/ namespace.

When you finally sit down to run a manual git fetch or git pull, the heavy lifting (downloading the data) is already done. Your fetch will finish almost instantly because the objects are already sitting in your .git folder.

3. Loose Object Packing (Daily)

Every time you save a file, Git creates a "loose object." If you have 500 loose objects, Git has to open 500 separate files to read them. The daily maintenance task bundles these into "packfiles." Reading one large file is significantly faster for your OS than reading hundreds of tiny ones.

Checking your schedule

If you're curious about what Git set up on your machine, you can peek at the scheduler. On macOS or Linux, you can usually see the systemd or launchd entries. But an easier way is to check the Git config:

git config --global --list

Look for maintenance.strategy. By default, it’s set to incremental, which handles the hourly/daily/weekly split I mentioned above.

If you want to see if it’s actually working, check the list of registered repos:

git config --global --get-all maintenance.repo

Why shouldn't you just use git gc --auto?

Git has had an "auto" garbage collection feature for years. You’ve probably seen the message: Auto packing the repository in background for optimum performance.

The problem is that git gc --auto is reactive and disruptive. It triggers *after* a command that creates a lot of objects. It runs in the background, but it still eats up CPU cycles while you’re actively trying to work.

git maintenance is proactive. It moves the heavy lifting to when your computer is idle or at set intervals, so you never hit that "performance wall" in the middle of a sprint.

A small warning for the "Mono-repo" crowd

If you are working in a massive mono-repo with millions of objects, git maintenance is mandatory, not optional. However, be aware that the first time it runs a full pack-files task (weekly), it might spike your CPU.

I found that on my older laptop, I preferred to tweak the schedule to only run during lunch hours. You can customize the tasks if the defaults feel too aggressive:

# Disable the daily loose-object task if you prefer
git config maintenance.loose-objects.enabled false

The "Set it and Forget it" Reality

I started registering my main work repos for maintenance about a year ago. Since then, I haven't run git gc a single time. git log --graph stays fast, and my morning git fetch happens so quickly I sometimes think it failed.

If you're jumping between five different projects a day, take ten seconds to git maintenance register in each of them. Your future self, who isn't waiting for a hung terminal, will thank you.