从项目的遗留版本中恢复 git 历史记录
Recover git history from legacy version of project
长话短说:
- 项目正在迁移到新的 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
在历史上也有类似的困境,可以通过采摘樱桃来解决。在我的情况下,有大量具有复杂分支的提交可能难以挑选。有没有使用 rebase
或 rebase --onto
的有效方法?
Is there an efficient way to do this using rebase
or rebase --onto
?
一般情况下不会,因为可能存在分支和合并。 (如果新存储库中的历史是严格线性的,那么你可以用一个简单的 git rebase --onto
来做到这一点。它并不完全 有效 但它只是机器时间,所以谁在乎如何效率高吗?)
这个问题的一般解决方案是移植,通过git replace
。
让我们看看如果使用上图将原始存储库和新存储库 git fetch
都放入 第三个 (否则完全为空)存储库中会发生什么。你最终得到:
A--B--C--D--E--F <-- old/master
D'--G--H--I--J <-- new/master
(请注意,第三个存储库还没有自己的 master
)。我没有将 new/master
链中的第一个提交称为 init
,而是将其称为 D'
因为大概它与提交 snapshot 相同 D
在 old/master
中,但它具有不同的 哈希 .
没有任何东西——地球上没有力量——可以改变这些现有提交中的任何。但是,如果我们将提交 G
复制到父级为 D
的新提交 G'
怎么办?然后我们得到这个:
A--B--C--D--E--F <-- old/master
\
G'
D'--G--H--I--J <-- new/master
目前,新提交 G'
只是挂在存储库中,我们无法 找到 它。让我们添加一个 name,通过它我们可以找到 G'
。现在,我们称它为 graft
:
A--B--C--D--E--F <-- old/master
\
G' <-- graft
D'--G--H--I--J <-- new/master
现在,如果我们能以某种方式得到 Git,当它沿着 J
-then-I
-then-H
-then-G
-然后-D'
(然后停止)链,到,在最后一刻,将从G
切换到它的移植物G'
?也就是说,我们将建立某种虚线连接:
A--B--C--D--E--F <-- old/master
\
G' <-- graft
:
D'--G--H--I--J <-- new/master
并说服Git 运行 git log
as show J
then I
then H
then G'
然后 D
然后 C
然后 B
然后 A
.
它现在看起来像历史是这样读的,尽管实际上不是这样。1
这正是 git replace
所做的。它使 替换对象 。在提交的情况下,替换可以采用 graft 的形式,如 G'
。 Git 没有使用魔术名称 graft
,而是使用了一个更魔术的名称,refs/replace/<em>hash</em>
,其中 hash 是实际提交的哈希 ID G
。有些时候,您不需要知道这一点,而有些时候,您需要知道。
这种替换提交嫁接的问题是 git clone
默认情况下不会 clone 替换。2 所以你的第三个存储库在克隆时有点奇怪。有时这正是您想要的,如果是这样,那很好。有时不是,如果是这样,请考虑使用 git filter-branch
或类似于 convert 移植物到移植物所在的 fourth 存储库现在是永久性的,因为提交被复制到 new 提交(具有新的和不同的哈希 ID),其中 real——但重写的——历史使用嫁接历史,而不是原始历史。
1哲学问题:还是这样?历史是按照的方式解读的,还是Git向你解读历史的方式?
2这实际上是 Git 对脚注 1 中哲学问题的回答。你,却是被记录为真实的历史。在克隆时,Git 克隆 真实的 历史,并忽略嫁接。在克隆之后,您可以要求 Git 也复制移植物 ,但这不是默认设置。
除此之外,还可以运行 git --no-replace-objects log
看到真实的历史,在看嫁接历史的时候,每一个嫁接都有一个可选的装饰标记,这样你仔细看就可以看到.
长话短说:
- 项目正在迁移到新的 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
rebase
或 rebase --onto
的有效方法?
Is there an efficient way to do this using
rebase
orrebase --onto
?
一般情况下不会,因为可能存在分支和合并。 (如果新存储库中的历史是严格线性的,那么你可以用一个简单的 git rebase --onto
来做到这一点。它并不完全 有效 但它只是机器时间,所以谁在乎如何效率高吗?)
这个问题的一般解决方案是移植,通过git replace
。
让我们看看如果使用上图将原始存储库和新存储库 git fetch
都放入 第三个 (否则完全为空)存储库中会发生什么。你最终得到:
A--B--C--D--E--F <-- old/master
D'--G--H--I--J <-- new/master
(请注意,第三个存储库还没有自己的 master
)。我没有将 new/master
链中的第一个提交称为 init
,而是将其称为 D'
因为大概它与提交 snapshot 相同 D
在 old/master
中,但它具有不同的 哈希 .
没有任何东西——地球上没有力量——可以改变这些现有提交中的任何。但是,如果我们将提交 G
复制到父级为 D
的新提交 G'
怎么办?然后我们得到这个:
A--B--C--D--E--F <-- old/master
\
G'
D'--G--H--I--J <-- new/master
目前,新提交 G'
只是挂在存储库中,我们无法 找到 它。让我们添加一个 name,通过它我们可以找到 G'
。现在,我们称它为 graft
:
A--B--C--D--E--F <-- old/master
\
G' <-- graft
D'--G--H--I--J <-- new/master
现在,如果我们能以某种方式得到 Git,当它沿着 J
-then-I
-then-H
-then-G
-然后-D'
(然后停止)链,到,在最后一刻,将从G
切换到它的移植物G'
?也就是说,我们将建立某种虚线连接:
A--B--C--D--E--F <-- old/master
\
G' <-- graft
:
D'--G--H--I--J <-- new/master
并说服Git 运行 git log
as show J
then I
then H
then G'
然后 D
然后 C
然后 B
然后 A
.
它现在看起来像历史是这样读的,尽管实际上不是这样。1
这正是 git replace
所做的。它使 替换对象 。在提交的情况下,替换可以采用 graft 的形式,如 G'
。 Git 没有使用魔术名称 graft
,而是使用了一个更魔术的名称,refs/replace/<em>hash</em>
,其中 hash 是实际提交的哈希 ID G
。有些时候,您不需要知道这一点,而有些时候,您需要知道。
这种替换提交嫁接的问题是 git clone
默认情况下不会 clone 替换。2 所以你的第三个存储库在克隆时有点奇怪。有时这正是您想要的,如果是这样,那很好。有时不是,如果是这样,请考虑使用 git filter-branch
或类似于 convert 移植物到移植物所在的 fourth 存储库现在是永久性的,因为提交被复制到 new 提交(具有新的和不同的哈希 ID),其中 real——但重写的——历史使用嫁接历史,而不是原始历史。
1哲学问题:还是这样?历史是按照的方式解读的,还是Git向你解读历史的方式?
2这实际上是 Git 对脚注 1 中哲学问题的回答。你,却是被记录为真实的历史。在克隆时,Git 克隆 真实的 历史,并忽略嫁接。在克隆之后,您可以要求 Git 也复制移植物 ,但这不是默认设置。
除此之外,还可以运行 git --no-replace-objects log
看到真实的历史,在看嫁接历史的时候,每一个嫁接都有一个可选的装饰标记,这样你仔细看就可以看到.