Your .git folder is growing, and you’re not sure why. Old commits you amended, branches you deleted, rebases you ran last week. They all leave behind orphaned objects that Git quietly holds onto.
So what does git prune do about it? It removes those unreachable objects from your local object database, freeing up disk space and keeping your repository clean.
But here’s the thing. Most developers never run it directly, and for good reason. Git prune is a piece of a larger garbage collection process, and using it wrong can cause real problems.
This article covers how git prune works, what it actually deletes, how it compares to git gc and git remote prune, whether it’s safe to run, and where it fits into a proper repository maintenance workflow.
What Is Git Prune
Git prune is a housekeeping command that removes unreachable objects from your local Git repository. These objects sit inside the .git/objects directory and no longer serve any purpose.
When you run git prune, Git scans the object database and deletes anything that isn’t connected to a branch, tag, or reflog entry. Commits, blobs, and tree objects that nothing points to anymore get wiped out.
The command only touches your local repository. It never reaches out to a remote repository or modifies anything on a server like GitHub.
Stack Overflow’s developer survey found that 93% of developers use Git as their primary version control system. With that kind of adoption, understanding maintenance commands like prune matters more than most people think.
One thing that trips people up: git prune is a plumbing command. Most developers never run it directly. It gets called automatically as part of git gc (garbage collection), which handles a broader set of cleanup tasks. But knowing what prune does on its own gives you a clearer picture of how Git manages storage internally.
If you’ve ever wondered why your .git folder keeps growing even after you delete branches or rewrite history, unreachable objects are usually the answer. Git prune is the command that actually removes them.
What Are Unreachable Objects in Git

Git stores everything as objects. Every file snapshot is a blob. Every directory listing is a tree. Every saved state is a commit. These objects connect to each other in a directed acyclic graph, and Git uses SHA-1 hashes to identify each one.
An object becomes “unreachable” when no ref points to it anymore. No branch, no tag, no reflog entry, nothing.
How Objects Become Orphaned
This happens more often than you’d expect. Here are the most common scenarios:
- Amending a commit: Running
git commit --amendcreates an entirely new commit object and abandons the old one - Rebasing: Git rebase rewrites your commit history, leaving behind the original commits as orphans
- Hard resets: Using git reset to move a branch pointer backward makes every commit after the new position unreachable
- Deleting branches: If you delete a branch in Git that had unmerged commits, those commits become dangling
History rewriting tools like git filter-branch or the BFG Repo Cleaner produce massive amounts of unreachable objects. I’ve seen repositories balloon by gigabytes after a large history rewrite, simply because the old objects were still sitting there.
Loose Objects vs Packed Objects
Git stores objects in two forms.
Loose objects are individual files in .git/objects, each named by its SHA-1 hash. Every new commit, blob, or tree starts life as a loose object.
Packed objects get compressed together into pack files. Git uses delta compression here, storing only the differences between similar objects. This is what makes repositories surprisingly small despite tracking every version of every file.
When Git accumulates roughly 6,700 loose objects (the default gc.auto threshold), it triggers automatic garbage collection to repack them. The GitHub Blog notes that GitHub stores over 18.6 petabytes of Git data across its servers, and efficient object packing is what makes that feasible.
How Git Prune Works

The mechanics are straightforward. Git prune walks through every reference in your repository, builds a complete list of reachable objects, and then deletes everything that didn’t make the cut.
The Reachability Traversal
Git starts from all known refs: branches, tags, and reflog entries. From each ref, it follows the commit graph downward through parent commits, trees, and blobs. Any object it touches during this traversal is marked as reachable.
Everything else gets flagged for deletion.
The reflog is the safety net here. Even if you amended a commit or did a hard reset, the old commit hash stays in the reflog for a default of 30 days (for unreachable entries) or 90 days (for reachable ones). As long as the reflog points to it, git prune won’t touch it.
Git Prune Syntax and Flags
| Flag | What It Does | When to Use |
|---|---|---|
| –dry-run / -n | Shows what would be removed without deleting anything | Always run this first |
| –verbose / -v | Prints the name of each deleted object | Debugging, auditing |
| –expire | Only prunes objects older than the given date | Safety buffer for recent work |
| –progress | Shows progress during the operation | Large repositories |
The basic syntax is just git prune. But you’d almost always want to run git prune -n first to preview what’s about to disappear.
The --expire flag adds a time-based guard. Running git prune --expire=2.weeks.ago only removes objects that have been sitting around for at least two weeks. That way, if something became unreachable five minutes ago because of an operation still in progress, you won’t accidentally destroy it.
Git Prune vs Git GC
This is the part that confuses most people. Git prune and git gc are not the same thing. Git prune is one step inside a larger cleanup process that git gc coordinates.
What Git GC Actually Does
When you run git gc, it calls several sub-commands in sequence:
git pruneto remove unreachable objectsgit repackto consolidate loose objects into pack filesgit reflog expireto clean up old reflog entriesgit rerere gcto clean up conflict resolution records
Git gc is the all-in-one cleanup. Git prune is the surgical removal of dead objects.
When Each One Runs Automatically
Git gc auto-triggers when loose objects exceed around 6,700 or when there are more than 50 pack files. Common Git commands like git commit, git merge, and git rebase all check whether auto-gc should fire.
Git prune never runs on its own. It only executes when called directly or through git gc.
The default gc.pruneExpire setting is 2 weeks. That means even when git gc triggers automatically, it won’t prune objects younger than 14 days. This protects you from losing something you might still need.
GitLab’s engineering team reported that switching to a smarter maintenance strategy halved their CPU load during repository optimizations on GitLab.com. That gives you a sense of how much compute these operations can demand at scale.
Which Should You Use
Almost always git gc. Or better yet, git maintenance run.
Running git prune directly is only useful in specific situations. Maybe you’ve already expired your reflogs manually and want to clean up immediately. Or you’re scripting a custom maintenance workflow and need granular control. For day-to-day work, git gc handles the full pipeline and git prune would just be one part of what you need.
Git Prune vs Git Remote Prune

