GitPruning Objects (git prune)

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

Tip
Unless you have a specific reason to call `git prune` directly, use `git gc` instead. `git gc` is safer because it respects the 2-week grace period and also handles packing, reflog expiry, and commit graph updates. Direct `git prune` skips some of those safeguards.
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

Bash
git prune --dry-run

Example dry-run output

Text
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

Bash
git prune --expire=now
Warning
Pruned objects are gone forever. The 2-week grace period exists for good reason: if two Git processes are running concurrently (common in CI/CD environments), one process might write an object that the other has not yet referenced. Bypassing the grace period with `--expire=now` can corrupt a repository that is actively in use. Only use this on a repository you know is completely idle and has no concurrent access.
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

Bash
# Preview first
git remote prune origin --dry-run

# Then prune
git remote prune origin

Example output

Text
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
Note
`git remote prune` only removes remote-tracking refs — it does NOT touch your local branches or any objects. It is completely safe and can be run at any time.
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

Bash
git fetch --prune
# or shorter alias
git fetch -p

Example output

Text
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

Bash
# 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

Bash
# 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 false

  • You can manually expire: git reflog expire --expire=now --all