删除没有 parent 的 Git 提交

Remove a Git commit with no parent

我正在 Git 上教 class。我不知道怎么做,但我的一个学生设法连续获得三个提交 没有以前的历史记录 !该学生甚至设法将该行的三个提交合并到 master(在该学生研究和发现 --allow-unrelated-histories 之后),但在历史树中可以看到,而不是拆分 master ,那三个提交就像尾巴一样挂在那里。

早些时候发生的一件事是,在尝试将存储库 fooproj 克隆到一个单独的目录 fooproj-copy 时,学生不小心将它 克隆到了 [=16] 的子目录中=],以便 Bitbucket 将其显示为远程存储库中的子项目。不过,我让学生删除了这个子目录 fooproj-copy,并推送了新的提交,所以我认为一切都很好。我不知道他们是如何在没有 parents.

的情况下获得提交的

所以我说我会删除这三个提交(即使它们已经合并到 master 中)。我做了一个 git reset --hard HEAD~1 并且它走上了那一行提交。我又做了一次 git reset --hard HEAD~1,它一直在继续。但是还有一个 git reset --hard HEAD~1,我已经到了队伍的末尾;由于这三个提交中没有更多 parents,我收到此错误:

fatal: ambiguous argument 'HEAD~1': unknown revision or path not in the working tree.

所以通常情况下,如果我想丢弃一些提交,我只需在提交 之前 对提交进行硬重置。但是在这种情况下在我想丢弃的提交之前没有提交。我怎样才能摆脱它们?

将其画成图形:

       D
        \
A--B--C--M--N   <-- master
           /
          E

提交 ADE 都是根提交(没有父级)。

根提交很容易进行:git checkout --orphan 将您置于未出生的分支上,以便该分支上的下一次提交创建分支本身并创建新的根提交。或者,git fetch 从另一个具有不相关历史记录的存储库获取以根提交终止的提交链(或复杂图)。或者,git commit-tree 可以使用任意父项编写新提交。

您不能真正删除这些提交——或者 任何 提交,真的——直接。您所能做的就是让它们无法访问.

如果至少有一个名称指向至少一个提交,那么提交 X 是可访问的,并且通过其祖先链最终将您引导至 X 。在这种情况下,名称 master 直接指向提交 N,一个合并。 N 指向 EM,所以两者都是可达的。 M 指向 CD,所以两者都是可达的。

在这种情况下,如果你将master指向C,所有DMEN 变得无法访问。因此,它们最终将过期并被垃圾收集。但是如果在 N 之后有一个提交——我们称之为 F——你想保留,你无能为力。您可以 复制 提交:

       D
        \
A--B--C--M--N--F   [abandoned]
       \   /
        \ E
         \
          F'       <-- master

并开始使用 F',即 F 的副本,作为 master 的提示——但这意味着您没有保留 F。您只保留不同的提交 F'.

首先,根据您的说法,您可能不应该在 Git 上教授 class。本身就已经非常难了,要是给不太了解的人教我不知道学生们会怎么处理(我不是要人身攻击你,你可能是好意的,已经结束了教 class 是有充分理由的,但我真的认为由熟悉它的人来教它很重要)。


现在,按字面意思回答问题删除没有 parent 的 Git 提交只需确保不再有任何对它的引用

完成 git 将在一段时间后自动删除其文件;如果您想立即完成(可能是因为该提交或一系列提交占用了大量 space),您可以使用 git gc --prune=all.

所以在你的情况下,如果看起来这些提交不是其他分支的一部分并且没有任何标签,你只需要将你的分支重置为历史上提交不存在的某个点参考。


但是如果您想要解决存储库的特定问题:

从你所说的看来,学生所做的合并提交似乎将学生的最后一次提交作为其第一次提交 parent,因此至少最初他将 master 合并到他的分支中.
~ 命令跟在 first parent 之后,因此当您执行第一个 git reset --hard HEAD~1 时,您将 master 重置为学生的提交行(并使用以下 2 个命令你达到了他的第一次提交)。

所以现在在那个存储库中你可能已经丢失了 你在 master 上的所有其他提交

如果您有另一个克隆,最好返回它,否则您的原始提交行可能仍会被您的 reflog 引用,因此仍然存在。

在接下来的内容中,我假设您在学生合并后没有对 master 进行其他提交,从您所说的看来是这样。

您似乎没有使用任何图形存储库浏览器,如果是这样,请键入 gitk --all 并使用它来查看您的 branches/references 的确切状态,然后继续执行您将要执行的操作正在做(你必须在每次修改后用 F5 手动刷新它)。如果没有图形浏览器,基本上不可能很好地处理 git,但是您尝试将许多选项传递给 git 日志。

键入 git reflog 并查看它是否列出了您的提交。您需要找到您学生的合并提交或紧接其之前的提交。

为确保不丢失任何其他内容,请先将标签添加到您当前所在的位置 (git tag temp1)。

然后,如果您找到了您学生的合并提交,请先标记它 (git tag temp-studentmerge <sha1-of-the-commit>);代替 放置该提交的(缩写的)sha1,当然(它在 git reflog 的左侧第一列中列出)。

然后git reset --hard temp-studentmerge。更新gitk;你现在应该再次看到你所有的提交。

现在您必须重置为合并的正确 parent;最简单和最安全的选择是在图形浏览器中查看它的 sha1,然后执行 git reset --hard <sha1>。否则 git reset --hard HEAD^2 应该可以,根据您的描述。
^<n> 符号引用第 个 parent,而 ~ 仅引用第一个 parent。

现在检查一切是否正确,然后删​​除临时标签(git tag -d temp1git tag -d temp-studentmerge)。

如果您使用 gitk,您仍然会看到学生的提交,如果您使用简单的 F5,请改用 Shift-F5 (Reload) 并且您赢了再也见不到他们了。

提交 objects 实际上应该仍然在你的存储库中的某个地方,因为 git 删除不再引用的 objects 只有当它们超过某个时间时(我'我其实不太确定细节)。
如果出于某种原因您想立即删除它们的任何痕迹,您可以执行 git gc --prune=all.