最近接手一个非常老的项目,因为使用SVN进行控制,而现在大部分都已经使用Git进行版本控制,所以就想迁移到Git,Git的优点自然是不用在这里说了,必经是当前公认最好用的。
因为项目比较老,代码比较多,提交多达3w多次,同时svn还有几十个分支和标签,这些历史记录丢失将对今后追溯debug造成很多麻烦,所以关键是如何才能保留所有的这些提交记录、分支、标签。
转换仓库
1. 提取开发者信息
为了实现正确的数据迁移,我们要对开发者信息,即提交作者的信息做一个映射,这就需要先生成一个用户对应关系的文件,格式如下:
JefferyWang = JefferyWang <jefferywang@example.com>
为了能够快速从svn内提取作者信息,可以使用下面的脚本,来快速提取在svn内有过提交的用户名,然后再做映射的处理,生成上面格式的文件即可:
svn log -q | awk -F '|' '/^r/ {sub("^ ", "", $2); sub(" $", "", $2); print $2}' | sort -u
当然如果你们所有作者的邮箱后缀都是一样的,可以使用下面这段脚本:
svn log -q | awk -F '|' '/^r/ {sub("^ ", "", $2); sub(" $", "", $2); print $2" = "$2" <"$2"@example.com>"}' | sort -u > authors.txt
2. 仓库转换
标准的svn文件布局
如果你的svn仓库是标准的目录结构,即只包含/trunk
, /branches
, tags
几个目录的结构,那么可以直接在运行命令的时候加上参数--stdlayout
。
命令格式为git svn clone --stdlayout --authors-file=authors.txt <svn-repo>/<project> <git-repo-name>
,下面是一个示例:
# git v1.x
git svn clone --stdlayout --authors-file=authors.txt http://svn.example.com/test_rep/demo_proj demo
# git v2.x
git svn clone --prefix="" --stdlayout --authors-file=authors.txt http://svn.example.com/test_rep/demo_proj demo
非标准的svn文件布局
针对非标准的目录结构,我们分别做显式指定参数--trunk
, --branches
, --tags
。
# git v1.x
git svn clone --trunk=/trunk --branches=/branches --branches=/bugfixes --tags=/tags --authors-file=authors.txt http://svn.example.com/test_rep/demo_proj demo
# git v2.x
git svn clone --prefix="" --trunk=/trunk --branches=/branches --branches=/bugfixes --tags=/tags --authors-file=authors.txt http://svn.example.com/test_rep/demo_proj demo
大仓库的转换策略
有些svn仓库可能非常大,而转换过程是个非常漫长的过程,这个也取决于机器性能。因此,如果你不在乎老的一些历史记录的话,那么你可以只保留部分提交历史,来加速转换的过程。
命令格式:git svn clone -r${REVNUMBER}:HEAD --stdlayout --authors-file=authors.txt <svn-repo>/<project> <git-repo-name>
git svn clone -r52733:HEAD --stdlayout --authors-file=authors.txt https://svn.waterstrong.com/demo demo
3. 仓库清理
这里查看我们的仓库,就发现转换工作基本已经完毕了,如果你只关注trunk和master分支,那么可以不用在乎这一部分,直接跳到下一节即可。这一步主要是将分支和标签进行本地化。
git svn clone
操作并不会将svn的分支和标签导入到新的git仓库里面,并且在本地分支中也找不到svn的分支和标签。转换后,其实是把svn的分支和标签导入为git的远程分支和标签了,如下面示意图所示:
这个策略主要是为了svn和git双向同步服务的,但是我们切换为git后,应该大部分都会禁止在svn再进行提交,所以我们就需要对这些进行一下清理。
第一种方法:
Atlassian提供了一个工具svn-migration-scripts.jar
,可以进行清理。
下载地址:
使用方法:
java -Dfile.encoding=utf-8 -jar ~/svn-migration-scripts.jar clean-git
--force
第二种方法:
只需要执行两个命令:
# 标签本地化
for t in $(git for-each-ref --format='%(refname:short)' refs/remotes/tags); do git tag ${t/tags\//} $t && git branch -D -r $t; done
# 分支本地化
for b in $(git for-each-ref --format='%(refname:short)' refs/remotes); do git branch $b refs/remotes/$b && git branch -D -r $b; done
当然,如果你使用git v2.x版本,你可能通过网上查询的资料,在执行git svn clone
的时候没有加参数--prefix=""
,那么执行之后会在远程的分支名称前面添加origin/
,直接按照上面的脚本处理会有问题,你可以按照下面的命令进行处理
# 标签本地化
for t in $(git for-each-ref --format='%(refname:short)' refs/remotes | grep "origin/tags"); do git tag ${t/origin\/tags\//} $t && git branch -D -r $t; done
# 分支本地化
for b in $(git for-each-ref --format='%(refname:short)' refs/remotes); do git branch ${b/origin\//} refs/remotes/$b && git branch -D -r $b; done
4. 收尾工作
添加远程仓库地址
git remote add origin JefferyWang@github.com:test/demo.git
将本地分支和标签推送到远程仓库
git push origin --all
git push origin --tags