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