是否有一种简单的方法可以删除所有远程对等分支不再存在的跟踪分支?

例子:

分支机构(本地和远程)

主人原始/主起源/bug-fix-a起源/bug-fix-b起源/bug-fix-c

在本地,我只有一个主分支。现在我需要处理bug-fix-a,所以我检查它,处理它,并将更改推到远程。接下来,我对bug-fix-b做同样的操作。

分支机构(本地和远程)

主人bug-fix-abug-fix-b型原始/主起源/bug-fix-a起源/bug-fix-b起源/bug-fix-c

现在我有本地分支机构master,bug-fix-a,bug--fix-b。主分支维护者将把我的更改合并到主分支中,并删除他已经合并的所有分支。

因此,当前状态为:

分支机构(本地和远程)

主人bug-fix-abug-fix-b型原始/主起源/bug-fix-c

现在我想调用一些命令来删除分支(在本例中为bug-fix-a、bug-fix-b),这些分支在远程存储库中不再表示。

它类似于现有命令git remote prune origin,但更类似于git local prune origin。


当前回答

从这里大量的其他答案中,我得出了以下结论(我认为只有2.13和更高的数字),它应该适用于任何类似UNIX的shell:

git for-each-ref --shell --format='ref=%(if:equals=[gone])%(upstream:track)%(then)%(refname)%(end)' refs/heads | while read entry; do eval "$entry"; [ ! -z "$ref" ] && git update-ref -d "$ref" && echo "deleted $ref"; done

这特别地使用了每个ref而不是branch(因为branch是一个为人类可读输出而设计的“瓷器”命令,而不是机器处理),并使用其--shell参数来获得正确转义的输出(这使我们不必担心ref名称中的任何字符)。

其他回答

这将删除除本地主引用和当前使用的引用之外的所有合并的本地分支:

git branch --merged | grep -v "*" | grep -v "master" | xargs git branch -d

这将删除所有已从“源”引用的远程存储库中删除的分支,但仍在“远程/源”中本地可用。

git remote prune origin

我认为没有内置命令来执行此操作,但可以安全地执行以下操作:

git checkout master
git branch -d bug-fix-a

当您使用-d时,git将拒绝删除分支,除非它完全合并到HEAD或其上游远程跟踪分支中。因此,您可以始终循环每个ref的git输出,并尝试删除每个分支。这种方法的问题是,我怀疑您可能不希望因为origin/bug-fix-d包含其历史而删除bug--fix-d。相反,您可以创建如下脚本:

#!/bin/sh

git checkout master &&
for r in $(git for-each-ref refs/heads --format='%(refname:short)')
do
  if [ x$(git merge-base master "$r") = x$(git rev-parse --verify "$r") ]
  then
    if [ "$r" != "master" ]
    then
      git branch -d "$r"
    fi
  fi
done

警告:我尚未测试此脚本-请小心使用。。。

git remote prune origin
git branch -d bug-fix-a bug-fix-b

使用脚本执行此操作的风险在此处解决:https://stackoverflow.com/a/47939403/4592031

Windows解决方案

对于Microsoft Windows Powershell:

git结帐主机;git远程更新原点--prune;git branch-vv|选择字符串-模式“:gone]”|%{$_.toString().Trim().Split(“”)[0]}|%{git branch-d$_}

解释

git checkout master切换到master分支

git远程更新原点--修剪远程分支

gitbranch-vv获取所有分支的详细输出(git引用)

选择String-Pattern“:gone]”仅获取从远程删除的记录。

%{$_.toString().Trim().Split(“”)[0]}获取分支名称

%{gitbranch-d$_}删除分支

另一个答案。

如果你正在寻找一些新奇的东西,这里是我在fzf中使用的别名。

LANG=C git for-each-ref --sort=-committerdate --format '%(refname:short) %(upstream:track)' refs/heads \
  | awk '$2 == "[gone]" {print $1}' \
  | fzf --multi --bind ctrl-a:select-all,ctrl-t:toggle-all \
        --preview='git log --color=always --graph --format=compact {1}' \
        --header='Select [gone] branch to remove' \
        --header-first --reverse \
  | xargs --no-run-if-empty git branch -d

解释:

强制LANG=C以避免[gone]模式上的本地化对每个引用使用git,以避免git分支中的任何格式更改使用awk过滤。我尝试使用--format=“%(if:equals=[gone])%…”,但这意味着要管理空行。使用fzf选择要删除的分支。添加快捷键:[ctrl-a]选择全部,[ctrl-t]反转选择([tab]照常选择)。您可以使用[enter]进行验证,也可以使用[esc]中止。我使用这个分支选择器作为确认阶段。在右侧显示所选分支的日志(以记住日期和内容…)。我使用下面列出的紧凑格式。您可以使用任何(不太宽)日志格式。--如果为空,则不运行。它不是强制性的,有助于避免在中止进程时出错(与没有分支名称的gitbranch-d相关)。

别名可能看起来很糟糕:

[alias]
      gone = "!f() { LANG=C git for-each-ref --sort=-committerdate --format '%(refname:short) %(upstream:track)' refs/heads | awk '$2 == \"[gone]\" {print $1}' | fzf --multi --bind ctrl-a:select-all,ctrl-t:toggle-all --preview='git log --color=always --graph --format=compact {1}' --header='Select [gone] branch to remove' --header-first --reverse | xargs --no-run-if-empty git branch -d ; }; f"
[pretty]
      compact = %Cred%h%Creset%C(auto)%d%Creset%n  Author: %an <%aE>%n  Date:  %cd%n     %s

(与上文相同,“)