删除不再在远程上的跟踪分支

有没有一种简单的方法来删除其远程等效不再存在的所有跟踪分支?

示例:

分支机构(本地和远程)

  • 大师
  • 来源/主
  • 起源/bug-Fix-a
  • bug-Fix-b
  • bug-Fix-c

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

分支机构(本地和远程)

  • 大师
  • bug-Fix-a
  • bug-Fix-b
  • 来源/主
  • 起源/bug-Fix-a
  • bug-Fix-b
  • bug-Fix-c

现在我有本地分支大师bug-Fix-abug-Fix-b。主分支维护者将把我的更改合并到大师并删除他已经合并的所有分支。

所以现在的状态是:

分支机构(本地和远程)

  • 大师
  • bug-Fix-a
  • bug-Fix-b
  • 来源/主
  • bug-Fix-c

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

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

830229 次浏览

我认为没有内置命令可以做到这一点,但执行以下操作是安全的:

git checkout mastergit branch -d bug-fix-a

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

#!/bin/sh
git checkout master &&for r in $(git for-each-ref refs/heads --format='%(refname:short)')doif [ x$(git merge-base master "$r") = x$(git rev-parse --verify "$r") ]thenif [ "$r" != "master" ]thengit branch -d "$r"fifidone

警告:我还没有测试过这个脚本-只能小心使用…

git fetch -p

这将修剪远程上不再存在的任何分支。

删除所有已合并到master的分支,但不要尝试删除master本身:

git checkout master && git pull origin master && git fetch -p && git branch -d $(git branch --merged | grep master -v)

或添加别名:

alias gitcleanlocal="git checkout master && git pull origin master && git fetch -p && git branch -d $(git branch --merged | grep master -v)"

说明:

git checkout master检出主分支

git pull origin master确保本地分支合并了所有远程更改

git fetch -p删除对已删除的远程分支的引用

git branch -d $(git branch master --merged | grep master -v)删除所有已合并到master的分支,但不要尝试删除master本身

git remote prune origin修剪不在远程上的跟踪分支。

git branch --merged列出已合并到当前分支的分支。

xargs git branch -d删除标准输入上列出的分支。

小心删除git branch --merged列出的分支。该列表可能包括master或您不希望删除的其他分支。

为了让自己有机会在删除分支之前编辑列表,您可以在一行中执行以下操作:

git branch --merged >/tmp/merged-branches && \vi /tmp/merged-branches && xargs git branch -d </tmp/merged-branches

我在这里找到了答案:如何删除所有已合并的git分支?

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

确保我们留住主人

您可以通过在第一个分支之后添加另一个grep来确保master或任何其他分支不会被删除。在这种情况下,你会去:

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

因此,如果我们想保留masterdevelopstaging,我们会这样做:

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

把这个化名

由于它有点长,您可能需要在.zshrc.bashrc中添加一个别名。我的名为gbpurge(用于git branches purge):

alias gbpurge='git branch --merged | grep -v "\*" | grep -v "master" | grep -v "develop" | grep -v "staging" | xargs -n 1 git branch -d'

然后重新加载.bashrc.zshrc

. ~/.bashrc


. ~/.zshrc

我不确定多长时间,但我现在使用git-up,它可以解决这个问题。

我做git up,它开始跟踪新分支并删除旧分支。

只是为了清楚,它不是开箱即用的git命令-https://github.com/aanand/git-up

顺便说一句,它还隐藏脏树,并使回扣仍然只有git up

希望对别人有用

最安全的方法是使用“管道”命令git for-each-ref和插值变量%(upstream:track),当分支不再在遥控器上时,它将是[gone]

git fetch -p && for branch in $(git for-each-ref --format '%(refname) %(upstream:track)' refs/heads | awk '$2 == "[gone]" {sub("refs/heads/", "", $1); print $1}'); do git branch -D $branch; done

