【问题标题】:How can I save a git "rebase in progress"?如何保存 git“rebase 进行中”?
【发布时间】:2017-01-16 22:07:43
【问题描述】:

我正处于一个巨大的“正在进行的变基”之中,其中有许多冲突

我想把这个进展搁置一旁,尝试用另一种方法解决这个问题。

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

【问题讨论】:

  • 我会做以下事情。存储更改,从中创建一个新分支,例如分支 2,然后存储应用更改,以便您可以在该分支上工作。在最初的分支上,我会采用另一种方法。
  • @AndreiT:不幸的是,您不能存储冲突的合并(因为每个存储只是一组提交,而冲突时您不能提交)。

标签: git rebase


【解决方案1】:

如果您作为 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 之后,复制B2B1' 之后,以此类推。

对于您在说明中留下的每个“选择”操作,交互式 rebase 会按字面意思运行 git cherry-pick。非交互式 rebase 有几个选项,包括运行 git cherry-pick

在挑选提交时,如果在应用时发生冲突,Git 可以使用三向合并。这仍然可能因冲突而失败。这会停止变基。或者,当使用交互式变基时,您可以选择“编辑”一个提交,在这种情况下,Git 会挑选该提交,然后停止变基。无论哪种情况,Git 都会留下足够的信息让您让 Git 稍后恢复变基。

冲突在索引中

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

请注意,索引与 工作树 是分开的,后者包含正常的非 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~1saveme;或者只提交saveme,如果只有一个提交。

【讨论】:

  • 在您描述的四个插槽中,我可以看到来自合并库的版本来自哪里,并且“我们的”和“他们的”似乎都很明显。但是什么是“正常的无冲突文件”,从哪里来的?
  • @dumbledad:有史以来第一个不冲突的版本,来自你(或其他人,大概)。如果存在冲突,则插槽 0 未被占用:不存在未冲突的版本。现在由 解决冲突(使用工作树版本和/或三个 slot-1-2-3 版本),然后在您想出的任何内容上运行 git add,将其写入插槽 0 作为正常的无冲突版本。然后进入下一次提交,因此每次提交中的任何内容也根据定义不冲突。 啊,我看到了措辞问题,会解决的。
  • 感谢您的精彩回答。
【解决方案2】:

我想出了一个可行的方法,但有点小技巧:

将 repo 重新克隆到不同的目录,保持 rebase-in-progress 原样。

【讨论】:

  • 这确实有效:它为您提供了一个具有自己的索引和工作树的克隆,并且新克隆中的索引和工作树独立于 in 的索引和工作树-进度变基。另一个非常方便的技巧(从 Git 2.5 开始可用,但在 2.6 之前我不太信任它)是git worktree add,它创建了一个具有自己独立索引的新工作树。但是,有一个限制:每个添加的工作树都必须在 它自己的 分支上。正在进行的 rebase 在 no 分支(分离的 HEAD)上,但是一旦你完成 rebase,它会尝试回到原来的分支,所以在这里要小心。
  • 但是对于大型存储库,这可能会占用太多空间。
  • @dumbledad 我几乎无法想象一个 repo 如此之大以至于你不能一次在磁盘上拥有 2 个副本。如果 repo 有很多大型媒体文件,也许我可以看到它。到那时,也许是时候重新考虑对代码进行版本控制了。
【解决方案3】:

正如torek 提到的,rebase 只是一系列精选。如果你正处于一个冲突的 rebase 中间(意味着一些樱桃选择已经发生)并且你必须中止它,执行git rebase --abort

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

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

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

【讨论】:

    猜你喜欢
    • 2023-03-18
    • 2011-12-07
    • 2019-02-18
    • 1970-01-01
    • 2021-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-22
    相关资源
    最近更新 更多