How do I delete branches which have already been merged? Can I delete them all at once, instead of deleting each branch one-by-one?
NOTE: You can add other branches to exclude like master and dev if your workflow has those as a possible ancestor. Usually I branch off of a "sprint-start" tag and master, dev and qa are not ancestors.
First, list locally-tracking branches that were merged in remote (consider using -r
flag to list all remote-tracking branches).
git branch --merged
You might see few branches you don't want to remove. We can add few arguments to skip important branches that we don't want to delete like master or a develop. The following command will skip master branch and anything that has dev in it.
git branch --merged| grep -Ev "(^\*|master|main|dev)"
If you want to skip, you can add it to the egrep command like the following. The branch skip_branch_name
will not be deleted.
git branch --merged| grep -Ev "(^\*|master|main|dev|skip_branch_name)"
To delete all local branches that are already merged into the currently checked out branch:
git branch --merged | grep -Ev "(^\*|master|main|dev)" | xargs git branch -d
You can see that master and dev are excluded in case they are an ancestor.
You can delete a merged local branch with:
git branch -d branchname
If it's not merged, use:
git branch -D branchname
To delete it from the remote use:
git push --delete origin branchname
git push origin :branchname # for really old git
Once you delete the branch from the remote, you can prune to get rid of remote tracking branches with:
git remote prune origin
or prune individual remote tracking branches, as the other answer suggests, with:
git branch -dr branchname
git branch --merged | grep -v "\*" | xargs -n 1 git push --delete origin
git branch --merged | grep -v "\*" | xargs -n 1 git branch -d
–
Flashlight git checkout master origin/master
at some later point. git branch --merged | grep -E -v "\*|master" | xargs -n 1 git branch -d
FTW –
Metamer master@{1}
or master@{yesterday noon}
–
Alternant --no-color
if you use color by default. Example: git branch --merged develop --no-color | cat | grep -v "\* develop" | xargs -n 1 git branch -d
–
Menopause git branch --merged | %{$_.trim()} | ?{$_ -notmatch 'develop' -and $_ -notmatch 'master'} | %{git branch -d $_}
–
Ensemble fatal: branch name required
if you have no branches that should be deleted. To avoid that you can pass -r
to xargs
so it won't run git branch -d
if the stdin is empty. (This a GNU xargs extension, according to the man page). –
Holism git branch -r --merged | %{$_.trim()} | ?{$_ -match '/bug/' -or $_ -match '/feature/'} | %{$_ -replace 'origin/',''} | %{git push --delete origin $_}
–
Babi alias
to my ~/.profile
! Very helpful answer! –
Parodist clean-branches = !git branch --merged | egrep -v "(^\*|master|dev)" | xargs git branch -d
doesn't work for me (bad config file error) –
Seljuk alias git-branch-clean="git branch --merged | egrep -v \"(^\*|master|dev|staging|production)\" | xargs git branch -d"
–
Parodist git branch --merged | egrep -v "(^\*|master|dev)" | xargs -I % echo 'git branch -d % ; git push --delete freephile % ;'
Using echo helps you look before you leap. % is the replacement string in xargs. Change 'echo' to 'sh -c' to execute. Does both local and remote prunes. –
Funk git branch --merged
without further arguments will show all branches which are merged into the current checkedout branch. You should always use something like git branch --merged origin/master
–
Currajong git push origin --delete $(git branch -r --merged origin/master | grep origin | egrep -v '>|master|develop' | cut -d/ -f2-)
–
Compliance ui = never
under the [color]
section in .gitconfig –
Dolorisdolorita --no-color
switch before --merged
: git branch --no-color --merged | egrep -v "(^\*|master|dev)" | xargs git branch -d
–
Tailgate git branch --merged | egrep -v "(^\*|master)$"
. –
Farrison egrep -v "^(\* .*| *master| *dev)$"
–
Unstrained git log upstream...currentBranch --cherry-pick --right-only
doesn't return anything –
Duelist bash git branch --merged | egrep -v "(^\*|master|dev)" | xargs git branch -d
But I was tired to run it every time so I added an alias in my .bash_profile. I have an article about how to do it here –
Lonlona git branch -r --merged | egrep -v "(^\*|master|dev)" | sed 's/origin\//:/' | xargs -n 1 git push origin
- disclaimer: My bash skills aren't great, so check you are happy with what this does before running. Looked to work for me. –
Bach master
and/or dev
, you will need to trim some of the output since git push
does not work with paths. The command that I've been using lately: git branch -r | egrep -v "^\*|master|dev" | cut -d '/' -f 2 | xargs git push --delete origin
–
Song egrep
... –
Saltish for i in `git branch --merged| egrep -v "(^\*|master|dev)"`; do git branch -d ${i}; done;
–
Corporeity git branch -r --merged | grep -v master | sed 's/origin\//:refs\/heads\//' | xargs -n 1 git push origin
–
Burma -d
deleting git fetch origin; git branch | grep -v -E "(^\*|master|staging|beta)" | xargs -I % sh -c "git checkout % && (git rebase || git rebase --abort); git branch | grep -v -E "(^\*|master|staging|beta)" | xargs -L 1 git branch -d
–
Arquit To delete all branches on remote that are already merged:
git branch -r --merged | grep -v master | sed 's/origin\//:/' | xargs -n 1 git push origin
In more recent versions of Git
git branch -r --merged | grep -v master | sed 's/origin\///' | xargs -n 1 git push --delete origin
UPDATE (by @oliver; since does not fit in comment, but enough answers already): if you are on branch ABC then ABC will appear in the results of git branch -r --merged
because the branch is not specified, so branch defaults to current branch, and a branch always qualifies as merged to itself (because there are no differences between a branch and itself!).
So either specify the branch:
git branch -r --merged master | grep -v master ...
OR first checkout master:
git checkout master | git branch -r --merged | grep -v ...
dev
so I had to change that –
Mcneely | grep origin
after grep -v master
to prevent pushing branches of other remotes to origin. Highly recommending testing the output beforehand, using git branch -r --merged | grep -v master | grep origin | sed 's/origin\//:/' | xargs -n 1 echo
–
Meshach develop
branch as well. git branch -r --merged | grep -v master | grep -v develop | sed 's/origin\///' | xargs -n 1 git push --delete origin
. Now this turned out to be my alias. –
Hearthstone -r
argument, which I've not seen mentioned anywhere else. It's taken for granted that only local branches are worth doing some housekeeping on. But remotes are full of garbage too. –
Schnell | grep origin
you can also add --no-verify
at the end to skip git pre-push hook –
Radiative myFeatureBranch
it will wipe origin/myFeatureBranch
. Probably it's best to git checkout master
first. –
Radiative grep -v '^ master$'
), but that still includes the actual master branch when I try it. –
Jeaninejeanlouis git branch -r --merged | grep -v master | grep -v develop | sed 's/origin\///' | xargs -n 1 git push --delete origin
WARNING: This command will delete and close open pull requests. –
Fencer sed
step by using the --format
to include only the needed part of the ref git branch --format "%(refname:lstrip=3)" --remotes --merged
The available field names are documented here. They are not the same as the format for other git sub commands. –
Danielladanielle git checkout master &&
, not git checkout master |
. –
Raychel drm = !git checkout master && git branch -r --merged | grep -v master | sed \"s/origin\\///\" | xargs -n 1 git push --delete origin
should work as an alias in .gitconfig
–
Mononuclear -n 1
to a higher value deletes more branches at once –
Willumsen git branch -r --merged | sed '/main/d' | sed 's/\//\n/' | cat | xargs -L2 bash -c 'git push --delete $0 $1'
–
Dudgeon Just extending Adam's answer a little bit:
Add this to your Git configuration by running git config -e --global
[alias]
cleanup = "!git branch --merged | grep -v '\\*\\|master\\|develop' | xargs -n 1 -r git branch -d"
And then you can delete all the local merged branches doing a simple git cleanup
.
git branch --merged master
since you want to look at what has been merged into master, not currently checked out branch? –
Avivah develop
or dev
and in that case the command will fail with fatal: malformed object name
it's better to have a generic command and you have the responsibility to run it –
Oakie master
–
Midterm -r
to xargs
will prevent unnecessary errors (branch name required
) when running this alias multiple times or when there is no branch left to be deleted. My alias looks like this: cleanup = "!git branch --merged | grep -v -P '^\\*|master|develop' | xargs -n1 -r git branch -d"
–
Rf nuke = "!sh -c \"git branch --merged master | grep -v -P '\\*|master' | xargs -n1 -r git branch -D\""
For windows users, this is what ended up finally working for me: Thanks @Rf for getting me almost all the way there. –
Tope You'll want to exclude the master
, main
& develop
branches from those commands.
Local git clear:
git branch --merged | grep -v '\*\|master\|main\|develop' | xargs -n 1 git branch -d
Remote git clear:
git branch -r --merged | grep -v '\*\|master\|main\|develop' | sed 's/origin\///' | xargs -n 1 git push --delete origin
Sync local registry of remote branches:
git fetch -p
git config --global --add fetch.prune true
to prune automatically on fetch or pull. –
Carcajou git branch -r --merged | grep -v '\*\|master\|develop' | grep '^\s*origin/' | sed 's/origin\///' | tr "\n" " " | xargs git push --delete origin
–
Warton poc-stuff
branch where I would try out stuff. When I ran this command, that got deleted... –
Alar This also works to delete all merged branches except master.
git branch --merged | grep -v '^* master$' | grep -v '^ master$' | grep -v '^* main$' | grep -v '^ main$' | xargs git branch -d
Edit: Also ignoring main branch as it's being used these days
master
in it. Try grep -v ^master$
for the middle. –
Consecution | grep -v '^\*'
to avoid deletting current branch if you are not on master –
Simons grep -v '^ master$'
. If you type it in yourself and miss one, you'll delete master
if you're not on it. –
Vanover git branch
will list each branch name on a new line with two spaces to the left if it is not the currently checked out branch. You have essentially guaranteed that anyone who runs this command will delete their master branch unless it is the currently checked out branch. –
Vanover For those of you that are on Windows and prefer PowerShell scripts, here is one that deletes local merged branches:
function Remove-MergedBranches
{
git branch --merged |
ForEach-Object { $_.Trim() } |
Where-Object { $_ -NotMatch "^\*" } |
Where-Object { -not ( $_ -Like "*master" -or $_ -Like "*main" ) } |
ForEach-Object { git branch -d $_ }
}
Or the short version:
git branch --merged | %{$_.trim()} | ?{$_ -notmatch 'dev' -and $_ -notmatch 'master' -and $_ -notmatch 'main'} | %{git branch -d $_.trim()}
git branch --merged | ?{-not ($_ -like "*master")} | %{git branch -d $_.trim()}
–
Lade for /f "usebackq" %B in (``git branch --merged^|findstr /v /c:"* " /c:"master"``) do @git branch -d %B
(sigh, replace double-backquotes with single, I'm not sure how to format a literal that contains backquotes) –
Regale 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).
git config
atomic? –
Evelynneven !f(){ git branch ...
. It's a function declaration, right? Why not start directly with git branch ...
? –
Evelynneven git checkout master && git branch -d `git branch --merged` && git checkout -
Except that it would delete develop
, but might be a simpler approach. –
Basically develop
); other caveats would be related to switching back and forth between branches, e.g.: uncommitted changes need to be handled before switching branches; depending on what you have going on in other tools (editors, profiler, running app server from your branch, etc.) switching branches might disrupt your flow. –
Nuisance So many bad answers here. This is what you probably want:
git branch --delete $(git branch --merged master --no-contains master --format='%(refname:short)')
This selects all local branches that have been merged into master
(including master
), and that aren't descendent of master
(which excludes master
itself). The --format
is necessary because by default Git prints asterisks for the currently checked out branches. You might be able to do this with git for-each-ref
too but it seems more complicated (it lists remote branches too).
You don't want:
git branch --merged
(withoutmaster
): This will list branches that have been "merged" into your currently checked out commit (i.e.HEAD
), which is probably unexpected and not what you want.| grep -v master
: There's no need for this; you can just use--no-contains
.| xargs
: Again, no need.git branch --delete
can delete more than one branch.
Update: I've been using this for a while and it works pretty well. There are two minor flaws:
If there are no branches to delete it will give you an error. That's fine for interactive use IMO. You shouldn't be using this in a script anyway since it's Bash and Bash should not be used for scripting.
It won't delete branches that point to the same commit as
master
. I have a tool that automatically rebases all my branches, and for ones that have been merged it can leave them in this state. This may not matter to you.
xargs
either. –
Loner Using Git version 2.5.0:
git branch -d `git branch --merged`
master
branch btw! –
Metamorphic master
. –
Mia git branch -d $(git branch --merged | grep -v master)
–
Latini If you're on Windows you can use Windows Powershell or Powershell 7 with Out-GridView to have a nice list of branches and select with mouse which one you want to delete:
git branch --format "%(refname:short)" --merged | Out-GridView -PassThru | % { git branch -d $_ }
after clicking OK Powershell will pass this branches names to git branch -d
command and delete them
You can add the commit to the --merged option. This way you can make sure only to remove branches which are merged into i.e. the origin/master
Following command will remove merged branches from your origin.
git branch -r --merged origin/master | grep -v "^.*master" | sed s:origin/:: |xargs -n 1 git push origin --delete
You can test which branches will be removed replacing the git push origin --delete with echo
git branch -r --merged origin/master | grep -v "^.*master" | sed s:origin/:: |xargs -n 1 echo
My favorite and simple script:
git branch --merged | grep -E -v "(master|main|develop|other)" | xargs git branch -d
git branch | grep -E -v "(master|develop|other)" | xargs git branch -D
–
Colubrine git branch --merged | grep -E -v "(master|main|develop|other)" | xargs git branch -d
–
Measurable I use the following Ruby script to delete my already merged local and remote branches. If I'm doing it for a repository with multiple remotes and only want to delete from one, I just add a select statement to the remotes list to only get the remotes I want.
#!/usr/bin/env ruby
current_branch = `git symbolic-ref --short HEAD`.chomp
if current_branch != "master"
if $?.exitstatus == 0
puts "WARNING: You are on branch #{current_branch}, NOT master."
else
puts "WARNING: You are not on a branch"
end
puts
end
puts "Fetching merged branches..."
remote_branches= `git branch -r --merged`.
split("\n").
map(&:strip).
reject {|b| b =~ /\/(#{current_branch}|master)/}
local_branches= `git branch --merged`.
gsub(/^\* /, '').
split("\n").
map(&:strip).
reject {|b| b =~ /(#{current_branch}|master)/}
if remote_branches.empty? && local_branches.empty?
puts "No existing branches have been merged into #{current_branch}."
else
puts "This will remove the following branches:"
puts remote_branches.join("\n")
puts local_branches.join("\n")
puts "Proceed?"
if gets =~ /^y/i
remote_branches.each do |b|
remote, branch = b.split(/\//)
`git push #{remote} :#{branch}`
end
# Remove local branches
`git branch -d #{local_branches.join(' ')}`
else
puts "No branches removed."
end
end
\/
at the beginning of the reject statement for the remote_branches
line. Is that a typo or does it serve a purpose? –
Satyriasis b.split(/\//)
line now –
Satyriasis How to delete merged branches in PowerShell console
git branch --merged | %{git branch -d $_.Trim()}
If you want to exclude master or any other branch names, you can pipe with PowerShell Select-String like this and pass the result to git branch -d
:
git branch -d $(git branch --merged | Select-String -NotMatch "master" | %{$_.ToString().Trim()})
* master
:) –
Ringster Note: I am not happy with previous answers, (not working on all systems, not working on remote, not specifying the --merged branch, not filtering exactly). So, I add my own answer.
There are two main cases:
Local
You want to delete local branches that are already merged to another local branch. During the deletion, you want to keep some important branches, like master, develop, etc.
git branch --format "%(refname:short)" --merged master | grep -E -v '^master$|^feature/develop$' | xargs -n 1 git branch -d
Notes:
git branch output --format
".." is to strip whitespaces and allow exact grep matchinggrep -E
is used instead ofegrep
, so it works also in systems without egrep (i.e.: git for windows).grep -E -v '^master$|^feature/develop$'
is to specify local branches that I don't want to deletexargs -n 1 git branch -d
: perform the deletion of local branches (it won't work for remote ones)- of course you get an error if you try deleting the branch currently checked-out. So, I suggest to switch to master beforehand.
Remote
You want to delete remote branches that are already merged to another remote branch. During the deletion, you want to keep some important branches, like HEAD, master, releases, etc.
git branch -r --format "%(refname:short)" --merged origin/master | grep -E -v '^*HEAD$|^*/master$|^*release' | cut -d/ -f2- | xargs -n 1 git push --delete origin
Notes:
- for remote, we use the
-r
option and provide the full branch name:origin/master
grep -E -v '^*HEAD$|^*/master$|^*release'
is to match the remote branches that we don't want to delete.cut -d/ -f2-
: remove the unneeded 'origin/' prefix that otherwise is printed out by thegit branch
command.xargs -n 1 git push --delete origin
: perform the deletion of remote branches.
You can use gbda
alias if you're using OhMyZSH with git plugin.
I use this:
git branch --delete $(git branch --format '%(refname:short)' --merged | grep --invert-match 'main\|master\|branch-to-skip')
It lists all merged branched in the specified format, then it feeds that list to git branch --delete.
kuboon's answer missed deleting branches which have the word master in the branch name. The following improves on his answer:
git branch -r --merged | grep -v "origin/master$" | sed 's/\s*origin\///' | xargs -n 1 git push --delete origin
Of course, it does not delete the "master" branch itself :)
There is no command in Git that will do this for you automatically. But you can write a script that uses Git commands to give you what you need. This could be done in many ways depending on what branching model you are using.
If you need to know if a branch has been merged into master the following command will yield no output if myTopicBranch has been merged (i.e. you can delete it)
$ git rev-list master | grep $(git rev-parse myTopicBranch)
You could use the Git branch command and parse out all branches in Bash and do a for
loop over all branches. In this loop you check with above command if you can delete the branch or not.
git branch --merged | grep -Ev '^(. master|\*)' | xargs -n 1 git branch -d
will delete all local branches except the current checked out branch and/or master
.
Here's a helpful article for those looking to understand these commands: Git Clean: Delete Already Merged Branches, by Steven Harman.
You can use git-del-br
tool.
git-del-br -a
You can install it via pip
using
pip install git-del-br
P.S: I am the author of the tool. Any suggestions/feedback are welcome.
I use a git-flow esque naming scheme, so this works very safely for me:
git branch --merged | grep -e "^\s\+\(fix\|feature\)/" | xargs git branch -d
It basically looks for merged commits that start with either string fix/
or feature/
.
On Windows with git bash installed egrep -v
will not work
git branch --merged | grep -E -v "(master|test|dev)" | xargs git branch -d
where grep -E -v
is equivalent of egrep -v
Use -d
to remove already merged branches or
-D
to remove unmerged branches
The accepted solution is pretty good, but has the one issue that it also deletes local branches that were not yet merged into a remote.
If you look at the output of you will see something like
$ git branch --merged master -v
api_doc 3a05427 [gone] Start of describing the Java API
bla 52e080a Update wording.
branch-1.0 32f1a72 [maven-release-plugin] prepare release 1.0.1
initial_proposal 6e59fb0 [gone] Original proposal, converted to AsciiDoc.
issue_248 be2ba3c Skip unit-for-type checking. This needs more work. (#254)
master be2ba3c Skip unit-for-type checking. This needs more work. (#254)
Branches bla
and issue_248
are local branches that would be deleted silently.
But you can also see the word [gone]
, which indicate branches that had been pushed to a remote (which is now gone) and thus denote branches can be deleted.
The original answer can thus be changed to (split into multiline for shorter line length)
git branch --merged master -v | \
grep "\\[gone\\]" | \
sed -e 's/^..//' -e 's/\S* .*//' | \
xargs git branch -d
to protect the not yet merged branches. Also the grepping for master to protect it, is not needed, as this has a remote at origin and does not show up as gone.
Based on some of these answers I made my own Bash script to do it too!
It uses git branch --merged
and git branch -d
to delete the branches that have been merged and prompts you for each of the branches before deleting.
merged_branches () {
local current_branch=$(git rev-parse --abbrev-ref HEAD)
for branch in $(git branch --merged | cut -c3-)
do
echo "Branch $branch is already merged into $current_branch."
echo "Would you like to delete it? [Y]es/[N]o "
read REPLY
if [[ $REPLY =~ ^[Yy] ]]; then
git branch -d $branch
fi
done
}
If you'd like to delete all local branches that are already merged in to the branch that you are currently on, then I've come up with a safe command to do so, based on earlier answers:
git branch --merged | grep -v \* | grep -v '^\s*master$' | xargs -t -n 1 git branch -d
This command will not affect your current branch or your master branch. It will also tell you what it's doing before it does it, using the -t flag of xargs.
Alias version of Adam's updated answer:
[alias]
branch-cleanup = "!git branch --merged | egrep -v \"(^\\*|master|dev)\" | xargs git branch -d #"
Also, see this answer for handy tips on escaping complex aliases.
Below query works for me
for branch in `git branch -r --merged | grep -v '\*\|master\|develop'|awk 'NR > 0 {print$1}'|awk '{gsub(/origin\//, "")}1'`;do git push origin --delete $branch; done
and this will filter any given branch in the grep pipe.
Works well over http clone, but not so well for the ssh connection.
Try the following command:
git branch -d $(git branch --merged | grep -vw $(git rev-parse --abbrev-ref HEAD))
By using git rev-parse
will get the current branch name in order to exclude it. If you got the error, that means there are no local branches to remove.
To do the same with remote branches (change origin
with your remote name), try:
git push origin -vd $(git branch -r --merged | grep -vw $(git rev-parse --abbrev-ref HEAD) | cut -d/ -f2)
In case you've multiple remotes, add grep origin |
before cut
to filter only the origin
.
If above command fails, try to delete the merged remote-tracking branches first:
git branch -rd $(git branch -r --merged | grep -vw $(git rev-parse --abbrev-ref HEAD))
Then git fetch
the remote again and use the previous git push -vd
command again.
If you're using it often, consider adding as aliases into your ~/.gitconfig
file.
In case you've removed some branches by mistake, use git reflog
to find the lost commits.
As of 2018.07
Add this to [alias]
section of your ~/.gitconfig
:
sweep = !"f() { git branch --merged | egrep -v \"(^\\*|master|dev)\" || true | xargs git branch -d; }; f"
Now you can just call git sweep
to perform that needed cleanup.
I've been using the following method to remove merged local AND remote branches in one cmd.
I have the following in my bashrc
file:
function rmb {
current_branch=$(git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/')
if [ "$current_branch" != "master" ]; then
echo "WARNING: You are on branch $current_branch, NOT master."
fi
echo "Fetching merged branches..."
git remote prune origin
remote_branches=$(git branch -r --merged | grep -v '/master$' | grep -v "/$current_branch$")
local_branches=$(git branch --merged | grep -v 'master$' | grep -v "$current_branch$")
if [ -z "$remote_branches" ] && [ -z "$local_branches" ]; then
echo "No existing branches have been merged into $current_branch."
else
echo "This will remove the following branches:"
if [ -n "$remote_branches" ]; then
echo "$remote_branches"
fi
if [ -n "$local_branches" ]; then
echo "$local_branches"
fi
read -p "Continue? (y/n): " -n 1 choice
echo
if [ "$choice" == "y" ] || [ "$choice" == "Y" ]; then
# Remove remote branches
git push origin `git branch -r --merged | grep -v '/master$' | grep -v "/$current_branch$" | sed 's/origin\//:/g' | tr -d '\n'`
# Remove local branches
git branch -d `git branch --merged | grep -v 'master$' | grep -v "$current_branch$" | sed 's/origin\///g' | tr -d '\n'`
else
echo "No branches removed."
fi
fi
}
original source
This doesn't delete the master branch, but removes merged local AND remote branches. Once you have this in you rc file, just run rmb
, you're shown a list of merged branches that will be cleaned and asked for confirmation on the action. You can modify the code to not ask for confirmation as well, but it's probably good to keep it in.
Write a script in which Git checks out all the branches that have been merged to master.
Then do git checkout master
.
Finally, delete the merged branches.
for k in $(git branch -ra --merged | egrep -v "(^\*|master)"); do
branchnew=$(echo $k | sed -e "s/origin\///" | sed -e "s/remotes\///")
echo branch-name: $branchnew
git checkout $branchnew
done
git checkout master
for k in $(git branch -ra --merged | egrep -v "(^\*|master)"); do
branchnew=$(echo $k | sed -e "s/origin\///" | sed -e "s/remotes\///")
echo branch-name: $branchnew
git push origin --delete $branchnew
done
For me git branch --merged
doesn't show branches that were merged via GitHub PR. I'm not sure of the reasons, but I use the following line to delete all local branches that do not have remote tracking branch:
diff <(git branch --format "%(refname:short)") <(git branch -r | grep -v HEAD | cut -d/ -f2-) | grep '<' | cut -c 3- | xargs git branch -D
Explanation:
git branch --format "%(refname:short)"
gives a list of local branchesgit branch -r | grep -v HEAD | cut -d/ -f2-
gives a list of remote branches, filtering outHEAD
diff <(...) <(...)
gives a diff of output of two commands inside parenthesesgrep '<'
filters branches that exist in first list, but not in the secondcut -c 3-
gives line starting from 3rd character, thus removing prefix<
xargs git branch -D
executesgit branch -D
against each branch name
Alternatively, you can avoid grep -v '<'
like this:
diff --old-line-format="%L" --new-line-format="" --unchanged-line-format="" <(git branch --format "%(refname:short)") <(git branch -r | grep -v HEAD | cut -d/ -f2-) | xargs git branch -D
To avoid accidentally running the command from any other branch than master I use the following bash script. Otherwise, running git branch --merged | grep -v "\*" | xargs -n 1 git branch -d
from a branch that has been merged of off master could delete the master branch.
#!/bin/bash
branch_name="$(git symbolic-ref HEAD 2>/dev/null)" ||
branch_name="(unnamed branch)" # detached HEAD
branch_name=${branch_name##refs/heads/}
if [[ $branch_name == 'master' ]]; then
read -r -p "Are you sure? [y/N] " response
if [[ $response =~ ^([yY][eE][sS]|[yY])$ ]]; then
git branch --merged | grep -v "\*" | xargs -n 1 git branch -d
fi
else
echo "Refusing to delete branches that are not merged into '$branch_name'. Checkout master first."
fi
The simplest way I found to do it removing only local branches, not remote ones:
$ git branch --merged | grep -v master | xargs -n 1 git branch -D
This command will delete only branches already merged in your master one. Be careful if you don't want to delete other branches, such as a staging
.
gbda
to apply this command –
Tracery git cleanup
script from the git-toolbelt
Deletes all branches that have already been merged into master or develop. Keeps other branches lying around. Will be most conservative with deletions.
Removes branches both locally and in the origin remote.
To delete local branches that have been merged to master branch I'm using the following alias (git config -e --global
):
cleanup = "!git branch --merged master | grep -v '^*\\|master' | xargs -n 1 git branch -D"
I'm using git branch -D
to avoid error: The branch 'some-branch' is not fully merged.
messages while my current checkout is different from master branch.
Let's say I have a remote named upstream and an origin (GitHub style, my fork is origin, upstream is upstream).
I don't want to delete ANY masters, HEAD, or anything from the upstream. I also don't want to delete the develop branch as that is our common branch we create PRs from.
List all remote branches, filtered by ones that were merged:
git branch -r
Remove lines from that list that contain words I know are in branch names I don't want to remove:
sed '/develop\|master\|HEAD\|upstream/d'
Remove the remote name from the reference name (origin/somebranch becomes somebranch):
sed 's/.*\///'
Use xargs to call a one-liner:
xargs git push --delete origin
Pipe it all together you get:
git branch -r --merged | sed '/develop\|master\|HEAD\|upstream/d' | sed 's/.*\///' | xargs git push --delete origin
This will leave me with only some branches that I have worked on, but have not merged. You can then remove them one by one as there shouldn't be too many.
Find branches you no longer want:
git branch -ar
Say you find branch1, branch2, and branch3 you want to delete:
git push --delete origin branch1 branch2 branch3
$ git config --global alias.cleanup
'!git branch --merged origin/master | egrep -v "(^\*|master|staging|dev)" | xargs git branch -d'
(Split into multiple lines for readability)
Calling "git cleanup" will delete local branches that have already been merged into origin/master. It skips master, staging, and dev because we don't want to delete those in normal circumstances.
Breaking this down, this is what it's doing:
git config --global alias.cleanup
- This is creating a global alias called "cleanup" (across all your repos)
- The
!
at the beginning of the command is saying that we will be using some non-git commands as part of this alias so we need to actually run bash commands here git branch --merged origin/master
- This command returns the list of branch names that have already been merged into
origin/master
- This command returns the list of branch names that have already been merged into
egrep -v "(^\*|master|staging|dev)"
- This removes the master, staging, and dev branch from the list of branches that have already been merged. We don't want to remove these branches since they are not features.
xargs git branch -d
- This will run the
git branch -d xxxxx
command for each of the unmerged branches. This deletes the local branches one by one.
- This will run the
For Windows you can install Cygwin and remove all remote branches using following command:
git branch -r --merged | "C:\cygwin64\bin\grep.exe" -v master | "C:\cygwin64\bin\sed.exe" 's/origin\///' | "C:\cygwin64\bin\xargs.exe" -n 1 git push --delete origin
git-delete-merged-branches
from git-extras
repo.
https://github.com/tj/git-extras/blob/master/Commands.md#git-delete-merged-branches
git branch --merged
cannot detect. –
Oldham Just created python script for that:
import sys
from shutil import which
import logging
from subprocess import check_output, call
logger = logging.getLogger(__name__)
if __name__ == '__main__':
if which("git") is None:
logger.error("git is not found!")
sys.exit(-1)
branches = check_output("git branch -r --merged".split()).strip().decode("utf8").splitlines()
current = check_output("git branch --show-current".split()).strip().decode("utf8")
blacklist = ["master", current]
for b in branches:
b = b.split("/")[-1]
if b in blacklist:
continue
else:
if input(f"Do you want to delete branch: '{b}' [y/n]\n").lower() == "y":
call(f"git branch -D {b}".split())
call(f"git push --delete origin {b}".split())
tl;dr: git branch --format='%(if:notequals=main)%(refname:short)%(then)%(if:notequals=master)%(refname:short)%(then)%(refname:short)%(end)%(end)' --merged | xargs --max-args=1 --no-run-if-empty echo git branch --delete
, then remove the echo
if you're happy to actually delete the listed branches.
Look ma, no tools!
Git can filter branches without using external tools, meaning there's a cross-platform, relatively simple way to exclude the default branch from searches. To list branches merged into the "main" branch, excluding the "main" branch itself: git branch --format='%(if:notequals=main)%(refname:short)%(then)%(refname:short)%(end)' --merged=main
. For the "master" branch: git branch --format='%(if:notequals=master)%(refname:short)%(then)%(refname:short)%(end)' --merged=master
We can also combine the two, printing the branch name only if it matches neither "main" nor "master": git branch --format='%(if:notequals=main)%(refname:short)%(then)%(if:notequals=master)%(refname:short)%(then)%(refname:short)%(end)%(end)' --merged
The last one comes with a small caveat: you should check out the default branch (either "main" or "master", in this case) first, because --merged
without a value means "merged into HEAD", which could be pointing to one of the branches you want to delete. But trying to delete the current local branch won't work anyway, so this is not a risk if that's what you're doing.
Another advantage of this approach is that there's no extra leading spaces or asterisk to filter out.
These commands will print an empty line rather than nothing for the excluded branch, but xargs
takes care of that. If you want to process the lines using something other than xargs
you might need special handling for the empty lines (like passing through sed '/^$/d'
)
See git help for-each-ref
for more info about --format
.
Windoze-friendly Python script (because git-sweep
choked on Wesnoth repository):
#!/usr/bin/env python
# Remove merged git branches. Cross-platform way to execute:
#
# git branch --merged | grep -v master | xargs git branch -d
#
# Requires gitapi - https://bitbucket.org/haard/gitapi
# License: Public Domain
import gitapi
repo = gitapi.Repo('.')
output = repo.git_command('branch', '--merged').strip()
for branch in output.split('\n'):
branch = branch.strip()
if branch.strip(' *') != 'master':
print(repo.git_command('branch', '-d', branch).strip())
If you are using branching model like HubFlow or GitFlow you can use this command to remove the merged feature branches:
git branch --merged | grep feature.* | grep -v "\*" | xargs -n 1 git branch -d
If you wish to delete local branches that have been merged as well as delete their remotes here's the one-liner I prefer:
git branch --merged | xargs -I_br -- sh -c 'git branch -d _br; git push origin --delete _br'
My Bash script contribution is based loosely on mmrobin's answer.
It takes some useful parameters specifying includes and excludes, or to examine/remove only local or remote branches instead of both.
#!/bin/bash
# exclude branches regex, configure as "(remote|branch1|branch2|etc)$"
excludes_default="(upstream|master|main|next|maint.*|proposed.*)$"
excludes=
includes=
base=master
merged="--merged"
local=1
remote=1
while [ $# -gt 0 ]; do
case "$1" in
-i) shift; includes="$includes $1" ;;
-e) shift; excludes="$excludes $1" ;;
-b) shift; base="$1" ;;
--no-local) local=0 ;;
--no-remote) remote=0 ;;
--all) merged= ;;
*) echo "Unknown argument $1"; exit 1 ;;
esac
shift # next option
done
if [ "$excludes" == "" ]; then
excludes="__NOTHING__"
else
excludes="($(echo $excludes | sed -e 's/ /|/g'))"
fi
if [ "$includes" == "" ]; then
includes=".*"
else
includes="($(echo $includes | sed -e 's/ /|/g'))"
fi
current_branch=$(git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/')
if [ "$current_branch" != "$base" ]; then
echo "ERROR: You are on branch $current_branch, NOT $base. Change branch or specify base with -b."
exit 1
fi
echo -e "NOTE: no fetch performed, do it manually...\n"
remotes=$(git remote)
remote_branches=$(git branch --no-color --format="%(refname)" -r $merged \
| grep -v HEAD | grep -v "/$current_branch$" | grep -v -E "$excludes" | grep -v -E "$excludes_default" | grep -E "$includes" \
| sed -e 's|refs/remotes/||g' -e 's|refs/heads/||g')
local_branches=$(git branch --no-color --format="%(refname)" $merged \
| grep -v HEAD | grep -v "$current_branch$" | grep -v -E "$excludes" | grep -v -E "$excludes_default" | grep -E "$includes" \
| sed -e 's|refs/remotes/||g' -e 's|refs/heads/||g')
if [ -z "$remote_branches" ] && [ -z "$local_branches" ]; then
echo "No existing branches have been merged into $current_branch."
else
echo "This will remove the following branches:"
if [ "$remote" == 1 -a -n "$remote_branches" ]; then
echo "$remote_branches"
fi
if [ "$local" == 1 -a -n "$local_branches" ]; then
echo "$local_branches"
fi
read -p "Continue? (y/n): " -n 1 choice
echo
if [ "$choice" == "y" ] || [ "$choice" == "Y" ]; then
if [ "$remote" == 1 ]; then
# Remove remote branches
if [ -z "$remotes" ]; then
echo "No remote branches removed."
fi
for remote in $remotes
do
branches=$(echo -n "$remote_branches" | grep "$remote/" | sed "s/$remote\/?\(.*\)/:\1 /g" | tr '\n' ' ')
git push $remote $branches
done
fi
if [ "$local" == 1 ]; then
# Remove local branches
locals=$(echo -n "$local_branches" | tr '\n' ' ')
if [ -z "$locals" ]; then
echo "No local branches removed."
else
git branch -d "$locals"
fi
fi
fi
fi
Given you want to delete the merged branches, you need to delete the remote-tracking branches only, unless you state otherwise.
So to delete those branches you can do it by
git branch --remote --merged origin/master | egrep -v "(^\*|master|development)" | cut -b 10- | xargs git push --delete origin
This will delete all merged branches (merged to master) except master
and development
.
I use a Python script which automates the steps from the most popular answer and also checks remaining branches to see if they would produce any changes if merged and interactively prompts to delete them if not: https://github.com/robertknight/prune-merged-branches.
Same @Adam Dymitruk 's answer, but with some improvements/fixes...
How do I delete all Git branches which have been merged?
First, list locally/remotely tracking branches that were merged in remote...
NOTE: You might see few branches that you don't want to remove. We can add few arguments ("grep") to skip important branches that you don't want to delete like "master" or a "hom"...
FOR LOCALLY-TRACKING BRANCHES
git branch --merged | grep -vE '(master|hom)$'
FOR REMOTE-TRACKING BRANCHES
git branch --remotes --merged | grep -vE 'origin/(master|hom)$'
To delete all locally/remotely tracking branches that are already merged into the CURRENTLY CHECKED OUT BRANCH (normally "main" or "master")...
FOR LOCALLY-TRACKING BRANCHES
git branch --merged | grep -vE '(master|hom)$' | xargs git branch --delete
FOR REMOTE-TRACKING BRANCHES
git branch --remotes --merged | grep -vE 'origin/(master|hom)$' | sed 's/ origin\///' | xargs git push --delete origin
TIP: To use multiple resulting commands use "xargs -n 1".
NOTE: If it's not merged (WARNING! ⚠️), use git branch --delete --force
.
Improvements
The "grep" commands are safer because they match the names "by the whole" and by the end of the string. In other words, less risk of matching the part of the branch name.
The command...
git branch --remotes --merged | grep -vE 'origin/(master|hom)$' | sed 's/ origin\///' | xargs git push --delete origin
...effectively deletes branches remotely.
- More organized and succinct procedure.
The following creates an alias git cleanup
with one optional parameter branch. By default it uses the default branch of remote origin. Every branch that has been merged into this will be deleted. There also is a -d
/--dryrun
option to show what would be deleted.
Setup
git config --global alias.cleanup '!COMMAND="git branch -D"; while [[ $# -gt 0 ]]; do case "$1" in -d|--dryrun) COMMAND="echo"; shift; ;; *) MAIN_BRANCH="$1"; shift;; esac; done; MAIN_BRANCH="${MAIN_BRANCH:-$(git symbolic-ref refs/remotes/origin/HEAD)}"; git for-each-ref --merged="$MAIN_BRANCH" --no-contains="$MAIN_BRANCH" --format="%(refname:short)" refs/heads/ | xargs -n1 -r $COMMAND;#'
Usage:
git cleanup # delete all branches that have been merged into origin/HEAD
git cleanup master2 # delete all branches that have been merged into master2
git cleanup master2 -d # do a dryrun (show names of branches that would be delted)
Readable code
Who can read that "oneliner"? Well here you go
COMMAND="git branch -D";
while [[ $# -gt 0 ]]; do
case "$1" in
-d|--dryrun)
COMMAND="echo";
shift;
;;
*)
MAIN_BRANCH="$1";
shift
;;
esac;
done;
MAIN_BRANCH="${MAIN_BRANCH:-$(git symbolic-ref refs/remotes/origin/HEAD)}";
git for-each-ref --merged="$MAIN_BRANCH" --no-contains="$MAIN_BRANCH" --format="%(refname:short)" refs/heads/ | xargs -n1 -r $COMMAND;
#
What is different to the other answers?
- uses
--no-contains
option to filter out the identity branch (branch into which all other branches that are deleted have been merged) instead ofgrep -v
(this works better if you are specific about your branch and specify refs/heads/master for example) - uses the remote HEAD to check for the default branch name
- has a parameter to specify which branch to use as merge master
- has a dryrun option
© 2022 - 2024 — McMap. All rights reserved.
git branch -D
deletes any branch whether it as been merged or not. – Trinitytrinketmaster
each branch is. However, your local client will still list the old branches if you rungit branch -a
; usegit fetch --prune
to remove them (as per this answer ). – Understandinggit delete-merged --doit origin
orgit delete-merged --doit --local
– Feinbergrm -fr work && git clone http://example.com/work.git
over the years has become the easiest way to get out of a pickle with git. – Cytastergit branch -a --merged| egrep -v "(^\*|master|main|dev)" | sed 's/remotes\/origin\///g' | xargs git push --delete origin
Thanks, tried this to remove remote branches – Myrongbda
– Heliotropin