合并同一项目的两个 git 个存储库,链接文件历史记录
Merging two git repositories of the same project, linking file history
我有一个很久以前就开始的项目,并做出了很多承诺。该项目随后被放弃了大约两年,在此期间我忘记了我一直在对该项目使用 git 版本控制。我拿起它,将所有文件复制到一台新机器上,并启动了一个新的 git 存储库,其中包含约 100,000 行代码和数十个文件,现在它有自己冗长的提交历史记录。我最近重新发现了旧的仓库,并试图将两个仓库的提交历史合并在一起,
然而,结果并不完整。如果我查看 github 上的提交历史记录,来自旧存储库和新存储库的提交是完整的,但是每个单独的文件历史记录不会扩展回旧存储库的一系列提交,仍然显示它们只是在在创建新存储库时提交。当我手动复制所有内容以启动新 repo 时,一些未传输的文件根本没有显示。
项目的文件结构和命名约定自旧存储库历史结束以来发生了显着变化,一些文件关联可能不明显。如果我必须一次手动 link 旧的和新的,我可以做到,但自动解决方案会更好。
我假设您按照从最上面的答案到您链接的问题的步骤进行操作。这些不是针对这种情况的最佳步骤。
您的项目有两个历史片段。如果我们假设第一段有提交
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
不是 然后被认为是 D
历史的一部分。事实上,git 的默认历史过滤规则(例如用于日志输出)将完全排除 A
、B
和 C
,因为来自 git'从s的角度来看,没有它们就可以解释代码的状态。
(目前关于您的问题的大多数评论都是转移注意力的问题,例如相似性启发式。在我看来,这些评论是由没有真正仔细研究您所采取的步骤的人撰写的紧随其后。)
有几种不同的方法可以达到所需的状态。如果这是一个只有你自己使用的 repo,或者如果你可以协调所有 repo 用户进行历史重写,那么 "re-parenting" 操作将是一个很好的解决方案。这是一个永久性的修复,将创造一个无缝的历史;但是,因为它会改变当前 repo 分支的历史,所以与任何其他用户的协调很重要。 "recovering from upstream rebase".
部分的 git rebase
文档中一般描述了重写共享历史记录的问题
另一种选择是使用 git replace
。这样做的好处是它不是历史重写,但它确实有一些已知问题,并且需要在每个克隆中进行一些特殊设置。 (如果设置没有完成,这只是意味着特定的克隆没有看到完整的历史。)
这里有一个 post 讨论了执行这些操作的方法:
还有其他变体,很难说哪种最适合您的情况。如果您想更广泛地探索可能性,您可以查阅 git filter-branch
和 git replace
.
的文档
我有一个很久以前就开始的项目,并做出了很多承诺。该项目随后被放弃了大约两年,在此期间我忘记了我一直在对该项目使用 git 版本控制。我拿起它,将所有文件复制到一台新机器上,并启动了一个新的 git 存储库,其中包含约 100,000 行代码和数十个文件,现在它有自己冗长的提交历史记录。我最近重新发现了旧的仓库,并试图将两个仓库的提交历史合并在一起,
然而,结果并不完整。如果我查看 github 上的提交历史记录,来自旧存储库和新存储库的提交是完整的,但是每个单独的文件历史记录不会扩展回旧存储库的一系列提交,仍然显示它们只是在在创建新存储库时提交。当我手动复制所有内容以启动新 repo 时,一些未传输的文件根本没有显示。
项目的文件结构和命名约定自旧存储库历史结束以来发生了显着变化,一些文件关联可能不明显。如果我必须一次手动 link 旧的和新的,我可以做到,但自动解决方案会更好。
我假设您按照从最上面的答案到您链接的问题的步骤进行操作。这些不是针对这种情况的最佳步骤。
您的项目有两个历史片段。如果我们假设第一段有提交
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
不是 然后被认为是 D
历史的一部分。事实上,git 的默认历史过滤规则(例如用于日志输出)将完全排除 A
、B
和 C
,因为来自 git'从s的角度来看,没有它们就可以解释代码的状态。
(目前关于您的问题的大多数评论都是转移注意力的问题,例如相似性启发式。在我看来,这些评论是由没有真正仔细研究您所采取的步骤的人撰写的紧随其后。)
有几种不同的方法可以达到所需的状态。如果这是一个只有你自己使用的 repo,或者如果你可以协调所有 repo 用户进行历史重写,那么 "re-parenting" 操作将是一个很好的解决方案。这是一个永久性的修复,将创造一个无缝的历史;但是,因为它会改变当前 repo 分支的历史,所以与任何其他用户的协调很重要。 "recovering from upstream rebase".
部分的git rebase
文档中一般描述了重写共享历史记录的问题
另一种选择是使用 git replace
。这样做的好处是它不是历史重写,但它确实有一些已知问题,并且需要在每个克隆中进行一些特殊设置。 (如果设置没有完成,这只是意味着特定的克隆没有看到完整的历史。)
这里有一个 post 讨论了执行这些操作的方法:
还有其他变体,很难说哪种最适合您的情况。如果您想更广泛地探索可能性,您可以查阅 git filter-branch
和 git replace
.