多仓库合并至 Mono-Repo 的实践指南
多仓库合并至 Mono-Repo 的实践指南
背景
在长期维护项目的过程中,我的代码资产分散在多个独立的 GitHub 仓库中,例如:
repo-a:项目 A(如前端组件库)repo-b:项目 B(如后端服务)repo-c:项目 C(如工具函数库)repo-d:项目 D(如文档站点)
这种分散的仓库管理方式逐渐暴露出了以下问题:
- 维护成本高:同类逻辑在不同仓库中需要重复修改,难以同步。
- 历史查阅困难:查看完整的项目变更历史需要在多个仓库间切换。
- CI/CD 流程割裂:构建或发布时,需要拉取多个仓库,增加了流水线的复杂度。
为了解决这些痛点,我决定将这些分散的仓库 合并为一个 Mono-Repo(单体仓库),并确保 完整保留每个仓库原有的提交历史。
方案选择
在 Git 中,合并多个仓库主要有以下几种方式:
- 直接复制文件:最简单的方式,但会完全丢失原有的提交历史(无法追溯是谁、在何时、修改了什么)。
- Git Submodule:保留了仓库的独立性,但父子仓库分离,Clone 和 CI 流程繁琐,体验较差。
- Git Subtree:完美保留历史,同时将子仓库的代码物理合并到主仓库的指定目录下,管理起来更像是一个完整的仓库。
综合考虑,我选择了 Git Subtree 方案,因为它:
- ✅ 支持完整保留每个子仓库的 commit 历史。
- ✅ 代码直接存在于主仓库中,不依赖外部链接,拉取方便。
- ✅ 支持双向同步(既可以获取子仓库更新,也可以推送修改回子仓库)。
实战步骤
1. 准备主仓库
首先,在 GitHub 创建一个新的空仓库(例如 my-mono-repo),然后将其克隆到本地并初始化:
# 克隆主仓库
git clone git@github.com:username/my-mono-repo.git
cd my-mono-repo
# 创建并切换到主分支
git checkout -b main
# 提交一个空的初始化 commit(必须步骤)
# 注意:Subtree 操作需要仓库中至少有一个 commit 才能进行
git commit --allow-empty -m "chore: initial commit for mono-repo"
2. 合并子仓库
使用 git subtree 将原有的仓库逐个合并进来。
以合并 repo-a 仓库为例:
# 1. 添加原仓库为远程地址(别名设为 repo-a-remote)
git remote add repo-a-remote git@github.com:username/repo-a.git
# 2. 拉取远程仓库数据
git fetch repo-a-remote
# 3. 使用 subtree 将其合并到指定的子目录(例如 packages/project-a)
# --prefix 指定目录路径
# repo-a-remote/main 指定远程仓库的分支
git subtree add --prefix=packages/project-a repo-a-remote main
重复上述步骤合并其他仓库:
# 合并第二个仓库
git remote add repo-b-remote git@github.com:username/repo-b.git
git fetch repo-b-remote
git subtree add --prefix=packages/project-b repo-b-remote main
# 合并第三个仓库
git remote add repo-c-remote git@github.com:username/repo-c.git
git fetch repo-c-remote
git subtree add --prefix=packages/project-c repo-c-remote main
# 合并第四个仓库
git remote add repo-d-remote git@github.com:username/repo-d.git
git fetch repo-d-remote
git subtree add --prefix=packages/project-d repo-d-remote main
注意:如果原仓库的主分支名为
master而非main,请将命令中的main替换为master。
3. 验证历史
合并完成后,我们可以检查一下提交历史,确认是否包含了原子仓库的记录:
# 查看提交日志
git log --graph --oneline --all
或者直接查看具体文件的历史,你会发现原有的 commit 信息、作者和时间都完整保留了下来。
4. 推送到远程仓库
确认无误后,将合并后的完整仓库推送到 GitHub:
git push -u origin main
至此,一个新的 Mono-Repo 就建立完成了。
成果展示
最终的仓库目录结构如下所示:
my-mono-repo/
├── README.md
├── packages/
│ ├── project-a/ <-- 原 repo-a 仓库内容
│ ├── project-b/ <-- 原 repo-b 仓库内容
│ ├── project-c/ <-- 原 repo-c 仓库内容
│ └── project-d/ <-- 原 repo-d 仓库内容
└── scripts/ <-- 统一管理的构建脚本
Mono-Repo 的优势:
- 统一视图:所有相关代码在一个仓库中,便于全局搜索和重构。
- 简化流程:统一管理 README、依赖和构建脚本,Release 发布更简单。
- 灵活独立:原子仓库可以选择保留、归档甚至删除(如果确定不再独立维护),而 Mono-Repo 已经拥有了完整数据的副本。
总结
通过这次重构,我深刻体会到了 Git Subtree 在处理仓库合并时的强大能力。它不仅解决了代码分散管理的问题,还完美地解决了“保留历史”这一核心诉求。
现在,我们可以更专注于代码本身的编写,而不必在多个仓库间疲于奔命。
Tip:在执行此类操作前,强烈建议先备份所有原子仓库,或在本地进行充分演练,防止误操作导致数据丢失。
💬 评论 0
还没有评论,来发表第一条评论吧~
加载评论中...