23

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Hum6
  • 241
  • 1
  • 2
  • 3

3 Answers3

27

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.

Remotes

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.

Remote-tracking names

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.)


Branch names on the remote

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.

Summary

  • 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.
torek
  • 448,244
  • 59
  • 642
  • 775
9

To list branches:

# -a = all branches
$ git branch -a

# -r = remote branches
$ git branch -r

To remove branches

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>
Guillaume Poussel
  • 9,572
  • 2
  • 33
  • 42
CodeWizard
  • 128,036
  • 21
  • 144
  • 167
2

If you list remote references with --heads parameter specified, you will get the branches.

git ls-remote --heads

Yevgen
  • 1,576
  • 1
  • 15
  • 17