如果您作为 rebase 的一部分而处于冲突合并中,那么您就会陷入困境。以下是您可以做的原因、方法和内容。
变基 = 重复挑选
从根本上说,Git 中的变基操作只是一系列精选操作。我们从这样的事情开始:
...--A1--A2--A3--A4--A5 <-- branchA
\
B1--B2--B3 <-- branchB
我们希望最终得到:
...--A1--A2--A3--A4--A5 <-- branchA
\ \
\ B1'-B2'-B3' <-- branchB
\
B1--B2--B3 [abandoned]
我们(或 Git)实现这一点的方法是使用 git cherry-pick 或等效的东西,复制现有的提交 B1(将其变成补丁并应用它)来在A5 之后,复制B2 到B1' 之后,以此类推。
对于您在说明中留下的每个“选择”操作,交互式 rebase 会按字面意思运行 git cherry-pick。非交互式 rebase 有几个选项,包括运行 git cherry-pick。
在挑选提交时,如果在应用时发生冲突,Git 可以使用三向合并。这仍然可能因冲突而失败。这会停止变基。或者,当使用交互式变基时,您可以选择“编辑”一个提交,在这种情况下,Git 会挑选该提交,然后停止变基。无论哪种情况,Git 都会留下足够的信息让您让 Git 稍后恢复变基。
冲突在索引中
作为一个快速提醒,让我们注意 Git 的 index 是您构建 next 提交的地方。通常每个要提交的文件都有一个条目,因此如果您的下一次提交将仅包含三个名为 README、file 和 otherfile 的文件,则将有三个索引条目。
请注意,索引与 工作树 是分开的,后者包含正常的非 Gitty 格式的文件。您可以编辑这些文件、编译它们、使用它们来提供网页等等,这与索引和存储库文件的内部 Git 格式不同。 (工作树也可以保存未跟踪的文件,这在变基期间并不重要。)
在冲突合并期间,每个索引条目都会公开其单独的槽。每个条目最多有四个插槽,它们都有编号。插槽零保存正常的、不冲突的文件(如果存在),否则它是空的。插槽 1-3,如果在使用中,包含三个必须解决的冲突部分。1 这些是 base 版本(来自 merge base)、“本地”或--ours 版本,以及另一个或--theirs 或有时是“远程”版本。你的工作是编辑文件的工作树版本,解决冲突,然后git add 结果。这会将调整后的工作树版本复制到索引中的插槽 0 中,从而清除插槽 1-3 的条目。现在文件已解析并准备好提交。
1因此,要么槽 0 被占用而 1-3 为空,否则槽 0 为空且槽 1-3 被占用。在某些奇怪的情况下,插槽 1、2 和/或 3 也可以为空,例如,如果您遇到修改/删除冲突或添加/添加冲突,但通常它是“0 空意味着 1-3 已满”反之亦然。
但只有一个索引
索引这个短语暗示只有一个。这基本是真的。
因为未合并状态在这个(“the”)索引中,并且只有一个索引,所以需要使用索引的任何其他事情都无法进行,直到你完成解决冲突(然后提交)。
如果您愿意,您可以简单地 git add 未修复/未解决的项目和 git commit 结果,只是为了消除冲突。这里的缺点是 Git 不会保留哪些文件有冲突:你会清除插槽 1-3 的条目,Git 会认为你已经完成了。
你可以保存索引——它是一个普通的文件;您可以将其从.git/index 复制到其他地方。但是因为它是一个具有各种特殊内部用途的二进制文件——索引也被称为“缓存”,它缓存内部文件系统数据以提高速度——这并不是很安全。 (如果 Git 有办法“导出”索引状态,然后再“导入”它,那就太好了,这样您就真的可以保存和恢复合并冲突状态。但 Git 没有t.)
因此,为了安全起见,建议完成解决这个冲突的合并状态。或者,如果您还没有开始解决,甚至不开始:那么没有工作可保存。
你现在在哪里
假设您启动了我在上面绘制的“分支 B”rebase,并且目前卡在复制提交 B2 的中间,并且一些冲突未解决。这是你现在实际拥有的:
...--A1--A2--A3--A4--A5 <-- branchA
\ \
\ B1' <-- HEAD
\
B1--B2--B3 <-- branchB
索引处于冲突状态。您还有一个“分离的 HEAD”:Git 正在以这种方式构建新的提交链。名称 HEAD 指向所有已完成的提交。
如果你已经完成了一些解析工作,你应该完成它(因为保存未解析状态太难了)或者至少记下未解析的内容(因为你可以将文件添加到下一次提交)然后运行 @987654342 @创建提交B2':
...--A1--A2--A3--A4--A5 <-- branchA
\ \
\ B1'-B2' <-- HEAD
\
B1--B2--B3 <-- branchB
如果您还没有完成任何解析工作,则没有要保存的实际工作,所以不要运行git commit。但是无论如何,现在是时候创建一个分支或标签名称了,指向HEAD现在指向的同一个提交:
$ git branch saveme # or git tag saveme
现在你有了这个:
...--A1--A2--A3--A4--A5 <-- branchA
\ \
\ B1'-B2' <-- HEAD, saveme
\
B1--B2--B3 <-- branchB
现在你可以:
$ git rebase --abort
这使得 Git 停止 rebase 尝试并返回到branchB:
...--A1--A2--A3--A4--A5 <-- branchA
\ \
\ B1'-B2' <-- saveme
\
B1--B2--B3 <-- HEAD->branchB
现在您已经保存了到目前为止所做的所有工作,并且可以稍后返回并重试 rebase。你有你(或 Git)为B1' 做出的决议,如果你提交了B2',你也有你为此做出的决议。这些分别是提交saveme~1 和saveme;或者只提交saveme,如果只有一个提交。