2018-07-20 如何将 SVN 迁移至 GIT 并保留所有历史记录

原文地址: http://pcme.info/blog/2013/09/06/how-to-migration-svn-to-git/

前言

GIT 已经无处不在,你们还在用 SVN 管理源代码吗?如果你和你的小伙伴们正在考虑,从 SVN 迁移至 GIT,如果你们的 SVN 仓库已经够庞大(1W+ commits)和复杂(后面复杂情况详解),又想在迁移之后保留所有更改记录,这篇文章也许正是你要找的。

准备工作

需要安装那些软件&工具?

  1. SubGit
  2. JRE
  3. Subversion

SubGit 是个提供从 SVN 安全迁移至 GIT 的商业工具软件,这里主要是用到它将 SVN 提交历史翻译为 GIT 提交这一免费功能。其官网提供二进制包下载(目前最新版本2.0.0,支持远程 SVN 地址)。 因为 SubGit 跨平台,基于 Java 写的,需要安装 JRE(JAVA 运行环境) 才能够运行。 此外,还将用到 SVN 命令,需要安装 Subversion 并配置至 %PATH% 环境变量(这里我是直接使用 VisualSVN Server 安装目录下 bin 自带的 Subversion)。

一. 简单情况

只需要几个步骤就能完成 SVN 至 GIT 的转换:

# 1. 解压 SubGit-2.0.0.zip 至路径如:x:\SubGit-2.0.0,x: 替换为实际分区。
# 2. 执行配置,会生成默认配置,如需密码访问远程 SVN 服务器,
#    在 SubGit_repository_name\SubGit\password 设置访问密码,
# 说明:
#   SubGit_repository_name\SubGit\authors.txt 配置 SVN 中用户提交转换 GIT 提交后对应的用户名和邮箱,
#   可将多个 SVN 帐号映射到一个用户名和邮箱,格式如下
#   svnUser = Git User <user@example.com>
x:\> x:\SubGit-2.0.0\bin\SubGit.bat configure --svn-url [http://svn.domain.com/svn/svn_repository_name/ or file:///x:/svn_repository_name] SubGit_repository_name

# 执行安装
x:\> x:\SubGit-2.0.0\bin\SubGit install x:\SubGit_repository_name

# Git Bash 中输入,克隆一份 GIT 仓库,不含工作区,推送至指定 GIT 服务器
$ git clone SubGit_repository_name working-tree --bare
$ git remote add remote_name git@github.com/own_name/project_name.git
$ git push remote_name --all
$ git push remote_name --tags

二. 复杂情况

有哪些复杂情况?

1. 目录变更

前期 SVN 仓库创建时没有使用标准结构(trunk,branchs,tags),后期修改为标准结构,比如: /svn/project_name/ <=> /svn/project_name/trunk …),想保留这些提交历史记录。

比较麻烦的就是这种情况,目前还没发现有哪些转换工具可以直接支持,这里通过一种变通的方式,即先把包含不正确结构历史记录的 SVN 仓库转换为都正确结构历史记录的 SVN 仓库。

用到几个 SVN 的命令:

# -r 1391:1391:指定导出范围
# --incremental 增量导出
# SVN 仓库存放路径
# 导出文件存放路径
x:\> svnadmin dump -r 1391:1391 --incremental x:\Repositories\project_name > x:\temp\svn-project_name-1391-1391-bak.dump

# 创建一个临时仓库,导入 SVN 记录
x:\> svnadmin create x:\Repositories\project_name_temp
# 原来是什么样,导入后还是什么样
x:\> svnadmin load x:\Repositories\project_name_temp < x:\temp\svn-project_name-1391-1391-bak.dump
# 将特定目录的提交分离出来
x:\> cat x:\temp\svn-project_name-0-1390-bak.dump | svndumpfilter exclude Documents --drop-empty-revs --renumber-revs > x:\temp\svn-project_name-0-1390-eDocuments-bak.dump
x:\> cat x:\temp\svn-project_name-0-1390-bak.dump | svndumpfilter include Documents --drop-empty-revs --renumber-revs > x:\temp\svn-project_name-0-1390-iDocuments-bak.dump
# 导入至目录:trunk/source,即 svn/abc <=> svn/trunk/source/abc
x:\> svnadmin load --parent-dir trunk/sources x:\Repositories\x:project_name_temp < x:\temp\svn-project_name-1391-1391-bak.dump

