How to easily prune your local branches without a remote using Git

Consider the following, not so exciting, git workflow, common in trunk-based development, where branches should be short-lived.

You start by cloning an existing repository...

$ git clone https://github.com/matthiasguentert/flux-minikube.git

You create a new local branch, make changes, and commit them...

$ git switch -c feat/the-next-big-thing
...
$ git add . 
$ git commit -m "implements the next big thing"
[feat/the-next-big-thing 914d7be] implements the next big thing
 1 file changed, 1 insertion(+)
 create mode 100644 TheNextBigThing.cs

You push the branch back to the remote...

$ git remote -v
origin	git@github.com:matthiasguentert/my-project.git (fetch)
origin	git@github.com:matthiasguentert/my-project.git (push)

$ git push --set-upstream origin feat/the-next-big-thing

At this point, the local branch has a remote tracking branch.

💡
Consider setting push.autosetupremote=true in your git config, so you don't have to set the upstream manually.

`git config --global --add --bool push.autoSetupRemote true`

You create a pull request that gets approved, and the remote branch gets deleted. You switch back to main, pull in the latest changes, and head to the next task.

$ git switch main 
$ git pull 
$ git switch -c feat/another-task
... 

Over time, your list of local branches without a remote tracking branch accumulates. Sure, you can periodically review your list of local branches and perform some manual cleanup...

$ git branch -va
  feat/feature1 9f9ccc9 [origin/feat/feature1: gone] a commit msg here
  feat/feature2 abcdeaf [origin/feat/feature2: gone] a commit msg and here
  feat/featureN 1234567 [origin/feat/featureN: gone] a commit msg there

$ git branch -d feat/feature1
$ git branch -d ... 

But this is tiring and repetitive...

The shortcut

So, long story short. I use two one-liners with aliases to save me some time every day.

# git list untracked branches
alias glu="git fetch -p; git branch -vva | grep ": gone\]" | awk '{print \$1}'"

# git remove untracked branches
alias gru="git fetch -p; git branch -vva | grep ": gone\]" | awk '{print \$1}' | xargs git branch -D"

This should work in Bash and Zsh. So add the alias either to ~/.zprofile when on Mac or your ~/.bashrc.

In case you are not using squash commit on merge, you should replace git branch -D with git branch -d.

Further reading

Trunk Based Development
A portal on this practice