Hugo Martins

Some Commands to Ensure Clean Branches

I like clean environments whenever I’m working. I hate mess and I hate messy git repositories even more. Because of this, I’m constantly looking at ways to clean up local copies of git repositories. Cleaning up frequently helps me keep my sanity and it helps ensure I’m leveled with remote copies, ensuring that work goes smoothly and faster, without any operational issues with git.

I do this mostly with a custom git sub-command and a git alias: clean-branches and pbr. I wrote both of these with a lot of help from StackOverflow folks because I’m not, at all, an expert on git. So, what do these two do? clean-branches iterates through a list of references in git and deletes a branch whenever that branch does not exist remotely anymore. pbr is a quick alias to update information about remote branches and prune all of those that don’t exist anymore remotely. These are specially useful if you work with a branch-based workflow where you tend to create a lot of branches on a daily basis. Let’s get into more detail about both items.

Starting with clean-branches, I wrote a script called git-clean-branchs.sh:

#!/bin/sh

set -e

git checkout master
git fetch -p

for branch in $(git for-each-ref --format '%(refname) %(upstream:track)' refs/heads | awk '$2 == "[gone]" {sub("refs/heads/", "", $1); print $1}');
do
    git branch -D "$branch"
done%

Going through the script we can see a few familiar things. set -e helps us keep sanity and try to exit the shell script if any of it starts erring. git checkout master guarantees that we are on our main branch and git fetch -p fetches information from remote and prunes references that no longer exists on remote (this is similar to what git pbr does actually). After everything is up-to-date, we move on to the juicy stuff.

git for-each-ref --format '%(refname) %(upstream:track)' refs/heads iterates all the refs/heads references (which map to the heads of each branch) and prints the difference between the local information and the remote information. You can read more about for-each-ref here as it has a few interesting options. Executing this on a repository with branches that don’t exist anymore remotely, it prints out something similar to:

$ git for-each-ref --format '%(refname) %(upstream:track)' refs/heads
refs/heads/master
refs/heads/test-branch [gone]

You can see now where awk '$2 == "[gone]" {sub("refs/heads/", "", $1); print $1}' helps, can’t you? It really just takes all the branches that have [gone], which means they have been removed remotely, removes the prefix refs/heads and prints out the name of the branch. There’s some incredible stuff that can be done with awk, it is a powerful albeit complex command.

After filtering out the necessary branches, the loop just deletes all of them locally so that we are now synchronized with remote and don’t have any local branches that don’t exist remotely anymore. We give execution permissions to this script and put it somewhere that is in our PATH variable. git will load it up and accept it as a valid command.

pbr is much simpler. It is a git alias in ~/.gitconfig mapping to remote update origin --prune. It will simply update all the the references in the repository and run pruning against all of the updated references - it will help keep clean all the references to branches that no longer exist remotely. A key difference between both is obvious: one deletes local branches, while the other one only manages remote references.

With these utilities I end up doing all that I need to keep my references (and branches) sync’ed with the remote ones, helping me keep my mental sanity and local repositories organized and clean.