GitLab项目/组迁移神器:一键迁移

2 阅读10分钟

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 工作原理

脚本通过持久化状态文件实现断点续传:

  1. 记录已处理内容:每成功创建一个组或项目,立即将ID保存到 migration_state.json
  2. 加载上次进度:启动时自动加载状态文件,跳过已处理的内容
  3. 自动跳过:处理前检查是否已在状态文件中,避免重复
  4. 中断保护: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_apiread_repository 权限
  • 目标 GitLab Token: 需要 apiwrite_repository 权限,建议使用管理员 Token

7.2 文件管理

  • 状态文件 (migration_state.json):不要手动修改,否则可能导致进度错乱
  • 日志文件 (migration.log):可随时查看或删除
  • 临时目录 (temp_repos/):脚本会自动清理,也可手动删除

7.3 断点续传注意事项

  1. 不要跨不同迁移任务使用同一状态文件 - 状态文件与具体迁移内容绑定
  2. 修改排除列表后 - 已记录在状态文件中的内容仍会被跳过
  3. 目标 GitLab 重置后 - 需要删除状态文件重新开始
  4. 状态文件损坏 - 脚本会自动备份,可从备份恢复

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 迁移结果

类型总数成功失败成功率
18180100%
项目56550100%

8.3 断点续传效果

场景处理项目数中断后恢复重复处理
无中断56-0
中断1次30 + 26✅ 自动跳过前30个0
中断2次20 + 20 + 16✅ 自动跳过前40个0

九、版本历史

版本日期更新内容
1.0.02026-03-19初始版本
2.0.02026-03-19新增断点续传功能,所有文件保存在当前目录

十、文件位置汇总

当前目录
├── gitlab_migrator.py          # 主程序
├── migration_state.json        # 状态文件(重要!)
├── migration.log               # 日志文件
├── temp_repos/                 # 临时目录
└── migration_state.json.backup.* # 状态文件备份