这种方法比使用“瓷器”命令更安全,因为没有意外匹配部分提交消息的风险。这是一个使用“瓷器”git命令的版本:

git fetch -p && for branch in $(git branch -vv | grep ': gone]' | awk '{print $1}'); do git branch -D $branch; done

它的工作方式是在命令之后

git fetch -p

删除远程引用,当您运行

git branch -vv

它将显示“已消失”作为远程状态。例如,

$ git branch -vvmaster                 b900de9 [origin/master: behind 4] Fixed bugrelease/v3.8           fdd2f4e [origin/release/v3.8: behind 2] Fixed bugrelease/v3.9           0d680d0 [origin/release/v3.9: behind 2] Updated commentsbug/1234               57379e4 [origin/bug/1234: gone] Fixed bug

这就是脚本迭代的内容。

根据上面的信息,这对我很有效:

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

它删除了远程上所有使用are': gone] '的本地分支。

这是我用于鱼壳的溶液。在Mac OS X 10.11.5fish 2.3.0git 2.8.3上测试过。

function git_clean_branchesset base_branch develop
# work from our base branchgit checkout $base_branch
# remove local tracking branches where the remote branch is gonegit fetch -p
# find all local branches that have been merged into the base branch# and delete any without a corresponding remote branchset localfor f in (git branch --merged $base_branch | grep -v "\(master\|$base_branch\|\*\)" | awk '/\s*\w*\s*/ {print $1}')set local $local $fend
set remotefor f in (git branch -r | xargs basename)set remote $remote $fend
for f in $localecho $remote | grep --quiet "\s$f\s"if [ $status -gt 0 ]git branch -d $fendendend

一些注意事项:

确保设置正确的base_branch。在这种情况下,我使用develop作为基分支,但它可以是任何东西。

这部分非常重要:grep -v "\(master\|$base_branch\|\*\)"。它确保您不会删除主分支或基分支。

我使用git branch -d <branch>作为额外的预防措施,以免删除任何尚未与上游或当前HEAD完全合并的分支。

一个简单的测试方法是将git branch -d $f替换为echo "will delete $f"

我想我还应该补充一点:自担风险!

这些答案中的大多数实际上并没有回答最初的问题。我做了很多挖掘,这个是我找到的最干净的解决方案。以下是该答案的一个稍微更彻底的版本:

  1. 检查您的默认分支。通常git checkout master
  2. 运行git fetch -p && git branch -vv | awk '/: gone]/{print $1}' | xargs git branch -d

说明:

通过修剪跟踪分支,然后删除显示它们在git branch -vv中“消失”的本地分支来工作。

备注:

如果您的语言设置为英语以外的语言,您需要将gone更改为相应的单词。仅本地的分支将不会被触及。已在远程删除但未合并的分支将显示通知,但不会在本地删除。如果您想删除这些,请将-d更改为-D

这将删除本地不存在的所有远程分支(在ruby中):

bs = `git branch`.split; bs2 = `git branch -r | grep origin`.split.reject { |b| bs.include?(b.split('/')[1..-1].join('/')) }; bs2.each { |b| puts `git  push origin --delete #{b.split('/')[1..-1].join('/')}` }

解释:

# local branchesbs = `git branch`.split# remote branchesbs2 = `git branch -r | grep origin`.split# reject the branches that are present locally (removes origin/ in front)bs2.reject! { |b| bs.include?(b.split('/')[1..-1].join('/')) }# deletes the branches (removes origin/ in front)bs2.each { |b| puts `git  push origin --delete #{b.split('/')[1..-1].join('/')}` }

我想出了这个bash脚本。它总是保留分支developqamaster

