TL;DR
Run:
git fetch --prune
(which can be abbreviated to git fetch -p if you prefer), or:
git remote origin prune
These should both do the same thing, but in some versions of Git (I can't remember which ones except that they were fairly old, maybe 1.8-ish era) one of the two has a bug that fails to prune some remote-tracking names.
Long-ish
When you run git fetch, your Git:
- calls up another Git, typically at some internet URL
- has that Git list out all of its branch names (and other names) and the commit hash IDs that go with them;
- check to see if that other Git has commits that you don't have, that you want;
- gets those commits from the other Git; and, last,
- creates or updates remote-tracking names for some or all of the branch names the other Git listed.
(The "some or all" in the last part accounts for the fact that some ways of running git fetch limit how many names your Git asks for and/or looks-at. We won't use these here, but note that if you have set up a single-branch clone, you have asked your Git to default to only looking at that one name. I will assume here that you have not set up a single-branch clone.1)
The problem here is simple enough. Suppose that on Monday, you run git fetch or git fetch origin, and your Git calls up a Git at some GitHub URL or whatever. Their Git, at this time, has three branches: master, feature/a, and feature/b. So your Git creates or updates your origin/master, your origin/feature/a, and your origin/feature/b.
Now it's Tuesday, and you run git fetch again. Your Git calls up their Git and they're down to just master because both features have been merged. So your Git gets any new commits needed and then updates your origin/master. There is no feature/a; there is no feature/b; so your Git does nothing with these two names that don't exist. Your origin/feature/a is left alone, and your origin/feature/b is left alone.
You now have two stale remote-tracking names!
You could manually delete them:
git branch -r -d origin/feature/a origin/feature/b
but it is a lot more convenient to have your Git recognize that, gee, it must have created origin/feature/a and origin/feature/b on its own, so maybe it should delete them on its own now too. The --prune option does exactly that: it takes the full list that they gave you, and uses that to whittle away any remote-tracking names that must be left over from earlier.
1To find out, run:
git config --get-all remote.origin.fetch
If this prints the single line:
+refs/heads/*:refs/remotes/origin/*
you have a standard, everyday setup. If not, you may have a single-branch clone; at the very least, you set up something unusual.
You can set up pruning as your default
Using:
git config --global fetch.prune true
you can tell Git that you want git fetch to default to git fetch --prune for all your repositories (that's the --global part). Or, leave out the --global to set this setting for one particular repository.
The only drawback I know of, which is quite slight, is that if someone accidentally deletes some branch on origin, and you have this option set and run git fetch origin, your Git deletes your remote-tracking name, which you could have used earlier to restore the branch name in the Git at origin. In other words, it changes your setup so that you are less able to rescue other people who did something dumb. If rescuing others is not high on your to-do list, that's pretty minor.