Git `remote.origin.prune` vs `fetch.prune`: Which Should You Set?

Published · Updated · Programming

cover image for article

Remote-tracking branches are Git's cached view of branches on another server: origin/feature/login, not your local feature/login. When a branch is deleted on GitHub or GitLab, Git does not remove that cached name until a fetch with pruning asks it to. The result is familiar: a branch appears in git branch -r, but the server says it no longer exists.

The two configuration keys solve the same problem at different scope. The difference matters most once you use more than one remote.

What pruning actually deletes

Pruning deletes stale remote-tracking references. It does not delete local branches, commits, working-tree files, or a branch on the server.

git fetch --prune origin

After that command, origin/feature/login disappears locally only when it is no longer advertised by origin. A local feature/login remains, even if its upstream is gone. That separation is intentional: a deleted pull-request branch can still contain local work worth recovering.

Use a dry inspection before assuming the deletion was expected:

git fetch --prune --dry-run origin
git branch -vv

The first command shows stale tracking references that would be removed. The second identifies local branches still configured to track them.

remote.origin.prune is scoped to one remote

git config remote.origin.prune true

This enables pruning when Git fetches origin. It is the right choice when origin is your everyday collaboration remote but another remote is an archive, a vendor mirror, or something whose old refs you deliberately want to retain for a while.

The origin part is simply the remote name. For an upstream fork relationship, you might use:

git config remote.upstream.prune true

fetch.prune is the default for all remotes

git config --global fetch.prune true

This sets the default pruning behavior for every fetched remote. A global setting is usually reasonable for a developer machine with normal short-lived feature branches. You can override it for one repository or remote when a mirror needs different retention behavior:

git config remote.archive.prune false

Think in terms of policy: fetch.prune supplies the broad default; remote.<name>.prune expresses an exception or a more precise local rule.

Why prune, and when not to overthink it

Pruning reduces visual noise in branch pickers, shell completion, and review scripts. Its main benefit is correctness of your local mental model, not a meaningful performance gain on an ordinary repository. A huge number of stale refs can make some commands noisier, but "speed up Git" is not a good primary reason to enable it.

Be cautious if a script treats remote-tracking references as an archive. Fetch them into a real local branch, tag the relevant commit, or retain a separate mirror. A cache is not a retention policy.

A sane setup

For most people, this is enough:

git config --global fetch.prune true
git config --global fetch.pruneTags true

Then inspect branch tracking before deleting local work:

git fetch --prune
git branch -vv

If the repository has just moved, update its URL before fetching; the remote-migration workflow covers that without discarding your clone. If pruning reveals an upstream mismatch, avoid an impulsive force-push and use the rebase-versus-merge guide to choose the next step.

More practical engineering notes

Slaptijack's Koding Kraken