如何保存 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
之后,复制 B2
到 B1'
之后,依此类推。
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 提交的地方。通常每个要提交的文件都有一个条目,因此如果您的下一次提交只包含三个名为 README
、file
和 otherfile
的文件,则会有三个索引条目.
请注意,索引与 工作树 是分开的,后者包含正常 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~1
和 saveme
的提交;或者只提交 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 更改以及您在流产后所做的更改。
我正处在一个巨大的 "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
之后,复制 B2
到 B1'
之后,依此类推。
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 提交的地方。通常每个要提交的文件都有一个条目,因此如果您的下一次提交只包含三个名为 README
、file
和 otherfile
的文件,则会有三个索引条目.
请注意,索引与 工作树 是分开的,后者包含正常 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~1
和 saveme
的提交;或者只提交 saveme
,如果只有一个提交。
我想出了一个可行的方法,但有点麻烦:
Re-clone 将 repo 放入不同的目录,留下 rebase-in-progress as-is.
如 git rebase --abort
.
稍后您可以点击 git reflog
,这是过去 git 事件的历史记录。这将显示一个列表,您将在其中逐一看到中止事件和所有之前的 cherry-pick
事件。
每个事件都属于一个哈希。您现在可以跳转到 git reset --hard <hash-from-reflog-list>
中止前的最后一步。
你当然不会处于变基模式,但你现在可以使用正常的樱桃选择继续变基。
注意!这将回滚所有其他 git 更改以及您在流产后所做的更改。