一、搬家前的准备:为什么搬?以及搬家的核心思路

首先,我们得统一思想:为什么要从SVN迁到Git? 这不是赶时髦。SVN是集中式版本控制,像一个中央文件柜,大家排队取放。而Git是分布式版本控制,每个开发者电脑上都有一个完整的仓库副本,这让分支创建、合并、离线工作变得极其高效。对于现代敏捷开发和DevOps流程,Git的优势非常明显。

那么,搬家的核心目标是什么?

  1. 完整保留历史:包括每一次提交、谁提交的、什么时候提交的、提交信息是什么,一个都不能少。
  2. 平滑转移权限:SVN的目录级权限需要映射到Git的仓库级或分支级保护策略上。
  3. 最小化团队影响:让开发者能快速适应新流程,最好能“无感”切换。

实现这个目标,我们主要依靠一个强大的工具:git svn。它是Git自带的一个组件,专门用来和SVN仓库“对话”,并能把SVN的历史完美地“翻译”成Git能理解的样子。

技术栈声明:本文所有命令行操作均在 Linux / macOS 的 Bash 环境或 Windows 的 Git Bash 环境中完成,核心工具为 git-svn

二、核心搬迁步骤:用 git-svn 克隆历史

这是最关键的一步,我们要把SVN仓库“克隆”成一个本地的Git仓库。但SVN的布局(Trunk, Branches, Tags)和Git不同,我们需要告诉 git svn 如何映射。

假设我们有一个典型的SVN仓库,结构如下:

https://svn.company.com/repos/myproject/
├── trunk/
├── branches/
│   ├── feature-login
│   └── hotfix-20240101
└── tags/
    ├── v1.0.0
    └── v1.1.0

我们需要创建一个“作者映射文件”,因为SVN的用户名(如 zhangsan)需要转换成Git标准的 姓名 <邮箱> 格式。

步骤1:创建作者映射文件 authors.txt

# 文件内容示例,格式为:svn用户名 = Git姓名 <Git邮箱>
zhangsan = 张三 <zhangsan@company.com>
lisi = 李四 <lisi@company.com>
admin = 系统管理员 <admin@company.com>
# 对于未知用户,可以统一映射
(no author) = 未知贡献者 <unknown@company.com>

你可以从SVN日志中提取所有用户来生成这个文件,可以用这个命令(需要先安装svn命令行工具):

svn log --quiet https://svn.company.com/repos/myproject | grep -E "^r[0-9]+ \| " | awk -F '|' '{print $2}' | sort | uniq | sed 's/^ *//;s/ *$//' > users.txt

然后手动将 users.txt 中的名字编辑成 authors.txt 的格式。

步骤2:执行完整的 git svn clone 这是完整的克隆命令,它会遵循SVN的标准布局。

git svn clone https://svn.company.com/repos/myproject \
               --stdlayout \          # 告诉git-svn使用trunk, branches, tags的标准结构
               --authors-file=authors.txt \  # 指定作者映射文件
               --no-metadata \        # 不在提交信息中添加冗余的git-svn元数据,让历史更干净
               myproject-git          # 克隆到本地的目录名

这个命令会运行一段时间,时间长短取决于SVN仓库的历史提交数量。它会把:

  • trunk 映射为 Git 的默认分支(通常是 mastermain)。
  • branches/ 下的每一个目录映射为一个 Git 远程分支。
  • tags/ 下的每一个目录映射为一个 Git 标签(但会是“轻量标签”,我们需要稍后处理)。

执行完成后,你就得到了一个完整的本地Git仓库 myproject-git,所有的SVN提交都变成了Git提交。

三、搬迁后的整理与优化:让新家更符合Git习惯

克隆下来的仓库还有些“SVN痕迹”,我们需要做一些整理,让它变成一个纯粹的、好用的Git仓库。

步骤3:将SVN标签转换为Git的“附注标签” Git有两种标签:轻量标签(只是个引用)和附注标签(是一个完整的对象,包含打标签者、日期和信息)。git svn 创建的是轻量标签,我们最好将其转为附注标签。

# 进入克隆好的仓库目录
cd myproject-git

# 这个命令会列出所有由git-svn创建的远程标签引用(格式为tags/*)
git for-each-ref refs/remotes/origin/tags | cut -d / -f 5- |
while read ref; do
  # 获取标签对应的提交ID
  git tag -a "$ref" "origin/tags/$ref" -m "从SVN标签 $ref 迁移"
  # 删除旧的远程标签引用
  git branch -rd "origin/tags/$ref"
done

步骤4:清理和重命名远程分支 git svn 会把远程分支放在 refs/remotes/origin/ 下。我们需要把它们变成真正的本地分支,并清理掉 origin 这个远程(因为它指向的是SVN服务器,我们之后要指向新的Git服务器)。

# 将 origin/ 开头的远程分支(除了tags)创建为本地分支
git for-each-ref refs/remotes/origin | cut -d / -f 4- |
grep -v '^tags/' |  # 排除标签
while read branch; do
  # 例如,将 origin/feature-login 创建为本地分支 feature-login
  git branch "$branch" "refs/remotes/origin/$branch"
done

# 查看所有分支,确认转换成功
git branch -a

# 删除指向SVN的远程 origin
git remote rm origin

现在,你的本地仓库已经是一个“纯净”的Git仓库了,包含了所有历史、分支和标签。

