如何将多个 Git 存储库合并为一个并交错历史

Posted

技术标签:

【中文标题】如何将多个 Git 存储库合并为一个并交错历史【英文标题】:How to merge several Git repos into one and interleave histories 【发布时间】:2017-04-19 23:54:38 【问题描述】:

我的情况是我有两个 Git 存储库需要合并到一个存储库中(实际上还有更多存储库,但我可以从两个开始)。

这两个存储库是:

主存储库 A。 第二个存储库,B

存储库 B 中的代码依赖于存储库 A 中的代码(但 不是 反之亦然),以及两个存储库的历史记录按时间顺序彼此跟随 - 大致(即 repo B 中的特定提交通常需要来自 repo A 的提交,提交时间非常相似)。

两个存储库中存在冲突的分支和标签名称(不能保证它们属于一起),但只需要保留来自 A 的引用。

新存储库 C 的要求是:

    需要保留来自 A 的所有引用(分支和标签)。 仅需要保留来自 B 的主分支提交(即由git log --first-parent master 报告的提交)。 每个源存储库中的文件都应该放在新存储库的子文件夹中(即A中的文件应该进入A/,文件格式为B应进入B/)。 当在存储库 C(例如发布标签)中签出特定提交(包括在合并之前完成的提交)时,两个源存储库的兼容文件都应位于A/B/ 目录(至少在一两次提交中)。

到目前为止,我尝试了几种方法,包括this和git-stitch-repo,但均未成功(它们不满足上述要求)。

在这一点上,我已经设法:

使用 git filter-branch 将每个 repo 中的所有文件移动到子目录中。例如。对于回购 A: mkdir A mv * .gitignore A/ 2> /dev/null git commit -a -m 'DROPME' > /dev/null git filter-branch --tag-name-filter cat --index-filter 'git ls-files -s | sed "s-\t\"*-&A/-" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE" ||:' -- --全部 git reset --hard origin/master git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d 使用git fast-export/fast-import将repo B导入A。 设备一种用于生成映射的方法,以便对于 A 中的给定 SHA,有一个应从 B 插入的零个、一个或多个 SHA:s 的列表强>。

我现在期望的是,git filter-branch 的一些巧妙用法应该使我能够将选定的提交从 B 插入到 A 的主分支中。但是怎么做呢?

【问题讨论】:

【参考方案1】:

结果证明解决方案比我希望的要复杂得多。它涉及操作和组合两个(或更多)git fast-export 流的输出,并使用git fast-import 将它们导入新的存储库。

简而言之,一个新的fast-import 流是通过遍历两个输入流,并根据来自主分支的按日期排序的日志在它们之间来回切换而生成的。

我已在名为 join-git-repos.py 的 Python 脚本中实现了该解决方案,并将其放入 GitHub 存储库 here。

【讨论】:

这太棒了,感谢开源脚本! +1【参考方案2】:

首先,将 repo A 中的所有内容移动到子目录 A/。没什么特别的,只是git mv。这会保留 A 中的所有分支和标签以及提交 ID。

然后使用git subtree使B的主分支成为目录B/中A的子树。

git subtree add -P B/ <remote for B> master

你已经完成了。


如果您希望 A 上的旧版本标签也反映当时 B 中的内容...哦。通过在每个发布标签之前将 B 合并到 A 中,您可以做到这一点而不会严重破坏您的历史记录。

你有这个。

          * - * - *           * - * - * branch
    v1   /         \    v2   /
* - * - * - * - * - * - * - * - * - * master
                                   /
  * -- * ---- * - * - * --------- *

提交的底线是 B。B 的提交是布局的,因此它们与 A 的时间对齐。

你想要这样的东西。

          * - * - *           * - * - * branch
    v1   /         \    v2   /
* * * - * - * - * - * * * - * - * - * master
  |                   |            /
  * -- * ---- * - * - * --------- *

这已经在每个发布标签之前将 B 合并到 A 中。这样可以避免编造 A 和 B 一起开发的人为历史。

我不知道如何以自动化方式做到这一点。问题是rebase 不保留标签,只合并。因此,将合并提交添加到 v1 将丢失 v2 标记,我不确定如何识别 rebase 提交的原始提交是什么。

祝你好运。

【讨论】:

我会尝试的,但我看不出这个解决方案如何满足问题中的要求 4。还是我错过了什么? @m-bitsnbites 您希望来自 repo A 的旧版本标签来猜测 repo B 当时的内容是什么?那会搞砸你的历史真的很糟糕。我不知道这在逻辑上是否可行,同时还保留了 A 的分支历史。 @m-bitsnbites 我将生成的存储库结构的图示放在一起,这是 IMO 的最佳折衷方案,但我不知道如何以自动方式进行转换。

以上是关于如何将多个 Git 存储库合并为一个并交错历史的主要内容,如果未能解决你的问题,请参考以下文章

如何合并两个 Git 存储库?

合并两个 Git 存储库而不破坏文件历史记录

将多个 SVN 存储库合并为一个

将多个项目合并到一个解决方案中,同时保留 git 历史记录

如何将 2 台机器上的 2 个本地 git 存储库合并为一个?

将项目上传到github时如何解决“拒绝合并不相关的历史”?