为什么 Git 比 SVN 好
在版本控制系统的选型上,是选择git还是SVN?
对于开源项目来说这不算问题。使用Git极大地提高了开发效率、扩大了开源项目的参与度、 增强了版本控制系统的安全性,选择Git早已是大势所趋。
但对于企业用户来说这个决心不太好下。部分原因是出于对Git的误解,部分原因是尚不了解 Git到底能给项目管理带来什么好处。希望本文能对您项目的版本控制系统选型提供帮助。
误解1:SVN只能检出(checkout)一个版本(revision)的代码,而Git却可以脱库!
SVN脱库的工具SVN本身就提供: svnsync 。这个工具主要用于SVN的版本库镜像。 例如将版本库http://host.name/svn/repo 脱库到本地的 dump 目录,命令如下:
1 2 3 4 5 | $ svnadmin create dump $ printf '#!/bin/sh\nexit 0\n' > dump/hooks/pre-revprop-change $ chmod a+x dump/hooks/pre-revprop-change $ svnsync init file://$(pwd)/dump http://host.name/svn/repo $ svnsync sync file://$(pwd)/dump |
如果使用 git-svn 则为SVN"脱库"更简便。
1 | $ git svn clone -s http://host.name/svn/repo dump |
有人认为SVN可以对目录授权,从而阻止对整个版本库进行脱库操作。 下面就来看看SVN的授权究竟是否可靠。
误解2:SVN能对目录进行精细授权,而Git太不安全
SVN的目录授权对管理员来说是灾难,管理负担相当重,在分支或里程碑众多的时候很难作对。 这是因为SVN的分支和里程碑(tags)本身就是一个目录(使用目录拷贝实现的)。
例如管理员为名为demo的SVN版本库授权。一个并不太复杂的主线(/trunk)授权如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | [demo:/trunk] @demo-admin = rw @leaders = r
[demo:/trunk/doc] @demo-dev = rw @designers = rw
[demo:/trunk/src/apps] @demo-dev = rw
[demo:/trunk/src/common] @demo-dev = rw
[demo:/trunk/src/html] @designers = rw
[demo:/trunk/src/secret] * = @demo-admin = rw jiangxin = rw |
如果项目创建了维护分支 /branches/1.x ,若和 /trunk 授权相同,则需要将上述授权在 /branches/1.x 下重建。需要在授权文件中再添加如下授权指令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | [demo:/branches/1.x] @demo-admin = rw @leaders = r
[demo:/branches/1.x/doc] @demo-dev = rw @designers = rw
[demo:/branches/1.x/src/apps] @demo-dev = rw
[demo:/branches/1.x/src/common] @demo-dev = rw
[demo:/branches/1.x/src/html] @designers = rw
[demo:/branches/1.x/src/secret] * = @demo-admin = rw jiangxin = rw |
如果版本库的分支和里程碑越来越多,配置的工作量相当可观,稍有不慎不是授权文件格式破坏导致SVN无法工作, 就是造成开放授权。
我曾经写过SVN路径授权的补丁,并写了一款SVN版本库管理的开源软件 (参见 《pySvnManager手册》 ), 但想完美解决这个问题很难。我的一个设想是在SVN对分支和里程碑授权检查时缺省使用 /trunk 的授权,但这样的实现要求使用SVN严格遵循约定俗成的三个顶级目录的规范。
误解3:Git能随意改变历史提交,这对于版本控制来说是不合适的
对于已经推送到远程共享服务器的提交,Git就不能再像本地一样随意更改了。 因为推送到共享版本库的提交一旦被其他程序员获取,便扩散出去, 如覆水难收,难掩众人悠悠之口。所以Git更改历史提交只对本地有效,是安全的。
相比之下,SVN本地工作区和集中式版本库之间没有缓冲,一旦发现提交了错误内容, 或写了错误的提交说明,则无法更改,除非SVN管理员介入。 SVN也允许配置为可修改历史提交说明,但是一旦管理员放开此功能, 历史提交的提交说明有可能被批量、恶意更改,并且无法恢复。
误解4:SVN对中文支持更好,Git库中的中文目录和文件名会出现乱码
我也曾经这么认为,并在《Git权威指南》第3章中用了大量篇幅介绍中文支持的注意事项。 并推荐使用Cygwin作为首选客户端,以避免GBK字符集为跨平台开发的版本库引入乱码。
误解5:SVN的认证方式比Git丰富,比如可以实现LDAP认证
误解6:SVN更易上手,更易管理;而Git太难和太灵活了,不适合团队?
如果想把配置管理做好,无论是 SVN 还是 Git 都不容易,否则 《SVN Book》 以及我写 《Git权威指南》 也不会有那么厚了。
● 如何配置SVN悲观锁,以便更好地对二进制文件编辑进行协同。
● 维护合并追踪的 svn:mergeinfo 属性,以便能够正确的分支合并。还要防止无此功能的客户端对其的破坏。
● 如何使用 svn:eol-style 属性,以便正确处理跨平台开发时的文件换行符问题。
● SVN管理员如何对版本库进行整理,如撤出不当提交、修改错误的提交说明。
SVN对分支当做路径来授权,造成管理的负担(参见 前面的描述 ), 因此使用SVN实现灵活的特性分支开发、可靠的发布控制(维护分支冻结)很难。
企业应用Git的困惑之一是如何裁剪出适合自己的工作流。实际上Git本身已经给出范例:
1 | $ git help workflows |
理解Git的应用模型并选用合适的服务器端软件(如 Gitolite),可以定制出适合自己的工作流。 例如下表就是在企业中使用Git版本控制系统的典型角色划分:
系统管理员 | 配置管理员 | 发布工程师 | 整合工程师 | 模块负责人 | 开发工程师 | ||
(SYSadm) | (SCMadm) | (RELeng) | (INTegrator) | (MODmaster) | (DEV) | ||
创建版本库 | ✔ | ||||||
版本库授权 | ✔ | ||||||
版本库改名 | ✔ | ? | |||||
删除版本库 | ✔ | ? | |||||
创建Tag | ✔ | ||||||
删除Tag | ✔ | ||||||
创建一级分支 | ✔ | ||||||
为分支授权 | ✔ | ||||||
向 maint 分支强推 | ✔ | ||||||
向 master 分支强推 | ✔ | ||||||
向 maint 分支写入 | ✔ | ||||||
向 master 分支写入 | ✔ | ✔ | |||||
创建个人专有分支 | ✔ | ✔ | ✔ | ✔ | ✔ | ||
创建个人专有版本库 | ✔ | ✔ | ✔ | ✔ | ✔ | ||
为个人专有版本库授权 | ✔ | ✔ | ✔ | ✔ | ✔ |
谁说Git没有好的图形工具?SVN 有 TortoriseSVN,Git 同样有 TortoiseGit。 只不过Git的命令行太好用,使得图形操作显得笨拙。
至于Windows用做开发环境是否还有前途,看看火热的iOS、Android开发、和优雅的 MacBook 就知道了。
Git可以很容易地对比两个分支,知道一个分支中哪些提交尚未合并到另一分支,反之亦然。
1 | $ git log other.. |
1 | $ git log ..other |
针对同一个项目,Git可以设置不同层级的版本库(多版本库), 或者通过不同的分支(多分支)实现对发布的控制。
● 设置只有发布管理员才有权限推送的版本库或者分支,用于稳定发布版本的维护。
● 设置只有项目经理、模块管理员才有权推送的版本库或者分支,用用于整合测试。
审核新成员提交时,从其个人版本库或个人分支获取(fetch)提交,从提交说明、代码规范、编译测试 等多方面对提交逐一审核。审核通过执行 git merge 命令合并到开发主线中。
因为Git基于对内容的追踪而非对文件名追踪,所以遇到一方或双方对文件名更改时, Git能够很好进行自动合并或提供工具辅助合并。而SVN遇到同样问题时会产生树冲突, 解决起来很麻烦。
Git的基于DAG(有向非环图)的设计比SVN的线性提交提供更好的合并追踪, 避免不必要的冲突,提高工作效率。这是开发者选择Git、抛弃SVN的重要理由。
以为创建完毕里程碑标签(tag)便完成软件版本的发布是有风险的, 往往会由于之前的版本(维护版本)中的一些 Hotfix 提交没有合并到最新版本而造成已修复问题在新版本中重现。
Git分支和合并追踪可以解决这个问题。例如用 maint 分支跟踪最新的发行版, 当确定里程碑tag v1.6.4 为最新发行版时,在 maint 分支执行如下命令以切换到最新发行版:
1 | $ git checkout maint $ git merge --ff-only v1.6.4 |
如果合并成功,代表发行版 v1.6.4 包含了所有前一个发行版的提交。 反之说明前一个发行版某个或某些Hotfix提交尚未合并到最新发行版中。
● SVN版本库服务器端历史数据被篡改,或者硬盘故障导致历史数据被篡改时, 客户端很难发现。管理员的备份也会被污染。
● SVN作为集中式版本控制系统,存在单点故障的风险。备份版本库的任务非常繁重。