github - How can I delete all Git branches which have been merged? - Stack Overflow

Ask questions Research chat →

https://stackoverflow.com/questions/6127328/how-can-i-delete-all-git-branches-which-have-been-merged · scraped

terminal

Attachments

Scraped Content

— 727 words · 2026-02-14 03:14:35 UTC ·

Excerpt

I've used Adam's answer for years now. That said, that there are some cases where it wasn't behaving as I expected: branches that contained the word "master" were ignored, e.g. "notmaster" or "masterful", rather than only the master branch branches that contained the word "dev" were ignored, e.g. "dev-test", rather than only the dev branch deleting branches that are reachable from the HEAD of the current branch (that is, not necessarily master) in detached HEAD state, deleting every branch reachable from the current commit 1 & 2 were straightforward to address, with just a change to the regex. 3 depends on the context of what you want (i.e. only delete branches that haven't been merged into master or against your current branch). 4 has the potential to be disastrous (although recoverable with git reflog), if you unintentionally ran this in detached HEAD state. Finally, I wanted this to all be in a one-liner that didn't require a separate (Bash|Ruby|Python) script. TL;DR Create a
I've used Adam's answer for years now. That said, that there are some cases where it wasn't behaving as I expected: branches that contained the word "master" were ignored, e.g. "notmaster" or "masterful", rather than only the master branch branches that contained the word "dev" were ignored, e.g. "dev-test", rather than only the dev branch deleting branches that are reachable from the HEAD of the current branch (that is, not necessarily master) in detached HEAD state, deleting every branch reachable from the current commit 1 & 2 were straightforward to address, with just a change to the regex. 3 depends on the context of what you want (i.e. only delete branches that haven't been merged into master or against your current branch). 4 has the potential to be disastrous (although recoverable with git reflog), if you unintentionally ran this in detached HEAD state. Finally, I wanted this to all be in a one-liner that didn't require a separate (Bash|Ruby|Python) script. TL;DR Create a git alias "sweep" that accepts an optional -f flag: git config --global alias.sweep '!git branch --merged $([[ $1 != "-f" ]] \ && git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)" \ | xargs git branch -d' and invoke it with: git sweep or: git sweep -f The long, detailed answer It was easiest for me to create an example git repo with some branches and commits to test the correct behavior: Create a new git repo with a single commit mkdir sweep-test && cd sweep-test && git init echo "hello" > hello git add . && git commit -am "initial commit" Create some new branches git branch foo && git branch bar && git branch develop && git branch notmaster && git branch masterful git branch --list bar develop foo * master masterful notmaster Desired behavior: select all merged branches except: master, develop or current The original regex misses the branches "masterful" and "notmaster" : git checkout foo git branch --merged | egrep -v "(^\*|master|dev)" bar With the updated regex (which now excludes "develop" rather than "dev"): git branch --merged | egrep -v "(^\*|^\s*(master|develop)$)" bar masterful notmaster Switch to branch foo, make a new commit, then checkout a new branch, foobar, based on foo: echo "foo" > foo git add . && git commit -am "foo" git checkout -b foobar echo "foobar" > foobar git add . && git commit -am "foobar" My current branch is foobar, and if I re-run the above command to list the branches I want to delete, the branch "foo" is included even though it hasn't been merged into master: git branch --merged | egrep -v "(^\*|^\s*(master|develop)$)" bar foo masterful notmaster However, if I run the same command on master, the branch "foo" is not included: git checkout master && git branch --merged | egrep -v "(^\*|^\s*(master|develop)$)" bar masterful notmaster And this is simply because git branch --merged defaults to the HEAD of the current branch if not otherwise specified. At least for my workflow, I don't want to delete local branches unless they've been merged to master, so I prefer the following variant using git rev-parse: git checkout foobar git branch --merged $(git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)" bar masterful notmaster Detached HEAD state Relying on the default behavior of git branch --merged has even more significant consequences in detached HEAD state: git checkout foobar git checkout HEAD~0 git branch --merged | egrep -v "(^\*|^\s*(master|develop)$)" bar foo foobar masterful notmaster This would have deleted the branch I was just on, "foobar" along with "foo", which is almost certainly not the desired outcome. With our revised command, however: git branch --merged $(git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)" bar masterful notmaster One line, including the actual delete git branch --merged $(git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)" | xargs git branch -d All wrapped up into a git alias "sweep": git config --global alias.sweep '!git branch --merged $([[ $1 != "-f" ]] \ && git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)" \ | xargs git branch -d' The alias accepts an optional -f flag. The default behavior is to only delete branches that have been merged into master, but the -f flag will delete branches that have been merged into the current branch. git sweep Deleted branch bar (was 9a56952). Deleted branch masterful (was 9a56952). Deleted branch notmaster (was 9a56952). git sweep -f Deleted branch foo (was 2cea1ab).

Visibility

Visible to everyone

Reading Status

Related Bookmarks

My Note


Saved!

Annotations

Export as Markdown
+ Annotate selection

Add Annotation