I have tried git remote -r
to view all remote existing branches, but I found the result includes those branches that are deleted remotely.
Is there a way to filter out these "zombie" branches?
I have tried git remote -r
to view all remote existing branches, but I found the result includes those branches that are deleted remotely.
Is there a way to filter out these "zombie" branches?
This question has several possible underlying meanings, so it has several possible answers. The key issue here is that there are at least two different Git repositories involved.
Before we get to the two answers, let's mention that a remote is just a short name like origin
. The remote itself holds the URL by which your Git calls up some other Git. You can have as many remotes as you like. There are additional things you can do with a remote, besides just storing a URL, but storing a URL is sort of the main thing. The git remote
command is the main user-facing command for working with remotes, but since git clone
creates the remote named origin
for you initially, you almost never actually need to use git remote
.
Your Git, on your computer, keeps and updates your Git repository. Your Git has your branch names, tag names, and other names, and a collection of commits. The commits are the part that your Git shares with other Git repositories, though there is some degree of name-sharing as well.
Meanwhile, because you Git has called up another Git before, and remembers the URL for that other Git under the name origin
, your Git also remembers their Git's branch names. To do this, your Git creates, in your repository, remote-tracking names1 such as origin/master
. These names simply remember that, the last time your Git called up the other Git at origin
, they said that their master
was some particular hash ID.
When you run git fetch origin
(or just git fetch
, if there's only one remote named origin
), your Git calls up the other Git at that point, obtains any new commits that they have that you don't, and updates all your remote-tracking origin/*
names.
Running git branch -r
will list your remote-tracking names, so git branch -r
shows you what your Git saw in their Git, the last time your Git updated using their Git. Note that git branch -a
includes git branch -r
, but adds the word remotes/
in front of the origin/master
names.2
One problem that occurs here, though, is that they can delete some branch name(s). Suppose, for instance, that their Git had a branch named feature/tall
yesterday, but no longer has that branch. Your Git called up their Git yesterday, and created or updated your origin/feature/tall
to match their feature/tall
. Now your Git calls up their Git and they don't list a feature/tall
.
By default, your Git does nothing about this.
If you run git fetch --prune
, however, your Git calls up their Git as usual, lists out all their branch names, and discovers that you have a stale origin/feature/tall
, made back when they had feature/tall
, and removes it.
So in general, you should add --prune
to each git fetch
, so that your Git's remote-tracking names are deleted as soon as your Git notices that they are stale (but not before then). You can configure your Git to do this automatically:
git config fetch.prune true
You can configure your Git to have this as its default for all your repositories, on your laptop or whatever machine you use, with:
git config --global fetch.prune true
See the git config
documentation for a (very long) list of everything git config
can actually do.
1The Git documentation mostly calls these remote-tracking branch names, but I think the meaning is eventually clearer if we leave the word branch out of here.
2All of Git's names—branch names, tag names, remote-tracking names, and other such names—live in namespaces and have longer, fully-qualified names to make them explicit. For instance, your master
branch is really refs/heads/master
; your tag v1.2
, if you have one, is really refs/tags/v1.2
. Remote-tracking names like origin/master
are really refs/remotes/origin/master
.
Git normally strips off the refs/heads/
part of branch names, the refs/tags/
part of tag names, and the refs/remotes/
part of remote-tracking names, when showing you these abbreviated names. For some reason, git branch -a
only strips off the refs/
part when showing remote-tracking names, even though git branch -r
strips off the refs/remotes/
part. (The reason for this is not clear to me.)
Because there is a second Git involved here, you could also just have your Git call it up right now, have it list out all its branch names, and have your Git print those names. The git ls-remote
command does exactly this:
git ls-remote origin
calls up the Git at origin
, has them list out their branch and tag and other such names, and then prints all of them. (It does not update any of your remote-tracking names: that's left to git fetch
, and also to git remote update
.)
The git remote show
command can also call up the other Git and get information directly from it, and show that. Sometimes it does and sometimes it does not. This is documented, though not terribly clearly; see the git remote
documentation for details.
git branch -r
shows your Git's remote-tracking names. This is quick as it is entirely local. But it may be out of date.
git fetch
updates your Git's remote-tracking names; add -p
or --prune
to make it clean out stale ones, or set fetch.prune
to true. This is slow since it also adds any new commits they have that you don't, but afterward, your remote-tracking names are quick to use.
git remote update
basically does the same thing as git fetch
. Since git fetch
is shorter to type in, I recommend using git fetch
instead.
git ls-remote
calls up another Git and gets its branch names. This is somewhat slow, but not as slow as git fetch
if there are new commits, as it does not obtain the commits—only the names and corresponding hash IDs.
git remote show
sometimes works locally, and sometimes calls up the other Git. I find its behavior somewhat confusing and not very helpful, and generally recommend avoiding it.# -a = all branches
$ git branch -a
# -r = remote branches
$ git branch -r
git fetch
git fetch
will update your local repository with the content form the remote.
Adding --prune
will remove deleted branch from the remote but will not delete them locally if they are checked out
$ git fetch --all --prune
git remote prune
In order to clean up remote-tracking branches, meaning deleting references to non-existing remote branches, use the “git remote prune” command and specify the remote name.
$ git remote prune <remote name>
If you list remote references with --heads
parameter specified, you will get the branches.
git ls-remote --heads