WEBKT

不改一行代码:10个独立仓库平滑迁移至 Monorepo 的工程化指南

3 0 0 0

在互联网工程实践中,当业务线扩张到一定规模,维护 10 个甚至更多独立的 Git 仓库往往会变成一场灾难:跨仓库的代码复用难、版本依赖冲突严重、CI/CD 配置碎片化。

很多团队想转向 Monorepo(单体仓库) 架构,但最担心的就是:迁移是否需要大规模重构?原来的 Commit 提交记录会不会丢失?

答案是:完全可以做到零重构、全历史保留迁移。 本文将为你拆解一套基于 Git Subtree 和 pnpm Workspaces 的平滑迁移方案。

一、 核心思路:为什么要选 Git Subtree?

在合并仓库时,传统的 cp -rf 方案会丢失所有的 Git 提交历史,这对于后续的代码追溯(git blame)是致命的。

我们选择 Git Subtree,因为它:

  1. 保留历史:能够将被迁移仓库的所有分支历史完整合并进新仓库。
  2. 操作简单:不需要像 git filter-branch 那样进行复杂的重写。
  3. 物理隔离:在 Monorepo 中以子目录形式存在,不影响原代码逻辑。

二、 实战步骤:手把手带你迁移

假设你现在有 10 个仓库,我们以其中一个名为 service-auth 的仓库为例,演示如何将其迁入新的 monorepo-main 仓库。

1. 初始化 Monorepo 基座

首先,创建一个新的空仓库作为“基座”:

mkdir monorepo-main
cd monorepo-main
git init
touch .gitignore
git add .
git commit -m "chore: initial monorepo"

2. 添加远程源并执行 Subtree 迁移

不需要克隆子仓库,直接在基座仓库执行:

# 添加子仓库作为远程源
git remote add origin-auth https://github.com/your-team/service-auth.git

# 使用 subtree add 命令,将 master 分支内容抓取到 packages/auth 目录下
# --prefix 指定存放目录,--squash(可选)决定是否压缩历史,建议不压缩以保留完整记录
git subtree add --prefix=packages/auth origin-auth master

执行完毕后,你会发现 packages/auth 目录下出现了该仓库的所有代码,且运行 git log 可以看到原仓库的所有提交记录。

3. 重复操作完成 10 个仓库

对剩余 9 个仓库重复上述步骤,修改 --prefix 路径即可。


三、 依赖管理:实现“不重构”的关键

迁移后,如果 10 个项目各自为政,Monorepo 就失去了意义。为了在不重构代码的前提下实现依赖共享,我们推荐使用 pnpm

1. 配置 Workspace

在根目录下创建 pnpm-workspace.yaml

packages:
  - 'packages/*'

2. 处理依赖冲突

运行 pnpm install。由于 pnpm 使用基于内容寻址的存储机制,即使 10 个项目依赖了不同版本的 React 或 Lodash,它们也能在各自的 node_modules 中和谐共处。

  • 技巧:如果某些公共依赖版本一致,可以手动将其提升到根目录的 package.json 中,进一步减少磁盘占用。

四、 工作流调整:迁移后的“第一天”

代码搬完家后,你需要调整以下三点以确保团队无缝切换:

1. 统一构建指令

在根目录 package.json 中配置脚本,利用递归指令一键运行所有子项目:

{
  "scripts": {
    "dev": "pnpm -r --parallel dev",
    "build": "pnpm -r build"
  }
}

2. CI/CD 增量构建

这是迁移 Monorepo 后最怕的问题:改一个项目,10 个项目全部重新构建。
解决方案:引入 Turborepo
在根目录添加 turbo.json,它会自动分析代码变更,只有发生改动的目录才会触发构建。

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    }
  }
}

3. 权限与分支保护

虽然代码在一起了,但你依然可以通过 GitHub/GitLab 的 CODEOWNERS 文件,规定只有特定成员能审批 packages/auth 下的 Merge Request,维持原有的管理边界。


五、 避坑指南:迁移中的 3 个细节

  1. 绝对路径引用:检查原代码中是否有硬编码的绝对路径(如 /home/user/project/...)。迁移到子目录后,这些路径可能失效。建议改为相对路径或环境变量。
  2. Git Hooks:原仓库的 .huskypre-commit 钩子需要迁移到根目录下统一管理。
  3. 大文件清理:如果原仓库中包含大量的二进制文件,建议在迁移前用 git filter-repo 清理一下,否则合并后的 Monorepo 体积会剧增。

结语

通过 Git Subtree,我们实现了物理位置的搬迁;通过 pnpm Workspaces,我们实现了依赖环境的并轨。这套方案最大的优势在于:它不改变任何业务逻辑,开发者甚至察觉不到代码路径的变化,就能享受到 Monorepo 带来的工程化红利。

现在,你可以放心地把那 10 个令人头疼的仓库合而为一了。

码农架构师 GitMonorepo前端工程化

评论点评