如何删除所有已合并的 Git 分支?

我有很多 Git 分支。如何删除已经合并的分支?有没有一种简单的方法可以将它们全部删除,而不是一个一个地删除它们?

答案

更新:

如果您的工作流程可能将其作为祖先,则可以添加其他分支以排除像 master 和 dev 这样的分支。通常,我从 “sprint-start” 标签分支出来,而 master,dev 和 qa 不是祖先。

首先,列出所有在远程中合并的分支。

git branch --merged

您可能会看到几个不想删除的分支。我们可以添加一些参数来跳过不想删除的重要分支,例如 master 或 developer。以下命令将跳过 master 分支以及其中包含 dev 的所有内容。

git branch --merged| egrep -v "(^\*|master|dev)"

如果要跳过,可以将其添加到 egrep 命令中,如下所示。分支skip_branch_name不会被删除。

git branch --merged| egrep -v "(^\*|master|dev|skip_branch_name)"

删除所有已经合并到当前签出分支中的本地分支:

git branch --merged | egrep -v "(^\*|master|dev)" | xargs git branch -d

您可以看到,如果 master 和 dev 是祖先,则将它们排除在外。


您可以使用以下方法删除合并的本地分支:

git branch -d branchname

如果未合并,请使用:

git branch -D branchname

要在旧版 Git 中从远程删除它,请使用:

git push origin :branchname

在最新版本的 Git 中使用:

git push --delete origin branchname

从远程删除分支后,可以通过以下方法修剪掉远程跟踪分支:

git remote prune origin

或如其他答案所示,修剪各个远程跟踪分支,方法是:

git branch -dr branchname

希望这可以帮助。

要删除远程上已合并的所有分支:

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

在最新版本的 Git 中

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

UPDATE(@oliver;由于不适合注释,但已经有足够的答案了) :如果您在分支 ABC 上,则 ABC 将出现在git branch -r --merged的结果中,因为未指定分支,所以分支默认到当前分支,并且分支始终符合合并到其自身的资格(因为分支与自身之间没有区别!)。

因此,要么指定分支:

git branch -r --merged master | grep -v master ...

或第一个结帐管理员:

git checkout master | git branch -r --merged | grep -v ...

只是扩大了亚当的答案:

通过运行git config -e --global将其添加到您的 Git 配置中

[alias]
    cleanup = "!git branch --merged | grep  -v '\\*\\|master\\|develop' | xargs -n 1 git branch -d"

然后,您可以执行简单的git cleanup删除所有本地合并分支。

这也适用于删除除 master 以外的所有合并分支。

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

您将要从这些命令中排除masterdevelop分支。

本地 git clear:

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

远程 git 清除:

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

同步远程分支机构的本地注册表:

git fetch -p

对于那些使用 Windows 并喜欢 PowerShell 脚本的用户,以下是一种删除本地合并分支的方法:

function Remove-MergedBranches
{
  git branch --merged |
    ForEach-Object { $_.Trim() } |
    Where-Object {$_ -NotMatch "^\*"} |
    Where-Object {-not ( $_ -Like "*master" )} |
    ForEach-Object { git branch -d $_ }
}

我已经使用亚当的答案多年了。就是说,在某些情况下,它的行为不符合我的预期:

  1. 包含单词 “master” 的分支被忽略,例如 “notmaster” 或 “masterful”,而不仅仅是 master 分支
  2. 包含单词 “dev” 的分支将被忽略,例如 “dev-test”,而不仅仅是 dev 分支
  3. 删除当前分支的 HEAD 可以访问的分支(即不一定是主节点)
  4. 在分离的 HEAD 状态下,从当前提交中删除每个可到达的分支

1 和 2 很容易解决,只是对正则表达式进行了更改。 3 取决于您想要的上下文(即,仅删除尚未合并到 master 或当前分支中的分支)。如果您无意中在分离的 HEAD 状态下运行此git reflog ,则 4 可能会造成灾难性的后果(尽管可以使用git reflog恢复)。

