删除远程不存在的分支。

65
==> git branch -a
* master
  test
  remotes/origin/master
  remotes/origin/test

当有人删除了remotes/origin/test时,在我的电脑上我仍然可以看到它。

我知道我可以这样做并删除test

==> git remote prune
==> git branch -d test
==> git branch -a
* master
  remotes/origin/master

但是如果我有更多的本地分支,而且它们不在远程上,那么我如何快速删除它们?


2
可能是删除不再存在于远程的跟踪分支的重复问题。 - rds
10个回答

87

这是我删除不再相关的本地分支的方法:

git branch --merged origin/master | xargs git branch -d

你可能需要根据你的具体配置进行微调(例如,在下面的注释中查看如何排除特定分支),但在管道之前这里的第一个命令应该会给你一个列表,其中包含已合并到主分支中的所有本地分支。


50
由于担心误删本地主分支,我对此进行了修改:`git branch --merged origin/master | grep -v master | xargs git branch -d`(该命令用于删除本地已经合并到远程主分支的所有分支,但排除了本地主分支) - thexfactor
15
以上评论中有一个微小的更改,为了避免意外删除除“master”以外的其他分支(即Gitflow中的“develop”),最好不要反转grep命令,而是采取以下方式:git branch --merged origin/develop | grep "^\s*feature/" | xargs git branch -d。请注意,我已经尽力使翻译通俗易懂并保持原始含义,如有需要,请随时告诉我。 - Steve Ardis
2
如果你想删除除了远程之外的所有内容,另一个小修改是让我们确保只排除"master"而不是"mastery"或"postmaster",通过在grep中添加"-w": git branch --merged origin/master | grep -vw master | xargs git branch -d - nofinator
1
此解决方案仅适用于所有分支最终合并到名为“master”的主干分支的情况。在具有长期存活的发布分支的工作流中,这种情况并不适用,这些分支永远不会合并到主干分支。此外,请注意,现在默认分支的名称是“origin/main”,而不是“origin/master”。 - AliA
1
对于在Windows上使用的用户,我为你们提供以下版本,因为xargs在那里不存在。PowerShell版本: git branch --merged origin/main | ForEach-Object { git branch -d $_.Trim() } 也可以简化为以下形式: git branch --merged origin/main | % { git branch -d $_.Trim() } 还有其他可能性,但我们就在这里停止讨论PowerShell吧。CMD版本: FOR /F "tokens=*" %i IN ('git branch --merged origin/main') DO git branch -d %i - Reza

59

简单的修剪操作不会删除本地分支。

这里有另一种方法可以实现真正的删除。首先确保执行“git fetch -p”命令以获取远程仓库最新状态。

git branch -vv | grep ': gone]'|  grep -v "\*" | awk '{ print $1; }' | xargs -r git branch -d

这将检查所有本地分支及其远程分支,并删除已删除其远程分支的所有本地分支。

详细信息:

git branch -vv

该命令将列出您本地的分支,并显示有关远程分支的信息,如果该远程分支不存在,则会显示“gone”。

grep ': gone]'

将获取与“gone]”短语匹配的分支。

grep -v "\*"

将只获取不包含星号的行。这将忽略您当前所在的分支,还可以防止执行带有末尾的“*”的“git branch -d”,这将导致删除所有本地分支。

awk '{print $1}'

将获取输出直到第一个空格,这将导致本地分支名称。

xargs git branch -d

将输出(分支名称)用于“git branch -d”命令并将其附加到其中,最终删除该分支。如果您还想删除尚未完全合并的分支,则可以使用大写字母“D”而不是“d”来强制删除。


在macOS上工作过,但是一些没有远程源的分支已经被遗留下来了。很有趣... - Johnny Five
1
@Leo 如果您在非 GNU Linux 系统中执行该命令,则会出现此错误。仅基于 GNU Linux 的系统具有 -r 选项,该选项负责在输入参数为空时跳过执行(在本例中为分支名称)。您可以从命令中删除该选项,但可能会得到一些无效的参数错误,您可以忽略它们。 - kcm
2
我喜欢在使用xargs时添加一个-p参数(特别是当您使用git branch-D选项时),以便对运行的每个命令进行明确确认。为了实现这一点,请使用xargs -rp git branch -D修改最后一个命令。 - Qortex
1
我喜欢这个答案,因为它适用于已经在远程进行了压缩合并的分支,而不仅仅是合并的分支。 - snowe
1
注意:这可能会受到本地化的影响。因此,请确保使用 LANG=C git branch -vv | ...进行翻译 - MKesper
显示剩余3条评论

