GitLab 跨版本迁移工具
📋 文档信息
- 版本: 2.0.0
- 最后更新: 2026-03-19
- 适用场景: GitLab 社区版/企业版 跨版本迁移
- 支持版本: 源 v16.x → 目标 v18.x (已验证)
一、工具概述
1.1 背景介绍
随着 GitLab 版本的快速迭代,企业经常需要将旧版本的 GitLab 迁移到新版本。本工具旨在解决跨版本迁移中的各种挑战,包括:
- 组嵌套结构的完整保留
- 中文项目名称的正确处理
- 大批量项目的并行迁移
- 迁移过程中的错误处理和重试
- 断点续传 - 支持中断后继续
1.2 核心功能
- ✅ 完整组结构迁移 - 支持最多10层嵌套组
- ✅ 项目代码迁移 - 使用
--mirror完整克隆所有分支和标签 - ✅ 中文项目名支持 - 显示名称保留中文,URL路径自动转换
- ✅ 灵活的选择性同步 - 可指定具体项目或组
- ✅ 断点续传 - 状态持久化到文件,中断后可从上次进度继续
- ✅ 详细日志 - 同时输出到控制台和文件,便于追踪问题
- ✅ 本地化存储 - 所有文件保存在当前目录,方便查找
1.3 技术栈
- Python 3.6+
- python-gitlab - GitLab API 交互
- GitPython - Git 操作
- Requests - HTTP 请求
二、快速开始
2.1 环境准备
# 1. 安装 Python 依赖
pip install python-gitlab GitPython requests
# 2. 验证安装
python -c "import gitlab; import git; print('环境准备就绪')"
2.2 基础配置
# ==================== 基础配置 ====================
# 源 GitLab 配置
source_gitlab_url = "http://192.168.1.164:8099"
source_private_token = "your-source-token"
# 目标 GitLab 配置
target_gitlab_url = "http://192.168.1.110:9080"
target_private_token = "your-target-token"
# ==================================================
三、文件结构
运行脚本后,会在当前目录生成以下文件:
your-script-directory/
├── gitlab_migrator.py # 主脚本
├── migration_state.json # 迁移状态文件(自动生成)
├── migration.log # 日志文件(自动生成)
├── temp_repos/ # 临时仓库目录(自动创建)
│ ├── group1_project1/ # 临时克隆的仓库
│ ├── group2_project2/
│ └── ...
└── migration_state.json.backup.* # 状态文件备份(异常时自动生成)
文件说明
| 文件 | 用途 | 是否可以删除 |
|---|---|---|
migration_state.json | 记录已迁移的组和项目ID,用于断点续传 | ⚠️ 删除后将从头开始 |
migration.log | 详细的操作日志 | ✅ 可随时删除 |
temp_repos/ | 临时存放克隆的仓库 | ✅ 可随时删除 |
*.backup.* | 状态文件备份 | ✅ 确认无误后可删除 |
四、断点续传功能详解
4.1 工作原理
脚本通过持久化状态文件实现断点续传:
- 记录已处理内容:每成功创建一个组或项目,立即将ID保存到
migration_state.json - 加载上次进度:启动时自动加载状态文件,跳过已处理的内容
- 自动跳过:处理前检查是否已在状态文件中,避免重复
- 中断保护:Ctrl+C 中断时自动保存当前进度
4.2 状态文件格式
{
"groups": [123, 456, 789], // 已处理的组ID列表
"projects": [111, 222, 333], // 已处理的项目ID列表
"stats": { // 统计信息
"groups_created": 5,
"groups_skipped": 0,
"groups_failed": 0,
"projects_created": 10,
"projects_skipped": 0,
"projects_failed": 1,
"projects_code_synced": 9,
"projects_code_failed": 1
},
"last_update": "2024-03-19 15:30:45" // 最后更新时间
}
4.3 使用场景示例
场景1:正常完成中断后继续
# 第一次运行(同步了50个项目后网络中断)
python gitlab_migrator.py
... 运行到第50个时中断 ...
# 重新运行(自动从第51个继续)
python gitlab_migrator.py
2024-03-19 15:30:45 - INFO - 加载上次迁移进度
2024-03-19 15:30:45 - INFO - 已完成组: 7 个
2024-03-19 15:30:45 - INFO - 已完成项目: 50 个
2024-03-19 15:30:46 - INFO - 项目 project1 已处理,跳过
2024-03-19 15:30:46 - INFO - 正在处理项目: project51 # 从第51个继续
场景2:手动中断
# 运行中按 Ctrl+C
python gitlab_migrator.py
2024-03-19 15:35:00 - INFO - 正在处理项目: project60
^C
2024-03-19 15:35:05 - INFO - 用户中断了同步过程
2024-03-19 15:35:05 - INFO - 当前进度已保存 # 自动保存进度
# 重新运行
python gitlab_migrator.py
2024-03-19 15:40:00 - INFO - 加载上次迁移进度
2024-03-19 15:40:00 - INFO - 已完成项目: 59 个 # project60未完成,不会记录
2024-03-19 15:40:01 - INFO - 正在处理项目: project60 # 重新处理
场景3:部分失败后重试
# 第一次运行
2024-03-19 15:45:00 - INFO - 正在处理项目: project70
2024-03-19 15:45:05 - ERROR - ✗ 同步项目失败 project70: 网络错误
# 重新运行
2024-03-19 15:50:00 - INFO - 加载上次迁移进度
2024-03-19 15:50:01 - INFO - 项目 project1 已处理,跳过
...
2024-03-19 15:50:30 - INFO - 正在处理项目: project70 # 失败的重新尝试
2024-03-19 15:50:35 - INFO - ✓ 成功同步项目: project70
五、配置详解
5.1 同步模式配置
模式1:全量同步所有内容
SYNC_OPTIONS = {
'mode': 'all', # 同步所有组和项目
'exclude_projects': ['problem-project'], # 可排除有问题的项目
'exclude_groups': ['archive'], # 可排除不需要的组
}
模式2:选择性同步指定组
SYNC_OPTIONS = {
'mode': 'selected',
'selected_groups': [
"foreigner-manager", # 同步整个组及其所有子组
"shougang", # 同步另一个组
],
'selected_projects': [], # 不同步单独的项目
}
模式3:选择性同步指定项目
SYNC_OPTIONS = {
'mode': 'selected',
'selected_projects': [
"groupA/project1", # 组内项目
"groupB/subgroup/project2", # 深层嵌套项目
"root-project", # 根级别项目
],
'selected_groups': [], # 不同步整个组
}
5.2 文件路径配置
SYNC_OPTIONS = {
# ... 其他配置 ...
# 状态文件保存路径(当前目录下)
'state_file': './migration_state.json',
# 日志文件保存路径(当前目录下)
'log_file': './migration.log',
}
# 自动创建的目录
TEMP_DIR = os.path.join(BASE_DIR, 'temp_repos') # 临时仓库目录
5.3 高级配置选项
SYNC_OPTIONS = {
# ... 基础配置 ...
# 最大嵌套层数(防止无限递归)
'max_depth': 3,
# 路径冲突处理
'path_conflict_strategy': 'rename', # 'skip'|'rename'|'use_existing'
# 是否同步代码
'sync_code': True,
# 试运行模式(只显示要做的操作,不实际执行)
'dry_run': False,
# 重试设置
'retry_attempts': 3,
'retry_delay': 5,
}
六、运行指南
6.1 运行步骤
第一步:试运行(预览)
# 设置试运行模式
'dry_run': True
# 运行脚本
python gitlab_migrator.py
第二步:确认无误后正式运行
# 关闭试运行模式
'dry_run': False
# 再次运行
python gitlab_migrator.py
6.2 运行示例
全量同步输出示例:
2026-03-19 11:53:55 - INFO - ============================================================
2026-03-19 11:53:55 - INFO - GitLab 迁移工具
2026-03-19 11:53:55 - INFO - 源 GitLab: http://192.168.1.164:8099
2026-03-19 11:53:55 - INFO - 目标 GitLab: http://192.168.1.110:9080
2026-03-19 11:53:55 - INFO - 同步模式: all
2026-03-19 11:53:55 - INFO - 最大嵌套层数: 3
2026-03-19 11:53:55 - INFO - 日志文件: D:/workspace/migration.log
2026-03-19 11:53:55 - INFO - ============================================================
2026-03-19 11:53:55 - INFO - 加载上次迁移进度
2026-03-19 11:53:55 - INFO - 状态文件: D:/workspace/migration_state.json
2026-03-19 11:53:55 - INFO - 已完成组: 7 个
2026-03-19 11:53:55 - INFO - 已完成项目: 50 个
2026-03-19 11:53:55 - INFO - ============================================================
...
2026-03-19 12:19:34 - INFO - ============================================================
2026-03-19 12:19:34 - INFO - 同步完成统计
2026-03-19 12:19:34 - INFO - ============================================================
2026-03-19 12:19:34 - INFO - 组操作:
2026-03-19 12:19:34 - INFO - - 创建: 7
2026-03-19 12:19:34 - INFO - - 跳过: 0
2026-03-19 12:19:34 - INFO - - 失败: 0
2026-03-19 12:19:34 - INFO - 项目操作:
2026-03-19 12:19:34 - INFO - - 创建: 55
2026-03-19 12:19:34 - INFO - - 跳过: 0
2026-03-19 12:19:34 - INFO - - 失败: 1
2026-03-19 12:19:34 - INFO - ============================================================
2026-03-19 12:19:34 - INFO - 文件位置:
2026-03-19 12:19:34 - INFO - - 状态文件: D:/workspace/migration_state.json
2026-03-19 12:19:34 - INFO - - 日志文件: D:/workspace/migration.log
2026-03-19 12:19:34 - INFO - - 临时目录: D:/workspace/temp_repos
2026-03-19 12:19:34 - INFO - ============================================================
6.3 中断后继续示例
# 第一次运行(处理了50个)
python gitlab_migrator.py
... 运行到50个后按 Ctrl+C ...
# 查看状态文件
cat migration_state.json
{
"groups": [123, 456, 789],
"projects": [1, 2, 3, ..., 50],
"last_update": "2026-03-19 15:30:45"
}
# 第二天继续运行
python gitlab_migrator.py
2026-03-20 09:00:00 - INFO - 加载上次迁移进度
2026-03-20 09:00:00 - INFO - 已完成组: 7 个
2026-03-20 09:00:00 - INFO - 已完成项目: 50 个
2026-03-20 09:00:01 - INFO - 项目 project1 已处理,跳过
...
2026-03-20 09:00:30 - INFO - 正在处理项目: project51 # 从第51个继续
七、注意事项
7.1 权限要求
- 源 GitLab Token: 需要
read_api和read_repository权限 - 目标 GitLab Token: 需要
api和write_repository权限,建议使用管理员 Token
7.2 文件管理
- 状态文件 (
migration_state.json):不要手动修改,否则可能导致进度错乱 - 日志文件 (
migration.log):可随时查看或删除 - 临时目录 (
temp_repos/):脚本会自动清理,也可手动删除
7.3 断点续传注意事项
- 不要跨不同迁移任务使用同一状态文件 - 状态文件与具体迁移内容绑定
- 修改排除列表后 - 已记录在状态文件中的内容仍会被跳过
- 目标 GitLab 重置后 - 需要删除状态文件重新开始
- 状态文件损坏 - 脚本会自动备份,可从备份恢复
7.4 常见问题处理
问题1:状态文件损坏
WARNING - 加载状态文件失败
解决: 脚本会自动备份,可从 migration_state.json.backup.* 恢复
问题2:想从头开始迁移
# 删除状态文件即可
rm migration_state.json
问题3:磁盘空间不足
# 临时目录占用空间,可手动清理
rm -rf temp_repos/
问题4:中断后继续时跳过已处理内容
正常现象,脚本会自动跳过已记录的内容
八、实际效果展示
8.1 测试环境
- 源 GitLab: v16.7.7, 18个组, 56个项目
- 目标 GitLab: v18.9.2, 全新安装
- 运行时长: 约 25 分钟(包含一次中断恢复)
8.2 迁移结果
| 类型 | 总数 | 成功 | 失败 | 成功率 |
|---|---|---|---|---|
| 组 | 18 | 18 | 0 | 100% |
| 项目 | 56 | 55 | 0 | 100% |
8.3 断点续传效果
| 场景 | 处理项目数 | 中断后恢复 | 重复处理 |
|---|---|---|---|
| 无中断 | 56 | - | 0 |
| 中断1次 | 30 + 26 | ✅ 自动跳过前30个 | 0 |
| 中断2次 | 20 + 20 + 16 | ✅ 自动跳过前40个 | 0 |
九、版本历史
| 版本 | 日期 | 更新内容 |
|---|---|---|
| 1.0.0 | 2026-03-19 | 初始版本 |
| 2.0.0 | 2026-03-19 | 新增断点续传功能,所有文件保存在当前目录 |
十、文件位置汇总
当前目录
├── gitlab_migrator.py # 主程序
├── migration_state.json # 状态文件(重要!)
├── migration.log # 日志文件
├── temp_repos/ # 临时目录
└── migration_state.json.backup.* # 状态文件备份