这篇讲团队从 GitLab 整体切到 AngusGit 时,怎么保证切换那天不丢代码、出了问题能退回去。看完你可以按准备、首次同步、双跑、切换日、回滚五个阶段逐项验收,并给 GitLab 留两周只归档、不删除的回滚窗口。
先登录 AngusGit。GitLab 路径 platform/backend/order-service 只有两级可放:拿不准,或不同子组下有同名项目时,把子组并进仓库名,写成 platform/backend-order-service。仓库名里的点号改成连字符,例如 docs.site 改成 docs-site。在 设置 → 团队 点 新建团队,团队标识 沿用 GitLab 组的英文名。侧栏 仓库 → 新建仓库,添加 README 文件、添加 .gitignore、选择许可证 都不要勾,否则推送会被拒绝。
到 我的 → 访问令牌 点 生成新令牌,密码验证 填当前登录密码。同步脚本用一把勾了 git:write、仓库权限 WRITE 的令牌。Jenkins 只读拉代码另生成一把,只勾 git:read、仓库权限 READ。用户名都是 AngusGit 登录名,密码填令牌,生成后马上复制。先手动成功推一个仓库,在系统凭据窗口里保存,再跑脚本。
把对应关系写成 repos.txt,每行两列,先 GitLab 路径,再「所有者/仓库名」:
platform/backend/order-service platform/backend-order-service
然后执行(域名换成你的):
while read -r src dst; do
git clone --bare "https://gitlab.example.com/${src}.git" tmp.git || continue
git -C tmp.git branch -m master main 2>/dev/null
git -C tmp.git push "https://git.anguskit.com/${dst}.git" --all
git -C tmp.git push "https://git.anguskit.com/${dst}.git" --tags
rm -rf tmp.git
done < repos.txt
每个仓库用下面的命令对比,没有意外输出才算一致。master 改成 main 时,这一对名字不同是预期的:
diff \
<(git ls-remote --heads --tags https://gitlab.example.com/platform/backend/order-service.git | sort) \
<(git ls-remote --heads --tags https://git.anguskit.com/platform/backend-order-service.git | sort)
分支保护要等切换日最后一次同步之后再开。进入仓库 设置 → 分支保护,点 添加规则,分支名称 选盘点表里的分支(通常是 main)。打开 需要代码评审,必需的评审人数 填 GitLab 上记下的人数;打开 对管理员同样生效;允许强制推送 和 允许删除 保持关闭。同步期间不要开,否则脚本推 main 会被拒绝。
1. 这一轮只迁仓库
GitLab 上不只有代码,还有合并请求、议题、CI 流水线和各种集成。一次全部切走,任何一块出问题都会拖住整个迁移。建议分期:
| 期次 | 内容 | 说明 |
|---|---|---|
| 本轮 | Git 仓库、成员权限、分支保护、Webhook | 切换完成后,团队在 AngusGit 上提交、评审和合并 |
| 下一轮 | CI | 本轮先让现有构建能被 AngusGit 触发,下一轮再决定是否改写流水线 |
| 另行安排 | 议题、Wiki | 这些在 GitLab 的数据库里,不在 Git 仓库中,推送带不过来 |
CI 虽然放到下一轮,切换日之前也必须有着落。GitLab 不再收到推送之后,GitLab CI 也就不会再触发。本轮最稳妥的过渡做法,是让现有的 Jenkins 任务改从 AngusGit 拉代码,并用 Jenkins 的 轮询 SCM(Poll SCM)定时检查 main 有没有新提交,例如每两分钟一次。不管代码是命令行推送进来的,还是在拉取请求页点合并进来的,轮询都能发现。Jenkins 拉代码用开头那把只读令牌:只勾 git:read,仓库权限 READ,用户名填登录名,密码填令牌。
2. 准备:先盘点
迁移前把下面每一项都查一遍,结果写进一张表。切换日的每一步都要用到它。
| 盘点项 | 在 GitLab 上怎么查 | 记下什么 |
|---|---|---|
| 仓库清单 | 各群组的项目列表 | 路径、可见性(谁能看到仓库)、默认分支名、大致大小,以及在 AngusGit 上对应的「团队/仓库名」 |
| 成员和角色 | 群组和项目的 成员 页 | 每个人在每个仓库要读还是要写,谁是维护者 |
| 受保护分支 | 项目 设置 → 仓库 → 受保护分支 | 哪些分支禁止直接推送、合并前要几个人批准 |
| CI | 项目里的 .gitlab-ci.yml 和 Runner |
哪些仓库有构建;切换后用什么触发 |
| Webhook 和集成 | 项目 设置 → Webhooks | 每个 Webhook 的地址和用途,切换后要在 AngusGit 上重建 |
| 写死 Git 地址的地方 | 在代码里搜索 GitLab 域名 | .gitmodules、CI 和部署脚本、依赖声明里的 Git 地址、文档里的克隆命令 |
| 进行中的合并请求 | 每个项目的 合并请求 列表 | 切换日之前要合并或关闭,评审记录带不过来 |
| Git LFS | 项目里有没有 .gitattributes 中的 filter=lfs |
AngusGit 不支持 LFS,用到的仓库要先处理大文件 |
盘点表里「写死 Git 地址的地方」最容易漏。部署脚本、子模块、依赖声明里还写着 GitLab 地址,切换之后不会马上报错,要等 GitLab 下线那天才一起出问题。
3. 首次同步
在 AngusGit 建团队和空仓库
按盘点表的对应关系,建好团队和空仓库。新建仓库时三个初始化选项都不勾。
成员先只给读权限
把成员加进团队或仓库,这个阶段一律只给读取权限。双跑期间,AngusGit 上的代码只能从 GitLab 同步过来,不能有人直接往 AngusGit 推送;只给读权限,就从权限上保证了这一点。
分支保护先不开
同步脚本会直接把分支推到 AngusGit,包括
main。这时如果已经给main开了分支保护,同步推送会被拒绝。分支保护规则可以先整理好,等到切换日最后一次同步之后再建。跑一次同步脚本,然后验收
执行同步脚本,把
repos.txt里的仓库全部推过去。每个仓库都用开头的git ls-remote --heads --tags对比 GitLab 和 AngusGit,没有意外输出才算同步成功。master改成main时,这一对名字不同是预期的。
4. 双跑:GitLab 写,AngusGit 读
双跑期间,团队照常在 GitLab 上提交、评审、合并。AngusGit 每天从 GitLab 同步一次,让团队提前熟悉新地址和新界面。
双跑只有一条规则:任何时候只有一边能写。两边都能写,代码就会分叉。分叉之后,同步推送会被拒绝,最后只能手工合并,这正是迁移最怕的情况。
这段时间可以做这些事:
- 用定时任务每天执行一次同步脚本,每次跑完都用
git ls-remote对比一遍。 - 请几位成员从 AngusGit clone 仓库,确认 SSH 或 HTTPS 访问都没问题,访问令牌都已备好。
- 在一个测试仓库里,让大家把拉取请求、评审、合并完整走一遍。
- 新建一个测试用的 Jenkins 任务,从 AngusGit 拉代码、打开轮询 SCM,确认它能发现同步过来的新提交并开始构建。
5. 切换日
切换日按下面的顺序做,每一步做完再做下一步。
提前通知冻结时间
至少提前一天通知全员:几点开始停止向 GitLab 推送,几点之后改用 AngusGit。进行中的合并请求,要在冻结之前合并或关闭。
冻结 GitLab
到点后,把每个项目在 GitLab 上归档(项目 设置 → 通用 → 高级 → 归档项目)。归档后项目变成只读,任何人都推不上去,代码和合并请求记录都还在。
最后一次同步并对比
执行同步脚本,然后对每个仓库跑一遍
git ls-remote对比。全部一致才往下走;有不一致的,先查清楚是哪个分支、为什么。打开 AngusGit 的写权限和分支保护
按盘点表把成员的权限从只读改成读写,再在每个仓库的 设置 → 分支保护 里按开头添加规则:打开 需要代码评审 并填上盘点的人数,打开 对管理员同样生效,允许强制推送 和 允许删除 保持关闭。
切换 CI 和集成
把 Jenkins 任务的源码地址改成 AngusGit,打开轮询 SCM,再推一个测试提交,确认构建被触发。盘点表里的其他 Webhook,在 AngusGit 仓库的 设置 → Webhooks 里重建。
通知全员改地址
每个人在自己的本地仓库里执行一次
git remote set-url origin <AngusGit 地址>,然后git fetch确认能拉取。双跑期间直接从 AngusGit clone 的人,不需要改。
切换日结束前,还要按盘点表把写死 GitLab 地址的地方,逐个改成 AngusGit 地址并提交。
6. 回滚
GitLab 上的项目只归档,不删除。从切换日起留一个回滚窗口,例如两周。窗口内如果遇到无法马上解决的问题,例如权限大面积出错、构建全部断掉,可以退回 GitLab。
回滚时,AngusGit 上已经有了切换后的新提交,要先把它们带回 GitLab:
冻结 AngusGit
通知全员停止向 AngusGit 推送,把成员权限改回只读。
取消 GitLab 归档
GitLab 上归档的项目拒绝推送,先在项目 设置 → 通用 → 高级 里取消归档。
把新提交推回 GitLab
迁移时把
master改成了main的,先把它改回master;GitLab 在冻结之后没有新提交,这次推送是普通的追加,不需要强制推送。对比后通知全员改回地址
用
git ls-remote对比两边一致后,通知全员把origin改回 GitLab 地址,CI 也切回 GitLab。
回滚时用 git clone --bare 得到一份只存 Git 数据、没有可编辑工作区的副本,再推回 GitLab:
git clone --bare https://git.anguskit.com/platform/backend-order-service.git
cd backend-order-service.git
git branch -m main master
git push https://gitlab.example.com/platform/backend/order-service.git --all
git push https://gitlab.example.com/platform/backend/order-service.git --tags
回滚窗口结束、确认不再需要 GitLab 之后,再按公司的数据保留要求处理 GitLab 上的项目。
7. 出错时
| 你看到的 | 原因 | 怎么处理 |
|---|---|---|
双跑期间同步推送 main 被拒绝 |
AngusGit 上已经给 main 开了分支保护 |
先删掉这条保护规则,等切换日最后一次同步之后再建 |
同步推送 ! [rejected] … (non-fast-forward) |
有人在双跑期间直接往 AngusGit 推了代码,两边分叉 | 找到这条提交,确认是否需要保留。要保留的,在 GitLab 上重新提交一次;然后把 AngusGit 上的分支重置成 GitLab 的版本。同时检查成员权限是否真的只读 |
| 切换日对比有差异 | 冻结之前有推送没赶上同步,或者同步中途断开 | 再执行一次同步脚本,直到对比一致 |
| 切换后部分构建不再触发 | Jenkins 任务的源码地址还是 GitLab,或者没打开轮询 SCM;构建脚本里还写着 GitLab 地址 | 对照盘点表逐个改 Jenkins 任务和脚本里的地址 |
切换后有人推送报 repository '…' not found |
本地 origin 改错了,团队名或仓库名和对应规则不一致 |
从 AngusGit 仓库页的 代码 按钮复制地址,重新 git remote set-url |
常见问题
能不能让 GitLab 和 AngusGit 一直双向同步,慢慢切?
不建议。两边都能写,就一定会在某个时候分叉。分叉后由谁来合并、以哪边为准,都很难说清。双跑期间只保留一个写入方,切换就是一个明确的时间点。
几十个仓库要一天切完吗?
不必。可以按团队或业务线分批,每批走一遍第 5 节。但同一批里互相依赖的仓库,例如主仓库和它的子模块,要放在同一天切。
合并请求的讨论记录怎么办?
带不过来。重要的设计讨论,在 GitLab 归档前导出或整理成文档,放进仓库的 docs 目录;进行中的合并请求,冻结前合并或关闭。
切换后,CI 什么时候迁?
本轮先让 Jenkins 改从 AngusGit 拉代码,现有构建继续工作,等团队适应了新的仓库和评审流程,再单独安排一轮评估。AngusGit 团队版和企业版自带流水线,新建流水线时可以选 导入 GitLab CI,从 .gitlab-ci.yml 生成初稿。社区版没有 CI/CD 菜单。
小结
从 GitLab 迁到 AngusGit,本轮只迁仓库,CI 和议题另排。先盘点仓库、成员、受保护分支、CI、Webhook 和写死 Git 地址的地方。首次同步之后进入双跑:GitLab 是唯一能写的一边,AngusGit 只读、每日同步。切换日按顺序做:冻结 GitLab,最后同步,对比一致,然后打开写权限和分支保护,切换 CI,通知全员改地址。GitLab 项目只归档不删除,回滚窗口内需要退回时,把 AngusGit 的新提交推回 GitLab,再取消归档。
结论: 批量迁移期间必须保持「单边可写」:双跑期只写 GitLab、每日单向同步到只读的 AngusGit;切换日先归档冻结 GitLab 并用
git ls-remote对齐全部引用后,再开启 AngusGit 的写权限与分支保护。