长话短说:
- 项目正在迁移到新的 git 存储库服务器
- 有人仅复制项目文件并将整个项目推送到新服务器作为初始提交
- 工作将从新的初始提交开始持续相当长的一段时间
- 我设法找到旧项目的旧本地副本(在切换到新服务器之前),并希望将旧 git 历史记录插入到项目的当前版本中(在当前历史记录开始之前)。旧的本地项目中有一些额外不需要的提交
分支本质上是这样的:
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?历史上也有类似的困境,但通过 cherry-pick 可以解决。在我的情况下,有大量具有复杂分支的提交,可能很难挑选。有没有一种有效的方法可以使用rebase
或rebase --onto
来做到这一点?
最佳答案
Is there an efficient way to do this using
rebase
orrebase --onto
?
一般情况下不存在,因为一般情况下可能存在分支和 merge 。 (如果新存储库中的历史记录是严格线性的,那么您可以使用简单的 git rebase --onto
来完成此操作。它并不完全高效,但它只是机器时间,所以谁在乎它的效率如何?)
对此的一般解决方案是通过 git replace
移植 .
让我们看看如果您 git fetch
会发生什么使用上面的绘图,将原始存储库和新存储库 merge 到第三(否则完全是空的)存储库中。你最终会得到:
A--B--C--D--E--F <-- old/master
D'--G--H--I--J <-- new/master
(请注意,第三个存储库还没有自己的 master
)。而不是调用 new/master
中的第一次提交链条init
,我称之为D'
因为大概它与提交 D
具有相同的快照在old/master
,但它有不同的哈希。
没有任何东西(地球上没有任何力量)可以改变这些现有提交中的任何。但是如果我们复制提交 G
会怎样?到一个新的提交 G'
其父级是 D
?然后我们得到这个:
A--B--C--D--E--F <-- old/master
\
G'
D'--G--H--I--J <-- new/master
目前,新提交 G'
只是在存储库中闲逛,我们无法找到它。让我们添加一个名称,通过它我们可以找到 G'
。现在,我们将其称为 graft
:
A--B--C--D--E--F <-- old/master
\
G' <-- graft
D'--G--H--I--J <-- new/master
现在,如果我们能以某种方式获得 Git,当它沿着 J
倒退时会怎样? -然后- I
-然后- H
-然后- G
-然后- D'
(然后停止)链,在最后一刻它可以从 G
切换对其嫁接G'
?也就是说,我们将建立某种虚线连接:
A--B--C--D--E--F <-- old/master
\
G' <-- graft
:
D'--G--H--I--J <-- new/master
并说服 Git 运行 git log
如显示J
然后I
然后H
然后G'
然后D
然后C
然后B
然后A
。
现在看起来历史是这样读的,尽管事实并非如此。1
这正是git replace
做。它制作替换对象。在提交的情况下,替换可以采取移植的形式,例如 G'
。而不是使用神奇的名字 graft
,Git 使用了一个更加神奇的名字, refs/replace/<em>hash</em>
,其中 hash 是实际提交的哈希 ID G
。有时,您不需要知道这一点,有时,您需要知道。
这种替换提交移植的问题在于 git clone
默认情况下,不会克隆替换。2因此,克隆时您的第三个存储库有点奇怪。有时这正是您想要的,如果是这样,那也很好。有时不是,如果是这样,请考虑使用 git filter-branch
或类似于将移植到第四存储库,其中移植现在是永久的,因为提交被复制到新提交(使用新和不同的哈希 ID),其中真实(但重写的)历史使用嫁接的历史,而不是原始历史。
1哲学问题:是吗?历史记录的读取方式是现在,还是Git 向您读出的方式读取历史记录?
2这实际上是 Git 对脚注 1 中的哲学问题的回答。历史的读取方式与 Git 向您读取的方式相同,但记录为< em>真实的历史。在克隆时,Git 克隆真实历史,并忽略移植。您可以在克隆之后要求 Git 也复制移植物,但这不是默认设置。
除此之外,您还可以运行 git --no-replace-objects log
看真实的历史,在看嫁接历史的时候,每一个嫁接都有一个可选的装饰标记,这样你仔细看就可以看到。
关于git - 从项目的旧版本中恢复 git 历史记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60485200/