2. 有开发分支

在 SVN 仓库中有设 Develop 分支,比如:svn/project_name/trunk(稳定分支),svn/project_name/develop(开发分支)

通过修改 SubGit 配置,可以实现转换后 GIT 包含 develop 分支。

 …
  trunk = trunk:refs/heads/master
  branches = Develop:refs/heads/develop # 新增,Develop 指对应 SVN 目录(svn/project_name/Develop),注意大小写
  branches = branches/*:refs/heads/*
  tags = tags/*:refs/tags/*
  shelves = shelves/*:refs/shelves/*
  …

3. 实例

说明

项目A,托管在 Windows Server 安装的 VisualSVN Server 上,已有超过 1W+ commits,项目前期采用的结构为【1】。大概在 Commit Revesion:1391-1394 时,有位小伙伴意识到,我们应该用分支需要 Branchs,Tags,于是结构调整为【2】。再后来来了位新伙伴,说你们项目怎么没有 Develop 分支呢?于是调整新增了【3】:svn/project_a/develop 作为开发分支。

svn/project_a
  + sources
  + documents
svn/project_a
  + trunk
      +- sources
          +- documents
  + tags
      …
  + branchs
      …
svn/project_a
  trunk
      sources
          documents
  + develop
      + sources
          + documents
  tags
      …
  branchs
      …

操作

# 原仓库 x:\Reposities\project_a
# 1\. 创建临时仓库
svnadmin create x:\Repositories\project_a_temp

# 2\. 将目录改变的 Commits 提取出来 在临时仓库中顺序移至到了前面
svnadmin dump -r 1391:1391 --incremental x:\Reposities\project_a > x:\temp\svn-project_a-1391-1391-bak.dump
svnadmin load x:\Reposities\project_a_temp < x:\temp\svn-project_a-1391-1391-bak.dump

svnadmin dump -r 1394:1394 --incremental x:\Reposities\project_a > x:\temp\svn-project_a-1394-1394-bak.dump
svnadmin load x:\Reposities\project_a_temp < x:\temp\svn-project_a-1394-1394-bak.dump

# 3\. 导出结构【1】时的 Commits,在临时仓库中重写 Commit是 为结构【2】
svnadmin dump -r 0:1390 --incremental x:\Reposities\project_a > x:\temp\svn-project_a-0-1390-bak.dump

# 3.1 分别过滤 & 提取 Document 文档的提交记录,并将其重写 svn/sources/Documents/ <=> svn/trunk/sources/Documents
cat x:\temp\svn-project_a-0-1390-bak.dump | svndumpfilter exclude Documents --drop-empty-revs --renumber-revs > x:\temp\svn-project_a-0-1390-eDocument-bak.dump
svnadmin load --parent-dir trunk/sources x:\Reposities\project_a_temp < x:\temp\svn-project_a-0-1390-eDocument-bak.dump

cat x:\temp\svn-project_a-0-1390-bak.dump | svndumpfilter include Documents --drop-empty-revs --renumber-revs > x:\temp\svn-project_a-0-1390-iDocument-bak.dump
svnadmin load --parent-dir trunk/sources x:\Reposities\project_a_temp < x:\temp\svn-project_a-0-1390-iDocument-bak.dump

# 4\. 将剩下的 Commits 导出导入至临时仓库
svnadmin dump -r 1396:HEAD --incremental x:\Reposities\project_a > x:\temp\svn-project_a-1396:HEAD-bak.dump
svnadmin load x:\Reposities\project_a_temp < x:\temp\svn-project_a-1396:HEAD-bak.dump

# 5 SubGit 配置
x:\> x:\SubGit-2.0.0\bin\SubGit.bat configure --svn-url file:///x:/Reposities/project_a_temp x:\Reposities\SubGit_project_a

# 5.1 编辑开发者映射列表 SubGit_project_a\SubGit\authors.txt
# 5.2 编辑SubGit配置【config_001】,支持 Develop 分支导入

# 6\. 执行安装,漫长的等待…
x:\> x:\SubGit-2.0.0\bin\SubGit install x:\Reposities\SubGit_project_a

# 7\. 使用 Git Bash,克隆一份 GIT 仓库,不含工作区,推送所有分支(branchs)和标签(tags)至指定 GIT 服务器
$ git clone SubGit_project_a working-tree --bare
$ git remote add remote_name git@github.com/own_name/project_a.git
$ git push remote_name --all
$ git push remote_name --tags

写在最后

1W+ Commits 的 SVN 仓库迁移至 GIT 大概需要4-6个小时(源代码100Mb,仅当参考,但这已经比 GIT 提供的 git svn 命令不知要快多少倍了 ), 当完成迁移后为安全起见,我们还需要对源代码做一次校验,即,捡出 SVN 最新代码(svn/project_a/develop)和 GIT 最新代码(git clone git@gitserver.com project_a -b develop),让后使用 BCompare 或类似工具做一次差异比较,确认所有源代码无差异,这才恭喜你,完成了迁移。

由于 GIT 的学习有一定曲线,如果小伙伴开发团队比较大(20+ 人),小伙伴们对 GIT 接受程度肯定有所差异,为了减少迁移至 GIT 对大伙的影响,可以考虑采用 SubGit 提供的方案,同时支持 SVN 和 GIT 双向提交。

由于作者最开始这么做已经是半年前的时候(当时 SubGit 还是 v1.x.x 现在都 v2.0.0),其中 SubGit 配置和安装部分直接替换为 v2.0.0 用法。实例部分直接参考之前生产环境操作记录,重要部分也经过测试,但个人精力测试范围有限,如有遗漏或异常欢迎留言和反馈。

其他文章

https://www.lovelucy.info/codebase-from-svn-to-git-migration-keep-commit-history.html

http://www.blogjava.net/lishunli/archive/2012/01/15/368562.html

https://www.zybuluo.com/Jazka/note/173956

https://blog.csdn.net/Hello_Mr_Cc/article/details/72742503

https://blog.waterstrong.me/svn-to-git-migration/

https://www.jianshu.com/p/e6be6f76b21c

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Petrichor的专栏

git:指令备忘录

10810
来自专栏乐享123

Git Cheat 2

12120
来自专栏mwangblog

git文件状态,暂存与提交

14840
来自专栏程序猿DD

Spring Cloud构建微服务架构:服务网关(路由配置)【Dalston版】

在上一篇《Spring Cloud构建微服务架构:服务网关(基础)》一文中,我们通过使用Spring Cloud Zuul构建了一个基础的API网关服务,同时也...

21680
来自专栏柠檬先生

git 常用命令

git status 查看工作区和暂缓区状态 git add 将工作区放入暂缓区 git commit 将暂缓区放入 版本区 git add 把修改过的文件全部...

227100
来自专栏我的博客

artisan详解一

php artisan 详解 php artisan optimize php artisan optimize –force Since Laravel ...

30660
来自专栏noteless

eclipse svn插件卸载 重新安装 Subclipse卸载安装 The project was not built since its build path is incomplete T

使用的是eclipse kepler版本,崩溃了,想要重新安装,主要遇到了下面这几种问题

23030
来自专栏康怀帅的专栏

PhpStorm 配置 Laravel 智能提示

本文介绍了 PhpStorm 下的 Laravel 智能提示。 配置 composer 设置中搜索 composer 进行设置 安装 laravel-ide-h...

36340
来自专栏人人都是极客

Git与Repo快速入门

版本控制 版本控制是什么已不用在说了,就是记录我们对文件、目录或工程等的修改历史,方便查看更改历史,备份以便恢复以前的版本,多人协作。 一、原始版本控制 最原始...

346100
来自专栏技术博文

常用 Git 命令

转载自:http://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.html;感谢作者辛苦撰写! 一般来说,日...

30460

扫码关注云+社区

领取腾讯云代金券