Cleaning Git Branches

I like creating branches independently of how big or small the projects that I am working with are. The result is me ending with hundreds of branches that are not needed anymore. I try to clean up periodically, especially when I complete a chunk of work. I clean all branches except develop, the main branch of my projects, locally and in the remote repository with the one-liner command:

git branch | grep -v "develop" | xargs -p -t -I % sh -c 'git branch -d %; git push origin --delete %;'

Breaking down how the above command works, we have:

  • git branch: lists all the existing branches in your local repository
  • grep -v "develop": filters the list of branches from the previous command, removing the develop branch from the list.
  • xargs -p -t -I % sh -c '{ git branch -d %; git push origin --delete %; }' : is the command that does most of the magic.
  • git branch -d <branch-name> : deletes the local branch.
  • git push origin --delete <branch-name>: deletes the remote branch.

Basic introduction to xargs command

The xargs command in UNIX is a command-line utility for building an execution pipeline from standard input by converting the standard input into arguments to a command. By default, it reads items from standard input separated by blanks and executes a command or a set of commands once for each argument. Breaking down the options for our xargs command:

  • The -p option will print the command to be executed and prompt you to run it if you change your mind last minute for a specific branch and want to keep it.

  • The -I option allows you to get the output, in our case the branch name, into a placeholder (%) and do various things with it. Xargs will launch a new shell, and this subshell will execute the command(s) we give as input. Single quotes should enclose the commands to ensure that the parent shell will not expand them while passing them to the subshell.

And just like that, the branch cleaning is almost complete. There is one more thing I like to do.

git remote prune origin

This command deletes branch references to branches that no longer exist in the remote repository. A remote branch is usually deleted after merging a PR.

git fetch –prune origin

This command does the same as the above, but before pruning, it fetches the latest remote data.

The prune option, in both commands, only deletes the references in refs/remotes/ that do not point to an active branch on the remote. Local changes and branches are not deleted or lost.

Local and Remote Branches in Git

I believe a small introduction to how git manages branches is needed to understand what is happening with all the commands presented in this post.

When we create a local branch either with the command git checkout -b <branch-name> or with git branch <branch-name>, a new file in the project’s .git directory is created: refs/heads/<branch-name>. This file will contain all locally committed changes.

At this point, there is no connection between the local branch and a remote branch. When we set a local branch to track a remote branch with any of the commands that git offers, we will get a new file under .git directory: refs/remotes/<branch-name>, which keeps information for the remote branch.

So long story short, when we use the prune option, git deletes the files under refs/remotes, which are the pointers to remote branches that no longer exist. When cleaning local branches with git branch -d <branch-name>, git deletes the files under refs/heads/, and when git push origin --delete <branch-name>, it deletes the files under refs/remotes/ while updating the remote repository as well.