我遇到了同样令人费解的问题。我想将一大堆顺序提交的更改转换为单个提交,但是使用带有一系列 squash 的交互式 rebase 会令人沮丧地产生合并冲突。这是很久以前的事了,我刚才无法在一个最小的例子中重现它。无论如何,我确实知道如何解决它,所以你去:
我将介绍两种不同的方式来完成帖子所要求的工作——一种笨重但“不那么可怕”,因为它很清楚你在做什么,另一种很优雅,只使用git
命令,但需要你相信你的理解git
多一点。
设置
假设您有一个提交 IDidA
作为初始状态,提交 IDidB
作为最终状态,中间还有一堆其他提交。您想要压缩所有中间提交的事实表明您不再关心您是如何从初始状态到最终状态的——您只需要一个将您从一个点移动idA
到另一个点的提交idB
。
让我们假设它idB
对应于当前HEAD
并说它也是你的master
分支。我们将创建一个名为的新分支squashed
,它具有与 相同的工作树内容master
,但在提交 ID 之后只有一个提交idA
。
解决方案 1
git checkout master
(你可能已经在master
)
- 使用
bash
或您的文件资源管理器将整个工作树复制到其他地方。只需确保您忽略了.git
目录——这是大多数操作系统的默认行为。所以,要明确一点——打开 repo 文件夹,全选,复制并粘贴到某个新文件夹中,在您的桌面或任何您想要的地方。
git checkout -b squashed idA
创建一个新分支作为其最新提交,并将squashed
其idA
设为当前分支。
master
将(您在步骤 2 中放在其他位置)的内容粘贴回 repo 文件夹。如果您的文件资源管理器询问,请告诉它替换所有已更改的文件。
- 在你的 repo 的顶层,
git add .
然后git commit
. 您的新提交将包含将您从idA
带到的所有更改idB
。
解决方案 2
git branch squashed idA # make a new branch called `squash` with `idA` as its latest commit
git checkout master # master is "idB" in this case.
git symbolic-ref HEAD refs/heads/squashed
git commit
瞧。当您使用symbolic-ref
将 HEAD 移动到squash
分支的顶端时,工作树状态和新 HEAD 位置之间的差异集将被计算和暂存。
我想回应一些人表示这是一种“危险”、“低级”的git
黑客攻击的担忧。我会尽量解释它是如何工作的,你可以放心。您的.git
文件夹是所有版本化文件内容所在的位置。其中的两个文件夹是objects
和refs
。该refs
文件夹包含的文件告诉 git 你的分支的名称以及它们对应的提交。因此,例如,如果您打开.git/refs/heads/master
,您将看到最新提交到master
. 该objects
文件夹包含一堆文件,这些文件位于具有两个字符的十六进制名称的子文件夹中。对象可以是几个不同的东西,包括补丁、提交和整个文件。.git
文件夹的顶层还有一个index
文件。这是一篇关于索引文件内容的好文章。对于当前的讨论,您需要知道的是索引文件告诉您哪个对象(在objects
文件夹中)对应于当前分支上每个文件的最新提交版本并提交。
在这种背景下,解决方案的作用如下:该symbolic-ref
命令只是git
“突然”告诉您您在squash
分支而不是master
分支上,而无需触及工作树。这意味着您的文件都对应于master
您所知道和喜爱的状态,但是git
将其视为idA
一堆未提交的更改(即,index
文件指定所有文件的当前签出版本是 的idA
,并且这是衡量工作树变化的参考)。事实上,确切的变化会让你明白idB
. 这正是解决方案 1 所做的。在这种情况下,由于方式symbolic-ref
实施后,所有上述更改都已上演(即git add
-ed),因此您所要做的就是git commit
. 这并没有什么“危险”,因为它不会改变master
或任何objects
正在git
跟踪的东西。您刚刚在refs
名为 的文件夹中创建了一个新文件squashed
,并在文件夹中创建了一个objects
与该新组合提交相对应的新文件。你master
就在你离开它的地方,你只是有一个新的分支squashed
,它的内容相同,但提交历史更短。如果您不确定发生了什么,请放心,您可以放心git checkout master
,它就在您离开的地方。
如果你想领养squashed
你的新人master
,你可以继续
git branch -m master old-master
git branch -m squashed master
然后您将拥有所有多余提交的旧分支保存为old-master
,master
这正是您想要的。
希望这可以帮助!