git prune
git prune removes unreachable loose objects from the .git/objects directory — objects that cannot be reached by traversing commits, trees, or blobs from any reference (branch, tag, HEAD, stash, or reflog). In most workflows, you will never call git prune directly because git gc calls it internally. However, understanding prune is important because the term appears in several distinct Git contexts, including remote ref pruning.
git prune vs git gc
Command | What it does | When to use |
|---|---|---|
git prune | Removes unreachable loose objects from .git/objects | Rarely — gc calls this for you |
git gc | Full housekeeping: packs objects, runs prune, expires reflog, more | Preferred over prune directly |
git remote prune | Removes stale remote-tracking refs (branches deleted on remote) | Safe to run anytime |
git fetch --prune | Fetch + prune stale remote-tracking refs in one step | Everyday workflow |
Dry Run: Preview Before Pruning
Use --dry-run (or -n) to see which objects would be removed without actually deleting anything. This is a safe way to understand what prune would do before committing.
Preview prune without deleting
git prune --dry-run
Example dry-run output
would prune 4b825dc642cb6eb9a060e54bf8d69288fbee4904 would prune 9daeafb9864cf43055ae93beb0afd6c7d144bfa4 would prune e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
git prune --expire=now
By default, git prune respects a grace period (gc.pruneExpire, typically 2 weeks) and will NOT remove objects that are younger than that. The --expire=now flag overrides this, removing all unreachable objects regardless of age.
Prune all unreachable objects immediately
git prune --expire=now
git remote prune: Cleaning Stale Remote Refs
When a branch is deleted on a remote (like GitHub), your local repository still has a remote-tracking reference for it under refs/remotes/origin/. These stale refs are harmless but confusing. git remote prune removes them cleanly.
Remove stale remote-tracking refs
# Preview first git remote prune origin --dry-run # Then prune git remote prune origin
Example output
Pruning origin URL: https://github.com/user/repo.git * [pruned] origin/feature/old-login * [pruned] origin/feature/deprecated-api * [pruned] origin/hotfix/fix-null-pointer
git fetch --prune: The Recommended Approach
Instead of running fetch and prune as two separate steps, --prune on a fetch operation combines both. This is the most common and convenient way to keep remote-tracking refs current.
Fetch and prune stale refs in one command
git fetch --prune # or shorter alias git fetch -p
Example output
From https://github.com/user/repo - [deleted] (none) -> origin/feature/old-login - [deleted] (none) -> origin/hotfix/fix-null-pointer a1b2c3d..e4f5g6h main -> origin/main * [new branch] feature/new-auth -> origin/feature/new-auth
Always Prune on Fetch (Config)
If you always want pruning on every fetch, configure Git globally so you never need to remember the --prune flag.
Configure automatic pruning on every fetch
# Always prune when fetching from origin git config remote.origin.prune true # Or set globally for all remotes git config --global fetch.prune true
The Reflog as a Safety Net
Even after pruning loose objects, the reflog keeps commit references alive for 90 days (configurable via gc.reflogExpire). This means that as long as the reflog has an entry pointing to a commit, that commit and all its ancestors are considered reachable and will NOT be pruned — even if no branch points to them.
Check the reflog before pruning
# See all recent HEAD positions
git reflog
# See reflog for a specific branch
git reflog show main
# Recover a commit that was reset away (still in reflog)
git checkout -b recovery-branch HEAD@{3}Reflog is per-repo and per-clone — it does not exist on the remote
Default expiry: 90 days for reachable entries, 30 days for unreachable
To disable reflog (not recommended):
git config core.logAllRefUpdates falseYou can manually expire:
git reflog expire --expire=now --all