需求
SVN 存在着太多问题了,具体见:
使用 Git 作为版本控制可以享受很多好处:查找历史很快,容易新建分支等等。
是否使用 Git LFS?
- 但是游戏项目有自身的特点,那就是仓库极大,大概有 75G,需要将大文件转换为 Git LFS。
- 另外使用 Git LFS 也有额外的好处,就是在拉取更新时可以使用多线程同时下载,极大提升更新速度。
- 使用 Git LFS 后,仓库本身会变大,因为数据都不再压缩;但是用户本地的仓库并不会将所有历史的大文件都下载下来,只会保存当前分支引用的大文件,因此会维持在一个可以接受的大小。
因此确定最终需要将 SVN 转换为 Git + Git LFS。
调研
资料
官方文档
回答是用 git-svn 克隆 SVN 仓库后再用 git lfs migrate 迁移到 Git LFS
记录了使用 Atlassian 工具生成作者列表、git gc 导致转换过程需人工介入
使用不同方法转换的文章:
其他工具
调研结果
对比了一下第三方方案,发现很多已经很久没有更新了,而 Git 社区本身是非常活跃的。
虽然有很多第三方工具,但是考虑到成熟度、稳定性与兼容性,还是决定完全使用 Git 与 Git LFS 官方工具自己执行转换。
最终确定方案为:
- SVN 仓库转换为 Git 仓库
- Git 仓库转换为 Git LFS 仓库
环境
- svn 1.13.0
- Git 2.28.0
- Git LFS 2.11.0
- macOS 10.14.6
- Sourcetree 4.0.2 macOS
- Sourcetree 3.3.9 Windows
准备
软件升级
建议将使用到的软件都升级到最新版本,以解决可能出现的兼容性问题,包括 SVN、Git、Git LFS、Sourcetree、GitLab。
GitLab 存储位置
需要将仓库与 Git LFS 存储路径放到最大剩余空间的磁盘上。
使用 df -h 找到最大的磁盘,然后将仓库移动到那儿
确定由 /data/gitlab/git-data
移动到 /home/gitlab/git-data
1
2
3
4
5
|
git_data_dirs({
"default" => {
"path" => "/data/gitlab/git-data"
}
})
|
1
2
3
4
5
6
|
sudo gitlab-ctl stop
mv /data/gitlab /home
mv /var/opt/gitlab/gitlab-rails/shared/lfs-objects /home/gitlab/git-data
vi /etc/gitlab/gitlab.rb
gitlab-ctl reconfigure
sudo gitlab-ctl start
|
调整完成后一定要新建一个仓库验证是否成功,在新建的裸仓库中使用 /opt/gitlab/embedded/bin/git log
可以看到提交记录:
1
2
3
4
|
cd /home/gitlab/git-data/repositories/@hashed/xx/xx/xxxxxxx.git
/opt/gitlab/embedded/bin/git log
ls /home/gitlab/git-data/lfs-objects
|
SVN 转换为 Git
作者映射
转换仓库是一个清理作者信息非常好的机会。建议在获得仓库的所有权限后获取作者信息,以防遗漏。
不推荐在本地使用命令获取作者信息。
1
2
|
svn log --quiet http://svn.example.com/project/trunk/ | grep -E "r[0-9]+ \| .+ \|" | cut -d'|' -f2 | sed 's/ //g' | sort | uniq
8m 3s
|
需要将得到的列表添加映射名字并保存为 user.txt
执行转换
1
2
|
git svn clone http://svn.example.com/project \
--authors-file=users.txt --prefix "" -s fltx
|
关闭 GC
在转换过程中可以进入转换中的 Git 目录,执行以下命令关闭 gc 功能:
1
2
3
|
cd /work/project
git config gc.auto 0
git config --global gc.auto 0
|
结果比较
SVN 仓库大小 79.5G
Git 仓库大小 67,005,297,747 字节(磁盘上的 69.78 GB),共 117,796 项
验证
提交
查看最后一次提交
git log
查看首次提交时间是否正确
git log --reverse
查看提交统计信息
1
2
|
git shortlog -sn
1m 34s
|
文件内容
使用比较软件比较转换完成的 Git 仓库与原始 SVN 仓库,查找文件差异。比较时使用二进制比较文件内容。虽然这是转换的中间步骤,但是依然建议检测,确保及时发现问题。
清理
按照 ProGit 2nd 使用的方法删除不需要的东西,但在实际使用时可能是由于 SVN 仓库结构不合理,并没有删除掉什么东西。
1
2
3
4
5
|
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
git tag
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
for p in $(git for-each-ref --format='%(refname:short)' | grep @); do git branch -D $p; done
git branch -d trunk
|
备份
由于 SVN 转换为 Git 耗时太长,建议在执行后续操作时提前备份。备份时不用压缩,保存为固实文件即可:
1
2
3
4
|
cd /work
tar -cvf project.tar project
scp /work/project.tar remote:/home/gitlab/git-backup
10m 5s
|
转换为 Git LFS
查找大型二进制文件
find 方法
在开始查找前先将 git 目录移走,不然查找时会浪费大量时间,过滤的方法也会遍历这些文件,造成不必要的时间浪费
find . -type f -not -path "./.git/*"
用这种方法过滤会浪费大量时间
查找当前工作目录的大文件数量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
$ find . -type f -size +10k | grep -o -E "\.[^\.]+$" | sort | uniq -c | sort -rn
8966 .tga
6307 .prefab
5823 .png
4574 .FBX
1696 .anim
543 .jpg
506 .txt
367 .cs
302 .TGA
263 .jar
257 .xlsx
252 .lua
249 .cpp
239 .fbx
127 .h
121 .wav
104 .exr
96 .unity
96 .bin
86 .psd
85 .py
84 .asset
76 .dll
72 .cog
62 .mp3
51 .xml
36 .ogg
30 .vcxproj
29 .exe
27 .lib
26 .so
25 .shader
24 .aar
22 .c
21 .obj
19 .cginc
17 .PNG
16 .meta
16 .mat
15 .pdf
14 .vcproj
14 .cubemap
13 .html
12 .ttf
11 .md
9 .tif
8 .guiskin
7 .hlsl
6 .unitypackage
6 .pdb
6 .docx
6 .doc
6 .cube
3m 50s
|
Git LFS 方法
同时也可以使用 Git LFS 提供的命令查找文件的总大小,感觉比 find 命令找到的结果多了一层汇总功能,更好用
1
2
3
4
5
6
7
8
9
10
|
$ git lfs migrate info --everything --above="5MB"
migrate: Sorting commits: ..., done.
migrate: Examining commits: 100% (60286/60286), done.
*.exe 85 GB 3289/5785 files(s) 57%
*.lua 10 GB 1189/16545 files(s) 7%
*.xlsx 2.4 GB 278/17169 files(s) 2%
*.bin 1.5 GB 32/401 files(s) 8%
*.zip 1.1 GB 69/15067 files(s) 0%
9m 39s
|
执行转换
将上一步得到的大文件扩展名整理成 git lfs migrate
需要的格式。建议整个命令都在文本编辑器下编写修改,然后复制到终端中执行。
转换过程其实相对于 SVN 迁移要快得多:
1
2
3
4
5
6
7
8
|
$ git lfs migrate import --everything --include="*.unity3D,*.exr,*.unitypackage,*.pdf,*.psd,*.ai,*.fla,*.gif,*.jpg,*.jpeg,*.tga,*.tif,*.tiff,*.bmp,*.png,*.ttf,*.TTF,*.otf,*.aif,*.ogg,*.wav,*.rns,*.mp3,*.flv,*.mov,*.wmv,*.mpg,*.mpeg,*.avi,*.mp4,*.FBX,*.fbx,*.blend,*.lxo,*.so,*.bundle,*.a,*.dll,*.aar,*.srcaar,*.bin,*.mdb,*.ipa,*.swf,*.jar,*.apk,*.exe,*.rar,*.zip,*.gz,*.7z,*.PNG,*.TGA,*.obj,*.pdb,*.pyd,*.doc,*.docx,*.xlsx"
migrate: Sorting commits: ..., done.
migrate: Rewriting commits: 100% (60286/60286), done.
master 82ca815ca183062a543f77614d1409b5308a8c69 -> fbbf99a916662e8d94c2dbb78ebfaaf82b27f965
migrate: Updating refs: ..., done.
migrate: checkout: ..., done.
1h 35m 23s
|
统计空间占用
1
2
3
4
|
.git/objects
55,727,853,152 字节(磁盘上的 58.25 GB),共 0 项
.git/lfs/objects
128,682,453,395 字节(磁盘上的 128.88 GB),共 118,924 项
|
验证
确定文件为文本指针
打开原来的二进制文件,确定现在内容是 Git LFS 定义的文本指针。
检出
转换完成的仓库不能直接使用,需要将 LFS 文件检出
1
2
3
4
|
$ git lfs checkout
Checking out LFS objects: 100% (26057/26057), 7.7 GB | 20 MB/s, done.
7m 10s
|
文件内容
使用比较软件比较转换完成的 Git 仓库与原始 SVN 仓库,查找文件差异。比较时使用二进制比较文件内容。
推送
执行推送
使用以下命令向 GitLab 服务器推送
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
$ git remote add origin git@git.example.com:project/project.git
$ git lfs install
Updated git hooks.
Git LFS initialized.
$ git push -u origin --all
Locking support detected on remote "origin". Consider enabling it with:
$ git config lfs.https://git.example.com/project/project.git/info/lfs.locksverify true
Uploading LFS objects: 100% (74284/74284), 129 GB | 10 MB/s, done.
Enumerating objects: 888166, done.
Counting objects: 100% (888166/888166), done.
Delta compression using up to 8 threads
Compressing objects: 100% (838220/838220), done.
Writing objects: 100% (888166/888166), 1.90 GiB | 4.34 MiB/s, done.
Total 888166 (delta 662312), reused 75521 (delta 45182), pack-reused 0
remote: Resolving deltas: 100% (662312/662312), done.
remote: Checking connectivity: 888166, done.
To git.example.com:project/project.git
* [new branch] master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
4h 5m 13s
|
验证
注意这是最后的集成测试,要确保所有的内容在服务器与客户端都是正常的。
服务器验证 LFS 功能
推送的过程中需要检查 LFS 文件是否正确上传,SSH 进入 GitLab 服务器查看以下目录
1
|
ls /home/gitlab/git-data/lfs-objects
|
客户端验证 LFS 功能
在另一台机器上拉取验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
$ git lfs install --skip-smudge
$ git clone git@git.example.com:project/project.git
Cloning into 'fltx'...
remote: Enumerating objects: 888166, done.
remote: Counting objects: 100% (888166/888166), done.
remote: Compressing objects: 100% (221090/221090), done.
remote: Total 888166 (delta 662312), reused 888166 (delta 662312), pack-reused 0
Receiving objects: 100% (888166/888166), 1.90 GiB | 10.83 MiB/s, done.
Resolving deltas: 100% (662312/662312), done.
Updating files: 100% (112949/112949), done.
$ time git lfs pull
Downloading LFS objects: 100% (21707/21707), 6.8 GB | 7.1 MB/s
real 16m15.989s
user 0m0.000s
sys 0m0.015s
|
恢复 LFS 钩子
1
2
3
|
$ git lfs install
Updated git hooks.
Git LFS initialized.
|
使用
忽略文件
仓库正式使用前第一件事情是增加忽略文件:
1
|
curl -sL https://www.toptal.com/developers/gitignore/api/unity > .gitignore
|
Git 钩子
服务器钩子
GitLab 的推送检查需要付费,不想付费的话只能使用全局的钩子自行编写逻辑
需求:过滤非第一父提交、作者信息、LFS 大小检测、提交信息格式检测、关键文件提交检测、限制分支名字必须包含作者全拼
文件用户修改为 git:root
,增加可执行权限
测试时使用 GitLab 内置 ruby git
/opt/gitlab/embedded/bin/ruby
但是 sh 脚本无需修改 ruby 路径,因为 GitLab 在执行 sh 钩子脚本时已正确设置 PATH 环境变量,可以找到 ruby。
ruby 文件头部增加
1
2
|
Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = Encoding::UTF_8
|
LFS 大文件检测
需要将文件名改为 update
与 pre-receive
1
2
|
chown git:root update pre-receive
chmod +x update pre-receive
|
客户端钩子
需求:提交信息格式检测、关键文件提交检测
根据需要查找一些现成的钩子使用,如检查指定文件是否提交。考虑到客户端不能方便地安装不同软件,建议只使用 bash 脚本编写的钩子,没有额外依赖。
软件
准备好软件及其安装方法
GitLab 配置
注册账号、分配权限、配置 SSH
生成 SSH Key
1
2
|
ssh-keygen -t rsa -b 2048 -C "user@example.com"
cat ~/.ssh/id_rsa.pub | clip
|
传输
为了减少下载时间,使用 U 盘或移动硬盘工具传输已经初始化好的仓库
需要将仓库克隆到本地后打包成压缩包,传递给所有人后本地解压使用仓库
Git 中文显示
在 Git 安装路径下,找到 etc 目录的 bash.bashrc 文件,加入下面几行解决了
1
2
3
4
5
6
|
export LANG="zh_CN.UTF-8"
export LC_ALL="zh_CN.UTF-8"
## 设置为中文环境,使提示成为中文
export LANG="zh_CN"
## 输出为中文编码
export OUTPUT_CHARSET="utf-8"
|
1
2
3
4
5
|
git config --global core.quotepath false # 显示 status 编码
git config --global gui.encoding utf-8 # 图形界面编码
git config --global i18n.commitencoding utf-8 # 提交信息编码
git config --global i18n.logoutputencoding utf-8 # 输出 log 编码
git config --global svn.pathnameencoding utf-8
|
使用
- 编写安装软件文档
- 编写用户配置文档
- 手动拉取最新仓库与 LFS 对象压缩为一整个压缩包
- 教会几个核心人员如何安装与配置
- 所有人同时拉取仓库与安装配置 Git