These two commands share a name but do completely different things. Mixing them up is one of the most common Git mistakes I’ve seen.
What Git Remote Prune Does
git remote prune origin cleans up stale remote-tracking branches. These are the refs/remotes/origin/* references that your local repo keeps as a mirror of what exists on the remote.
If someone deleted a branch on the remote (say, after merging branches in GitHub), your local repo doesn’t automatically know about it. You’ll still see that remote-tracking branch locally until you prune it.
Running git remote prune origin removes those stale references. Alternatively, git fetch with the --prune flag does the same thing while also fetching new data.
Side-by-Side Comparison
| Aspect | git prune | git remote prune |
|---|---|---|
| Target | Unreachable objects in .git/objects | Stale remote-tracking branches |
| Scope | Local object database | Remote reference list |
| Frees disk space | Yes | Negligible |
| Risk level | Low (reflogs protect recent work) | Very low |
| Alternative | git gc | git fetch –prune |
GitHub now hosts over 630 million repositories, according to CoinLaw’s 2026 data. With that many repositories in active use, the distinction between pruning objects and pruning remote references is worth getting right.
Quick rule of thumb: if your git branch -r output shows branches that don’t exist on the remote anymore, you need git remote prune. If your .git folder is bloated with leftover commit data, you need git prune (or just run git gc).
When Git Creates Unreachable Objects

Understanding when orphaned objects appear helps you predict when pruning becomes necessary. Some workflows produce them constantly. Others almost never do.
Rebase-Heavy Workflows
Teams that rebase frequently before pushing to GitHub generate the most unreachable objects. Every interactive rebase rewrites the entire chain of affected commits. The originals don’t disappear; they just become dangling.
A developer who rebases ten commits daily creates ten orphaned commit objects, plus their associated trees and blobs, every single day. Multiply that across a team of 20 people and the objects pile up fast.
Commit Amendments and Resets
git commit –amend is probably the single most common source of unreachable objects for individual developers. Took me a while to realize that every amend creates a full copy of the old commit, not an in-place edit.
Git reset with the --hard flag is the other big one. Move your branch pointer back three commits and those three commits (plus everything they reference) become orphans.
Large History Rewrites
This is where it gets extreme.
Tools like git filter-repo and the BFG Repo Cleaner rewrite every affected commit in your history. One developer documented reducing a repository from 22.6 GB down to a manageable size after using BFG to strip large binary files, with thousands of objects flagged for deletion.
These rewrites can produce tens of thousands of unreachable objects in a single operation. Without running git gc or git prune afterward, your .git directory won’t shrink at all. The old data just sits there, taking up the same space it always did.
Other Scenarios That Create Orphans
- Interrupted or aborted merge operations
- Dropping commits during interactive rebase with git squash
- Switching between detached HEAD states without creating branches
- Running git clean on tracked files after a failed checkout
The Git workflow your team follows directly impacts how many orphaned objects accumulate. Teams using trunk-based development with frequent rebases will need more aggressive garbage collection than teams using long-lived feature branches with standard merges.
Is Git Prune Safe to Run
Yes. Under normal conditions, git prune is safe. Git has multiple layers of protection that prevent you from accidentally losing anything you might still need.
The biggest safety net is the reflog. And most developers don’t even know it’s working in the background.
How Reflogs Protect Your Work
Reachable reflog entries expire after 90 days by default. Unreachable entries expire after 30 days. As long as a commit appears anywhere in the reflog, git prune treats it as reachable and leaves it alone.
Atlassian’s Git documentation confirms that even after operations like reverting a commit in Git or amending, Git maintains references in the reflog. The old commit object stays protected.
The default gc.pruneExpire setting adds another buffer: objects younger than 2 weeks won’t be pruned regardless of reachability status.
When It Could Actually Cause Problems
Data loss from git prune only happens when you deliberately remove the safety nets first. Specifically:
- Running
git reflog expire --expire=now --allbefore pruning - Using
git prune --expire=nowwhile another Git operation is in progress - Manually deleting reflog files from
.git/logs/
The Gerrit project documentation notes that concurrent garbage collection on busy repositories can, in rare cases, cause data loss if an object becomes referenced by a new operation after the reachability check but before deletion. This is a known race condition, though it’s uncommon in practice.
Bottom line: always run git prune --dry-run first. If the output looks reasonable, you’re good.
How to Recover Objects After Git Prune
Once git prune deletes an object, it’s gone from your local repository. There’s no undo button. But the story doesn’t always end there.
Before Pruning: Git Reflog and Git Fsck
If you haven’t pruned yet, recovery is straightforward.
Git reflog shows every position HEAD has occupied recently. Find the commit hash you need, then create a new branch pointing to it with git branch recovery-branch abc1234.
Git fsck –lost-found scans the object database for dangling commits and blobs, then writes them to .git/lost-found/. One developer documented recovering a full day of work using this approach after an accidental git reset HEAD wiped their recent changes.
After Pruning: Your Options Are Limited
| Recovery Method | Works After Prune? | Conditions |
|---|---|---|
| git reflog | No | Reflog entries were pruned too |
| git fsck –lost-found | No | Objects no longer exist locally |
| Re-fetch from remote | Sometimes | Only if commits existed on the remote |
| Filesystem recovery tools | Rarely | Depends on OS, disk type, timing |
If the lost commits were previously pushed to a remote, you can pull them back from GitHub or whatever hosting service you use. That’s the most reliable recovery path after a local prune.
This is why source control best practices always include pushing regularly to a remote. Your remote is effectively a backup that git prune can’t touch.
Practical Use Cases for Git Prune
Most developers never need to run git prune manually. But there are specific situations where it (or its parent command, git gc) becomes necessary.
Heavy Rebase Workflows
Teams that rebase constantly accumulate orphaned objects at a much higher rate than merge-based teams. If your codebase has 20+ developers rebasing daily, the .git/objects directory can grow noticeably within weeks.
CoinLaw data shows GitHub hosts over 180 million developers as of 2026. Even a small fraction of those working in rebase-heavy workflows means millions of repositories accumulating dangling objects every day.
CI/CD Build Servers
Build servers are the biggest consumers of git housekeeping. They clone, fetch, and build continuously, often running dozens of operations per hour on the same repository.
GitHub recommends using git clone --depth=1 for CI pipelines that don’t need full history. But persistent build agents that maintain full clones still need periodic garbage collection to keep disk usage under control.
One GitHub community member reported that git gc --aggressive --prune=now reduced their .git directory from 287 MB to 33.4 MB, roughly an 88% reduction.
After Large History Rewrites
Running tools like git filter-repo or BFG Repo Cleaner to strip sensitive data or binary files leaves behind massive amounts of unreachable objects. The old history still exists in .git/objects until you explicitly clean it up.
The cleanup sequence after a rewrite typically looks like this:
git reflog expire --expire=now --allgit gc --prune=now --aggressive
Without those steps, your repository size won’t change at all despite rewriting every commit.
When You Don’t Need to Bother
If you’re a solo developer working on small projects, git gc runs automatically and handles everything. The auto-gc threshold of 6,700 loose objects is rarely hit in normal day-to-day work. Just let Git do its thing.
Git Prune in a Typical Maintenance Workflow
Git prune rarely exists in isolation. It fits into a broader configuration management and maintenance routine that keeps repositories clean and fast.
The Recommended Cleanup Sequence
If you’re running manual maintenance (instead of relying on auto-gc), the order matters:
Step 1: Expire reflog entries with git reflog expire --expire=2.weeks.ago --all
Step 2: Run git gc, which calls git prune, git repack, and other sub-commands
Step 3: Verify integrity with git fsck
Skipping step 1 means git prune won’t remove anything still referenced by the reflog. That’s usually fine (it’s a safety feature), but if you’re trying to reclaim space after a history rewrite, you’ll need to expire those entries first.
Git Maintenance: The Modern Replacement
The git maintenance command was introduced in Git 2.29 (October 2020) as a structured replacement for ad-hoc git gc invocations. It schedules background tasks on hourly, daily, and weekly intervals.
| Task | Frequency | What It Does |
|---|---|---|
| prefetch | Hourly | Pre-downloads objects from remotes |
| commit-graph | Hourly | Updates commit-graph for faster traversals |
| loose-objects | Daily | Packs loose objects into pack files |
| incremental-repack | Daily | Optimizes pack files using multi-pack index |
| gc | Weekly (optional) | Full garbage collection, including pruning |
Running git maintenance start registers your repository and sets up the schedule automatically. The Git 2.52 release introduced a new geometric repack strategy within git maintenance that prunes unreachable objects less frequently but more efficiently, according to the GitHub Blog.
GitLab’s engineering team built their own heuristic optimization strategy around these principles. Their approach scans for loose objects older than two weeks and only triggers git prune when a certain threshold is exceeded, which cut CPU load by more than 50% on GitLab.com.
Server-Side vs Local Maintenance
Local repositories and server-side bare repositories have different maintenance needs.
Local: git maintenance start is usually enough. The DevOps team rarely needs to intervene unless the repository is unusually large.
Server-side: Hosting platforms like GitHub and GitLab run their own garbage collection processes. GitHub stores over 18.6 petabytes of Git data and uses custom-engineered GC pipelines to handle repositories at that scale, as described on the GitHub Blog.
For teams running self-hosted Git servers (Gitea, Gerrit, self-managed GitLab), scheduling git gc or git maintenance run through cron jobs is standard practice. The collaboration between dev and ops teams determines how aggressive the cleanup schedule should be. Build servers with continuous integration pipelines typically need more frequent maintenance than repositories that only receive a handful of pushes per week.
FAQ on What Does Git Prune Do
Does git prune delete branches?
No. Git prune only removes unreachable objects (commits, blobs, trees) from the local object database. It does not touch branches, tags, or any references. To clean up stale remote-tracking branches, use git remote prune origin or git fetch --prune instead.
Is git prune the same as git gc?
Not exactly. Git gc is the parent command that runs git prune alongside git repack, git reflog expire, and git rerere gc. Git prune handles only the object deletion step. Most developers should run git gc rather than git prune directly.
Will git prune remove my recent commits?
No, as long as the reflog still references them. By default, Git keeps reflog entries for 90 days (reachable) and 30 days (unreachable). The default gc.pruneExpire setting also protects objects younger than two weeks.
How do I preview what git prune will delete?
Run git prune --dry-run (or git prune -n). This prints the SHA-1 hashes of every object that would be removed without actually deleting anything. Always use this flag first, especially after large history rewrites.
Does git prune affect the remote repository?
No. Git prune operates exclusively on your local object database inside the .git/objects directory. It never communicates with a remote server. Remote repositories have their own separate garbage collection processes managed by the hosting platform.
How often should I run git prune?
You almost never need to run it manually. Git triggers automatic garbage collection (which includes pruning) when loose objects exceed roughly 6,700. For most workflows, letting git gc –auto handle it is sufficient.
What is the difference between git prune and git remote prune?
git prune removes unreachable objects from the local object database. git remote prune removes stale remote-tracking branch references that no longer exist on the remote. They target completely different things despite sharing a name.
Can I recover objects after running git prune?
Not locally. Once pruned, objects are permanently deleted from your machine. If the commits were previously pushed, you can re-fetch them from the remote. That’s why pushing to a remote repository regularly is a reliable backup strategy.
Does git prune speed up my repository?
Indirectly, yes. Removing unreachable loose objects reduces the number of files Git must scan during operations like git gc and git fsck. The bigger performance gains come from repacking, which git gc handles alongside pruning.
Should I use git prune or git maintenance?
Use git maintenance if you’re on Git 2.29 or later. It schedules background tasks (including garbage collection and pruning) automatically on hourly, daily, and weekly intervals. It’s the modern, hands-off approach to repository upkeep.
Conclusion
Understanding what does git prune do comes down to one thing: it removes unreachable objects from your local Git object database. Dangling commits, orphaned blobs, abandoned tree objects. All gone.
But you probably shouldn’t run it on its own. Let git gc or git maintenance handle the full cleanup pipeline, including repacking, reflog expiration, and pruning together.
The reflog keeps you safe for 90 days by default. The –dry-run` flag lets you preview before deleting. And pushing to a remote gives you a backup that no local prune can touch.
For teams running rebase-heavy workflows or managing large repositories on CI/CD build servers, periodic garbage collection is worth scheduling. Solo developers on smaller projects can let auto-gc do the work.
Know what git prune deletes, know what protects you from mistakes, and you’ll never stress about repository maintenance again.
- How to Make a Repository Public in GitHub - July 14, 2026
- Production Incident Communication Without Separate Monitoring and Status-Page Systems - July 13, 2026
- How to Set Up Subscriptions on Google Play (Developer Guide) - July 12, 2026



