How can I delete all Git branches which have been merged?

北战南征 提交于 2019-11-26 03:16:14

问题


I have many Git branches. How do I delete branches which have already been merged? Is there an easy way to delete them all instead of deleting them one by one?


回答1:


UPDATE:

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 all branches that were merged in remote.

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| egrep -v "(^\*|master|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| egrep -v "(^\*|master|dev|skip_branch_name)"

To delete all local branches that are already merged into the currently checked out branch:

git branch --merged | egrep -v "(^\*|master|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 in old versions of Git use:

git push origin :branchname

In more recent versions of Git use:

git push --delete origin branchname

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

Hope this helps.




回答2:


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 ...



回答3:


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 git branch -d"

And then you can delete all the local merged branches doing a simple git cleanup.




回答4:


This also works to delete all merged branches except master.

git branch --merged | grep -v '^* master$' | grep -v '^  master$' | xargs git branch -d



回答5:


You'll want to exclude the master & develop branches from those commands.

Local git clear:

git branch --merged | grep -v '\*\|master\|develop' | xargs -n 1 git branch -d

Remote git clear:

git branch -r --merged | grep -v '\*\|master\|develop' | sed 's/origin\///' | xargs -n 1 git push --delete origin

Sync local registry of remote branches:

git fetch -p



回答6:


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" )} |
    ForEach-Object { git branch -d $_ }
}



回答7:


I've used Adam's answer for years now. That said, that there are some cases where it wasn't behaving as I expected:

  1. branches that contained the word "master" were ignored, e.g. "notmaster" or "masterful", rather than only the master branch
  2. branches that contained the word "dev" were ignored, e.g. "dev-test", rather than only the dev branch
  3. deleting branches that are reachable from the HEAD of the current branch (that is, not necessarily master)
  4. 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:

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).



回答8:


Git Sweep does a great job of this.




回答9:


Using Git version 2.5.0:

git branch -d `git branch --merged`



回答10:


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



回答11:


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



回答12:


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()})



回答13:


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 :)




回答14:


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.




回答15:


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.




回答16:


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.




回答17:


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.




回答18:


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.




回答19:


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/.




回答20:


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 -vdcommand 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.




回答21:


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
}



回答22:


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.




回答23:


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.




回答24:


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




回答25:


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



回答26:


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.




回答27:


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 branches
  • git branch -r | grep -v HEAD | cut -d/ -f2- gives a list of remote branches, filtering out HEAD
  • diff <(...) <(...) gives a diff of output of two commands inside parentheses
  • grep '<' filters branches that exist in first list, but not in the second
  • cut -c 3- gives line starting from 3rd character, thus removing prefix <
  • xargs git branch -D executes git 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



回答28:


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



回答29:


$ 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:

  1. git config --global alias.cleanup
    • This is creating a global alias called "cleanup" (across all your repos)
  2. 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
  3. git branch --merged origin/master
    • This command returns the list of branch names that have already been merged into origin/master
  4. 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.
  5. 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.



回答30:


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.



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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!