为什么说 Git 比 SVN 更好
在版本控制系统的选型上,是选择Git还是SVN?
对于开源项目来说这不算问题。使用Git极大地提高了开发效率、扩大了开源项目的参与度、 增强了版本控制系统的安全性,选择Git早已是大势所趋。
但对于企业用户来说这个决心不太好下。部分原因是出于对Git的误解,部分原因是尚不了解 Git到底能给项目管理带来什么好处。希望本文能对您项目的版本控制系统选型提供帮助。
对SVN的迷信和对Git的误解
误解1:SVN只能检出(checkout)一个版本(revision)的代码,而Git却可以脱库!
这个误解是如此普遍,简直成了SVN在企业市场中封杀Git的尚方宝剑。其实稍微思考一下 这个谣言就很难传播。既然SVN能够读取授权访问的文件的每一个版本,那么就能够重组这些版本, 进而实现对版本库的完整复制。即SVN也可以脱库。
SVN脱库的工具SVN本身就提供: svnsync
。这个工具主要用于SVN的版本库镜像。 例如将版本库 http://host.name/svn/repo
脱库到本地的 dump
目录,命令如下:
$ 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“脱库”更简便。
$ git svn clone -s http://host.name/svn/repo dump
有人认为SVN可以对目录授权,从而阻止对整个版本库进行脱库操作。 下面就来看看SVN的授权究竟是否可靠。
误解2:SVN能对目录进行精细授权,而Git太不安全
SVN的目录授权对管理员来说是灾难,管理负担相当重,在分支或里程碑众多的时候很难作对。 这是因为SVN的分支和里程碑(tags)本身就是一个目录(使用目录拷贝实现的)。
例如管理员为名为demo的SVN版本库授权。一个并不太复杂的主线(/trunk)授权如下:
[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
下重建。需要在授权文件中再添加如下授权指令:
[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严格遵循约定俗成的三个顶级目录的规范。
Git对于写操作可以精细到目录和分支级别(使用Gitolite作为服务器), 但作为分布式版本库控制系统,在设计上只能实现版本库量子化的读授权。 即某用户对整个版本库要么都能读,要么对整个版本库都不能读。
那么如何控制Git版本库的读授权呢?实际上Git可以通过子模组来实现细粒度的读授权。 即在项目需要精细授权的场合,将版本库拆分为多个Git版本库进行单独授权, 再使用子模组将多个版本库整合为一个。这个操作并不复杂,而且有助于实现项目的模块化。
误解3:Git能随意改变历史提交,这对于版本控制来说是不合适的
Git对历史提交的修改只对本地提交有意义。本地提交就像是和共享版本库间的缓冲。 在未将本地提交推送到远程共享版本库之前,开发者可以后悔。可以对不完整的提交说明进行补充, 可以移除错误的提交,可以压缩合并提交等。Git对提交历史灵活的操作是Git独有的功能, 是提交审核的必备工具。
对于已经推送到远程共享服务器的提交,Git就不能再像本地一样随意更改了。 因为推送到共享版本库的提交一旦被其他程序员获取,便扩散出去, 如覆水难收,难掩众人悠悠之口。所以Git更改历史提交只对本地有效,是安全的。
相比之下,SVN本地工作区和集中式版本库之间没有缓冲,一旦发现提交了错误内容, 或写了错误的提交说明,则无法更改,除非SVN管理员介入。 SVN也允许配置为可修改历史提交说明,但是一旦管理员放开此功能, 历史提交的提交说明有可能被批量、恶意更改,并且无法恢复。
误解4:SVN对中文支持更好,Git库中的中文目录和文件名会出现乱码
我也曾经这么认为,并在《Git权威指南》第3章中用了大量篇幅介绍中文支持的注意事项。 并推荐使用Cygwin作为首选客户端,以避免GBK字符集为跨平台开发的版本库引入乱码。
一个好消息是Windows下最常用的Git客户端 msysGit 也支持Unicode了。 使用最新版本(1.7.10)的 msysGit 无需设置任何Git配置变量, 版本库中的中文文件名、目录名、提交说明都使用Unicode编码。 配合使用Unicode版的TortoiseGit(最新的1.7.9.0版本已是Unicode版), Windows用户就不再为跨平台开发的字符集问题而伤脑筋了。
误解5:SVN的认证方式比Git丰富,比如可以实现LDAP认证
我为客户配置的Git支持HTTP、SSH协议,和Gitweb。其中HTTP协议、Gitweb都使用LDAP认证, 实现统一的口令管理。并且无论是HTTP协议、SSH协议,还是Gitweb都使用同一套Gitolite授权。
误解6:SVN更易上手,更易管理;而Git太难和太灵活了,不适合团队?
如果想把配置管理做好,无论是 SVN 还是 Git 都不容易,否则 《SVN Book》 以及我写 《Git权威指南》 也不会有那么厚了。
觉得SVN更简单的,看看下面的错误你有没有犯?
- 很多公司的SVN版本库没有遵照约定俗成的三个顶级目录。
- 如何配置SVN悲观锁,以便更好地对二进制文件编辑进行协同。
- 维护合并追踪的 svn:mergeinfo 属性,以便能够正确的分支合并。还要防止无此功能的客户端对其的破坏。
- SVN如何正确的反删除,直接添加删除的文件是不对的。
- 如何使用 svn:eol-style 属性,以便正确处理跨平台开发时的文件换行符问题。
- SVN管理员如何对版本库进行整理,如撤出不当提交、修改错误的提交说明。
- 版本库的安全性问题,如何做好版本库的备份。
SVN对分支当做路径来授权,造成管理的负担(参见 前面的描述 ), 因此使用SVN实现灵活的特性分支开发、可靠的发布控制(维护分支冻结)很难。
企业应用Git的困惑之一是如何裁剪出适合自己的工作流。实际上Git本身已经给出范例:
$ git help workflows
理解Git的应用模型并选用合适的服务器端软件(如 Gitolite),可以定制出适合自己的工作流。 例如下表就是在企业中使用Git版本控制系统的典型角色划分:
系统管理员 | 配置管理员 | 发布工程师 | 整合工程师 | 模块负责人 | 开发工程师 | ||
---|---|---|---|---|---|---|---|
(SYSadm) | (SCMadm) | (RELeng) | (INTegrator) | (MODmaster) | (DEV) | ||
创建版本库 | ✔ | ||||||
版本库授权 | ✔ | ||||||
版本库改名 | ✔ | ? | |||||
删除版本库 | ✔ | ? | |||||
创建Tag | ✔ | ||||||
删除Tag | ✔ | ||||||
创建一级分支 | ✔ | ||||||
为分支授权 | ✔ | ||||||
向 maint 分支强推 | ✔ | ||||||
向 master 分支强推 | ✔ | ||||||
向 maint 分支写入 | ✔ | ||||||
向 master 分支写入 | ✔ | ✔ | |||||
创建个人专有分支 | ✔ | ✔ | ✔ | ✔ | ✔ | ||
创建个人专有版本库 | ✔ | ✔ | ✔ | ✔ | ✔ | ||
为个人专有版本库授权 | ✔ | ✔ | ✔ | ✔ | ✔ |
再来谈谈Git的使用,实际上Git的设计模型非常简单,理解了其设计思想,就可以很容易地掌握 git reset
, git checkout
, git rebase
, git push
, git pull
等命令。
误解7:程序员不喜欢命令行
谁说Git没有好的图形工具?SVN 有 TortoriseSVN,Git 同样有 TortoiseGit。 只不过Git的命令行太好用,使得图形操作显得笨拙。
至于Windows用做开发环境是否还有前途,看看火热的iOS、Android开发、和优雅的 MacBook 就知道了。
Git能做到,而SVN难以做到的事情
Git分支功能最为强大,分支管理能力让SVN望尘莫及
Git可以很容易地对比两个分支,知道一个分支中哪些提交尚未合并到另一分支,反之亦然。
-
查看当前分支比other分支多了哪些提交:
$ git log other..
-
查看other分支比当前分支多了哪些提交:
$ git log ..other
我不认为SVN的分支是真正的分支,因为分支最基本的提交隔离SVN就没能实现。 在SVN中一次提交可以同时更改主线(/trunk)和分支中的内容, 所以判断一个分支中哪些提交未合并到另外的分支,完全不能对SVN抱有希望。
Git可以实现更好的发布控制
针对同一个项目,Git可以设置不同层级的版本库(多版本库), 或者通过不同的分支(多分支)实现对发布的控制。
- 设置只有发布管理员才有权限推送的版本库或者分支,用于稳定发布版本的维护。
- 设置只有项目经理、模块管理员才有权推送的版本库或者分支,用用于整合测试。
隔离开发,提交审核
如何对团队中的新成员的开发进行审核呢?在Git服务器上可以实现用户自建分支和自建版本库的功能, 这样团队中的新成员既能将本地提交推送到服务器以对工作进行备份, 又能够方便团队中的其他成员对自己的提交进行审核。
审核新成员提交时,从其个人版本库或个人分支获取(fetch)提交,从提交说明、代码规范、编译测试 等多方面对提交逐一审核。审核通过执行 git merge
命令合并到开发主线中。
对合并更好的支持,更少的冲突,更好的冲突解决
因为Git基于对内容的追踪而非对文件名追踪,所以遇到一方或双方对文件名更改时, Git能够很好进行自动合并或提供工具辅助合并。而SVN遇到同样问题时会产生树冲突, 解决起来很麻烦。
Git的基于DAG(有向非环图)的设计比SVN的线性提交提供更好的合并追踪, 避免不必要的冲突,提高工作效率。这是开发者选择Git、抛弃SVN的重要理由。
保证已修复Bug不再重现
以为创建完毕里程碑标签(tag)便完成软件版本的发布是有风险的, 往往会由于之前的版本(维护版本)中的一些 Hotfix 提交没有合并到最新版本而造成已修复问题在新版本中重现。
Git分支和合并追踪可以解决这个问题。例如用 maint 分支跟踪最新的发行版, 当确定里程碑tag v1.6.4 为最新发行版时,在 maint 分支执行如下命令以切换到最新发行版:
$ git checkout maint
$ git merge --ff-only v1.6.4
如果合并成功,代表发行版 v1.6.4 包含了所有前一个发行版的提交。 反之说明前一个发行版某个或某些Hotfix提交尚未合并到最新发行版中。
版本库的安全性
SVN版本库安全性很差,是管理员头痛的问题。
- SVN版本库服务器端历史数据被篡改,或者硬盘故障导致历史数据被篡改时, 客户端很难发现。管理员的备份也会被污染。
- SVN作为集中式版本控制系统,存在单点故障的风险。备份版本库的任务非常繁重。
Git在这方面完胜SVN。首先Git是分布式版本控制系统,每个用户都相当于一份备份, 管理员无需为数据备份而担心。再有Git中包括提交、文件内容等都通过SHA1哈希保证数据的完整性, 任何恶意篡改历史数据都会被及时发现从而被挫败。
更多的十条喜欢Git的理由
更多的十条喜欢Git的理由,参见 《Git权威指南》 第11-21页。
- 异地协同工作。
- 现场版本控制。
- 重写提交说明。
- 无尽的后悔药。
- 更好用的提交列表。
- 更好的差异比较。
- 工作进度保存。
- 作为SVN前端实现移动办公。
- 无处不在的分页器。
- 快。
什么情况推荐使用SVN
SVN具有的悲观锁的功能,能够实现一个用户在编辑时对文件进行锁定,阻止多人同时编辑 一个文件。这一悲观锁的功能是 Git 所不具备的。对于以二进制文件 (Word文档、PPT演示稿) 为主的版本库,为避免多人同时编辑造成合并上的困难, 建议使用SVN做版本控制。
GIT和SVN之间的五个基本区别
开源中国 Git 代码托管平台 —— http://git.oschina.net
如果你在读这篇文章,说明你跟大多数开发者一样对GIT感兴趣,如果你还没有机会来试一试GIT,我想现在你就要了解它了。
GIT不仅仅是个版本控制系统,它也是个内容管理系统(CMS),工作管理系统等。如果你是一个具有使用SVN背景的人,你需要做一定的思想转换,来适应GIT提供的一些概念和特征。所以,这篇文章的主要目的就是通过介绍GIT能做什么、它和SVN在深层次上究竟有什么不同来帮助你认识它。
那好,这就开始吧…
1.GIT是分布式的,SVN不是:
这是GIT和其它非分布式的版本控制系统,例如SVN,CVS等,最核心的区别。如果你能理解这个概念,那么你就已经上手一半了。需要做一点声明,GIT并不是目前第一个或唯一的分布式版本控制系统。还有一些系统,例如Bitkeeper, Mercurial等,也是运行在分布式模式上的。但GIT在这方面做的更好,而且有更多强大的功能特征。
GIT跟SVN一样有自己的集中式版本库或服务器。但,GIT更倾向于被使用于分布式模式,也就是每个开发人员从中心版本库/服务器上chect out代码后会在自己的机器上克隆一个自己的版本库。可以这样说,如果你被困在一个不能连接网络的地方时,就像在飞机上,地下室,电梯里等,你仍然能够提 交文件,查看历史版本记录,创建项目分支,等。对一些人来说,这好像没多大用处,但当你突然遇到没有网络的环境时,这个将解决你的大麻烦。
同样,这种分布式的操作模式对于开源软件社区的开发来说也是个巨大的恩赐,你不必再像以前那样做出补丁包,通过email方式发送出去,你只需要创建一个分支,向项目团队发送一个推请求。这能让你的代码保持最新,而且不会在传输过程中丢失。GitHub.com就是一个这样的优秀案例。
有些谣言传出来说subversion将来的版本也会基于分布式模式。但至少目前还看不出来。
2.GIT把内容按元数据方式存储,而SVN是按文件:
所有的资源控制系统都是把文件的元信息隐藏在一个类似.svn,.cvs等的文件夹里。如果你把.git目录的 体积大小跟.svn比较,你会发现它们差距很大。因为,.git目录是处于你的机器上的一个克隆版的版本库,它拥有中心版本库上所有的东西,例如标签,分 支,版本记录等。
3.GIT分支和SVN的分支不同:
分支在SVN中一点不特别,就是版本库中的另外的一个目录。如果你想知道是否合并了一个分支,你需要手工运行像这样的命令svn propget svn:mergeinfo,来确认代码是否被合并。感谢Ben同学指出这个特征。所以,经常会发生有些分支被遗漏的情况。
然而,处理GIT的分支却是相当的简单和有趣。你可以从同一个工作目录下快速的在几个分支间切换。你很容易发现未被合并的分支,你能简单而快捷的合并这些文件。
4.GIT没有一个全局的版本号,而SVN有:
目前为止这是跟SVN相比GIT缺少的最大的一个特征。你也知道,SVN的版本号实际是任何一个相应时间的源代 码快照。我认为它是从CVS进化到SVN的最大的一个突破。因为GIT和SVN从概念上就不同,我不知道GIT里是什么特征与之对应。如果你有任何的线 索,请在评论里奉献出来与大家共享。
更新:有些读者指出,我们可以使用GIT的SHA-1来唯一的标识一个代码快照。这个并不能完全的代替SVN里容易阅读的数字版本号。但,用途应该是相同的。
5.GIT的内容完整性要优于SVN:
GIT的内容存储使用的是SHA-1哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。这里有一个很好的关于GIT内容完整性的讨论 –http://stackoverflow.com/questions/964331/git-file-integrity
GIT和SVN之间只有这五处不同吗?当然不是。我想这5个只是“最基本的”和“最吸引人”的,我只想到这5点。如果你发现有比这5点更有趣的,请共享出来,欢迎。
相关链接
- Git 的详细介绍:请点这里
- Git 的下载地址:请点这里
-
SVN与Git比较
摘要
Svn是目前得到大多数人认可,使用得最多的版本控制管理工具,而Git的优势在于易于本地增加分支和分布式的特性,可离线提交,解决了异地团队协同开发等svn不能解决的问题。本文就这两种版本控制工具的异同点作详细介绍。目录
摘要: 1
一、 集中式 vs 分布式 2
1. Subversion属于集中式的版本控制系统 2
2. Git属于分布式的版本控制系统 4
二、 版本库与工作区 6
1. SVN的版本库和工作区是分离的 7
2 .Git 的版本库和工作区如影随形 7
三、 全局版本号和全球版本号 8
1. SVN与Git版本号比较 9
四、 部分检出 9
1. SVN的部分检出 10
2. Git的检出 10
五、 更新和提交 10
1.更新操作 11
2.SVN中的commit命令 11
3.Git中的暂存区域(stage) 11
六、 分支和里程碑的实现 14
1.Subversion的分支/里程碑 14
2.Git 的轻量级分支和里程碑 14
3.多分支间的切换 15
七、 分支合并 16
1. SVN的分支合并 16
2.Git的分支合并 16
八、 撤消操作 19
1.提交的撤销 19
2.提交说明的修改 20
3.修改和重构历史提交 20
九、 权限管理 21
十、 客户端操作 22
1.TortoiseSVN 22
2.Git客户端 23
十一、 Svn与Git协作 26
1.git svn 27
一、 集中式vs分布式
两种不同类型的版本控制系统:集中式和分布式
1. Subversion属于集中式的版本控制系统
集中式的版本控制系统都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。多年以来,这已成为版本控制系统的标准做法。
图:1.1 集中化的版本控制系统
这种做法带来了许多好处,特别是相较于老式的本地VCS 来说。现在,每个人都可以一定程度上看到项目中的其他人正在做些什么。而管理员也可以轻松掌控每个开发者的权限。
事分两面,有好有坏。这么做最显而易见的缺点是中央服务器的单点故障。若是宕机一小时,那么在这一小时内,谁都无法提交更新、还原、对比等,也就无法协同工作。如果中央服务器的磁盘发生故障,并且没做过备份或者备份得不够及时的话,还会有丢失数据的风险。最坏的情况是彻底丢失整个项目的所有历史更改记录,被客户端提取出来的某些快照数据除外,但这样的话依然是个问题,你不能保证所有的数据都已经有人提取出来。
Subversion原理上只关心文件内容的具体差异。每次记录有哪些文件作了更新,以及都更新了哪些行的什么内容。如下图所示:
图:1.2 集中式版本控制系统记录文件内容的差异
Subversion的特点概括起来主要由以下几条:
• 每个版本库有唯一的URL(官方地址),每个用户都从这个地址获取代
码和数据;
• 获取代码的更新,也只能连接到这个唯一的版本库,同步以取得最新数据;
• 提交必须有网络连接(非本地版本库);
• 提交需要授权,如果没有写权限,提交会失败;
• 提交并非每次都能够成功。如果有其他人先于你提交,会提示“改动基于
过时的版本,先更新再提交”… 诸如此类;
• 冲突解决是一个提交速度的竞赛:手快者,先提交,平安无事;手慢者,
后提交,可能遇到麻烦的冲突解决。
2. Git属于分布式的版本控制系统
自2005年诞生于以来,Git 日臻成熟完善,在高度易用的同时,仍然保留着初期设定的目标。它的速度飞快,极其适合管理大项目,它还有着令人难以置信的非线性分支管理系统,可以应付各种复杂的项目开发需求。
与SVN不同,Git记录版本历史只关心文件数据的整体是否发生变化。Git 并不保存文件内容前后变化的差异数据。实际上,Git 更像是把变化的文件作快照后,记录在一个微型的文件系统中。每次提交更新时,它会纵览一遍所有文件的指纹信息并对文件作一快照,然后保存一个指向这次快照的索引。为提高性能,若文件没有变化,Git 不会再次保存,而只对上次保存的快照作一连接。Git 的工作方式如下图所示。