清理旧的远程git分支

936

我在两台不同的计算机上工作(A和B),并将一个常用的git remote存储在dropbox目录中。

假设我有两个分支,master和devel。两者都在追踪它们的远程对应分支origin/master和origin/devel。

现在当我在电脑A上删除了本地和远程的devel分支。

git push origin :heads/devel
git branch -d devel

在电脑 A 上运行 git branch -a 命令,我得到了以下分支列表:

  • master
  • origin/HEAD
  • origin/master

在电脑 B 上运行 git fetch 命令后,可以用命令 git branch -d devel 删除本地的 devel 分支,但无法删除远程的 devel 分支。

git push origin :heads/devel 命令返回以下错误信息:

error: unable to push to unqualified destination: heads/proxy3d
The destination refspec neither matches an existing ref on the remote nor begins with refs/, and we are unable to guess a prefix based on the source ref.
fatal: The remote end hung up unexpectedly

在运行命令之后,git branch -a 仍然列出了远程分支中的 origin/devel。

如何在电脑 B 上清理远程分支?


5
有人尝试过在 Dropbox 文件夹中存储 git 仓库并告诉我,这样做有点不稳定(但没有提供额外的细节)。 - Thorbjørn Ravn Andersen
5
可能是因为你需要等待提交后完全同步,才能确保在另一台机器上使用它是安全的(即使那时候还需要进行另一次同步)。 - ataulm
1
当我的公司自动添加OneDrive时,我也遇到了很多问题。不要在OneDrive文件夹中放置git仓库! - Juh_
10个回答

1612

首先,机器B上运行git branch -a命令的结果是什么?

其次,你已经从origin中删除了heads/devel,因此你不能从机器B中删除它。

尝试:

git branch -r -d origin/devel
或者
git remote prune origin
或者
git fetch origin --prune

您可以在git语句的末尾添加--dry-run来查看其执行结果而不实际运行它。

git remote prunegit branch的文档。


68
git remote prune origin 对我有用 => * [pruned] origin/my-old-branch - Hari Honor
161
git remote prune origin --dry-run 会展示哪些内容将被删除,但并不会实际执行删除操作。 - Wolfram Arnold
45
git fetch origin --prune 是清除已删除分支的完美命令。 - Sébastien Barbieri
17
如果你有一个跟踪已经不存在的远程分支的本地分支,这不会删除任何东西。对于这种情况,最好的方法是先运行 git branch -vv,然后运行 git branch -D 分支名,最后再运行 prune 命令。 - Roman Starkov
12
您可以通过设置以下选项来使拉取/获取时自动进行修剪:git config remote.origin.prune true - Patrick James McDougle
显示剩余9条评论

183

考虑运行:

git fetch --prune
在每个仓库中定期删除已经跟踪被删除的远程GIT仓库中不再存在的分支。这可以进一步简化。
git config remote.origin.prune true

这是一个针对“每个仓库”的设置,可以使任何将来的git fetch或git pull自动执行prune操作。

要为您的用户设置,请编辑全局的.gitconfig文件并添加。

[fetch]
    prune = true

然而,建议使用以下命令来完成此操作:

git config --global fetch.prune true

或者将其应用于整个系统(而不仅仅是用户)

git config --system fetch.prune true

同等地,git fetch -p - Aiden Cullo
运行这些命令后,然后执行 git branch,仍然显示旧的分支... 同样使用 git fetch -p -vv 运行,显示它没有 "触及" 任何旧分支。 - Ricky Levi

45

我需要在这里添加一个答案,因为其他答案要么不涵盖我的情况,要么过于复杂。我与其他开发人员一起使用github,我只希望从我的计算机中一次性删除所有本地分支,这些本地分支的远程已经(可能已合并并)从github PR中删除。 不,像git branch -r --merged这样的东西并不包括那些在本地未被合并或根本未被合并(废弃)的分支,因此需要另一种解决方案。

无论如何,第一步我从其他答案中得到:

git fetch --prune

我试运行了git remote prune origin,在我的情况下似乎会做同样的事情,所以我选择最简单的版本。

现在,git branch -v应该将其远程分支删除的标记为[gone]。 因此,我所需要做的就是:

git branch -v|grep \\[gone\\]|awk '{print $1}'|xargs -I{} git branch -D {}

就这么简单,它可以删除我在上述情况下想要的所有内容。

