如何修剪远程不再存在的本地跟踪分支

使用git remote prune origin,我可以删除不在远程上的本地分支。

但我也想删除从这些远程分支创建的本地分支(检查它们是否未合并会很好)。

我怎么能这么做?

568809 次浏览

不知道如何一次完成所有操作,但gitgit branch -d <branchname>只会在完全合并时删除本地分支。注意小写的d。

git branch -D <branchname>(注意大写D)将删除本地分支,无论其合并状态如何。

如果要删除所有已经合并到master中的本地分支,可以使用以下命令:

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

如果您使用main作为主分支,您应该相应地修改命令:

git branch --merged main | grep -v '^[ *]*main$' | xargs git branch -d

更多信息

git help fetch提供的信息中,有这样一个小项目:

 -p, --pruneAfter fetching, remove any remote-tracking branches which no longer exist on the remote.

所以,也许git fetch -p就是你要找的?

编辑:好的,对于那些在事实发生3年后仍在争论这个答案的人,这里有更多关于我为什么提出这个答案的信息…

首先,OP说他们想要“删除那些从远程分支创建的本地分支[不再在远程分支上]”。这在git中是不可能的。这是一个例子。

假设我在中央服务器上有一个repo,它有两个分支,称为AB。如果我将该repo克隆到我的本地系统,我的克隆将具有名为origin/Aorigin/B的本地引用(还不是实际分支)。现在让我们假设我执行以下操作:

git checkout -b A origin/Agit checkout -b Z origin/Bgit checkout -b C <some hash>

这里的相关事实是,由于某种原因,我选择在我的本地存储库上创建一个名称与其原始存储库不同的分支,并且我还有一个本地分支(尚未)存在于原始存储库上。

现在,假设我删除了远程仓库上的AB分支并更新了我的本地仓库(某种形式的git fetch),这导致我的本地引用origin/Aorigin/B消失了。现在,我的本地仓库仍然有三个分支,AZC。这些分支在远程仓库上都没有相应的分支。其中两个是“从…远程分支创建的”,但即使我知道原点上曾经有一个名为B的分支,我也无法知道Z是从B创建的,因为它在过程中被重命名了,可能是有充分的理由的。所以,实际上,如果没有一些外部进程记录分支起源元数据,或者一个了解历史的人,就不可能分辨出OP要删除的三个分支中的哪一个(如果有的话)。如果没有一些B1不会自动为你维护的外部信息,B2大约是你能得到的最接近的,任何自动尝试OP要求的内容的方法都有删除太多分支的风险,或者缺少OP希望删除的一些分支。

还有其他的场景,例如,如果我在origin/A上创建了三个单独的分支来测试某种东西的三种不同方法,然后origin/A消失了。现在我有三个分支,显然它们不能都匹配名称,但它们是从origin/A创建的,因此对OP问题的字面解释需要删除所有三个分支。然而,如果你能找到一种可靠的方法来匹配它们,这可能并不理想……

修剪后,您可以使用git branch -r获取远程分支列表。可以使用git branch -vv检索具有远程跟踪分支的分支列表。因此,使用这两个列表,您可以找到不在远程列表中的远程跟踪分支。

这一行应该可以做到(需要bashzsh,不适用于标准的Bourne shell):

git fetch -p ; git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}' | xargs git branch -d

此字符串获取远程分支的列表并通过标准输入将其传递到egrep。并过滤具有远程跟踪分支的分支(使用git branch -vv并过滤具有origin的分支),然后获取该输出的第一列,即分支名称。最后将所有分支名称传递到删除分支命令中。

由于它使用-d选项,因此它不会删除未合并到您运行此命令时所在分支中的分支。

我很确定git remote prune origin是你想要的。

您可以将其作为git remote prune origin --dry-run运行,以查看它在不进行任何更改的情况下做了什么。

Schleis的变体对我不起作用(Ubuntu 12.04),所以让我提出我的(清晰而闪亮的:)变体:

备选案文1(我倾向于这一备选案文):

git for-each-ref --format='%(refname:short) %(upstream)' refs/heads/ | awk '$2 !~/^refs\/remotes/' | xargs git branch -D

备选案文2:

a.模拟:

