从 Git 中删除重复的未命名分支

Remove duplicate unnamed branch from Git

我以某种方式设法为我对未命名分支中的存储库所做的每个提交创建了一个副本,如下面 GitHub 的网络图所示:

我该怎么做才能清理并删除重复的提交?我查看了 ,但它似乎更适用于单个重复项而不是整个重复项。

附加信息: 我是存储库的唯一所有者和用户。当我从一台新计算机提交但没有正确设置我的用户名和电子邮件地址时,这个问题首先出现。我使用方法 posted here on GitHub 纠正了这个提交。执行此操作后,系统提示我从远程存储库拉取到我的本地存储库,然后再推送我所做的一些其他更改,此时它拉取了一组重复提交。

如果您所说的未命名分支是指悬挂提交,那么 git gc --prune=all 应该可以解决问题。但是在运行这个命令之前,确保你没有任何一个,因为这个动作是不可逆的。

More on git gc

然后你可以执行 git push --prune 删除没有本地副本的遥控器。

从某种意义上说,Git根本没有未命名的分支。从另一种意义上说,它有无数个未命名的分支。无论哪种方式,尝试显示所有这些都没有什么意义1——而 GitHub 网络图则没有。这不是 GitHub 网络图

This is what a GitHub network graph is. 它试图(在我看来并不完全成功 :-) )显示多个 related 的并集(通过 GitHub- fork,我想)存储库。因此,您所看到的不一定是您自己的存储库中的任何内容,而是跨越多个不同的存储库,可能包括您自己的一些存储库,但可能字面上 everything 完全在其他几个存储库中。它可能 在您的存储库中,但也可能不在。您现有的问题中没有足够的信息来诊断这个问题

因为 Git 主要通过 添加新提交 来工作(同时保留每个旧提交,通过 Git 调用 references),你所做的大多数事情只会向你的网络图添加更多提交,甚至可能添加更多行。 通过 copying 提交来工作,这会——并且确实——添加另一行,但要确保 first 复制的提交有一个link 回到一些现有的提交(这是变基的 --onto 目标),然后删除 Git 用来查找原始提交链的名称(这是 git rebase本身)。

换句话说,这(至少在概念上——ElpieKay 的回答是特定于一种特定情况的,以合并提交结束,我不在这里画)需要:

o--o--o--o   <-- main-branch

*--*--*   <-- side-branch

并首先在底行添加一个 copy,但用一个 link 回到顶部:

o--o--o--o   <-- main-branch
          \
*--*--*    *--*--*   <-- HEAD

(我省略了 side-branch 标签部分是为了保存 space,部分是因为它位于 "under" 复制的提交:它仍然指向原始三个提交中的最后一个,不过。)一旦复制完成,我们 "forget" 原来的三个通过只改变一个分支标签,给出:

o--o--o--o   <-- main-branch
          \
           *--*--*   <-- side-branch

三个原始提交恰好被遗忘因为他们不再有名字,side-branch,指向他们的提示 (最新)提交。

可以用两个不同的 Git 命令来完成这种事情,它们有不同的目的:

  • git rebase 复制一些(通常是小而简单的)提交集,最好是简单的线性链,到一组新的提交,同时为每个复制的提交更改存储的快照。这本质上等同于对每个要复制的提交执行一系列 git cherry-pick。然后,正如我们刚刚看到的,它移动了分支标签,而不是指向最后一个预复制提交,而是指向最后一个 post-copy 提交。

  • git filter-branch 复制任意一组提交,如果你愿意,包括合并,到一个新的集合。在此复制过程中,每个提取的提交快照都会根据/使用您选择的任意过滤进行修改。这可能包括省略一些提交、创建新提交、更改提交的父 linkages、and/or 对快照进行任意更改。因此,这 much 比 rebase 更灵活,但 much much 更难使用(而且不非常适合那种源代码树修改 rebase 是专门为两者设计的)。

这是否有帮助取决于 GitHub 向您展示的内容。 因为网络图不限于 您的 存储库,不能保证在 存储库中做任何事情都会改进你看到的任何东西。


1Every 提交可以被视为一个匿名分支,或者甚至是无限数量的匿名分支(尽管后者是没用,而前者有时有用)。

有一次显示匿名分支确实有意义是在查看带有 "detached HEAD" 的存储库时。这是因为分离的 HEAD 从字面上意味着名称 HEAD 解析为一个特定的提交,使该特定的提交 表现得像 分支提示提交。实际上,HEAD 是该分支的名称。但是HEAD应该移动,或者是"re-attached"。 (Your HEAD is there to move you around.) 一旦你移动它,提交就不再是那个匿名分支的尖端。

在上面显示的变基步骤中,我显示 HEAD 指向复制的提示提交。这实际上就是 Git 进行变基的方式,使用 HEAD 作为匿名分支的尖端。最后一步,移动原始分支的名称,将副本永久固定到位(永久,也就是说,直到您再次变基或完全删除名称)。