git 逐个提交变基
git rebase commit by commit
我有一个包含 10 个提交的分支。现在主人改变了,我想将我的分支重新定位到新主人的头上。但是,一些新的主提交甚至使我的分支的第一次提交无法编译(没有合并冲突)。如果你所有的提交最终都无法编译,那么重新设置分支对我来说似乎毫无意义。
我想做的是告诉 get "do a rebase, but wait after rebasing of each commits from my branch"。这样,我可以检测每次提交后代码是否仍然编译,使其编译(如果需要),然后提交并继续。
这可能吗?是否有干净的 rebase 的替代方案,以便每次提交仍能编译?
当你做一个交互式变基时,edit
选项允许你在提交时暂停,使用 git commit --amend
来添加对提交的任何修改,然后使用 git rebase --continue
.
是的,这是可能的。
最简单的方法可能是使用git rebase -i
并将每个pick
更改为edit
。在 Git 完成每个 cherry-pick 操作后,它将返回到命令行。您现在可以尝试构建(和 运行 测试),当出现问题时,修复它。一旦一切顺利,运行 git add -u
和 git commit --amend
视情况而定,然后 git rebase --continue
按顺序应用下一个提交。
在您熟悉此方法后(不要马上开始!)如果您的 Git 支持,也可以考虑使用 --exec
。
注意:如果要复制的提交——不管它被标记为pick
还是edit
——有合并冲突,git rebase
因合并冲突而停止。即使你标记了它edit
,Git也不会在解决冲突后再次停止,所以如果你必须解决一些事情并改变一些事情,你应该在这里同时做这两件事(并且不要使用 git commit --amend
因为你还在制作 first 副本)。
详细信息,以防您感兴趣/好奇
Git 在物理上不可能更改任何现有提交,因为每个提交的真实名称(其哈希 ID)是所有 内容的加密校验和,包括你的名字 author/committer、时间戳,当然还有快照附带的所有源代码。
然而,Git 很容易 复制 一个提交——好吧,只要它是一个普通的 non-merge 提交,有一个单身parent。给定哈希 ID 为 H 的提交,Git 可以将快照 H 转换为 change-set反对 H 的 parent。把这想象成 git show
所做的:它提取 parent 的快照,然后提取提交的快照,无论它们之间有什么不同 (git diff <parent-of-H> <H>
),这就是 改变了。然后可以将这些相同的更改应用于某些 other 提交,方法是检查 other 提交,然后提交结果。也就是说,如果我们有两个分支 branch1
和 branch2
,我们可以检查第二个分支的 tip commit 并创建一个新的临时分支:
...--G--H--I--J <-- branch1
\
K--L <-- branch2, temp (HEAD)
然后我们将提交 H
变成 change-set,将 change-set 应用于提交 L
,并创建一个具有新的不同哈希 ID 的新提交.这个新的提交非常像 H
,我们称它为 H'
来说明:
...--G--H--I--J <-- branch1
\
K--L <-- branch2
\
H' <-- temp (HEAD)
这个复制操作是git cherry-pick
。 (从技术上讲,每个 cherry-pick 操作都是一个完整的 three-way 合并,而不是仅仅应用一个 change-set 风格的补丁,但我们不需要担心这一点,除非并且直到各种复杂情况发生。 )
如果我们 重复 cherry-picking 对 branch1
上的所有提交,我们得到这个:
...--G--H--I--J <-- branch1
\
K--L <-- branch2
\
H'-I'-J' <-- temp (HEAD)
如果我们现在 "peel the label" branch1
关闭 J
并将其附加到 J'
,完全丢弃临时名称,我们最终得到:
...--G--H--I--J [abandoned]
\
K--L <-- branch2
\
H'-I'-J' <-- branch1 (HEAD)
因此,Rebase 实际上是一系列 cherry-pick 操作。 interactive rebase 使这一点变得明确:每个提交哈希 ID 变成脚本中的 pick
命令。
将 pick
更改为 edit
告诉 Git 在完成每个 cherry-pick 步骤后,它应该停止并 return 命令线。 (请注意 Git 也 停止并 returns 到命令行,如果存在合并冲突,则全部自行完成。) 运行 git rebase --continue
告诉 Git 查阅这留下的控制文件——这些由 Git 内部调用 sequencer 的东西管理——看看还有什么提交仍然需要任何类型的处理。因此,如果您将每个提交标记为 "to be edited",Git 在将 H
复制到 H'
后停止,这样您就有了:
...--G--H--I--J <-- branch1
\
K--L <-- branch2
\
H' <-- HEAD
(我已经更新绘图以依赖于 Git 真正做到这一点的方式:Git 不是试图发明一个临时分支名称,而是使用 "detached HEAD" 模式来做复制。)
此时,如果您进行了更改并且 运行 git add -u && git commit --amend
、Git 又进行了一次新提交——我们称之为 H-prime-prime 或 H"
—parent 与 H'
的 parent 相同:
...--G--H--I--J <-- branch1
\
K--L <-- branch2
|\
| H' [abandoned]
\
H" <-- HEAD
当你现在 运行 git rebase --continue
, Git cherry-picks commit I
to I'
appended to H"
,然后——因为你说 "edit"——再次停止:
...--G--H--I--J <-- branch1
\
K--L <-- branch2
|\
| H' [abandoned]
\
H"-I' <-- HEAD
等等。
我有一个包含 10 个提交的分支。现在主人改变了,我想将我的分支重新定位到新主人的头上。但是,一些新的主提交甚至使我的分支的第一次提交无法编译(没有合并冲突)。如果你所有的提交最终都无法编译,那么重新设置分支对我来说似乎毫无意义。
我想做的是告诉 get "do a rebase, but wait after rebasing of each commits from my branch"。这样,我可以检测每次提交后代码是否仍然编译,使其编译(如果需要),然后提交并继续。
这可能吗?是否有干净的 rebase 的替代方案,以便每次提交仍能编译?
当你做一个交互式变基时,edit
选项允许你在提交时暂停,使用 git commit --amend
来添加对提交的任何修改,然后使用 git rebase --continue
.
是的,这是可能的。
最简单的方法可能是使用git rebase -i
并将每个pick
更改为edit
。在 Git 完成每个 cherry-pick 操作后,它将返回到命令行。您现在可以尝试构建(和 运行 测试),当出现问题时,修复它。一旦一切顺利,运行 git add -u
和 git commit --amend
视情况而定,然后 git rebase --continue
按顺序应用下一个提交。
在您熟悉此方法后(不要马上开始!)如果您的 Git 支持,也可以考虑使用 --exec
。
注意:如果要复制的提交——不管它被标记为pick
还是edit
——有合并冲突,git rebase
因合并冲突而停止。即使你标记了它edit
,Git也不会在解决冲突后再次停止,所以如果你必须解决一些事情并改变一些事情,你应该在这里同时做这两件事(并且不要使用 git commit --amend
因为你还在制作 first 副本)。
详细信息,以防您感兴趣/好奇
Git 在物理上不可能更改任何现有提交,因为每个提交的真实名称(其哈希 ID)是所有 内容的加密校验和,包括你的名字 author/committer、时间戳,当然还有快照附带的所有源代码。
然而,Git 很容易 复制 一个提交——好吧,只要它是一个普通的 non-merge 提交,有一个单身parent。给定哈希 ID 为 H 的提交,Git 可以将快照 H 转换为 change-set反对 H 的 parent。把这想象成 git show
所做的:它提取 parent 的快照,然后提取提交的快照,无论它们之间有什么不同 (git diff <parent-of-H> <H>
),这就是 改变了。然后可以将这些相同的更改应用于某些 other 提交,方法是检查 other 提交,然后提交结果。也就是说,如果我们有两个分支 branch1
和 branch2
,我们可以检查第二个分支的 tip commit 并创建一个新的临时分支:
...--G--H--I--J <-- branch1
\
K--L <-- branch2, temp (HEAD)
然后我们将提交 H
变成 change-set,将 change-set 应用于提交 L
,并创建一个具有新的不同哈希 ID 的新提交.这个新的提交非常像 H
,我们称它为 H'
来说明:
...--G--H--I--J <-- branch1
\
K--L <-- branch2
\
H' <-- temp (HEAD)
这个复制操作是git cherry-pick
。 (从技术上讲,每个 cherry-pick 操作都是一个完整的 three-way 合并,而不是仅仅应用一个 change-set 风格的补丁,但我们不需要担心这一点,除非并且直到各种复杂情况发生。 )
如果我们 重复 cherry-picking 对 branch1
上的所有提交,我们得到这个:
...--G--H--I--J <-- branch1
\
K--L <-- branch2
\
H'-I'-J' <-- temp (HEAD)
如果我们现在 "peel the label" branch1
关闭 J
并将其附加到 J'
,完全丢弃临时名称,我们最终得到:
...--G--H--I--J [abandoned]
\
K--L <-- branch2
\
H'-I'-J' <-- branch1 (HEAD)
因此,Rebase 实际上是一系列 cherry-pick 操作。 interactive rebase 使这一点变得明确:每个提交哈希 ID 变成脚本中的 pick
命令。
将 pick
更改为 edit
告诉 Git 在完成每个 cherry-pick 步骤后,它应该停止并 return 命令线。 (请注意 Git 也 停止并 returns 到命令行,如果存在合并冲突,则全部自行完成。) 运行 git rebase --continue
告诉 Git 查阅这留下的控制文件——这些由 Git 内部调用 sequencer 的东西管理——看看还有什么提交仍然需要任何类型的处理。因此,如果您将每个提交标记为 "to be edited",Git 在将 H
复制到 H'
后停止,这样您就有了:
...--G--H--I--J <-- branch1
\
K--L <-- branch2
\
H' <-- HEAD
(我已经更新绘图以依赖于 Git 真正做到这一点的方式:Git 不是试图发明一个临时分支名称,而是使用 "detached HEAD" 模式来做复制。)
此时,如果您进行了更改并且 运行 git add -u && git commit --amend
、Git 又进行了一次新提交——我们称之为 H-prime-prime 或 H"
—parent 与 H'
的 parent 相同:
...--G--H--I--J <-- branch1
\
K--L <-- branch2
|\
| H' [abandoned]
\
H" <-- HEAD
当你现在 运行 git rebase --continue
, Git cherry-picks commit I
to I'
appended to H"
,然后——因为你说 "edit"——再次停止:
...--G--H--I--J <-- branch1
\
K--L <-- branch2
|\
| H' [abandoned]
\
H"-I' <-- HEAD
等等。