Do you clean up your git branches as you go or are you, like I am, a lazy hoarder?
$ git branch | wc -l
150
Look at all those things I did that I don’t care about anymore.
Yesterday I googled a little to try and find some magic incantation that would just clean up my branches for me. There are some, but I find that they’re either too conservative or too liberal for me. By “too conservative” I mean that they try to only delete branches that have been merged, except that they’re not actually very accurate, because they aren’t aware of GitHub’s “Squash and Merge” or “Rebase and Merge”, which I use pretty much exclusively. By “too liberal” I mean that some people recommend just literally deleting all of your branches.
I want to have control over the situation.
I can just run git branch -D branch-name-goes-here
over and over, one-by-one, for all of my branches, but that would take several minutes, which I definitely technically have, but don’t want to spend that way, even while curled up with a podcast.
What I really kind of want is some kind of interactive process that gives me total control but doesn’t take that long to do. So I made a little shell script, which looks like this to use:
As you may notice, it takes some loose inspiration from git’s interactive rebase.
It does something like this:
- get your list of branches
- open your default editor (whatever you have the
$EDITOR
global variable set to) (vim for me) - wait for you to mark which branches should be deleted
- delete the ones you marked
git lets you plug in little scripts by just naming them git-whatever-you-want
and putting that script on your $PATH
and I think it’s fun to take advantage of that.
Here’s the latest version of the script as of this writing:
#!/usr/bin/env bash
set -euo pipefail
file="/tmp/git-cleanup-branches-$(uuidgen)"
function removeCurrentBranch {
sed -E '/\*/d'
}
function leftTrim {
sed -E 's/\*?[[:space:]]+//'
}
all_branches=$(git branch | removeCurrentBranch | leftTrim)
# write branches to file
for branch in $all_branches; do
echo "keep $branch" >> $file
done
# write instructions to file
echo "
# All of your branches are listed above
# (except for the current branch, which you can't delete)
# change keep to d to delete the branch
# all other lines are ignored" >> $file
# prompt user to edit file
$EDITOR "$file"
# check each line of the file
cat $file | while read -r line; do
# if the line starts with "d "
if echo $line | grep --extended-regexp "^d " > /dev/null; then
# delete the branch
branch=$(echo $line | sed -E 's/^d //')
git branch -D $branch
fi
done
# clean up
rm $file
It follows the “chainable shell function” pattern I’ve written about before.
It uses set -o pipefail
, my favorite recent discovery in shell scripting, which makes sure that each command succeeds, not just each expression.
I should probably do a separate blog post about that with more detail.
Yeah, I guess that’s pretty much it. Have fun shell scripting out there.