Cleaning Up Git Branches

Navigating a git repository can get unwieldy when there are lots of branches, especially if many of those branches aren’t relevant anymore. The other day we realized that we had 192 branches on our front-end codebase, 127 of which were already merged.

Time to clean up!

TL;DR

You can chain together unix commands to do all the cleanup in a single go.

$ git checkout master && git branch -r --merged | grep -v master | sed -e 's/origin\//:/' | xargs git push origin

If we want to do the same cleanup locally, the command is:

$ git checkout master && git branch --merged | grep -v master | xargs git branch -d

Unpacking the command

On a normal day, you might type git push. Git is smart enough to know that you probably want to push to a remote named origin, and that you want to push any local branches to remote branches of the same name. If you want to be specific about it, you can say:

$ git push origin feature-x

which will push the local feature-x branch up to the remote feature-x branch.
This is great if your local branches are named the way you want, but what if they’re not? For example, what if you want to deploy staging to Heroku? Heroku runs whatever is in master. If you push your local staging to a heroku branch named staging it will be there, but nobody will see it. You would have to rename your master to real-master, rename staging to master, and then push that up to Heroku. There’s a better way. You can specify the local and remote names separately:

$ git push origin new-and-improved-master:master

Which brings us to the deletion command:

$ git push origin :the-branch-name

This pushes up an empty branch reference to the specific branch on the remote, which deletes it.

Tedious, Tedious Repetition

So we could go ahead and type out git push origin :old-feature-x over and over again to delete the 132 merged branches, or we could write a script that does it all at once.

Glorious Unix Pipes

To see all the remote branches, we can use usual git branch command with the --remote flag (-r for short).

$ git branch -r

There are two flags that can quickly show us which are merged into the branch that you are currently on: --merged and --no-merged That should be master, if you’re cleaning up, otherwise this can go horribly wrong.

$ git branch -r --merged

The output includes two lines that you don’t want to delete:


origin/HEAD ->origin/master
origin/master

Let’s get rid of it using grep’s --invert-match flag (-v for short).

$ git branch -r --merged | grep -v master

Each branch in the output is prefixed with origin/, which doesn’t quite work. We need to change that to :. Sed can help.

git branch --remote --merged | grep -v master | sed -e 's/origin\//:/'

Now we need to loop. We certainly could use a bash for loop:

$ for reference in `git branch -r --merged | grep -v master`; do git push origin $reference; done

On the other hand, there’s a handy unix command named xargs, which allows you to run a command on each line of the input.

When Things Get Out of Date

Sometimes your local environment knows about branches, but the remote has already deleted them. That will blow up, since you’ll be trying to delete branches that don’t exist. Not a huge deal, but it can be avoided using:

$ git remote update --prune

Too Much Typing

Finally, you can create an alias in your shell configuration file (e.g. .bash_profile):

alias gitpurge="git checkout master && git remote update --prune | git branch -r --merged | grep -v master | sed -e 's/origin\//:/' | xargs git push origin"

Update: Thanks to @luislavena for pointing out that git remote update --prune is the equivalent of git fetch origin && git remote prune origin.