更新 - 我分两个阶段写了原始答案,因此有点混乱。信息保持不变,但我会稍微整理一下...
根据您如何压缩提交,您可能会遇到两种非常相似的情况。粗略地说:
1) 如果您从一个分支压缩到另一个分支(例如通过 git merge --aquash
),则您将拥有以下图形:
x
A
其中,!M
是某个先前合并操作的撤销,而 AB!MCD
是对提交 A
、B
、!M
、C
和 D
进行压缩的结果。
2) 如果您在一个分支上“原地”压缩了提交记录,例如通过使用命令 git rebase -i
,那么图形将更像:
x
A
这里的
branch_for_pr@{2}
是一个reflog条目,工具不像现有引用那样显式显示reflog条目,所以看起来
A
..
D
好像"消失"了,但至少在发生压缩的本地克隆中,它们并没有消失。(确实,reflog条目最终会过期, 默认情况下大约三个月后会过期,或者当您明确地使其过期时。在reflog条目过期后,gc可能会删除旧提交,除非在此期间您已执行某些操作使其可访问。此外,reflogs在推送或拉取时不会共享。)
您可以使用类似以下命令查找reflog条目:
git reflog branch_for_pr
并且可以在知道不再需要它之前,将新的分支或标签放在它上面,以消除引用日志到期可能会隐藏它的风险。 如果这样做,结果看起来像第一个图表(来自上面的(1))。
本条目的其余部分假设实际分支指向 D。所以...怎么办?
最简单的方法是还原 branch_for_pr 上的 !M。这似乎是违反直觉的,但你几乎可以还原任何东西;它不必是 HEAD 的祖先。所以你需要一个解析为 !M 的表达式 - 它可以是其提交 ID,或者在本例中类似于 work_branch~2 的东西。
git checkout branch_for_pr
git revert work_branch~2
一些需要了解的背景知识:
提交是项目的快照,其中包含一小部分元数据。元数据包括“父级”列表,将快照相对于历史中的其他快照放置在哪里。许多 git 命令都操作提交的补丁 - 即该提交与其父提交之间的差异(在合并的情况下可能是其一个父提交)。提交的元数据不包括任何特殊关系,“此提交撤消的提交”或“合并到此提交的提交”,这些信息基本上已丢失。
如上所示,“撤消”只是一种汇编与其父提交以某种方式相关的提交的简便方法,由另一个提交与其父级的关系控制。git 不记录特定提交是通过撤消另一个提交生成的(更不用说是那个提交是什么了)。
同样,“压缩”只是指定要应用于 HEAD 以创建新提交的一组更改的简便方法。得到的提交并不“知道”它是一个压缩提交。
因此,这些事情可能会改变您对正在尝试做什么的看法,并影响您如何解决这个问题。对于“重新执行合并”的建议解决方案而言,一个更为紧迫的问题是,Git 在合并撤消方面有些奇怪。一旦您撤消了合并,重新合并它就不那么容易了。您要么必须从新提交生成分支(以使重新合并成为可能),要么只需撤消撤消 - 这正是我建议在您的最终分支上直接执行的操作。
有许多此想法的变体,可能会产生“更清晰”的历史记录;但这是最简单的方法,避免了可能会让事情变得更加混乱的情况。
!M
,而不是第二个分支ref。 - Mark Adelsberger