38

根据 git-fetch 手册页面的说明,git fetch -p 命令将“在获取后删除远程跟踪分支中已不存在的任何内容。” 如果您有本地分支正在跟踪这些远程分支,则可能需要手动修剪这些分支。


6
如何删除本地分支而不在远程的分支?-p 参数只会删除远程跟踪分支。 - Dozer
7
你可否可靠地区分出一个本地分支是用来追踪远程分支,还是为了本地开发而创建?如果我的本地分支名字叫做 foo,它最初是用来追踪 remotes/origin/foo 远程分支,还是为了测试一些新想法而创建的本地分支?如果你能对每个本地分支准确回答这个问题,那么你可以使用 git branch -D 命令删除不需要的分支。如果你不能够准确判断,试图自动化处理将会很危险。 - twalberg
2
手册中提到,--prune选项将“删除在远程不存在的任何远程跟踪引用”。因此,不跟踪远程分支的本地分支不会受到影响。 - user368683
这显然是最简单和最有效的方法。 这应该是被采纳的答案。 - Antoine Viallon
这只删除引用,分支仍然存在。 - Dan Friedman

10

我最终得到了与kcm的方法非常相似的东西。 我想要一个能够清除所有本地分支的指令,这些分支正在跟踪远程分支(在origin上),而远程分支已被删除(gone)。 我不想删除从未设置为跟踪远程分支的本地分支(例如:我的本地开发分支)。 另外,我希望使用一个简单的一行命令,只使用git或其他简单的CLI工具,而不编写自定义脚本。 我最终使用了一些grepawk来制作这个简单的命令,然后将其添加为别名到我的~/.gitconfig中。

[alias]
  prune-branches = !git remote prune origin && git branch -vv | grep ': gone]' | awk '{print $1}' | xargs -r git branch -D

这里有一个git config --global ...命令,可轻松将其添加为git prune-branches:

git config --global alias.prune-branches '!git remote prune origin && git branch -vv | grep '"'"': gone]'"'"' | awk '"'"'{print $1}'"'"' | xargs -r git branch -d'

注意:正如Matteo在另一个答案的早先评论中指出的那样,使用-D标记来执行git branch非常危险。因此,在配置命令中,我使用-d选项来执行git branch而不是-D;我在我的实际配置中使用-D。我使用-D是因为我不想听到Git抱怨未合并的分支,我只希望它们消失。您可能也需要这个功能。如果是这样,请在该配置命令的结尾处使用-D代替-d


8
我写了一个名为git-dangling-branches的简单shell脚本来实现这个目的。如果你指定-D选项,它将删除所有没有refs/remotes/origin/<branch_name>的本地分支。当然,在执行此操作时应该小心谨慎。
#!/bin/bash -e
if [[ "$1" == '-D' ]]; then
  DELETE=1
else
  DELETE=0
fi

REMOTE_BRANCHES="`mktemp`"
LOCAL_BRANCHES="`mktemp`"
DANGLING_BRANCHES="`mktemp`"
git for-each-ref --format="%(refname)" refs/remotes/origin/ | \
  sed 's#^refs/remotes/origin/##' > "$REMOTE_BRANCHES"
git for-each-ref --format="%(refname)" refs/heads/ | \
  sed 's#^refs/heads/##' > "$LOCAL_BRANCHES"
grep -vxF -f "$REMOTE_BRANCHES" "$LOCAL_BRANCHES" | \
  sort -V > "$DANGLING_BRANCHES"
rm -f "$REMOTE_BRANCHES" "$LOCAL_BRANCHES"

if [[ $DELETE -ne 0 ]]; then
  cat "$DANGLING_BRANCHES" | while read -r B; do
    git branch -D "$B"
  done
else
  cat "$DANGLING_BRANCHES"
fi
rm -f "$DANGLING_BRANCHES"

2
这就是我想要的答案。我对清除本地分支很感兴趣,这些分支在github上不存在相同的名称。其他答案会删除远程跟踪分支和所有已合并的分支,但这不是我想要的。所以感谢您提供的解决方案。在运行之前,我将-D更改为-d,以防万一。 - Chris Lear
这太棒了。由于我的操作系统不支持mktemp,我不得不将其更改为使用实际文件名的touch,但这绝对是精彩的。 - earl3s
我还在顶部添加了这个:git remote prune origin $DRY_RUN,其中 $DRY_RUN 设置为 --dry-run 如果没有传递 -D。 这样可以很好地清理我的系统。 - earl3s

5

快速

git fetch -p
git branch -v | grep "\[gone\]"

然后手动检查并删除输出中的分支。


详细说明

基于此主题中的答案以及这里的答案,最简单的手动解决方案是使用以下命令删除不再存在于远程服务器上的所有远程跟踪引用:

git fetch -p

然后使用以下命令查看已从远程跟踪中删除,但仍在您本地工作树中的内容:

git branch -v

手动删除输出中带有 [gone] 的分支。

要获取简明列表,请运行

git branch -v | grep "\[gone\]"

这也可能受到本地化的影响。请确保使用 LANG=C git branch -v | ... - MKesper
一行解决方案: git fetch -p && LC_ALL=C git branch --format "%(refname:lstrip=-2) %(upstream:track)" | grep -wF '[gone]' | awk '{ print $1 }' | xargs -n1 git branch -d - Antoine Viallon
更一般地说,如果系统不是英文的,则可以发出以下命令:git branch -v | grep "\[*\]" | awk '{print $1}' | xargs git branch -D,承认除了已删除的分支之外,没有显示任何其他内容。 - Rick Stanley

2
这是非常简单的。
git branch --merged | xargs git branch -d

这个选项与之前显示的更复杂的选项相同,即只删除其远程分支已被删除的分支。

0

您可以通过迭代引用来实现此操作,我使用以下命令删除了所有没有远程分支的本地分支,并且它起作用了。 警告:此操作会强制删除未完全合并的分支,请谨慎使用。

git branch -D `git for-each-ref --format="%(fieldName)" refs/heads/<branch-name-pattern>`

%(fieldName) = refname:short)

refs/heads/ = 如果分支名称有共同的前缀/后缀,则可以添加后缀 例如:refs/heads/*abc*

更多信息请参考git-for-each-ref(1)手册页面


使用您的解决方案,您将强制删除未完全合并的分支,这非常危险! - Matteo
"git for-each-ref --format=%(refname:short) refs/heads" 将列出所有本地分支,无论它们是否在远程上。然后 "git branch -D" 将删除它们,即使它们有未提交的更改。这不是问题的正确答案。 - AliA

0

首先,执行:

git fetch && git remote prune origin

然后:

git branch -a | grep -v ${$(git branch -a | grep remotes | cut -d/" -f3-)/#/-e} | xargs git branch -D

  • 列出所有没有 remote/origin 前缀的远程分支:git branch -a | grep remotes | cut -d'/' -f3-
  • 从 Git 中删除所有列出的分支:git branch -a | grep v ${<去掉前缀的所有远程分支>/#/-e}
  • 删除本地分支需要使用 -D 参数,因为并不是所有的分支都需要标记为已合并。例如,在 Github 中执行 squash and merge 操作并不会将它们标记为已合并。

1
我得到了pipe braceparam cmdsubst pipe pipe dquote>,但不知道它是什么意思。 - Leo
1
寻找匹配的 " 时出现意外的 EOF,你确定那个切割语法是正确的吗? - dcsan
这个流水线不起作用。即使它能工作,也不是正确的答案,因为“git branch -a”列出了所有分支,然后管道的第二部分试图过滤掉其中带有“remotes”的条目。这将给出所有本地分支的列表,而不仅仅是不在远程的分支。 - AliA

0
总结所有这些评论,这是我的个人版本。首先,我们列出所有的分支,然后我们筛选出那些不再原点上的分支,我们使用“cut”来获取名称,并以安全的方式删除它们,以确保我们不会丢失任何提交。
git branch -v | grep "\[gone\]" | cut -f 3 -d ' ' | xargs git branch -d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接