这篇讲内网 GitLab 上的公开、内部和私有项目,分别怎么搬到 AngusGit。看完你可以按可见性(谁能看到仓库)选择导入或推送,把多层子组定成「团队/仓库名」,再用 git clone --bare 加 git push --all 和 --tags 推进空仓库,并用 git ls-remote 对比两边是否一致。
先登录 AngusGit。公开项目在侧栏 仓库 点 导入仓库。仓库 URL 填项目页 克隆 里的 HTTPS 地址,不要填 SSH。所有者 选团队,仓库名称 按下一节的路径规则填,名称只能用中文、字母、数字、连字符和下划线。只给内部看时打开 设为私有仓库。这三项填完,开始导入 才能点。
推送前在 我的 → 访问令牌 点 生成新令牌,密码验证 填登录密码,勾 git:write,仓库权限 WRITE,生成后马上复制。用户名填登录名,密码填令牌。先手动成功推一个仓库,在系统凭据窗口里保存,后面的批量脚本才不会每行都问密码。改用 SSH 时,没有 ~/.ssh/id_ed25519.pub 就执行 ssh-keygen -t ed25519 -C "you@example.com",把 cat ~/.ssh/id_ed25519.pub 的那一行登记到 我的 → SSH密钥。用户名是 git,端口是 2222。
下文的 GitLab 地址用 https://gitlab.example.com/platform/backend/order-service.git 举例:platform 是群组,backend 是子组,order-service 是项目,换成你自己的路径即可。公开仓库导入长什么样,看已经导入的 xlliu/hello-world-import:只有 main 和一条提交。整个团队怎么双跑和回滚,见文末的 GitLab 迁移清单一文。
1. 先看 GitLab 项目的可见性
项目页名称旁边、或 设置 → 通用 → 可见性 里,三种的差别如下:
| GitLab 可见性 | 不登录能读吗 | 在 AngusGit 上用哪条路 |
|---|---|---|
| 公开 | 能 | 只要当前代码:导入仓库。要保留历史:推送 |
| 内部 | 不能,要登录 GitLab | 推送 |
| 私有 | 不能,要是项目成员 | 推送 |
判断方法很简单:打开浏览器的无痕窗口,不登录访问项目页,能看到代码才能用导入。
2. 公开项目:直接导入
侧栏点 仓库 → 右上角 导入仓库,仓库 URL 填 GitLab 项目页 克隆 里的 HTTPS 地址,不要填 SSH 地址,因为 AngusGit 服务器上没有你的 SSH 密钥。同一张表单导入公开仓库的结果,见 GitHub 上的 Hello-World 导入成的 xlliu/hello-world-import。
真正去拉代码的是 AngusGit 服务器,它要能访问那台 GitLab。导入只带默认分支当时的文件;源分支叫 master 时,AngusGit 上的分支名仍是 main。表单按开头填写:仓库 URL、所有者、仓库名称,需要时打开 设为私有仓库。
3. 定好路径怎么对应
GitLab 的项目可以放在多层群组下面,例如 platform/backend/order-service。AngusGit 的仓库地址只有两级:所有者(个人或团队)加仓库名,所以整批项目都要按同一条规则对应过去。常见的两种对应方式:
| GitLab 路径 | 方式一:一级群组对应团队 | 方式二:子组并进仓库名 |
|---|---|---|
platform/backend/order-service |
platform/order-service |
platform/backend-order-service |
platform/frontend/order-web |
platform/order-web |
platform/frontend-order-web |
方式一的地址短,但不同子组下有同名项目时会撞名。方式二不会撞名,地址稍长。拿不准就用方式二。
还要检查项目名里有没有点号。AngusGit 的仓库名只能用中文、字母、数字、连字符和下划线,点号会被去掉,例如 docs.site 要提前改成 docs-site。
4. 私有和内部项目:克隆后推送
在 AngusGit 建团队和空仓库
按第 3 节定好的规则,先在 AngusGit 建好团队,再在团队下建同名的空仓库。新建仓库时,添加 README 文件、添加 .gitignore、选择许可证 三项都不要勾选。任何一项勾上,仓库就不再是空的,推送时会被拒绝。
从 GitLab 完整克隆
用你自己的 GitLab 账号克隆。GitLab 开了双因素认证时,HTTPS 的密码一栏要填 GitLab 的个人访问令牌,权限勾选
read_repository就够了。--bare会把 GitLab 上的所有分支和标签都拉下来,不生成工作区。主分支叫 master 时先改名
AngusGit 新建的空仓库默认分支是
main。GitLab 项目的主分支叫master时,先在本地改名;已经是main就跳过。改名之后,团队成员的本地习惯、GitLab CI 里写死的分支名,都要跟着改成main。推送所有分支和标签
第一行推所有分支,第二行推所有标签。HTTPS 的用户名填 AngusGit 登录名,密码填一把勾了
git:write的访问令牌。用 SSH 地址也可以。
对应的完整命令如下。gitlab.example.com 和 platform/backend-order-service 都要换成你的地址,并先建成空仓库。主分支已经叫 main 时,跳过 git branch -m。推送用的 HTTPS 地址换成你的空仓库;私有仓库克隆时会要访问令牌,按开头生成 git:write 令牌,不要把令牌写进文章或截图。
git clone --bare https://gitlab.example.com/platform/backend/order-service.git
cd order-service.git
git branch -m master main
git push https://git.anguskit.com/platform/backend-order-service.git --all
git push https://git.anguskit.com/platform/backend-order-service.git --tags
--bare 得到的是裸仓库:只存 Git 数据,没有可编辑的工作区,正好用来原样转推。为什么不用 --mirror?--mirror 会把远程的所有引用都拉下来,包括 GitLab 为每条合并请求保存的 refs/merge-requests/*。之后再 git push --mirror,这些引用会原样出现在 AngusGit 仓库里,既没用,还会干扰分支列表和统计。--bare 配合 --all 和 --tags,只推分支和标签,正好是需要的部分。
5. 一次搬一批
项目多时,把对应关系写进一个文本文件,每行两列:GitLab 路径和 AngusGit 上的「所有者/仓库名」。AngusGit 上的空仓库要先按第 4 节第一步建好。
platform/backend/order-service platform/backend-order-service
platform/frontend/order-web platform/frontend-order-web
保存为 repos.txt,然后执行:
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
脚本逐行克隆、改名、推送,然后删掉本地副本。运行之前,先按开头手动搬一个仓库,让系统的凭据管理器记住 GitLab 和 AngusGit 两边的令牌;否则脚本每一行都会停下来问密码。
AngusGit 的 批量导入 也能一次处理很多地址,但它和单个导入一样只读公开项目、只带当前文件,而且导入的仓库全部是公开的。内网 GitLab 的项目,用上面的脚本更合适。
6. 验收:两边的分支和标签要一模一样
每个仓库推完,对比两边的分支和标签。下面的命令分别列出 GitLab 和 AngusGit 上所有分支、标签指向的提交,再逐行比较:
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)
没有任何输出,说明两边一致。如果第 4 节把 master 改成了 main,会看到这一对差异,属于预期;除此之外的差异,说明有分支或标签没推上去,重新执行一次推送即可。
最后打开 AngusGit 上的仓库,在 分支 页签核对数量,在提交记录里看作者和时间是否和 GitLab 一致。
上图左边是导入的结果,不是一次完整推送;推送验收要以 git ls-remote 没有意外差异为准。master 改成 main 时,这一对名字对不上是预期的。
7. 哪些东西不在 Git 仓库里
推送只搬 Git 仓库里的内容。下面这些要在 AngusGit 上重新配置:
| GitLab 上的内容 | 是否随 git push 过去 | 在 AngusGit 上怎么处理 |
|---|---|---|
| 项目成员和角色 | 否 | 把成员加进 AngusGit 的团队,或在仓库 设置 → 访问 里加协作者 |
| 受保护分支 | 否 | 在仓库 设置 → 分支保护 里重新建规则 |
| 合并请求、议题、Wiki | 否 | 进行中的合并请求在 GitLab 上合并或关闭后再搬 |
.gitlab-ci.yml |
文件过去但不会运行 | 在控制台用 导入 GitLab CI 转成 .anguscicd.yaml |
| Webhook | 否 | 在仓库 设置 → Webhooks 里重新添加 |
8. 出错时
| 你看到的 | 原因 | 怎么处理 |
|---|---|---|
| 导入时提示 源仓库URL「…」无效或无法正常访问 | 项目是内部或私有的;填的是 SSH 地址;或者 AngusGit 服务器连不上 GitLab | 内部和私有项目按第 4 节推送。公开项目确认填的是 HTTPS 地址,并在 AngusGit 服务器上执行 git ls-remote <地址> 检查网络 |
克隆 GitLab 时 HTTP Basic: Access denied |
GitLab 开了双因素认证,或者密码不对 | 密码一栏改填 GitLab 个人访问令牌 |
推送时 ! [rejected] main -> main (fetch first) |
AngusGit 上的仓库不是空的 | 删掉重建一个三项都不勾的空仓库 |
推送时 repository '…' not found |
AngusGit 上还没建这个仓库,或者所有者、仓库名和第 3 节的对应规则不一致 | 先建空仓库,再核对地址里的团队名和仓库名 |
| 验收时只有部分标签对不上 | 第二行 --tags 没执行,或者中途断开 |
重新执行一次 git push <地址> --tags |
常见问题
GitLab 上的项目以后还会更新,能先搬一份,过段时间再补吗?
能。最简单的做法是再执行一遍第 4 节:重新 git clone --bare,需要时改名,再推送。AngusGit 上已有的提交不会重复,推送只会把 GitLab 上新增的提交补过去。前提是这段时间里没有人直接往 AngusGit 推代码,否则两边分叉,推送会被拒绝。正式切换前反复同步、最后冻结 GitLab 再同步一次的做法,见 GitLab 迁移清单一文。
能保留 GitLab 合并请求里的评审记录吗?
不能。评审记录存在 GitLab 的数据库里,不在 Git 仓库中。需要留档时,在 GitLab 上导出或截图保存。
项目用了 Git LFS 怎么办?
AngusGit 不支持 Git LFS。推过去之后,LFS 管理的大文件只剩指针文件。这类文件要改成普通文件提交,或者放到制品库里。
GitLab 的子模块地址要改吗?
要。仓库里的 .gitmodules 如果写的是 GitLab 地址,子模块仍然会从 GitLab 拉。等子模块对应的仓库也搬到 AngusGit 之后,把 .gitmodules 里的地址改成 AngusGit 地址,再提交一次。
小结
从 GitLab 搬仓库,先看项目的可见性。公开项目可以直接用 导入仓库,但只带当前文件;内部和私有项目,以及要保留历史的项目,在 AngusGit 建空仓库,git clone --bare 之后用 --all 和 --tags 推送。子组路径提前定好对应规则,项目名里的点号提前改掉。一批项目用一个对应关系文件加循环脚本处理。每个仓库推完,用 git ls-remote --heads --tags 对比两边,一致才算搬完。
结论: GitLab 内部和私有仓库(以及需保留历史的仓库)应先将多层子组映射为「团队/仓库名」,再用
git clone --bare加git push --all/--tags推入空仓库,并用git ls-remote对比验收。