git-clear() {git pull -a > /dev/null
local branches=$(git branch --merged | grep -v 'develop' | grep -v 'master' | grep -v 'qa' | sed 's/^\s*//')branches=(${branches//;/ })
if [ -z $branches ]; thenecho 'No branches to delete...'return;fi
echo $branches
echo 'Do you want to delete these merged branches? (y/n)'read yncase $yn in[^Yy]* ) return;;esac
echo 'Deleting...'
git remote prune originecho $branches | xargs git branch -dgit branch -vv}

这将删除所有合并的本地分支,除了本地主引用和当前正在使用的引用:

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

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

git remote prune origin

我使用这种方法,所以我可以有更多的控制权。

git分支-D$(git分支|grep-v"master"|grep-v"开发")

这是删除任何未命名的分支:masterdevelop

我用一个简短的方法来做这个技巧,我建议你也这样做,因为它可以节省一些时间,给你更多的可见性

只需将以下代码片段添加到您的. bashrc(macos上的. bashfile)中。

git-cleaner() { git fetch --all --prune && git branch --merged | grep -v -E "\bmaster|preprod|dmz\b" | xargs -n 1 git branch -d ;};
  1. 把所有遥控器拿来
  2. 仅从git获取合并分支
  3. 从此列表中删除“受保护/重要”分支
  4. 删除其余的(例如,清理和合并的分支)

您必须编辑grep正则表达式以满足您的需求(在这里,它可以防止删除master、preprod和dmz)

我通常不会回答一个已经有16个答案的问题,但所有其他答案都是错误的,正确的答案非常简单。问题说,“有没有一种简单的方法来删除所有远程等效不再存在的跟踪分支?”

如果“简单”意味着一次性删除它们,不脆弱,不危险,并且不依赖并非所有读者都拥有的工具,那么正确的答案是:不。

有些答案很简单,但他们没有按照要求做。另一些人按照要求做了,但并不简单:所有这些都依赖于通过文本操作命令或脚本语言解析Git输出,这可能并不存在于每个系统上。此外,大多数建议使用瓷器命令,其输出不是旨在通过脚本解析的(“瓷器”是指用于人类操作的命令;脚本应该使用较低级别的“管道”命令)。

进一步阅读:


如果您想安全地执行此操作,对于问题中的用例(已在服务器上删除但仍作为本地分支存在的垃圾收集跟踪分支)并且仅使用高级Git命令,您必须

  • git fetch --prune(或者git fetch -p,这是一个别名,或者git prune remote origin,它在没有获取的情况下做同样的事情,并且可能不是你大多数时候想要的)。
  • 注意任何报告为已删除的远程分支。或者,稍后要查找它们,git branch -v(任何孤立的跟踪分支都将标记为“[已删除]”)。
  • git branch -d [branch_name]在每个孤立的跟踪分支上

(这是其他一些答案所提出的)。

如果你想编写一个解决方案的脚本,那么for-each-ref就是你的起点,就像这里的Mark Longair的回答对另一个问题的回答一样,但是我看不到一种在不编写外壳脚本循环或使用xargs或其他东西的情况下利用它的方法。


背景说明

要理解发生了什么,你需要意识到,在跟踪分支的情况下,你不是一个分支,而是三个分支。(请记住,“分支”只是指向提交的指针。)

给定一个跟踪分支feature/X,远程存储库(服务器)将拥有这个分支并将其称为feature/X。您的本地存储库有一个分支remotes/origin/feature/X,这意味着,“这是远程告诉我它的功能/X分支,上次我们谈过,”最后,本地存储库有一个分支feature/X指向您的最新提交,并配置为“跟踪”remotes/origin/feature/X,这意味着您可以拉和推以保持它们对齐。

在某个时候,有人删除了遥控器上的feature/X。从那一刻起,你只剩下本地的feature/X(你可能不想再要了,因为功能X的工作大概已经完成了)和你的remotes/origin/feature/X,这当然是无用的,因为它的唯一目的是记住服务器分支的状态。

Git会让你自动清理冗余的remotes/origin/feature/X——这就是git fetch --prune所做的——但出于某种原因,它不允许你自动删除自己的feature/X……即使你的feature/X仍然包含孤立的跟踪信息,所以它有信息来识别已经完全合并的前跟踪分支。(毕竟,它可以给信息,让你自己手工操作。)

在大多数其他解决方案中,“消失”的模式匹配对我来说有点可怕。为了更安全,这使用#0标志来拉出每个分支的上游跟踪状态

我需要一个Windows友好的版本,所以这会删除所有使用Powershell列出为“已消失”的分支:

git branch --list --format "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)" |? { $_ -ne "" } |% { git branch -D $_ }

第一行列出上游分支“已消失”的本地分支的名称。下一行删除空行(对于未“消失”的分支输出),然后将分支名称传递给命令以删除分支。

这些都不适合我。我想要清除所有跟踪远程分支的本地分支,在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'

注意:在config命令中,我对git branch使用-d选项而不是-D,就像我在实际配置中所做的那样。我使用-D是因为我不想听到Git抱怨未合并的分支。您可能也需要此功能。如果是这样,只需在配置命令末尾使用-D而不是-d

这对我有效:

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

可能对一些人有用,简单的一行清除除了master和开发之外的所有本地分支

git branch | grep -v "master" | grep -v "develop" | xargs git branch -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().Trim().Split(" ")[0]}获取分支名称

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

