git - 从项目的旧版本中恢复 git 历史记录

标签 git git-rebase git-cherry-pick git-workflow

长话短说:

  1. 项目正在迁移到新的 git 存储库服务器
  2. 有人仅复制项目文件并将整个项目推送到新服务器作为初始提交
  3. 工作将从新的初始提交开始持续相当长的一段时间
  4. 我设法找到旧项目的旧本地副本(在切换到新服务器之前),并希望将旧 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 可以解决。在我的情况下,有大量具有复杂分支的提交,可能很难挑选。有没有一种有效的方法可以使用rebaserebase --onto来做到这一点?

最佳答案

Is there an efficient way to do this using rebase or rebase --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/

相关文章:

macos - GitHub for Mac 不再安装 Git 消息

git - Git 中有某种反向挑选吗?

Git:如何在不引起 merge 提交的情况下 merge 上游更改?

git - merge 到特定的提交

git - 如何在不使用 gitcherry-pick 的情况下解决此补丁发布问题?

git - 为什么 git log 会显示我的密码?

svn - HEAD 和 SVN Master 之间的 Git 差异

git - cherry-picked feature commits to new branch, reverted commits, rebase 不按预期工作

git - 当 git rebase 两个具有共享历史的分支时,是否有一种简单的方法可以让共同的历史保持共同?

git - Git 可以在 rebase 期间将 merge 转换为压缩吗?