合并多个仓库并保留历史提交记录

2023-06-08, 星期四, 14:47

培训Dev

与 GitHub / Gogs 等代码托管平台不同,Coding 使用项目的概念来组织互相关联但不必交织在一起的代码仓库,例如像这个配合某平台使用的工具集代码库,就包含了 3 个数据同步服务以及 2 个工具的源代码仓库。

在迁移到 Gogs 时,我们可以选择建立 5 个新的代码仓库,在本地副本中 git remote set-url origin <new-git-repo-url> && git push -u origin master。这样迁移副作用最小,不过对于一个处于归档状态的项目来说,即使用了前缀命名等方法,在一堆仓库中找到需要的那几个也似乎过于繁琐了。

也可以新建一个单一的代码仓库,把原仓库的资料按文件夹粘贴进去,不过这样提交历史就会全部丢失,所有代码的提交人就会变成最终 git push 的那个用户,这对代码考古学来说起码是个 K 级灾难了。(引入 submodule 也无助于解决代码仓库过多的问题。)

本文提供的方法至少可以保留原始仓库中主干分支的提交记录,以提到的这个项目为例,最终的代码仓库将调整为如下结构:

$ tree -L 1 .
.
├── README.md
├── bentusi
├── ioc-sync-api
├── ioc-sync-web
├── token-dispatcher
└── token-requester

首先拉取服务 1 ioc-sync-web 的代码:

git clone git@e.coding.net:ddrpa/ioc-sync-web.git
cd ioc-sync-web

将第二个服务 ioc-sync-api 的代码仓库地址添加为一个 remote 源,-f 参数表示在添加远程端点后立即 fetch。我这里直接使用代码仓库的名字 ioc-sync-api 作为源的名字,因此可以通过 ioc-sync-api/master 引用其主干分支。

git remote add -f ioc-sync-api git@e.coding.net:ddrpa/ioc-sync-api.git

merge 服务 2 ioc-sync-api 的代码,注意添加的参数:

git merge --allow-unrelated-histories --strategy ours --no-commit ioc-sync-api/master

创建单独的服务 2 目录 ioc-sync-api/,把服务 2 相关的代码移入,提交上述变更:

mkdir ioc-sync-api
git read-tree --prefix=ioc-sync-api/ -u ioc-sync-api/master
git add --all && git commit -m "ioc-sync-api-migrated"

现在这个目录的结构类似服务 1 的仓库中增加了一个服务 2 目录。如法炮制,处理剩下 3 个代码仓库,你甚至可以写一个 shell function 来免去输入这些字符的麻烦。

git remote add -f bentusi git@e.coding.net:ddrpa/bentusi.git
git merge --allow-unrelated-histories --strategy ours --no-commit bentusi/master
mkdir bentusi
git read-tree --prefix=bentusi/ -u bentusi/master
git add --all && git commit -m "bentusi-migrated"
...

最后创建服务 1 的文件夹 ioc-sync-web/,把服务 1 的资源文件移入(排除 .git.gitignore)。

mkdir ioc-sync-web
mv src ioc-sync-web/
...

合并各个子项目下的 .gitignore 配置,有相对路径的也要处理:

cat bentusi/.gitignore >> .gitignore
...

编写整个项目的 README.md 文件,最后变更项目的 origin 地址为新仓库的地址,提交代码。

在 Gogs 上可以追踪之前提交的 commit: