git remote prune, git prune, git fetch -prune等之间有什么区别

我的情况是这样的…某人在同一个回购中删除了他本地&远程回购……

大多数在Stack Overflow或其他网站上询问这类问题的人都有分支仍然显示在他们的远程跟踪分支列表git branch -a底部的问题:

* master
develop
feature_blah
remotes/origin/master
remotes/origin/develop
remotes/origin/feature_blah
remotes/origin/random_branch_I_want_deleted

然而,在MY的情况下,分支不应该在那里,是本地的:

* master
develop
feature_blah
random_branch_I_want_deleted
remotes/origin/master
remotes/origin/develop
remotes/origin/feature_blah

当我做以下任何一项时,它不会在本地被删除:

$ git prune

我还试过:

$ git remote prune origin
$ git fetch --prune

更多有用的信息:当我检查git remote show origin时,它是这样的:

* remote origin
Fetch URL: utilities:homeconnections_ui.git
Push  URL: utilities:homeconnections_ui.git
HEAD branch: master
Remote branches:
master                        tracked
develop                       tracked
feature_blah                  tracked
other123                      tracked
other444                      tracked
other999                      tracked
Local branches configured for 'git pull':
develop                      merges with remote develop
feature_blah                 merges with remote other999
master                       merges with remote master
random_branch_I_want_deleted merges with remote random_branch_I_want_deleted
Local refs configured for 'git push':
develop         pushes to develop     (local out of date)
master          pushes to master      (up to date)
feature_blah    pushes to feature_blah(up to date)

注意,它只在名为Local branches configured for 'git pull':的部分中

为什么?

359340 次浏览

git remote prunegit fetch --prune做同样的事情:如你所说,删除对远程上不存在的分支的引用。第二个命令连接到远程并在修剪之前获取其当前分支。

但是,它不会触及您已经签出的本地分支,您可以简单地删除它们

git branch -d  random_branch_I_want_deleted

如果分支未在其他地方合并,则将-d替换为-D

git prune做了一些不同的事情,它清除不可到达的对象,那些在任何分支或标记中不可到达的提交,因此不再需要。

我不怪你对此感到沮丧。最好的方法是这样看。每个远程分支可能有三个版本:

  1. 远程存储库上的实际分支 (例如,远程回购在https://example.com/repo.gitrefs/heads/master)
  2. 分支的本地快照(存储在refs/remotes/...下)
    (例如,local repo, refs/remotes/origin/master)
  3. 和可能跟踪远程分支的本地分支
    (例如,local repo, refs/heads/master)

让我们从git prune开始。这将删除不再被引用的对象,它不会删除引用。以你们为例,你们在当地有一个分支机构。这意味着有一个名为random_branch_I_want_deleted的引用,它引用了一些表示该分支历史的对象。因此,根据定义,git prune不会移除random_branch_I_want_deleted。实际上,git prune是一种删除在Git中累积但没有被任何东西引用的数据的方法。一般来说,它不会影响任何分支的视图。

git remote prune origingit fetch --prune都作用于refs/remotes/...下的引用(我将它们称为远程引用)。它不会影响本地分支机构。如果你只想删除特定远程下的远程引用,git remote版本是有用的。否则,两者做完全相同的事情。因此,简而言之,git remote prunegit fetch --prune作用于上面的第2条。例如,如果你使用git web GUI删除了一个分支,并且不希望它再出现在你的本地分支列表中(git branch -r),那么这是你应该使用的命令。

要移除一个本地分支,你应该使用git branch -d(如果没有在任何地方合并,则使用-D)。FWIW,如果远程分支消失,没有git命令自动删除本地跟踪分支。

注意,git remote --prunegit fetch --prune之间的一个差异正在被汤姆·米勒(tmiller)修复,即提交10 a6cc8(适用于git 1.9/2.0, 2014年Q1):

当我们有一个名为“__abc0”的远程跟踪分支;从之前的取回,而上游现在有了一个名为"frotz的分支"fetch将无法删除"frotz/nitfol"git fetch --prune"从上游。
git会通知用户使用&;__abc0 &;

因此:当upstream repo .有一个分支(&;frotz")与分支层次结构 . (&;frotz/xxx",一个可能的分支命名约定)具有相同的名称时,git remote --prune是成功的(从你的repo中清除远程跟踪分支),但git fetch --prune是失败的。

不了:

改变方式&;fetch --prune"通过在获取操作之前移动修剪操作来工作。
这样,它就会自动修复冲突,而不是警告用户冲突


另一个区别:

在Git 2.39 (Q4 2022)中,“git prune"(man)”可能会尝试遍历.git/objects/pack以删除其中的垃圾文件,并且当目录丢失时大声失败,这是不必要的。

.命令已被教导忽略此类失败

... 而当目录缺失时git fetch --prune仍然会大声失败

参见提交6974765 (19 Nov 2022) by Eric Wong (ele828)
(由Junio C Hamano—gitster提交7 d7ed48中合并,28 Nov 2022)

prune:对缺失目录的安静ENOENT

署名:Eric Wong

$GIT_DIR/objects/pack可以被删除以保存共享存储库中的inode。
$GIT_DIR/objects$GIT_DIR/objects/pack不存在的情况下安静修剪,但在其他情况下发出系统错误,以帮助用户诊断权限问题或资源限制

所以,没有“无法打开目录…”;当$GIT_DIR/objects$GIT_DIR/objects/pack缺失时,git修剪。

如果有人感兴趣的话。下面是一个快速的shell脚本,它将删除所有不能远程跟踪的本地分支。提醒一句:无论是否合并,这将消除任何未被远程跟踪的分支。

如果你们看到任何问题,请告诉我,我会解决它(等等等等)。

将它保存在PATH上名为git-rm-ntb的文件中(管它叫什么都行),然后运行:

git-rm-ntb <remote1:optional> <remote2:optional> ...

clean()
{
REMOTES="$@";
if [ -z "$REMOTES" ]; then
REMOTES=$(git remote);
fi
REMOTES=$(echo "$REMOTES" | xargs -n1 echo)
RBRANCHES=()
while read REMOTE; do
CURRBRANCHES=($(git ls-remote $REMOTE | awk '{print $2}' | grep 'refs/heads/' | sed 's:refs/heads/::'))
RBRANCHES=("${CURRBRANCHES[@]}" "${RBRANCHES[@]}")
done < <(echo "$REMOTES" )
[[ $RBRANCHES ]] || exit
LBRANCHES=($(git branch | sed 's:\*::' | awk '{print $1}'))
for i in "${LBRANCHES[@]}"; do
skip=
for j in "${RBRANCHES[@]}"; do
[[ $i == $j ]] && { skip=1; echo -e "\033[32m Keeping $i \033[0m"; break; }
done
[[ -n $skip ]] || { echo -e "\033[31m $(git branch -D $i) \033[0m"; }
done
}
    

clean $@