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!
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
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
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:
--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 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.
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.
June 11, 2014