如何保存 git "rebase in progress"?

How can I save a git "rebase in progress"?

我正处在一个巨大的 "rebase in progress" 中间,有许多 冲突

我想搁置这一进展并尝试使用其他方法解决此问题。

有什么方法可以保存 进行中的 变基以便稍后完成?

如果您正坐在作为变基一部分的冲突合并中,那么您有点卡住了。以下是您可以做的原因、方式和内容。

变基 = 重复 cherry-pick

从根本上说,Git 中的变基操作只是一系列 cherry-pick 操作。我们从这样的事情开始:

...--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 之后,复制 B2B1' 之后,依此类推。

Interactive rebase literally 运行s git cherry-pick 对于您在说明中留下的每个 "pick" 操作。 Non-interactive rebase 有几个选项,包括 运行ning git cherry-pick.

当 cherry-pick 提交时,如果在应用时发生冲突,Git 可以使用 three-way 合并。这仍然会因冲突而失败。这将停止变基。或者,当使用交互式变基时,您可以选择 "edit" 提交,在这种情况下 Git cherry-pick 提交然后停止变基。在任何一种情况下,Git 都会留下足够的信息供您 Git 稍后恢复变基。

索引中存在冲突

作为快速提醒,请注意 Git 的 index 是您构建 next 提交的地方。通常每个要提交的文件都有一个条目,因此如果您的下一次提交只包含三个名为 READMEfileotherfile 的文件,则会有三个索引条目.

请注意,索引与 工作树 是分开的,后者包含正常 non-Gitty 格式的文件。您可以编辑这些文件、编译它们、使用它们为网页提供服务等等,这与索引和存储库文件的内部 Git 格式不同。 (work-tree 也可以保存未跟踪的文件,这在 rebase 期间并不重要。)

在冲突合并期间,每个索引条目都会公开其各自的 。每个条目最多有四个插槽,并且已编号。槽 0 保存正常的、无冲突的文件(如果存在),否则它是空的。插槽 1-3,如果正在使用,则包含必须解决的三个冲突部分。1 这些是 base 版本(来自 merge base),"local" 或 --ours 版本,以及另一个或 --theirs 或有时 "remote" 版本。您的工作是编辑文件的 work-tree 版本,解决冲突,然后 git add 结果。这会将调整后的 work-tree 版本复制到索引中的槽零中,从而清除槽 1-3 条目。现在文件已解析并准备提交。


1因此,要么 插槽 0 被占用且 1-3 为空,要么插槽 0 为空且插槽 1- 3个被占用。在一些奇怪的情况下,插槽 1、2、and/or 3 也可以为空,例如,如果您遇到 modify/delete 冲突或 add/add 冲突,但通常是“0 空意味着1-3 已满”,反之亦然。


但是只有一个索引

短语 索引暗示只有一个。这大部分是真实的。

因为未合并状态在这个("the")索引中,而且只有一个索引,其他任何需要使用 在解决冲突(然后提交)之前,索引无法继续。

如果愿意,您可以简单地 git add un-fixed/un-resolved 项和 git commit 结果,只是为了避免冲突。这里的缺点是 Git 不会保留哪些文件发生冲突:您将清除插槽 1-3 条目,并且 Git 会认为您已经完成了。

您可以保存索引——这是一个普通文件;您可以将其从 .git/index 复制到其他地方。但是因为它是一个具有各种特殊内部用途的二进制文件——索引也称为 "cache" 并且它缓存内部文件系统数据以提高速度——这并不是很安全。 (如果 Git 有办法 "export" 索引状态,然后 "import" 稍后再次使用它,那就太好了,这样你真的 可以 保存和恢复合并冲突状态。但是 Git 没有。)

因此,为了安全起见,建议完成此冲突合并状态的解决。或者,如果您尚未开始 解决,甚至不开始:那么就没有工作可保存。

你现在在哪里

假设您开始了我在上面绘制的 "branch B" 变基,目前卡在复制提交 B2 的中间,一些冲突未解决。这是您现在实际拥有的:

...--A1--A2--A3--A4--A5   <-- branchA
          \           \
           \           B1'  <-- HEAD
            \
             B1--B2--B3   <-- branchB

索引处于冲突状态。您还有一个 "detached HEAD": Git 正在以这种方式构建新的提交链。名称 HEAD 指向所有已完成的提交。

如果你做了一些解决工作,哟应该完成它(因为保存未解决的状态太难了)或者至少记下未解决的问题(因为您可以将文件添加到下一次提交)然后 运行 git commit 创建提交 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 停止变基尝试并返回到 branchB:

...--A1--A2--A3--A4--A5   <-- branchA
          \           \
           \           B1'-B2'  <-- saveme
            \
             B1--B2--B3   <-- HEAD->branchB

现在您已经保存了到目前为止所做的所有工作,稍后可以返回并重试变基。您有您(或 Git)为 B1' 制定的决议,如果您提交 B2',您也有您为此制定的决议。这些分别是 saveme~1saveme 的提交;或者只提交 saveme,如果只有一个提交。

我想出了一个可行的方法,但有点麻烦:

Re-clone 将 repo 放入不同的目录,留下 rebase-in-progress as-is.

所述,rebase 只是一系列精选。如果你正处于一个冲突的 rebase 的中间(意味着一些 cherry-picks 已经发生)并且你必须中止它,执行 git rebase --abort.

稍后您可以点击 git reflog,这是过去 git 事件的历史记录。这将显示一个列表,您将在其中逐一看到中止事件和所有之前的 cherry-pick 事件。

每个事件都属于一个哈希。您现在可以跳转到 git reset --hard <hash-from-reflog-list> 中止前的最后一步。 你当然不会处于变基模式,但你现在可以使用正常的樱桃选择继续变基。

注意!这将回滚所有其他 git 更改以及您在流产后所做的更改。