comm -23 <( git branch | grep -v "/" | grep -v "*" | sort ) <( git br -r | awk -F '/' '{print $2}' | sort ) | awk '{print "git branch -D " $1}'

b.删除分支:

comm -23 <( git branch | grep -v "/" | grep -v "*" | sort ) <( git br -r | awk -F '/' '{print $2}' | sort ) | xargs git branch -D

删除任何与master不同步的分支

git co master && git branch | sed s/\*/\ / | xargs git branch -d 2> /dev/null

使用GUI?手动程序,但快速简单。

$ git gui

选择“分支->删除”。您可以使用ctrl单击(windows)选择多个分支并删除所有分支。

这将删除远程跟踪分支已被修剪的本地分支。(确保您在master分支上!)

git checkout mastergit branch -vv | grep ': gone]' | awk '{print $1}' | xargs git branch -d

详情:

  • git branch -vv显示远程已修剪的本地分支“已消失”。

    mybranch abc1234 [origin/mybranch: gone] commit comments
  • -d will check if it has been merged (-D will delete it regardless)

    error: The branch 'mybranch' is not fully merged.

更短更安全的单行:

git branch -d $(git branch --merged | cut -c 3- | grep -v master)

在运行之前,请务必签出到尚未合并的分支。因为您无法删除当前签入的分支。

根据上面的答案,我得到了这个单行解决方案:

git remote prune origin; git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}' | xargs git branch -d

它将列出其远程跟踪分支从远程删除的本地分支

$ git remote prune origin --dry-run

如果您想从本地取消引用这些未跟踪的本地分支

$ git remote prune origin

我已经把接受的答案变成了一个强大的脚本。你会发现它在我的git扩展存储库中

$ git-rprune --helpRemove old local branches that do not exist in REMOTE any more.With --test, only print which local branches would be deleted.Note: To do this automatically on each fetch / pull:git config --global fetch.prune trueUsage: git-rprune REMOTE [-t|--test|-f|--force] [-?|-h|--help]

使用@wisbucky答案的变体,我将以下内容作为别名添加到我的~/.gitconfig文件中:

pruneitgood = "!f() { \git remote prune origin; \git branch -vv | perl -nae 'system(qw(git branch -d), $F[0]) if $F[3] eq q{gone]}'; \}; f"

有了这个,一个简单的git pruneitgood将清理合并后不再需要的本地和远程分支。

以下是@wisbucky对Windows用户的回答的改编:

for /f "tokens=1" %i in ('git branch -vv ^| findstr ": gone]"') DO git branch %i -d

我使用posh-git,不幸的是PS不喜欢裸for,所以我创建了一个名为PruneOrphanBranches.cmd的普通ol命令脚本:

@ECHO OFFfor /f "tokens=1" %%i in ('git branch -vv ^| findstr ": gone]"') DO CALL :ProcessBranch %%i %1
GOTO :EOF
:ProcessBranchIF /I "%2%"=="-d" (git branch %1 %2) ELSE (CALL :OutputMessage %1)GOTO :EOF
:OutputMessageECHO Will delete branch [%1]GOTO :EOF
:EOF

在没有参数的情况下调用它来查看列表,然后用“-d”调用它来执行实际删除或“-D”用于任何未完全合并但无论如何都要删除的分支。

在git bash中尝试此操作,获取并修剪对已删除分支的引用,然后修剪跟踪已删除分支的本地分支:

git fetch -p && git branch -d `git branch -vv | grep ': gone]' | awk '{print $1}' | xargs`

请记住首先签出一个不会被删除的分支,这样就不会阻止分支的删除。

可以配置Git在获取时自动删除对已删除远程分支的引用:

git config --global fetch.prune true

之后调用git fetchgit pull时,对已删除远程分支的引用会自动删除。

如果使用Windows和Powershell,您可以使用以下命令删除已合并到当前签出的分支中的所有本地分支:

git branch --merged | ? {$_[0] -ne '*'} | % {$_.trim()} | % {git branch -d $_}

补充说明

  • 列出当前分支和已合并到其中的分支
  • 过滤掉当前分支
  • 为每个剩余的分支名称清除git输出中的任何前导或尾随空格
  • 删除合并的本地分支

值得先自己运行git branch --merged,以确保它只会删除您期望的内容。

