tl;dr Alias for removing local branches that are gone on remote
# ~/.gitconfig file
[alias]
gone = "!f() { git fetch --all --prune; git branch -vv | awk '/: gone]/{print $1}' | xargs git branch -D; }; f"
git gone
If your git workflow is using Pull Requests that are merged into main branch, after a while your local list of branches will get very messy, because most of the time the PR is merged and remote branch is deleted.
However on local you just create a new branch from main for a new feature/fix, and you leave the previous branch behind without deleting it.
Let's start with this, list all local branches with their remotes
git branch -vv
feature-1 4ea9770 [origin/feature-1] test commit
feature-2 4ea9770 [origin/feature-2] test commit
feature-3 4ea9770 [origin/feature-3] test commit
feature-4 4ea9770 [origin/feature-4] test commit
feature-5 4ea9770 [origin/feature-5] test commit
feature-6 4ea9770 [origin/feature-6] test commit
feature-7 4ea9770 [origin/feature-7] test commit
* main 4ea9770 [origin/main] test commit
Next we remove some remote branches, fetch remote info, and list branches again
git fetch --prune
git branch -vv
feature-1 4ea9770 [origin/feature-1: gone] test commit
feature-2 4ea9770 [origin/feature-2] test commit
feature-3 4ea9770 [origin/feature-3: gone] test commit
feature-4 4ea9770 [origin/feature-4] test commit
feature-5 4ea9770 [origin/feature-5] test commit
feature-6 4ea9770 [origin/feature-6] test commit
feature-7 4ea9770 [origin/feature-7: gone] test commit
* main 4ea9770 [origin/main] test commit
As you can see there are some lines that have gone
, that means that the remote branch is gone, so origin/feature-1
, origin/feature-3
, origin/feature-7
remote branches don't exist anymore, but we still have them locally. And with time this list can get very big.
Let's get back to our alias. Now that we have this gone
keyword in the branch list, we can hook in and get the branch name and delete it. For example to manually delete feature-7
we would do:
# Fetch info for all remotes and prune info of missing remote branches
git fetch --all --prune
# List all branches to see what is gone
git branch -vv
# Delete desired branch
git branch -D feature-7
Now let's combine all these 3 into a single one liner 🚀
git fetch --all --prune; git branch -vv | awk '/: gone]/{print $1}' | xargs git branch -D;
Here we use pipe (|
) operator to pass output from one command to another pipe.
git fetch --all --prune just fetches remotes info |
git branch -vv list branches and pipes output lines to awk |
awk '/: gone]/{print $1}' find lines with gone , select first column (which is branch name) and pipe to xargs |
xargs git branch -D takes received branches names and deletes them |
And to make this one liner a git alias we put it into a function that will be called 💻
You can add manually the alias in your ~/.gitconfig
file
# ~/.gitconfig file
[alias]
gone = "!f() { git fetch --all --prune; git branch -vv | awk '/: gone]/{print $1}' | xargs git branch -D; }; f"
Or follow these steps. Usually is a single line, but because of multiple quotes, double quotes and dollar signs we have to do it in multiple steps using a temporary file:
Put the alias code in alias
temporary file
echo "f() { git fetch --all --prune; git branch -vv | awk '/: gone]/{print \$1}' | xargs git branch -D; }; f" > alias
Consume alias
file for git alias command
git config --global alias.gone "!`cat alias`"
# remove temporary created file
rm alias
Now let's do a full demo 😮💨
git branch -vv
feature-1 4ea9770 [origin/feature-1] test commit
feature-2 4ea9770 [origin/feature-2] test commit
feature-3 4ea9770 [origin/feature-3] test commit
feature-4 4ea9770 [origin/feature-4] test commit
feature-5 4ea9770 [origin/feature-5] test commit
feature-6 4ea9770 [origin/feature-6] test commit
feature-7 4ea9770 [origin/feature-7] test commit
* main 4ea9770 [origin/main] test commit
Next I deleted all feature branches on remote. And call git gone
git gone
Fetching origin
From /Users/iamandrewluca/Temp/gone-bare
- [deleted] (none) -> origin/feature-1
- [deleted] (none) -> origin/feature-2
- [deleted] (none) -> origin/feature-3
- [deleted] (none) -> origin/feature-4
- [deleted] (none) -> origin/feature-5
- [deleted] (none) -> origin/feature-6
- [deleted] (none) -> origin/feature-7
Deleted branch feature-1 (was 4ea9770).
Deleted branch feature-2 (was 4ea9770).
Deleted branch feature-3 (was 4ea9770).
Deleted branch feature-4 (was 4ea9770).
Deleted branch feature-5 (was 4ea9770).
Deleted branch feature-6 (was 4ea9770).
Deleted branch feature-7 (was 4ea9770).
I like my local repo to always be clear. And in the past I had always to check what branches are deleted on remote and delete them also locally. Now I don't have to remember anything. Just git gone
Are you still here? 😊 Thanks for reading my blog posts! 🎉 🎈
You'll miss me when I'm gone ♥️
Initial idea by Tom Witkowski on Twitter
Cover Photo by Birti Ishar on Unsplash
Top comments (5)
Thanks, this is great! Minor tweak to use the
--filter
option to make sure we're not possibly matching on commit string contents (as unlikely as that may be):Some WIP branches be like: you'll gonna miss me when i'm gone 😄.
😀
Great article ! But it seems that it is not working with the
git config remote.origin.prune true
enabled because it looks like that this config also remove the reference to the remote branch :/Great :-)