SpaceFreeSpaceFree
← All guides

Why your .git folder keeps growing, and how to check it before running git gc

By Bhavik · Sources checked September 22, 2026

What is actually inside .git

A repository does not just store your current files. It stores every version of every file you have ever committed, plus history for branches you deleted and commits you amended away. That history lives as objects inside .git, and by default Git keeps growing that folder rather than shrinking it, since removing history is a destructive operation it will not do on its own.

Objects sit in one of two states. Freshly committed ones start as loose objects, one file each. Git later bundles many of them into a single pack file, which is smaller and faster to work with. Git's own documentation for git gc describes it as running housekeeping tasks including "compressing file revisions (to reduce disk space and increase performance)" and "removing unreachable objects which may have been created from prior invocations of git add" (git gc).

Step 1: measure before you touch anything

git count-objects -v reports exactly what is in the object database, with no changes made. Run it from inside the repository:

git count-objects -v

Git's documentation defines each field: count is "the number of loose objects", size is the disk space they use, in-pack and size-pack cover objects already bundled into packs, prune-packable is "the number of loose objects that are also present in the packs" (so they are redundant and could be pruned), and garbage is "the number of files in the object database that are neither valid loose objects nor valid packs" (git count-objects).

A high loose object count, or a non-zero prune-packable or garbage number, means there is real, measurable savings available. A repository that is mostly packed with nothing prunable is already about as small as Git will make it without rewriting history.

Step 2: find what is actually taking the space

Repositories rarely bloat evenly. Usually a handful of large files, a committed dependency folder, a build artifact, an accidentally checked-in video or database dump, account for most of the size. You can list every object in history sorted by size:

git rev-list --objects --all | git cat-file --batch-check="%(objecttype) %(objectsize) %(objectname) %(rest)" | sort -k2 -n -r | head -20
This only tells you what is large, not that git gc will remove it. If a large file is still reachable, meaning some commit that is part of your current history still references it, it stays in the repository no matter how many times you run gc. Removing it for good means rewriting history with a tool built for that, which is destructive and out of scope for this guide. Knowing it is there is still useful, since it explains why the .git folder is the size it is.

Step 3: run git gc

The plain command is safe to run on any repository:

git gc

This packs loose objects and prunes ones that are truly unreachable, meaning not referenced by any branch, tag or reflog entry. Git's docs for git fsck define an unreachable object as one that "isn't actually referred to directly or indirectly in any of the trees or commits seen" (git fsck). By default, git gc only prunes loose objects older than two weeks: "Prune loose objects older than date (default is 2 weeks ago, overridable by the config variable gc.pruneExpire)" (git gc). That delay exists on purpose, so an object another process just wrote but has not linked to a commit yet is not deleted out from under it.

Git also documents a --prune=now option that skips the two week delay. Its own warning is direct: "--prune=now prunes loose objects regardless of their age and increases the risk of corruption if another process is writing to the repository concurrently" (git gc). There is rarely a good reason to reach for it on a repository you care about; the default two week window costs you nothing.

Is --aggressive worth it?

Git's own documentation is unusually blunt about this flag. It recomputes every delta from scratch instead of reusing existing ones, which "will throw away any existing deltas and re-compute them, at the expense of spending much more time on the repacking." Its verdict: "Not using this at all is the right trade-off for most users and their repositories" (git gc).

In practice that means a plain git gc is the right first step almost every time. Save --aggressive for a repository you have already measured and confirmed is still large after a normal gc.

Two smaller things worth checking

The reflog keeps a local record of where your branches used to point, which is what lets git reflog rescue a commit you thought you lost. Git keeps entries for 90 days by default if they are still reachable, and 30 days if they are not: "gc.reflogExpire, which in turn defaults to 90 days" and "gc.reflogExpireUnreachable... defaults to 30 days" (git reflog). Until those windows pass, the commits they point to cannot be fully pruned even by git gc, which is normal and usually worth the safety net.

Old remote-tracking branches also add up in a repository you have had checked out a long time. git remote prune "delete[s] stale references", specifically remote-tracking branches for branches that no longer exist on the remote (git remote). It only removes references, not any commit data, so it is low risk:

git remote prune origin

Doing this without the command line

SpaceFree's git repository scanner runs the same count-objects check and the same large-blob search described above across every repository it finds, so you can see which ones are actually worth optimizing before running anything. When you ask it to clean one up, it runs a plain git gc, never --prune=now, and checks for a repo already in use by another program first so it does not start work that could corrupt it. Download SpaceFree.

Sources