(从http://railsware.com/blog/2014/08/11/git-housekeeping-tutorial-clean-up-outdated-branches-in-local-and-remote-repositories/移植/自动化。)

有一个整洁的npm包可以为你做这件事(它应该跨平台工作)。

安装它:npm install -g git-removed-branches

然后git removed-branches将显示所有过时的本地分支,git removed-branches --prune将实际删除它们。

更多信息在这里。

我想要的东西可以清除所有跟踪远程分支的本地分支,在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'

注意:对git branch使用-D标志可能非常危险。因此,在上面的配置命令中,我使用-d选项来git branch而不是-D;我在实际配置中使用-D。我使用-D是因为我不想听到Git抱怨未合并的分支,我只是希望它们消失。您可能也需要此功能。如果是这样,只需在配置命令末尾使用-D而不是-d

windows解决方案

对于Microsoft Windows Powershell:

git checkout master; git remote update origin --prune; git branch -vv | Select-String -Pattern ": gone]" | % { $_.toString().Trim().Split(" ")[0]} | % {git branch -d $_}

解释说明

git checkout master切换到主分支

git remote update origin --prune修剪远程分支

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

Select-String -Pattern ": gone]"仅获取已从远程删除的记录。

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

% {git branch -d $_}删除分支

您可以使用此命令:

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

Git Clean:删除已经合并的分支包括命令的分解

git branch --merged master | grep -v '^[ *]*master$' | xargs git branch -d的PowerShell版本

git branch --merged master | %{ if($_ -notmatch '\*.*master'){ git branch -d "$($_.Trim())" }}

这将删除所有已合并到master,而你在主分支上中的本地分支。

git checkout master切换。

根据上面的答案,我使用这个较短的一行:

git remote prune origin | awk 'BEGIN{FS="origin/"};/pruned/{print $2}' | xargs -r git branch -d

此外,如果您已经修剪并有本地悬挂的树枝,那么这将清理它们:

git branch -vv | awk '/^ .*gone/{print $1}' | xargs -r git branch -d

似乎没有一个安全的单行代码,太多的边缘情况(比如分支的名称中包含“master”等)。

  1. git branch -vv | grep 'gone]' > stale_branches.txt
  2. 查看文件并删除要保留的分支(例如主分支!)的行;您不需要编辑任何行的内容
  3. awk '{print $1}' stale_branches.txt | xargs git branch -d

我到达这个页面寻求“如何删除不再具有上游分支的本地签出分支”的答案

我也不关心本地分支是否已经合并,因为管道进入git branch -d只会警告而不是删除未合并的本地分支。

git branch -a | grep origin | tr -s ' ' | cut -d '/' -f3 | egrep -v -f /dev/fd/0 <(git branch -a | grep -v origin) | grep branch_prefix_that_I_care_about | xargs git branch -d
# translation# git branch -a | grep origin | tr -s ' ' | cut -d '/' -f3## this finds all remote branch names minus the "remote/origin/" part## <(git branch -a | grep -v origin)## this finds all local branch names and redirects it into the previous command## egrep -v -f /dev/fd/0 STUFF## this is doing some weird magic that I'm grokking as "take the set difference from whatever was piped in"### overall translation: (STUFF TO CONSIDER) | egrep magic <(STUFF TO REMOVE FROM CONSIDERATION) | do cool things with resulting stuff

要删除远程分支:

$git remote prune origin

要删除已经合并的本地分支:

$git branch -D $(git branch --merged)

正如@tzacks所指出的…有一个npm包对此很方便。只需执行:

npx git-removed-branches --prune

(我会评论,但没有足够的声誉)

PowerShell

git branch -D (git branch --merged |% { $_.trim() } )

以下是我的解决方案:

git fetch -pgit branch -vv | grep ": gone" | awk '{print $1}' | xargs git branch -d
  • -p是删除远程上不再存在的任何远程跟踪引用。所以第一步将删除对远程分支的引用。

  • -vv用于显示每个头的sha1和提交主题行,以及与上游分支的关系(如果有)。第二步将获取所有本地分支,grep命令将过滤掉已删除的分支。

这适用于我使用git 2.21.0-它删除了本地跟踪分支,这些分支合并到HEAD中,我以前在推送上有--set-upstream(我使用push.default=upstream是因为它最适合多个远程),并且上游分支已经被fetch --prune删除(或者隐式地如果fetch.prune=true在git config中):

git branch -vv --merged | grep ': gone]' | awk '{print $1}' | xargs git branch -d

使用--merged-d使其成为一个非常“安全”的删除。更激进的版本可以删除--merged并使用-D

当遥控器本身不再存在时,我在这里找不到有用的答案。我一直在寻找不再存在的远程数据库的分支,但没有找到删除它们的git命令。

我的解决方案是转到.git\refs\remotes目录并直接删除不再相关的文件。文件结构非常容易理解。它与您在git branch -r中看到的结构相同。

检查目标

for target in $(git branch | grep -Eiv "master|develop|branchYouWantToLive"); do echo $target; done

使用for&子命令运行

for target in $(git branch | grep -Eiv "master|develop|branchYouWantToLive"); do git branch -D $target; done

您可以扩展有关分支的其他内容。

除了Schleis答案(它可以完美地工作),这可以集成到Visual Studio中,因此在VS中打开git存储库时修剪本地分支非常简单。

您可以使用外部工具功能使用特定目录和命令调用sh.exe(git bash)。这位于工具>外部工具菜单选项中(在VS 2022 17.1.0中)。我使用的参数如下:

命令{Path-To-Git-Installation-Location}\bin\sh.exe

论点--cd=$(SolutionDir) -c "git fetch -p ; git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}' | xargs git branch -d"

初始目录$(SolutionDir)

Git Prune本地分支截图

值得注意的是,只有当您在VS中打开的解决方案位于git repo目录中时,这才有效。

最后一点-这可以通过设置>常规>键盘中的Visual Studio键绑定用户交互界面进行键绑定,并搜索工具。外部命令[n],其中n是您将此外部工具定位在的外部命令表中的位置(它们可以在工具>外部工具对话框中重新排序)。请参阅下面的屏幕截图。

外部工具命令的键绑定

此命令和下面的脚本在Linux和Windows中使用Git Bash(MinGW)。

最好使用git的内部命令,这样注释或名称就不会意外匹配并删除您不想删除的分支。有许多内部“原子”可以与git for-each-ref的格式选项一起使用以输出所需的信息。这样我们就不必依赖管道到awkgrep来检查输出上可能包含不必要信息的正则表达式。

下面的命令只使用git for-each-ref的内部低级命令来列出孤立的本地分支。一旦你有了这些,你就可以通过管道进入git branch -D。此外,不要忘记先修剪并获取你的远程引用,否则它将找不到任何匹配:

git fetch -pgit for-each-ref --format '%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)' 'refs/heads/**' | xargs -r git branch -D

以下是一个细分:

git fetch -p-修剪删除的引用并从远程获取新引用

git for-each-ref --format-列出使用特定输出格式的所有引用。

%(if:equals=[gone])%(upstream:track)-仅当上游跟踪分支为“[已消失]”时才输出。

%(then)%(refname:short)%(end)-输出分支名称(当跟踪消失时)。

refs/heads/**-限制头部引用(为了效率)。

| xargs -r git branch -D-管道输出作为删除参数。-r表示忽略空白输入。


就其本身而言,这个解决方案很长,键入起来很荒谬,而且很难记住。幸运的是,向git添加自定义命令很容易。下面是一个使用上面相同命令的脚本,但它允许用户查看将使用--dry-run选项选择哪些分支。

我将我的文件命名为git-prune-local并将其放入包含在我的PATH上的文件夹中。它还需要执行权限(chmod 755 git-prune-local)。

Git会自动查找像git-[command]这样的可执行文件。这样,您只需键入git prune-local即可删除正确的分支。

git-prune创建本地组件

#!/bin/sh
if [ $# -gt 1 ] || ([ ! -z $1 ] && [ $1 != "--dry-run" ])thenecho "Usage: git prune-local [--dry-run]"exitfi
git fetch -p --quietbranchesToDelete=$(git for-each-ref --format '%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)' 'refs/heads/**')
while read -r branchdoif [ ! -z $branch ]thenif [ ! -z $1 ]thenecho $branchelsegit branch -D $branchfifidone <<< "$branchesToDelete"

晚于盛宴,但您可以使用--no-包含选项来避免在使用分支--合并时删除您的主分支

git branch --no-contains master --merged master | xargs git branch -d