How can I delete all Git branches which have been

2018-12-31 19:09发布

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?

30条回答
怪性笑人.
2楼-- · 2018-12-31 19:42

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 $_ }
}
查看更多
弹指情弦暗扣
3楼-- · 2018-12-31 19:44

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.

查看更多
看风景的人
4楼-- · 2018-12-31 19:45

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
}
查看更多
旧人旧事旧时光
5楼-- · 2018-12-31 19:48

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.

查看更多
素衣白纱
6楼-- · 2018-12-31 19:48

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.

查看更多
旧时光的记忆
7楼-- · 2018-12-31 19:49

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 '!f(){ git branch --merged $([[ $1 != "-f" ]] \
&& git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)" \
| xargs git branch -d; }; f'

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 '!f(){ git branch --merged $([[ $1 != "-f" ]] \
&& git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)" \
| xargs git branch -d; }; f'

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).
查看更多
登录 后发表回答