繁体   English   中英

删除没有父项的 Git 提交

[英]Remove a Git commit with no parent

我正在教授一门关于 Git 的课程。 我不知道如何,但是我的一个学生设法连续获得了三个没有以前历史记录的提交! 学生甚至设法将那三行提交合并到master (在学生研究并发现--allow-unrelated-histories ),但在历史树中可以看到,这三个提交并没有拆分master ,而是只是挂在那里,像一条尾巴。

之前发生的一件事是,在尝试将存储库fooproj克隆到单独的目录fooproj-copy ,学生不小心将其克隆到了fooproj的子目录中,因此 Bitbucket 将其显示为远程存储库中的子项目。 我让学生删除了这个子目录fooproj-copy ,然后推送新的提交,所以我认为一切都很好。 我不知道他们是如何在没有父母的情况下获得承诺的。

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

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直接指向 commit 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 上教授课程。 本身就已经非常难了,如果是不熟的人教的我不知道学生会怎么做(我不是要人身攻击,你可能是善意的,已经结束了教这门课是有充分理由的,但我真的认为由熟悉它的人教授这门课很重要)。


现在,从字面上回答这个问题,要删除没有父项的 Git 提交,您只需确保不再有任何对它的引用

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

因此,在您的情况下,如果这些提交似乎不属于其他分支并且没有任何标签,则您只需要将分支重置为未引用提交的历史记录中的某个点。


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

从你所说的看来,学生所做的合并提交似乎将学生的最后一次提交作为其第一个父级,因此至少最初他将 master合并到了他的 branch 中
~命令遵循第一个父母,因此当您执行第一次git reset --hard HEAD~1您将 master 重置为您学生的提交行(并且使用以下 2 个命令您达到了他的第一次提交)。

因此,现在在该存储库中,您可能丢失在 master 上的所有其他提交

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

在接下来的内容中,我假设您在学生合并后还没有对 master 做出其他承诺,从您所说的来看似乎是这样。

似乎您没有使用任何图形存储库浏览器,如果是这样,请输入gitk --all并使用它来准确查看您的分支/引用的状态,并按照它进行操作(您必须刷新它)每次修改后手动按 F5)。 没有图形浏览器,基本上不可能很好地处理 git,但是您尝试传递给 git log 的许多选项。

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

为了确保不会丢失任何其他东西,首先在您当前所在的位置添加一个标签( git tag temp1 )。

然后,如果你发现你的学生的合并提交,首先也标记它( git tag temp-studentmerge <sha1-of-the-commit> ); 当然,代替 <sha1-of-the-commit> 放置该提交的(缩写)sha1(它列在 git reflog 左侧第一列中的那个)。

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

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

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

如果你使用 gitk,如果你做了一个简单的 F5,你仍然会看到学生的提交,而不是 Shift-F5 ( Reload ),你就不会再看到它们了。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM