长话短说:
分支本质上是这样的:
old_master
/
A--B--C--D--E--F
origin/new_master
/
init--G--H--I--J
其中提交:new_master
-> init
= old_master
-> D
因此最终结果将类似于:
origin/new_master
/
A--B--C--D--G--H--I--J
How to rebase commits from another repository with a different history?具有类似的困境历史智慧,可以通过选择樱桃来解决。在我的情况下,有大量带有复杂分支的提交可能很难挑选。是否有使用rebase
或rebase --onto
的有效方法?
答案 0 :(得分:3)
是否有使用
rebase
或rebase --onto
的有效方法?
通常情况下不存在分支合并的情况。 (如果新存储库中的历史记录严格地是线性的,那么您可以使用简单的git rebase --onto
来完成此操作。它的效率不是完全有效,只是机器时间,因此谁在乎它的效率如何? ?)
一般的解决方案是通过git replace
的 graft 。
让我们看看使用上面的图将git fetch
原始存储库和新存储库都同时放入第三(否则完全为空)存储库时会发生什么。您最终得到:
A--B--C--D--E--F <-- old/master
D'--G--H--I--J <-- new/master
(请注意,第三个存储库尚没有自己的master
)。我没有调用new/master
链init
中的第一次提交,而是将其称为D'
,因为它大概具有与提交{{1}相同的 snapshot }中的D
中,但具有不同的 hash 。
没有什么(地球上没有权力)可以改变这些现有提交中的任何。但是,如果我们将提交old/master
复制到父级为G
的新提交G'
会怎样?然后我们得到这个:
D
目前,新的提交A--B--C--D--E--F <-- old/master
\
G'
D'--G--H--I--J <-- new/master
只是在存储库中闲逛,我们无法查找。让我们添加一个名称,通过它我们可以找到G'
。现在,我们将其称为G'
:
graft
现在,当Git沿着A--B--C--D--E--F <-- old/master
\
G' <-- graft
D'--G--H--I--J <-- new/master
-然后-J
-然后-I
-然后-H
-然后向后走时,如果我们能以某种方式获得Git,该怎么办-G
链(然后停止),以便在最后一刻从D'
切换到其嫁接G
?也就是说,我们将建立某种形式的虚线连接:
G'
并说服Git运行A--B--C--D--E--F <-- old/master
\
G' <-- graft
:
D'--G--H--I--J <-- new/master
,如 show git log
然后J
然后I
然后H
然后G'
然后{{ 1}}然后D
然后C
。
现在它看起来 历史记录将以这种方式读取,即使实际上不是这样。 1
这正是B
所做的。它制作了替换对象。在提交的情况下,替换可以采用 graft 的形式,例如A
。 Git并没有使用魔术名称git replace
,而是使用了更魔术的名称G'
,其中 hash 是实际提交graft
的哈希ID。有时候,您不需要知道这一点,而有时候,您知道。
默认情况下,这种refs/replace/hash
不克隆 替换。 2 因此,您的第三个存储库是克隆时有点奇怪。有时候,这正是您想要的,如果是,那就很好。有时不是,如果是这样,请考虑使用G
或类似的 convert 移植物到第四存储库中,该移植物现在是永久性的,因为提交被复制到具有新的和不同的哈希ID的 new 提交中,其中 real (但被重写)的历史记录使用嫁接的历史记录,而不是原始历史记录。
1 哲学问题:或者是吗?历史记录是按照历史记录是的方式读取的,还是历史记录是Git向您读出的方式?
2 实际上,这是Git在脚注1中对哲学问题的回答。历史读取了Git向您读取的方式,但被记录记录为<真实的历史。克隆时,Git会克隆 real 历史记录,并忽略移植。克隆之后,您可以要求Git也复制嫁接 ,但这不是默认设置。
除此之外,您还可以运行git clone
并查看真实的历史记录,并且在查看嫁接的历史记录时,每个嫁接都会标记一个可选的装饰,以便您仔细观察就可以看到它。