弃用SVN选择Git的理由
目录
内容
1. 多人项目中使用SVN遇到的问题
在多人共同开发的项目中,使用 SVN 会存在许多版本和协作方面的问题,如下:
-
创建分支的过程太麻烦
在项目开发中,我们可能需要经常创建以下几类分支:- bug分支:为解决某个bug而创建的分支;
比如:当前正在开发功能A,突然需要临时解决线上的一个问题,这个问题需要基于线上的某个版本进行修复; - 功能分支:为开发某个功能而创建的分支;
比如:当前正在开发功能A,还未完成,突然又来了个紧急的功能B,这就需求需要基于功能A之前的版本进行开发; - 协作分支:为使两个或多个成员协作共同完成某个任务的开发工作;
比如:有个功能需要成员A和成员B共同开发,而其它成员负责其它功能,为了不影响其它成员的开发,成员A和成员B需要一个单独的分支来进行开发;
而创建 SVN 分支需要公司中专门的管理员去申请,这个过程一般又要包括:撰写邮件、抄送相关人员 等等;
其实有很多分支都是临时且频繁的,如 bug分支、协作分支 和 开发小功能的功能分支,如果创建这些分支还需要申请的话,就过于麻烦了;
- bug分支:为解决某个bug而创建的分支;
-
新分支需要重新配置项目环境
SVN 创建的分支是在一个新的目录里,即:从目录结构方面,可以理解为 SVN 是通过复制一份目录来实现新的分支的;这意味着开发人员需要另起一个工程(因为有很多工具没有提供SVN分支的切换操作,比如:WebStorm等IDE),拉取新分支,然后重新配置工程环境;当然,也有其它办法实现在当前工作目录中切换分支,但太麻烦; -
版本节点必须通过推送到服务器才能生成
SVN 的版本节点是通过将 更改 提交到服务器后由服务器生成的,所以每次使用 SVN 进行提交时,就会将代码推送到服务器;
而 我们往往又希望被推送到服务器的代码都是已经开发完成的代码,这就使得我们要等到把某个功能点开发完成 或 bug 修复完成后,再把所有的更改作为一个版本进行提交;然而在实现的功能点开发 或 bug修复中,我们可能经常需要把某一部分改变作为一个版本,以便将来能做更细致的回滚代码,SVN无法满足这种需求。 -
提交变更前需要确保拉取了服务器中最新的代码
在成员A进行提交变更到SVN服务器时,如果其它人往服务器上提交了新的版本 或 正在提交,则 成员A 的提交会被服务器的拒绝,此时成员A必须选把服务器上最新的版本更新下来,然后再进行提交;而这种操作有可能导致 成员A 的本地项目运行不起来,因为服务器上新的提交有是其它成员开发中的代码,或是有问题的代码; -
提交版本必须要有网
SVN 的版本库是放在服务器上,客端的提交操作只是向服务推送变更,然后由服务器生成版本,如果不能正常连接到服务器的话,是无法进行SVN的一切操作的;这就使得那些远程办公(比如:在家远程解协助解决问题) 或者 出差的公无法随时随地的提交代码; -
版本历史中不能体现合并操作
SVN 不会记录任何合并操作,当提交本地修改,版本库并不能判断出本次修改是通过 svn merge 合并来的,还是手工修改来的。这就使得不能过版本记录来判断分支是否包含特定的代码,从而导致容量基于错误的分支进行开发; -
很难执行基于分支的版本开发流程
由于SVN具有上文所说的 分支创建麻烦且不宜维护等特点,导致很多很优秀的基于分支的版本开发流程(如类似下面的版本开发流程)无法得到实施!
分支流转规范图
分支流转规范图的详细内容请见Git并行工作流程规范
以上问题均是由 SVN 的以下特性和机制所致:
- 集中式版本控制系统:导致了 问题 3——5
集中式
- 由服务器创建分支:导致了 问题1、7
- 目录拷贝式的分支创建:导致了 问题2
- 单线索的提交记录,每一个提交(最原始的提交0除外)都只有一个父节点;导致了 问题6
而Git的设计理念与SVN正好相反,使得Git正好能解决这些问题,原因:
- Git是分布式版本控制系统,每个终端都是一套完整的版本控制系统,所以:
- 在本地就可生成版本节点,不需要推送到服务器;解决了问题3
- 提交变更前无需拉取服务器中最新的代码;解决了问题4
- 提交版本时无需连网;解决了问题5
分布式
- 因为每个Git终端都是一套完整的版本控制系统,所以服务器和终端都可创建分支,所以:
- 创建 Git 分支不用向服务器仓库管理器去申请;解决了问题1、问题7
- Git的分支一个提交节点的引用,并不是一个目录的拷贝,工作区仍是以前的工具,即所有的分支都共享一个工作区,所以用Git创建分支,即快捷轻量,又不需要更改工作目录;解决了问题2
- Git的每个提交都可以引用多个父提交,所以Git的提交记录是多线索的,所以Git可以记录分支的合并信息;解决了问题7
综上,使用Git解决了以上遇到的所有问题;
2. Git与SVN的其它区别
以上描述的问题及区别,只是面对普通开者,在项目运用中体现出的,其实 Git 和 SVN 还有储多区别,具体信息如下:
2.1. 版本库与工作区
SVN的工作区和版本库是截然分开的,而Git的工作区和版本库是如影随形的。
SVN的版本库和工作区是分离的:
- SVN 的工作区和版本库物理上分开:SVN的版本库和工作区是存储在不同路径下,一般是在不同的主机中,SVN的企业级部署中,版本库在服务器上,只能通过 https, http, svn 等协议访问,而不能直接被用户接触到。
- SVN的工作区是一份版本库在某个历史状态下的快照,如:版本库最新的数据检出到工作区。
- SVN的工作区中每一个目录下都包含一个名为 .svn 的控制目录(隐藏的目录),该目录的作用是:
- 标识工作区和版本库的对应关系。
- 包含一份该子目录下检出文件的原始拷贝。当文件改动的差异比较或者本地改动的回退时,可以直接参考原始拷贝而无须通过网络访问远程版本库。
- SVN 的 .svn 控制目录会引入很多麻烦:
- .svn 下的文件原始考本,会导致在目录下按照文件内容搜索时,多出一倍的搜索时间和搜索结果。
- .svn 很容易在集成时,引入产品中,尤其是 Web 应用,将 .svn 目录带入Web服务器会导致安全隐患。因为一个不允许目录浏览的Web目录,可以通过 .svn/entries 文件查看到该目录下可能存在的文件。
Git的版本库和工作区如影随形:
-
Git 的版本库和工作区在同一个目录下,工作区的根目录有一个.git的子目录,这个名为 .git的目录就是版本库本身,它是Git 用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆镜像仓库的时候,实际拷贝的就是这个目录里面的数据。所以千万要小心删除这个文件。
-
工作区中其他文件为工作区文件,可能是从 .git 中检出的,或者是要检入的,或者是运行产生的临时文件等。
-
版本库可以脱离工作区而存在。但是工作区不能脱离版本库而存在,即工作区的根目录下必须有一个名为 .git 的版本库克隆文件。
-
Git 的版本库因为就在工作区中,能直接被用户接触到。
- 用户可以编辑 .git/config 文件,修改配置,增添新的源
- 用户可以编辑 .git/info/exclude 文件,创建本地忽略…
-
Git 的工作区中只在工作区的根目录下有一个 .git 目录,此外再无任何控制目录。Git 工作区下唯一的 .git 目录是版本库,并非 .svn 的等价物,如果删除了 .git 目录,而又没有该版本库的其他镜像(克隆)的话,你破坏了整个历史,版本库也永远的失去了。
-
Git 在本地的 .git 版本库,提供了完全的改动历史。除了和其他人数据交换外,任何版本库相关的操作都在本地完成,更多的本地操作,避免了冗长的网络延迟,大大节省了时间。例如:查看 log,切换到任何历史版本等操作都无须连接网络。
-
Git如何保证安全:本地创建一个Git库,因为工作区和库是在同一个目录中,如果工作区删除了,或者所在的磁盘分区格式化了,数据不是全都没有了么?其实我们可以这样做:
- 在一个磁盘分区中创建版本库(最好是用 –bare 参数创建),然后在另外的磁盘分区中克隆一个新的作为工作区。在工作区的提交要不时的PUSH到另外分区的版本库,这样就实现了本地的数据镜像。你甚至可以在本地创建更多的版本库镜像,安全性要比SVN的一个库加上一个工作区安全。
- 另一个办法:把你的版本库共享给他人,当他人克隆了你的版本库时,你就拥有了一个异地备份。
2.2. 全局版本号和全球版本号
SVN的版本号是全局版本号,即:每一次提交都具有整个版本库全局唯一的版本号。
Git的版本号是全球唯一的。Git 对于每一次提交,通过对文件的内容或目录的结构计算出一个SHA-1 哈希值,得到一个40位的十六进制字符串,Git将此字符串作为版本号。
SVN与Git版本号比较
-
所有保存在Git 数据库中的数据都是用此40位的哈希值作索引的,而不是靠文件名。
-
使用哈希值作版本号的好处就是对于一个分布式的版本控制系统,每个人每次提交后形成的版本号都不会出现重复。另一好处是保证数据的完整性,因为哈希值是根据内容或目录结构计算出来的,所以我们还可以据此来判断数据内容是否被篡改。
-
SVN 的版本号是连续的,可以预判下一个版本号,而 Git 的版本号则不是。
因为 SVN 是集中式版本控制,很容易实现版本号的连续性。Git 是分布式的版本控制系统,而且 Git 采用 40 位长的哈希值作为版本号,每个人的提交都是各自独立完成的,没有先后之分(即使提交有先后之分,也由于PUSH/PULL的方向和时机而不同)。Git 的版本号虽然不连续,但是是有线索的,即每一个版本都有对应的父版本(一个或者两个),进而可以形成一个复杂的提交链 -
Git 的版本号简化:Git 可以使用从左面开始任意长度的字串作为简化版本号,只要该简化的版本号不产生歧义。一般采用7位的短版本号(只要不会出现重复的,你也可以使用更短的版本号)。
2.3. 部分检出
SVN可以将整个库检出到工作区,也可以将某个目录检出到工作区。对于要使用一个庞大、臃肿的版本库的用户来说,部分检出是非常方便和实际的。
但是Git只能全部检出,不支持按照目录进行的部分检出。
SVN的部分检出:
-
在SVN中,从仓库checkout的一个工作树,每个子目录下都维护着自己的.svn目录,记录着该目录中文件的修改情况以及和服务器端仓库的对应关系。所以SVN可以checkout部分路径下的内容(部分检出),而不用checkout整个版本库或分支。
-
SVN 有一条命令:svn export ,可以将 SVN 版本库的一个目录下所有内容导出到指定的目录下。SVN 需要 svn export 命令是因为该命令可以导出一个干净的目录,即不包含 .svn 目录(包含配置文件和文件原始拷贝)。
Git的检出:
-
Git 没有部分检出,这并不是说只有将整个库克隆下来才能查看文件。有很多 git 工具,提供直接浏览git库的功能,例如 gitweb, trac 的 git 版本库浏览, redmine 的 git 版本库浏览。
-
Git-submodule 可以实现版本库的模块化:Git 通过子模块处理这个问题。
子模块允许你将一个Git 仓库当作另外一个Git仓库的子目录。这允许你克隆另外一个仓库到你的项目中并且保持你的提交相对独立。 -
Git 为什么没有实现 svn export 的功能?由于git的本地仓库信息完全维护在project根目录的.git目录下,(不像svn一样,每个子目录下都有单独的.svn目录)。所以,只要clone,checkout然后删除.git目录就可以了。
2.4. 撤消操作
- 提交的撤销
在SVN中一旦完成向服务器的数据提交,你就没有办法再从客户端追回,只能在后续的提交中修正(回退或者修改)等。因为SVN作为集中式的版本控制,不能允许个人对已提交的数据进行篡改。SVN具有一个非常重要的特性就是它的信息从不丢失,即使当你删除了文件或目录,它也许从最新版本中消失了 ,但这个对象依然存在于历史的早期版本中。
Git则不同,Git是分布式版本控制系统,代码库是属于个人,允许任意修改。Git通过对提交建立数字摘要来保证提交的唯一性和不可更改性,通过版本库在多人之间的多份拷贝来保障数据的安全性。Git可以丢弃最新的一个或几个提交,使用 git reset –hard命令可以永远丢弃最新的一个或者几个提交。
-
提交说明的修改
提交后如果对提交说明不满意,如何实现对提交说明的修改:- Git可以使用命令git commit –amend修改提交说明。
- Git可以修改最后一次提交说明,并不是说不能修改历史版本的提交说明,只是修改最后一个版本提交说明拥有最简单的命令;
- Git修改提交说明,会改变提交的commit-id。即修改提交说明后,将产生一个新的提交;
- Git可以通过git reset –hard ,git commit –amend,git rebase onto 等命令来实现对历史提交的修改;
- 使用stg工具可以更为简单的修改历史提交的提交说明,包括提交内容;
- SVN也可以修改提交说明,是通过修改提交的svn:log版本属性实现的:
- 不但可以修改最后一次提交的说明,并且可以修改历史提交的提交说明;
- SVN修改提交说明是不可逆的操作,可能会造成说明被恶意修改;
- SVN缺省关闭修改提交说明的功能。管理员在设置了提交说明更改的邮件通知后,才可以打开该功能。
- Git可以使用命令git commit –amend修改提交说明。
-
修改和重构历史提交
Git可以修改和重构历史提交:使用Git本身的reset以及 rebase 命令可以修改或者重整/重构历史提交,非常灵活。使用强大的 stg 可以使得历史提交的重构更为简洁,如果您对 stg 或者 Hg/MQ 熟悉的话。
SVN 修改历史提交,只能由管理员完成。
SVN 是集中式版本控制系统,从客户端一旦完成提交,就没有办法从客户端撤销提交。但是管理员可以在服务器端完成提交的撤销和修改,但是操作过程和代价较大。
2.5. 权限管理
SVN通过对文件目录授权来实现权限管理,子目录默认继承父目录的权限。但是也有缺憾,即权限不能在分支中继承,不能对单个文件授权。例如为 /trunk及其子目录的授权,不能继承到分支或者标签中相应的目录下。
Git 的授权做不到SVN那样精细。Git的授权模型只能实现非零即壹式的授权,要么拥有全部的写权限,要么没有写权限,要么拥有整个版本库的读权限,要么禁用。
从技术上将,Git可能永远也做不到类似SVN的路径授权(读授权):
-
如果允许按照路径授权,则各个克隆的关系将不再是平等的关系,有的内容多,有的内容少,分布式的理念被破坏
-
如果只有部分路径可读,则克隆出来的提交和原始提交的提交ID可能不同。因为提交ID是和提交内容有关的,克隆中提交的部分内容被丢弃,势必提交的ID也要重新计算
-
允许全部代码可读,只允许部分代码可写,在版本控制的管理下,是没有多大实际意义的,而且导致了提交的逻辑上的不完整。
3. 缺点总结
3.1. SVN
3.1.1. 优点
-
管理方便,逻辑明确,符合一般人思维习惯。
-
易于管理,集中式服务器更能保证安全性。
-
代码一致性非常高。
-
适合开发人数不多的项目开发。
3.1.2. 缺点
-
服务器压力太大,数据库容量暴增。
-
如果不能连接到服务器上,基本上不可以工作:不能提交,还原,对比等等。
-
不适合开源开发。但是一般集中式管理的有非常明确的权限管理机制(例如分支访问限制),可以实现分层管理,从而很好的解决开发人数众多的问题。
3.2. Git
3.2.1. 优点
-
适合分布式开发,强调个体。
-
公共服务器压力和数据量都不会太大。
-
速度快、灵活。
-
任意两个开发者之间可以很容易的解决冲突。
-
离线工作。
3.2.2. 缺点
-
学习周期相对而言比较长。
-
不符合常规思维。
-
代码保密性差,一旦开发者把整个库克隆下来就可以完全公开所有代码和版本信息。