这一堆的另一个答案,大量借鉴帕特里克的回答(我喜欢它,因为它似乎消除了gone]git branch输出中匹配的任何模糊性),但添加了一个*nix弯曲。

最简单的形式:

git branch --list --format \"%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)" \| xargs git branch -D

我在路径上的git-gone脚本中包装了这个:

#!/usr/bin/env bash
action() {${DELETE} && xargs git branch -D || cat}
get_gone() {git branch --list --format \"%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)"}
main() {DELETE=falsewhile [ $# -gt 0 ] ; docase "${1}" in(-[dD] | --delete) DELETE=true ;;esacshiftdoneget_gone | action}
main "${@}"

注意:--format选项似乎相当新;我需要将git从2.10.something升级到2.16.3才能获得它。

编辑:调整以包括关于Benjamin W.refname:short的建议

NB2-我只在bash中进行了测试,因此使用了哈希邦,但可能可移植到sh

grep gone <(git branch -v) | cut -d ' ' -f 3 | xargs git branch -d

上述命令可用于获取在远程合并和删除的分支,并删除在远程不再可用的本地分支

基于Git提示:删除旧的本地分支,看起来类似于jason.rickman解决方案,为此我使用Bash实现了一个名为git走了的自定义命令:

$ git goneusage: git gone [-pndD] [<branch>=origin]OPTIONS-p  prune remote branch-n  dry run: list the gone branches-d  delete the gone branches-D  delete the gone branches forcefully
EXAMPLESgit gone -pn    prune and dry rungit gone -d     delete the gone branches

git gone -pn结合了修剪和列出“已消失”分支:

$ git gone -pnbport/fix-server-broadcast         b472d5d2b [origin/bport/fix-server-broadcast: gone] Bump modulesfport/rangepos                     45c857d15 [origin/fport/rangepos: gone] Bump modules

然后您可以使用git gone -dgit gone -D扣动扳机。

备注

  • 我使用的正则表达式是"$BRANCH/.*: gone]",其中$BRANCH通常是origin。如果您的Git输出本地化为法语等,这可能不起作用。
  • Sebastian Wiesner还为Windows用户将其移植到Rust。那个也被称为git走了

在这里大量借鉴数量其他答案,我最终得到了以下内容(我相信只有git 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

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

我用gitpython编写了一个Python脚本来删除远程不存在的本地分支。

    import gitimport subprocessfrom git.exc import GitCommandErrorimport os
def delete_merged_branches():current_dir = input("Enter repository directory:")repo = git.Repo(current_dir)git_command = git.Git(current_dir)
# fetch the remote with prune, this will delete the remote references in local.for remote in repo.remotes:remote.fetch(prune=True)        
local_branches = [branch.name for branch in repo.branches]deleted_branches = []
# deleted_branches are the branches which are deleted on remote but exists on local.for branch in local_branches:try:remote_name = 'origin/'+ branchrepo.git.checkout(remote_name)except GitCommandError:# if the remote reference is not present, it means the branch is deleted on remote.deleted_branches.append(branch)            
for branch in deleted_branches:print("Deleting branch:"+branch)git_command.execute(["git", "branch", "-D",branch])
    
# clean up the work flow.repo.git.checkout('master')repo.git.pull()
if __name__ == '__main__':delete_merged_branches()

希望有人觉得有用,如果我错过了什么,请添加评论。

以下是使用git客户端为我工作的简单答案:

从您的机器中完全删除存储库,然后再次签出。

不要乱搞有风险的脚本。

你可以这样做:

git branch -vv | grep 'origin/.*: gone]' | awk '{print $1}' | xargs git branch -d

附言:Sam H.

先执行这个:

git remote prune origin

太长别读:

删除不在远程的所有本地分支

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

删除所有不在远程并且完全合并且未使用的本地分支正如之前许多答案中所说。

git fetch -p && git branch --merged | grep -v '*' | grep -v 'master' | xargs git branch -d

补充说明

  • git fetch -p将修剪远程上不再存在的所有分支
  • git branch -vv将打印本地分支,修剪后的分支将被标记为gone
  • grep ': gone]'只选择消失的分支
  • awk '{print $1}'过滤输出以仅显示分支的名称
  • xargs git branch -D将遍历所有行(分支)并强制删除此分支

为什么git branch -D而不是git branch -d,否则您将拥有未完全合并的分支。

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

还有另一个答案,因为没有一个解决方案适合我对优雅和跨平台的需求:

删除不在远程的本地分支的命令:

for b in $(git for-each-ref --format='%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)' refs/heads); do git branch -d $b; done

要将它与gitconfig集成,以便它可以使用git branch-prune运行:

Bash

git config --global alias.branch-prune '!git fetch -p && for b in $(git for-each-ref --format='\''%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)'\'' refs/heads); do git branch -d $b; done'

PowerShell

git config --global alias.branch-prune '!git fetch -p && for b in $(git for-each-ref --format=''%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)'' refs/heads); do git branch -d $b; done'

(需要帮助找到PowerShell和bash的通用命令)

为什么这个答案是最好的?

  • 提供完整的解决方案:将git branch-prune命令添加到您的git
  • 从Windows PowerShell工作正常
  • 核心思想是@jason.rickman防弹方法使用git for-each-ref
  • 解析和过滤使用--filter完成,因此不需要外部依赖项

说明:

  • 将新别名添加到您的~\.gitconfig。执行此操作后,您可以简单地执行git branch-prune
  • 在这个别名中:
    • 获取带有--prune标志的分支,该标志“修剪不再在远程上的远程跟踪分支”
    • 使用git for-each-ref#1,获取分支列表#2(没有远程)
    • 遍历此列表并安全地删除分支

如果您使用的是安装了Oh My Zshzsh shell,那么安全执行此操作的最简单方法是使用内置的自动完成。

首先确定要删除哪些分支:

~ git branch --merged
branch1branch2branch3* master

这将显示一个已经合并的分支列表

在你知道一些你想删除然后键入:

~ git branch -d

您所要做的就是点击[tab],它会向您显示本地分支的列表。使用tab完成或再次点击[tab],您可以循环浏览它们以使用[enter]选择分支。

Tab一遍又一遍地选择分支,直到您有一个要删除的分支列表:

~ git branch -d branch1 branch2 branch3

现在只需按回车键即可删除您的分支集合。

如果您在终端上没有使用zsh…拿过来。

我喜欢使用管道,因为它使命令更易于阅读。

如果您想删除除master之外的所有分支,这是我的解决方案。

git branch | grep -v master | xargs -n 1 git branch -D

要删除与您的条件匹配的其他分支,请修改第一个和第二个块。

git branch --merged | grep feature_name | xargs -n 1 git branch -D

基于Powershell的解决方案,我发现它比这里的许多实现更清晰。

# prune deleted remoted branchesgit fetch -p
# get all branches and their corresponding remote status# deleted remotes will be marked [gone]git branch -v |#find ones marked [gone], capture branchNameselect-string -Pattern '^  (?<branchName>\S+)\s+\w+ \[gone\]' |foreach-object{#delete the captured branchname.git branch -D $_.Matches[0].Groups['branchName']}

下面的命令解决了我的问题。

// To Delete Branch Locallygit branch -d localBranchName

// To Delete Branch Remotelygit push origin --delete remoteBranchName

也许这个命令是你想要的。

运行后:

git remote prune origin

然后运行:

diff <(git branch | sed -e 's/*/ /g') <(git branch -r | sed -e 's/origin\///g') | grep '^<'

这将显示所有不在(git分支-r)中但在(git分支)中的分支

这个方法有一个问题,它也会在本地显示之前没有推送的分支

对于Windows或其他不想/不能编写命令行脚本或不想使用PowerShell的人来说,这是一个更简单的解决方案。

将分支列表转储到文件中git branch > branches.txt
(或git branch --merged > branches.txt,如果你是腰带和吊带类型;git branch -d将防止删除未合并的分支)

在编辑器中打开该文件并合并所有行(我使用了崇高的文本,因此突出显示所有并按ctrl+j

在分支列表前面添加git branch -d

选择全部,复制并粘贴(在windows cmd窗口中右键单击)到命令行中。

真正的挑战是当维护者压缩提交时。然后,使用--merged等git内置功能的解决方案没有帮助。

工具g it-删除-合并-分支允许方便地删除分支。我特别喜欢交互模式。

安装(需要python3):

pip install git-delete-merged-branches

然后执行

git-delete-merged-branches --effort=3

--effort=3对于删除压扁的分支很重要。

替代品

  • @teppee s/g它-删除-压扁:安装node.js执行npx @teppeis/git-delete-squashed。支持main分支。
  • 删除git:未维护:缺少main分支的功能。@teppeis/git-delete-squash基于此。

虽然上述答案涵盖了如何手动修剪分支,但此答案增加了自动化来解决这个问题。git现在有一个新设置来修剪每个获取操作不再在远程上的陈旧分支。这很棒,因为我们不再需要每次删除分支时手动调用remote prunegit pull也调用git fetch)。

为每个获取启用修剪行为

要在全局配置中启用此功能:

git config --global fetch.prune true

让事情自动发生意味着你可以忘记在新机器上添加这个设置。它只是工作。

为特定遥控器上的每个获取启用修剪行为

git config --global remote.<name>.prune true

局部自动剪枝

我们也可以在没有--global标志的情况下应用相同的命令进行本地修剪。

. gitconfig

上面的命令适用于全局和本地.gitconfig,如下所示:

...[fetch]prune = true

我可以建议将其添加到anable配置或您的dotfiles存储库(.gitconfig)中,以便将来自动设置。

配置设置在每次获取时调用以下命令:

git remote prune <remote name>

总结

要将引用作为正常工作流程的一部分进行修剪,而无需记住运行引用,请在配置中设置fetch.prune全局或remote.<name>.prune每个远程。请参阅组件配置

我最近发布了一个我已经使用了很长一段时间的解决方案,它依赖于分支命名来包含问题编号,您可以看到这个答案链接到我的git_clean_branches版本,这是一个交互式脚本,用于删除已关闭问题的分支(通常通过壁球合并)。

这里的答案似乎都不适合我,也不完全符合我的要求(删除本地分支而不是远程分支)。所以我写了这个,它有点口,但它有效。

touch /tmp/remote-refs && touch /tmp/local-refs && touch /tmp/diff-refs;while shortened=$(git for-each-ref refs/remotes) && echo ${${shortened}//"\/remotes\/origin"} > /tmp/remote-refs &&shortened=$(git for-each-ref refs/heads | grep -v "master\|develop") && echo ${${shortened}//"\/heads"} > /tmp/local-refs &&diff=$(comm -13 /tmp/remote-refs /tmp/local-refs) && echo ${${diff}##*refs/} > /tmp/diff-refs &&xargs git branch -D </tmp/diff-refs; do :; done

只要确保先运行git remote prune origin即可

有时你只是想有一个干净的石板,但需要确定能够回到以前的工作在当地的分支机构。

如果是这样,最简单的方法可能只是存档当前本地克隆并创建一个新的克隆。

因此,对于仅在本地具有默认分支的存储库X,只需

move X X-2021-11-10git clone http://...../X

如果你需要完全相同的起源,问git

git -C X remote -v

你可以使用这个:

git fetch --prune

然后

git branch -vv | egrep -v "([origin/[a-zA-Z0-9/_-]+])" | awk "{print $1}" | xargs git branch -D

它删除所有未链接到远程服务器的本地分支

我亲自添加了一个别名,为我做🙃

alias gbs='git fetch --prune; git branch -vv | egrep -v "(\[origin\/[a-zA-Z0-9/_-]+\])" | awk "{print \$1}" | xargs git branch -D'

因为有些答案不能防止意外删除

git fetch -p && LANG=c git branch -vv | awk '/: gone]/&&!/^\*/{print $1}' | xargs git branch -d

过滤掉第一列中具有*的分支很重要。

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

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

看起来每个人都有一个解决方案。好吧,如果你更喜欢一个带有TUI(基于文本的用户交互界面)的交互工具,我写了一个名为git x清洁器的工具。它可以找到合并的分支、重新基于分支(具有相同提交消息的提交)、修剪的分支或手动选择的分支。

https://github.com/lzap/git-xcleaner

另一个答案。

如果你正在寻找一些花哨的东西,这是我与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 for-each-ref来避免git branch中的任何格式更改

  • 使用awk过滤。我尝试使用--format="%(if:equals=[gone])%...",但它意味着管理空行。

  • 使用fzf选择要删除的分支。

    添加快捷方式:[ctrl-a]用于全选,[ctrl-t]用于反转选择(和[tab]用于像往常一样选择)。您可以使用[enter]进行验证,并可以使用[esc]中止。我使用此分支选择器作为确认阶段。

  • 在右侧显示所选分支的日志(以记住日期和内容…)。

    我使用下面列出的compact格式。您可以使用任何(不太宽)日志格式。

  • --no-run-if-empty仅适用于GNUxargs

    它不是强制性的,有助于在您中止进程时避免错误(与没有分支名称的git branch -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

(与上面"上的转义相同)

列出所有本地分支,谁的远程分支现在消失了

#!/bin/shLC_ALL=C git for-each-ref --format='%(refname:short) %(upstream:track)' refs/heads/ |\sed -n '/ *\[gone\].*$/{s@@@;p}'

然后将其导入xargs git branch -D-d

该解决方案应该是POSIX兼容的。没有特定的shell内容,只有POSIX兼容sed/xargs

  • LC_ALL=C通过强制使用英语使脚本语言中立
  • for-each-ref使用管道命令
  • --format='%(refname:short) %(upstream:track)输出分支名称 跟踪信息
  • refs/heads仅本地分支
  • sed -n '/ *\[gone\].*$/匹配跟踪信息 [gone],这不是有效分支名称的一部分,因此无需担心
    • {s@@@;p}删除跟踪部分并仅打印这些行

nb:分支不能包含空格,所以我们不需要对xargs做任何清理。