【问题标题】:Merging two git repositories of the same project, linking file history合并同一个项目的两个git仓库,链接文件历史
【发布时间】:2019-04-12 14:06:48
【问题描述】:

我有一个很久以前就开始的项目,并做出了许多承诺。然后该项目被放弃了大约两年,在此期间我忘记了我一直在项目上使用 git 版本控制。我拿起它,将所有文件复制到一台新机器上,并启动了一个新的 git 存储库,其中包含大约 100,000 行代码和数十个文件,现在它拥有自己的冗长提交历史。我最近重新发现了旧的 repo,并尝试将两个 repo 的提交历史合并在一起,using the instructions here.

但是,结果不完整。如果我查看 github 上的提交历史记录,来自旧存储库和新存储库的提交是完整的,但是每个单独的文件历史记录不会延伸回旧存储库的一系列提交,仍然显示它们只是在创建时提交期间创建的的新存储库。当我手动复制所有内容以启动新 repo 时,一些未传输的文件根本不显示。

自旧存储库历史结束以来,项目的文件结构和命名约定发生了显着变化,并且某些文件关联可能并不明显。如果我必须手动将旧的与新的链接一次,我可以这样做,但自动解决方案会更好。

【问题讨论】:

  • 但每个单独的文件历史不会延伸回旧存储库的一系列提交,嗯,这是因为您确实 将所有文件复制到了新机器,并开始了一个新的 git repo,很可能 git 将它们视为两个不同的文件.. 那时,我已经放弃并将旧的 repo 用作博物馆..
  • 在旧仓库的最后一次提交和新仓库的第一次提交之间你改变了多少?
  • 请注意,无论您如何操作,您想要执行的操作都会更改所有新 repo 的哈希值;把所有东西结合起来可能不值得。
  • 在最后一次旧的/第一次新的提交之间几乎没有什么变化——我提到的结构性改革是最近的,是什么促使我去寻找更旧的历史,但第一个文件新的提交应该与旧的文件相当相似。
  • 如果新仓库的历史是线性的(没有合并),你可以使用git rebase。否则最好使用git filter-branch 来保留合并。我对任何一个都没有足够的信心给出一个肯定的答案(尤其不是filter-branch);在尝试任何事情之前,我都会备份两个存储库。

标签: git github merge git-merge


【解决方案1】:

我假设您已按照您所链接问题的最佳答案中的步骤进行操作。对于这种情况,这些不是最好的步骤。

您的项目有两个历史片段。如果我们假设第一段有提交

A -- B -- C <--(master)

第二段有提交

D -- E -- F <--(master)

那么一个完整的历史会像预期的那样运行

A -- B -- C -- D' -- E' -- F' <--(master)

(关于符号的说明:我在合并历史等中已将 D 替换为 D'。其原因可以说是技术性的,可能并不立即重要;总而言之,它只是意味着提交身份,D'D 不同,因为D'C 作为父级,而D 没有。但是字母保持不变,以表明D' 代表相同的状态代码 - 即相同的内容或TREE - 与D。)

您链接的答案并没有做到这一点。它满足了两个最基本的目标——将提交放在一个 repo 中,并将它们组合成一个图表——但它不满足最有价值的一个目标:为它们制作一个连贯的历史。相反,它会给你

   A -- B -- C
              \
D -- E -- F -- f*

其中f* 是一个合并提交(即具有多个父项的提交),其内容与F 匹配,但也将C 列为其历史记录的一部分。

问题在于C 不是 然后被识别为Ds 历史的一部分。事实上,git 的默认历史过滤规则(例如用于日志输出)将完全排除 ABC,因为从 git 的角度来看,没有它们可以解释代码的状态。

(关于您的问题,当前大多数 cmets 都在谈论相似性启发式等问题,但在我看来,这些 cmets 是由没有真正仔细研究您所遵循的步骤的人编写的。 )

有几种不同的方法可以达到所需的状态。如果这是一个只有你使用的 repo,或者如果你可以与所有 repo 用户协调进行历史重写,那么“re-parenting”操作将是一个很好的解决方案。这是一个永久修复,将创建一个无缝的历史;但是,因为它会改变当前 repo 分支的历史,所以与任何其他用户的协调很重要。重写共享历史的问题一般在 git rebase 文档中关于“从上游 rebase 恢复”一节中描述。​​

另一种选择是使用git replace。这样做的好处是它不是历史重写,但它确实存在一些已知问题,并且需要在每个克隆中进行一些特殊设置。 (如果设置没有完成,这只是意味着特定的克隆没有看到完整的历史记录。)

这里有一篇文章讨论了实现这些方法的方法:Git: Copy history of file from one repository to another

还有其他变体,很难说哪种最适合您的情况。如果您想更广泛地探索这些可能性,您可以查阅 git filter-branchgit replace 的文档。

【讨论】:

  • 我询问了CD 之间的区别(使用您的标签),以便我可以判断所需的历史是A -&gt; B -&gt; C -&gt; D' -&gt; E' -&gt; F',如您所说,还是A -&gt; B -&gt; C -&gt; E' -&gt; F';如果CD 具有相同的历史记录,那么C = D' 不需要同时包含两者,所以rebase 命令会有所不同。我没有跟进,因为后来我意识到git rebase 可能不会保留历史的所有重要方面(例如,合并),我无法给出答案,但我仍然认为这个问题是相关的用于确定如何重写历史记录。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-14
  • 2022-12-16
  • 1970-01-01
  • 1970-01-01
  • 2016-01-11
  • 2012-10-14
相关资源
最近更新 更多