较不常用的xargs语法是为了它能在Mac、BSD和Linux上同时使用。注意,这个命令不是一个模拟操作,所以它将强制删除所有标记为[gone]的分支。显然,在Git中,任何东西都不会永远消失,如果你发现已经删除了你想要保留的分支,你可以随时取消删除(以上命令将列出它们的哈希值,因此只需执行一个简单的git checkout -b <branch> <hash>即可)。

编辑:只需将这个别名添加到您的.bashrc/.bash_profile文件中,将两个命令合并为一个,并更新第二个命令以在所有shell上运行:

alias old_branch_delete='git fetch -p && git branch -vv | awk "/: gone]/{print \$1}" | xargs git branch -D'

17

这个命令将“模拟删除”除master以外的所有远程(origin)已合并分支。你可以更改它或在master之后添加其他分支:grep -v for-example-your-branch-here |

git branch -r --merged | 
  grep origin | 
  grep -v '>' | 
  grep -v master | 
  xargs -L1 | 
  awk '{sub(/origin\//,"");print}'| 
  xargs git push origin --delete --dry-run

如果看起来没问题,去掉--dry-run。此外,您可能希望先在分支上进行测试。


不错。我的修改使用perl代替第一个xargs + awk: git branch -r --merged | grep origin | grep -v '>' | grep -v master | perl -lpe '($junk, $) = split(///, $,2)' | xargs git push origin --delete - Andrew

14

这里有一个Bash脚本可以帮助你完成操作。它是http://snippets.freerobby.com/post/491644841/remove-merged-branches-in-git脚本的修改版本,我的修改使其支持不同的远程仓库位置。

#!/bin/bash

