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.