最后,我希望所有这些都在一个单一的代码中,不需要单独的(Bash | Ruby | Python)脚本。

TL; DR

创建一个 git 别名 “sweep”,它接受可选的-f标志:

git config --global alias.sweep '!git branch --merged $([[ $1 != "-f" ]] \
&& git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)" \
| xargs git branch -d'

并使用以下命令调用它:

git sweep

要么:

git sweep -f

详细而详尽的答案

对于我来说,最简单的方法是创建带有某些分支的示例 git repo 并提交以测试正确的行为:

一次提交即可创建一个新的 git repo

mkdir sweep-test && cd sweep-test && git init
echo "hello" > hello
git add . && git commit -am "initial commit"

创建一些新分支

git branch foo && git branch bar && git branch develop && git branch notmaster && git branch masterful
git branch --list
bar
  develop
  foo
* master
  masterful
  notmaster

期望的行为:选择所有合并的分支,除了:母版,开发版或当前版

原始正则表达式会错过分支 “masterful” 和 “notmaster”:

git checkout foo
git branch --merged | egrep -v "(^\*|master|dev)"
bar

使用更新的正则表达式(现在不包括 “开发” 而不是 “开发”):

git branch --merged | egrep -v "(^\*|^\s*(master|develop)$)"
bar
masterful
notmaster

切换到分支 foo,进行新的提交,然后基于 foo 签出新的分支 foobar:

echo "foo" > foo
git add . && git commit -am "foo"
git checkout -b foobar
echo "foobar" > foobar
git add . && git commit -am "foobar"

我当前的分支是 foobar,并且如果我重新运行以上命令以列出要删除的分支,则即使没有将其合并到 master 中,也会包含分支 “foo”:

git branch --merged | egrep -v "(^\*|^\s*(master|develop)$)"
bar
  foo
  masterful
  notmaster

但是,如果我在 master 上运行相同的命令,则不包括分支 “foo”:

git checkout master && git branch --merged | egrep -v "(^\*|^\s*(master|develop)$)"
bar
  masterful
  notmaster

这仅仅是因为git branch --merged如果没有另外指定,则默认为当前分支的 HEAD。至少对于我的工作流程,除非本地分支已合并到 master 分支,否则我不希望删除它们,因此我更喜欢以下变体:

git checkout foobar
git branch --merged $(git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)"
bar
  masterful
  notmaster

分离的 HEAD 状态

依赖git branch --merged的默认行为在分离的 HEAD 状态下会产生更重要的后果:

git checkout foobar
git checkout HEAD~0
git branch --merged | egrep -v "(^\*|^\s*(master|develop)$)"
bar
  foo
  foobar
  masterful
  notmaster

这将删除我刚才所在的分支 “foobar” 和 “foo”,这几乎肯定不是期望的结果。但是,使用我们修改后的命令:

git branch --merged $(git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)"
bar
  masterful
  notmaster

一行,包括实际删除

git branch --merged $(git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)" | xargs git branch -d

全部打包成一个 git 别名 “sweep”:

git config --global alias.sweep '!git branch --merged $([[ $1 != "-f" ]] \
&& git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)" \
| xargs git branch -d'

别名接受可选的-f标志。缺省行为是仅删除已合并到 master 的分支,但是-f标志将删除已合并到当前分支的分支。

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 Sweep在这方面做得很好。

使用 Git 2.5.0 版本:

git branch -d `git branch --merged`

您可以将提交添加到 --merged 选项。这样,您可以确保仅删除合并到原点 / 母版中的分支

以下命令将从您的来源中删除合并的分支。

git branch -r --merged origin/master | grep -v "^.*master" | sed s:origin/:: |xargs -n 1 git push origin --delete

您可以测试将删除哪些分支,将 git push origin --delete 替换为 echo

git branch -r --merged origin/master | grep -v "^.*master" | sed s:origin/:: |xargs -n 1 echo