四、在新家安顿:推送到Git服务器并设置权限

仓库整理好了,现在要把它推送到新的Git服务器(如 GitLab、GitHub、Gitee 等)。

步骤5:关联新的Git远程仓库并推送 首先在Git服务器上创建一个新的空白项目(例如,在GitLab上创建 myproject)。

# 添加新的Git远程仓库地址,这里用GitLab示例
git remote add origin https://gitlab.company.com/group/myproject.git

# 推送所有分支到新的远程仓库
# -u 参数设置上游分支,以后可以直接用 git push/pull
git push -u origin --all

# 推送所有标签到新的远程仓库
git push -u origin --tags

至此,代码历史已经完全迁移到了新的Git仓库中。

步骤6:权限与协作模式的迁移(重点) 这是和SVN差异最大的一块。SVN是路径级权限,而Git是仓库级。我们需要在Git服务器上用新的方式管理。

  • 仓库权限:在GitLab/GitHub中,通过设置“成员(Members)”来分配整个仓库的访问权限(所有者、维护者、开发者、访客)。
  • 分支保护:这是替代SVN目录权限的核心。例如:
    • main/master (对应SVN的 trunk):设置为“受保护分支”,只有维护者能直接推送,开发者需要通过合并请求(Merge Request/Pull Request)来合并代码。
    • release-* 分支:同样设置为受保护分支,确保发布版本的稳定。
    • feature-*hotfix-* 分支:可以允许开发者创建和推送,鼓励频繁提交。
  • 合并请求:推广使用合并请求来代替SVN的“提交即入库”。这是进行代码评审、自动化CI/CD检查的最佳实践。

你需要根据团队旧的SVN权限表,在Git服务器的项目设置中,仔细配置这些分支保护规则和成员角色。

五、后续工作与团队切换

步骤7:通知团队并更新本地仓库

  1. 正式公告,并给出一个明确的代码提交“冻结窗口期”。
  2. 为团队提供清晰的《Git使用指南》,重点说明新的工作流(例如:Git Flow 或 GitHub Flow)。
  3. 让每个团队成员执行以下操作:
    # 1. 备份旧的SVN工作副本(重要!)
    # 2. 克隆全新的Git仓库
    git clone https://gitlab.company.com/group/myproject.git
    cd myproject
    # 3. 查看所有分支
    git branch -a
    # 4. 切换到需要的分支进行开发
    git checkout -b feature-new-xxx origin/main
    

步骤8:善后与验证

  • SVN仓库归档:迁移完成后,将SVN仓库设置为只读,并通知大家不再向其中提交代码。保留一段时间以备查验。
  • 历史验证:随机抽查几个早期的SVN版本号(如 r123),在Git中用 git log --grep 或通过提交信息搜索对应的提交,确认内容一致。
  • 构建验证:用Git仓库的最新代码和某个历史标签的代码,执行完整的构建和测试流程,确保功能一致。

应用场景分析

  • 团队技术栈升级:团队向DevOps和敏捷开发转型,需要Git的强大分支能力和与CI/CD工具(如Jenkins, GitLab CI)的天然亲和性。
  • 项目开源或托管平台迁移:计划将项目开源到GitHub,或从旧的SVN托管服务迁移到现代化的Git平台(如GitLab)。
  • 合并与重组:公司并购或团队重组后,需要统一版本控制工具到Git。

技术优缺点

  • 优点
    • 历史无损git svn 工具非常成熟,能近乎完美地迁移提交历史。
    • 发挥Git优势:迁移后,团队可以享受分布式开发、闪电般的分支操作带来的效率提升。
    • 生态整合:更好地融入现代开发生态(代码评审、CI/CD、项目管理)。
  • 缺点/挑战
    • 权限模型转换:从路径级到仓库/分支级的权限映射需要重新设计和配置,是迁移中最大的逻辑转换点。
    • 学习曲线:对于习惯SVN的团队成员,需要学习Git的概念和工作流。
    • 迁移过程耗时:对于超大、历史悠久的SVN仓库,克隆和整理步骤可能非常漫长。

注意事项

  1. 充分测试:务必在测试环境对迁移流程进行完整演练,尤其是复杂仓库(非标准布局、有移动历史等)。
  2. 沟通与培训:技术迁移成功与否,一半取决于人。提前沟通、提供培训和支持至关重要。
  3. 规划冻结期:安排一个低活跃度的时期(如周末)进行最终迁移,减少代码冲突和团队影响。
  4. 处理大文件:如果SVN历史中有不适合用Git管理的大文件(二进制库、媒体文件),考虑用 git lfs 在迁移后管理,或将其移出版本库。
  5. 非标准布局:如果SVN仓库不是 trunk, branches, tags 的标准布局,需要使用 --trunk, --branches, --tags 参数手动指定路径,操作会更复杂。

文章总结

将SVN迁移到Git是一项系统工程,但绝非不可完成的任务。其核心在于利用 git svn 工具忠实地完成历史的转换,并深刻理解SVN的“集中式路径权限”与Git的“分布式仓库协作”模型之间的差异,从而在目标Git平台上做好权限与工作流的重新设计。成功的迁移不仅能保留宝贵的历史资产,更能为团队注入新的协作活力,是团队研发效能升级的关键一步。记住,耐心准备、充分测试、有效沟通,是确保这次“大搬家”平稳顺利的不二法门。