如何将多个 Git 回购合并为一个并交错历史记录
How to merge several Git repos into one and interleave histories
我的情况是我有两个 Git 个存储库,我需要将它们合并到一个存储库中(实际上还有更多的存储库,但我可以从两个开始)。
两个存储库是:
- 主存储库,A。
- 第二个仓库,B.
存储库 B 中的代码依赖于存储库 A 中的代码(但 不 反之亦然),并且两个存储库的历史按时间顺序相互跟随 - 大致(即回购 B 中的特定提交通常需要来自回购 的提交A 具有非常相似的提交时间)。
两个存储库中存在冲突的分支和标签名称(不能保证它们属于一起),但只需要保留来自 A 的引用。
新存储库 C 的要求是:
- 来自 A 的所有引用(分支和标签)都需要保留。
- 只有 B 的主分支提交需要保留(即
git log --first-parent master
报告的提交)。
- 每个源存储库中的文件应放入新存储库的子文件夹中(即来自 A 的文件应放入
A/
,文件格式为 B 应进入 B/
).
- 在存储库 C(例如发布标签)兼容文件中签出特定提交(包括在合并之前完成的提交)从两个源存储库应该在目录
A/
和 B/
中找到(至少在一两次提交中)。
到目前为止,我尝试了几种方法,包括this and git-stitch-repo,但都没有成功(它们没有满足上述要求)。
在这一点上,我已经设法:
- 使用 git filter-branch 将每个 repo 中的所有文件移动到子目录。例如。对于回购 A:
目录 A
mv * .git忽略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" ||:' -- --all
git 重置 --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,应该插入一个包含零个、一个或多个 SHA:s 的列表来自 B.
我现在期望的是 git filter-branch 的一些巧妙用法应该使我能够将从 B 中选择的提交插入到 [=37 的主分支中=]A。但是怎么办?
首先,将repo A 中的所有内容移动到子目录A/。没什么特别的,只是 git mv
。这将保留 A.
中的所有分支和标签以及提交 ID
然后使用git subtree
使B的master分支成为目录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 标记,我不确定如何识别变基提交的原始提交是什么。
祝你好运。
事实证明,解决方案比我希望的要复杂得多。它涉及操纵和组合两个(或更多)git fast-export
流的输出,并使用 git fast-import
.
将它们导入到新的存储库中
简而言之,通过遍历两个输入流,并根据来自主要分支的按日期排序的日志在它们之间来回切换,生成一个新的 fast-import
流。
我已经在名为 join-git-repos.py
的 Python 脚本中实现了解决方案,我将其放入 GitHub 存储库 here。
我的情况是我有两个 Git 个存储库,我需要将它们合并到一个存储库中(实际上还有更多的存储库,但我可以从两个开始)。
两个存储库是:
- 主存储库,A。
- 第二个仓库,B.
存储库 B 中的代码依赖于存储库 A 中的代码(但 不 反之亦然),并且两个存储库的历史按时间顺序相互跟随 - 大致(即回购 B 中的特定提交通常需要来自回购 的提交A 具有非常相似的提交时间)。
两个存储库中存在冲突的分支和标签名称(不能保证它们属于一起),但只需要保留来自 A 的引用。
新存储库 C 的要求是:
- 来自 A 的所有引用(分支和标签)都需要保留。
- 只有 B 的主分支提交需要保留(即
git log --first-parent master
报告的提交)。 - 每个源存储库中的文件应放入新存储库的子文件夹中(即来自 A 的文件应放入
A/
,文件格式为 B 应进入B/
). - 在存储库 C(例如发布标签)兼容文件中签出特定提交(包括在合并之前完成的提交)从两个源存储库应该在目录
A/
和B/
中找到(至少在一两次提交中)。
到目前为止,我尝试了几种方法,包括this and git-stitch-repo,但都没有成功(它们没有满足上述要求)。
在这一点上,我已经设法:
- 使用 git filter-branch 将每个 repo 中的所有文件移动到子目录。例如。对于回购 A:
目录 A mv * .git忽略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" ||:' -- --all git 重置 --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,应该插入一个包含零个、一个或多个 SHA:s 的列表来自 B.
我现在期望的是 git filter-branch 的一些巧妙用法应该使我能够将从 B 中选择的提交插入到 [=37 的主分支中=]A。但是怎么办?
首先,将repo A 中的所有内容移动到子目录A/。没什么特别的,只是 git mv
。这将保留 A.
然后使用git subtree
使B的master分支成为目录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 标记,我不确定如何识别变基提交的原始提交是什么。
祝你好运。
事实证明,解决方案比我希望的要复杂得多。它涉及操纵和组合两个(或更多)git fast-export
流的输出,并使用 git fast-import
.
简而言之,通过遍历两个输入流,并根据来自主要分支的按日期排序的日志在它们之间来回切换,生成一个新的 fast-import
流。
我已经在名为 join-git-repos.py
的 Python 脚本中实现了解决方案,我将其放入 GitHub 存储库 here。