current_branch=$(git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/')
if [ "$current_branch" != "master" ]; then
  echo "WARNING: You are on branch $current_branch, NOT master."
fi
echo -e "Fetching merged branches...\n"

git remote update --prune
remote_branches=$(git branch -r --merged | grep -v '/master$' | grep -v "/$current_branch$")
local_branches=$(git branch --merged | grep -v 'master$' | grep -v "$current_branch$")
if [ -z "$remote_branches" ] && [ -z "$local_branches" ]; then
  echo "No existing branches have been merged into $current_branch."
else
  echo "This will remove the following branches:"
  if [ -n "$remote_branches" ]; then
echo "$remote_branches"
  fi
  if [ -n "$local_branches" ]; then
echo "$local_branches"
  fi
  read -p "Continue? (y/n): " -n 1 choice
  echo
  if [ "$choice" == "y" ] || [ "$choice" == "Y" ]; then
    remotes=`echo "$remote_branches" | sed 's/\(.*\)\/\(.*\)/\1/g' | sort -u`
# Remove remote branches
for remote in $remotes
do
        branches=`echo "$remote_branches" | grep "$remote/" | sed 's/\(.*\)\/\(.*\)/:\2 /g' | tr -d '\n'`
        git push $remote $branches 
done

# Remove local branches
git branch -d `git branch --merged | grep -v 'master$' | grep -v "$current_branch$" | sed 's/origin\///g' | tr -d '\n'`
  else
echo "No branches removed."
  fi
fi

1
这个问题出现在一个名为“origin/feature/mybranch”的分支上,我不确定原因是什么。 - Stavros Korokithakis
1
问题出在两个 sed 命令中。将 sed 's/\(.*\)\/\(.*\)/\1/g' 替换为 sed 's/\([^/]*\)\/\(.*\)/\1/g' - Benoît Latinier

13
如果 git branch -r 显示了很多你不感兴趣的远程跟踪分支,并且你只想从本地删除它们,请使用以下命令:
git branch -r | grep -Ev 'HEAD|master|develop'  | xargs -r git branch -rd

更安全的做法是只删除已合并的内容:
git branch -r --merged | grep -Ev 'HEAD|master|develop'  | xargs -r git branch -rd

这可能对大型项目有用,其中您不需要其他团队成员的功能分支,但在初始克隆时会获取许多远程跟踪分支。
然而,仅仅执行此步骤是不够的,因为那些已删除的远程跟踪分支将在下一次git fetch时重新出现。
要停止获取这些远程跟踪分支,您需要在.git/config中明确指定要获取的引用。
[remote "origin"]
  # fetch = +refs/heads/*:refs/remotes/origin/*    ### don't fetch everything
  fetch = +refs/heads/master:refs/remotes/origin/master
  fetch = +refs/heads/develop:refs/remotes/origin/develop
  fetch = +refs/heads/release/*:refs/remotes/origin/release/*

在上面的示例中,我们只获取 masterdeveloprelease 分支,如需适应其他情况,请随意更改。

1
谢谢,第一个命令对我有用。你也可以使用 git fetch origin branchname 仅获取一个分支,或者创建一个别名如 ft = "!f() { git fetch origin $(git rev-parse --abbrev-ref HEAD);}; f" 来仅获取当前分支(我的个人首选)。祝好。 - GiovanyMoreno
也许与主题无关,但是“-r”标志是什么意思?我看到xargs有一个-R参数,但没有找到任何关于-r的信息。我的意思是,理论上如果我正确记得xargs的工作原理,即使没有-r,它也应该能正常工作。 - Aldo 'xoen' Giambelluca
我喜欢这个答案。有几点需要注意(显而易见的)。通过删除最后一个管道和最后一个命令“| xargs git branch -d”,可以“预览”将要删除的分支。此外,通过从“git branch -d”中删除“-r”选项(“--remotes”),我们只清理本地分支(考虑到默认情况下“git branch”不显示远程分支,这可能已经足够了)。 - Aldo 'xoen' Giambelluca
谢谢!这正是我所需要的。其他解决方案建议将修剪过的本地分支推送到远程以在那里删除它们,但当远程已被删除或不再存在时,这并没有帮助。这种方法删除了我无法访问的远程分支的本地引用。 - cautionbug

6

删除操作通常都是具有挑战性和危险的!!!因此,首先请执行以下命令查看将发生什么:

git push --all --prune --dry-run

通过以上操作,git 将会列出以下命令执行后的结果清单。

接着运行以下命令,从远程仓库中删除所有不在本地仓库中的分支:

git push --all --prune

15
这个命令似乎很危险... 它成功删除了我想要删除的内容(而至少四个以上的答案都做不到)。但它也删除了其他四个开发分支。Git 真是太糟糕了... - jww
4
这些分支可能不在你的本地副本上。但并非一无所有。使用 Git commit、Git reflog,然后 git reset --hard <checksum> 命令可以轻松回到任何保存过的提交(也称为“版本”),包括其它提交。分支只是一个标签,删除标签不会删掉已经保存的内容......它将永远有一个校验和。如果需要帮助,请告诉我。 - Timothy L.J. Stewart
5
当然,你会想要尽快恢复这些提交记录,因为Git的垃圾回收器最终会删除未被分支引用的提交记录。 - Andrew
1
这是错误的建议。^^ - Gui LeFlea
1
执行--dry-run命令是进行的正确方式。这样你就能够看到将会发生什么。这正是我一直在寻找的命令!我尝试了许多不同的命令和 GUI 进程,但都没有起作用,但这个可以。感谢@TimothyL.J.Stewart。 - renno
显示剩余3条评论

3
以下是使用SourceTree(v2.3.1)执行此操作的方法:
1. 点击“拉取”。 2. 选中“修剪跟踪分支…”。 3. 点击“确定”。 4.
请参考下图: enter image description here

2
# First use prune --dry-run to filter+delete the local branches
git remote prune origin --dry-run \
  | grep origin/ \
  | sed 's,.*origin/,,g' \
  | xargs git branch -D

# Second delete the remote refs without --dry-run
git remote prune origin

从本地和远程引用中修剪相同的分支(在我的例子中是从origin)。


alias git-prune-origin="git remote prune origin --dry-run | grep origin/ | sed 's,.*origin/,,g' | xargs git branch -D; git remote prune origin" 将上述命令组合成一个 shell 别名。 - Alvin

1

使用PowerShell回答

对于正在使用PowerShell的任何人来说,以下内容会让生活更轻松。

首先,如其他答案所建议的:

git fetch --prune

然后,要从branch -v中获取不再有远程分支的行:
git branch -v | findstr "gone" > .\branches.txt

最后要删除分支(请自行决定是否使用-D风险):

Get-Content .\branches.txt | ForEach-Object { git branch -d (($_.Trim() -split '\s+')[0]) }

你可能可以做成一行代码,但我喜欢检查将要被删除的分支的选项。

(PowerShell 版本: 7.3.6)


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