|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "Why Git is better than SVN" |
| 4 | +--- |
| 5 | + |
| 6 | +在版本控制系统的选型上,是选择Git还是SVN? |
| 7 | + |
| 8 | +对于开源项目来说这不算问题。使用Git极大地提高了开发效率、扩大了开源项目的参与度、 |
| 9 | +增强了版本控制系统的安全性,选择Git早已是大势所趋。 |
| 10 | + |
| 11 | +但对于企业用户来说这个决心不太好下。部分原因是出于对Git的误解,部分原因是尚不了解 |
| 12 | +Git到底能给项目管理带来什么好处。希望本文能对您项目的版本控制系统选型提供帮助。 |
| 13 | + |
| 14 | +## 对SVN的迷信和对Git的误解 ## |
| 15 | + |
| 16 | +### 误解1:SVN只能检出(checkout)一个版本(revision)的代码,而Git却可以脱库! ### |
| 17 | + |
| 18 | +这个误解是如此普遍,简直成了SVN在企业市场中封杀Git的尚方宝剑。其实稍微思考一下 |
| 19 | +这个谣言就很难传播。既然SVN能够读取授权访问的文件的每一个版本,那么就能够重组这些版本, |
| 20 | +进而实现对版本库的完整复制。即SVN也可以脱库。 |
| 21 | + |
| 22 | +SVN脱库的工具SVN本身就提供: ``svnsync`` 。这个工具主要用于SVN的版本库镜像。 |
| 23 | +例如将版本库 ``http://host.name/svn/repo`` 脱库到本地的 ``dump`` 目录,命令如下: |
| 24 | + |
| 25 | + $ svnadmin create dump |
| 26 | + $ printf '#!/bin/sh\nexit 0\n' > dump/hooks/pre-revprop-change |
| 27 | + $ chmod a+x dump/hooks/pre-revprop-change |
| 28 | + $ svnsync init file://$(pwd)/dump http://host.name/svn/repo |
| 29 | + $ svnsync sync file://$(pwd)/dump |
| 30 | + |
| 31 | +如果使用 ``git-svn`` 则为SVN“脱库”更简便。 |
| 32 | + |
| 33 | + $ git svn clone -s http://host.name/svn/repo dump |
| 34 | + |
| 35 | +有人认为SVN可以对目录授权,从而阻止对整个版本库进行脱库操作。 |
| 36 | +下面就来看看SVN的授权究竟是否可靠。 |
| 37 | + |
| 38 | +### 误解2:SVN能对目录进行精细授权,而Git太不安全 ### |
| 39 | + |
| 40 | +SVN的目录授权对管理员来说是灾难,管理负担相当重,在分支或里程碑众多的时候很难作对。 |
| 41 | +这是因为SVN的分支和里程碑(tags)本身就是一个目录(使用目录拷贝实现的)。 |
| 42 | + |
| 43 | +例如管理员为名为demo的SVN版本库授权。一个并不太复杂的主线(/trunk)授权如下: |
| 44 | + |
| 45 | + [demo:/trunk] |
| 46 | + @demo-admin = rw |
| 47 | + @leaders = r |
| 48 | + |
| 49 | + [demo:/trunk/doc] |
| 50 | + @demo-dev = rw |
| 51 | + @designers = rw |
| 52 | + |
| 53 | + [demo:/trunk/src/apps] |
| 54 | + @demo-dev = rw |
| 55 | + |
| 56 | + [demo:/trunk/src/common] |
| 57 | + @demo-dev = rw |
| 58 | + |
| 59 | + [demo:/trunk/src/html] |
| 60 | + @designers = rw |
| 61 | + |
| 62 | + [demo:/trunk/src/secret] |
| 63 | + * = |
| 64 | + @demo-admin = rw |
| 65 | + jiangxin = rw |
| 66 | + |
| 67 | +如果项目创建了维护分支 ``/branches/1.x`` ,若和 ``/trunk`` 授权相同,则需要将上述授权在 |
| 68 | +``/branches/1.x`` 下重建。需要在授权文件中再添加如下授权指令: |
| 69 | + |
| 70 | + [demo:/branches/1.x] |
| 71 | + @demo-admin = rw |
| 72 | + @leaders = r |
| 73 | + |
| 74 | + [demo:/branches/1.x/doc] |
| 75 | + @demo-dev = rw |
| 76 | + @designers = rw |
| 77 | + |
| 78 | + [demo:/branches/1.x/src/apps] |
| 79 | + @demo-dev = rw |
| 80 | + |
| 81 | + [demo:/branches/1.x/src/common] |
| 82 | + @demo-dev = rw |
| 83 | + |
| 84 | + [demo:/branches/1.x/src/html] |
| 85 | + @designers = rw |
| 86 | + |
| 87 | + [demo:/branches/1.x/src/secret] |
| 88 | + * = |
| 89 | + @demo-admin = rw |
| 90 | + jiangxin = rw |
| 91 | + |
| 92 | +如果版本库的分支和里程碑越来越多,配置的工作量相当客观,稍有不慎不是授权文件格式破坏, |
| 93 | +就是造成开放授权。 |
| 94 | + |
| 95 | +我曾经写过SVN路径授权的补丁,并写了一款SVN版本库管理的开源软件 |
| 96 | +(参见 [《pySvnManager手册》](http://www.ossxp.com/doc/pysvnmanager/user-guide/user-guide.html) ), |
| 97 | +但想完美解决这个问题很难。我的一个设想是在SVN对分支和里程碑授权检查时的缺省检查 |
| 98 | +``/trunk`` 的授权,但这样的实现要求使用SVN严格遵循约定俗成的三个顶级分支的规范。 |
| 99 | + |
| 100 | +Git对于写操作可以精细到目录和分支级别(使用Gitolite作为服务器), |
| 101 | +但作为分布式版本库控制系统,在设计上只能实现版本库量子化的读授权。 |
| 102 | +即某用户对整个版本库要么都能读,要么对整个版本库都不能读。 |
| 103 | + |
| 104 | +Git通过子模组来实现细粒度的读授权。即在项目需要精细授权的场合, |
| 105 | +采用将版本库拆分为多个Git版本库进行单独授权,再使用子模组将多个版本库整合为一个。 |
| 106 | +这个操作并不复杂,而且有助于实现项目的模块化。 |
| 107 | + |
| 108 | +### 误解3:Git能随意改变历史提交,这对于版本控制来说是不合适的 ### |
| 109 | + |
| 110 | +Git对历史提交的修改只对本地提交有意义。本地提交就像是本地版本库和共享版本库间的缓冲。 |
| 111 | +在未将本地提交推送到远程共享版本库之前,开发者可以后悔。可以对不完整的提交说明进行补充, |
| 112 | +可以移除错误的提交,可以压缩合并提交等。Git对提交历史灵活的操作是Git独有的功能, |
| 113 | +是提交审核的必备工具。 |
| 114 | + |
| 115 | +对于已经推送到远程共享服务器的提交,Git就不能再像本地一样随意更改了。 |
| 116 | +因为推送到共享版本库的提交一旦被其他程序员获取,便扩散出去, |
| 117 | +如覆水难收,难掩众人悠悠之口。所以Git更改历史提交只对本地有效,是安全的。 |
| 118 | + |
| 119 | +相比之下,SVN本地工作区和集中式版本库之间没有缓冲,一旦发现提交了错误内容, |
| 120 | +或写了错误的提交说明,则无法更改,除非SVN管理员介入。 |
| 121 | +SVN也允许配置为可修改历史提交说明,但是一旦管理员放开此功能, |
| 122 | +历史提交的提交说明有可能被批量、恶意更改,并且无法恢复。 |
| 123 | + |
| 124 | +### 误解4:SVN对中文支持更好,Git库中的中文目录和文件名会出现乱码 ### |
| 125 | + |
| 126 | +我也曾经这么认为,并在《Git权威指南》第3章中用了大量篇幅介绍中文支持的注意事项。 |
| 127 | +一个好消息是最新版本(1.7.10)的 msysGit 已经支持Unicode, |
| 128 | +即中文的文件名、目录名、提交说明使用Unicode编码保存在版本库中。 |
| 129 | +Windows用户配合使用Unicode版的TortoiseGit, |
| 130 | +就不再为跨平台开发的字符集问题而伤脑筋了。 |
| 131 | + |
| 132 | +### 误解5:SVN的认证配置比Git简单,比如可以实现LDAP认证 ### |
| 133 | + |
| 134 | +我为客户配置的Git支持HTTP和SSH协议及Gitweb,其中HTTP协议和Gitweb都使用LDAP认证,实现统一的口令管理。 |
| 135 | +并且无论是HTTP协议、SSH协议还是Gitweb都使用同一套Gitolite授权。 |
| 136 | + |
| 137 | +### 误解6:SVN更易上手,Git太难了 ### |
| 138 | + |
| 139 | +如果想把配置管理做好,SVN并不容易,否则 [《SVN Book》](http://svnbook.red-bean.com/) 也不会有那么厚了。 |
| 140 | + |
| 141 | +* 很多公司的SVN版本库没有遵照约定俗成的三个顶级目录。 |
| 142 | +* 配置SVN悲观锁,以便更好地对二进制文件编辑进行协同。 |
| 143 | +* 维护合并追踪的 svn:mergeinfo 属性,以便能够正确的分支合并。还要防止无此功能的客户端对其的破坏。 |
| 144 | +* SVN如何正确的反删除,直接添加删除的文件是不对的。 |
| 145 | +* 如何使用 svn:eol-style 属性,以便正确处理跨平台开发时的文件换行符问题。 |
| 146 | +* SVN管理员如何对版本库进行整理,及如何做好版本库的备份。 |
| 147 | + |
| 148 | +Git的设计模型非常简单,理解了其设计思想,就可以很容易地掌握 ``git reset``, |
| 149 | +``git checkout``, ``git rebase``, ``git push``, ``git pull`` 等命令。 |
| 150 | + |
| 151 | +### 误解7:程序员不喜欢命令行 ### |
| 152 | + |
| 153 | +谁说Git没有好的图形工具?SVN 有 TortoriseSVN,Git 同样有 TortoiseGit。 |
| 154 | +只不过Git的命令行太好用,使得图形操作显得笨拙。 |
| 155 | + |
| 156 | +至于Windows用做开发环境是否还有前途,看看火热的iOS、Android开发、和优雅的 MacBook 就知道了。 |
| 157 | + |
| 158 | +## Git能做到,而SVN难以做到的事情 ## |
| 159 | + |
| 160 | +### Git分支功能最为强大,分支管理能力让SVN望尘莫及 ### |
| 161 | + |
| 162 | +Git可以很容易地对比两个分支,知道一个分支中哪些提交尚未合并到另一分支,反之亦然。 |
| 163 | + |
| 164 | +* 查看当前分支比other分支多了哪些提交: |
| 165 | + |
| 166 | + $ git log other.. |
| 167 | + |
| 168 | +* 查看other分支比当前分支多了哪些提交: |
| 169 | + |
| 170 | + $ git log ..other |
| 171 | + |
| 172 | +我不认为SVN的分支是真正的分支,因为分支最基本的提交隔离SVN就没能实现。 |
| 173 | +在SVN中一次提交可以同时更改主线(/trunk)和分支中的内容, |
| 174 | +所以判断一个分支中哪些提交未合并到另外的分支,完全不能对SVN抱有希望。 |
| 175 | + |
| 176 | +### Git可以实现更好的发布控制 ### |
| 177 | + |
| 178 | +针对同一个项目,Git可以设置不同层级的版本库(多版本库), |
| 179 | +或者通过不同的分支(多分支)实现对发布的控制。 |
| 180 | + |
| 181 | +即设置只有发布管理员才有权限推送的用于稳定版本发布的版本库或者分支, |
| 182 | +设置只有项目经理才有权推送的用于整合测试的版本库或分支,以实现对产品发布的控制。 |
| 183 | + |
| 184 | +### 隔离开发,提交审核 ### |
| 185 | + |
| 186 | +如何对团队中的新成员的开发进行审核呢?在Git服务器上可以实现用户自建分支和自建版本库的功能, |
| 187 | +这样团队中的新成员既能将本地提交推送到服务器以对工作进行备份, |
| 188 | +又能够方便团队中的其他成员对自己的提交进行审核。 |
| 189 | + |
| 190 | +审核新成员提交时,从其个人版本库或个人分支获取(fetch)提交,从提交说明、代码规范、编译测试 |
| 191 | +等多方面对提交逐一审核。审核通过执行 ``git merge`` 命令合并到开发主线中。 |
| 192 | + |
| 193 | +### 更少的冲突,更好的冲突解决 ### |
| 194 | + |
| 195 | +因为Git基于对内容的追踪而非对文件名追踪,所以遇到一方或双方对文件名更改时, |
| 196 | +Git能够很好进行自动合并,而SVN面则会遇到树冲突,解决起来很麻烦。 |
| 197 | + |
| 198 | +### 更好的合并追踪,以保证已修复Bug不再重现 ### |
| 199 | + |
| 200 | +以为创建完毕里程碑标签(tag)便完成软件版本的发布是有风险的, |
| 201 | +往往会由于之前的版本(维护版本)中的一些 Hotfix |
| 202 | +提交没有合并到最新版本而造成已修复问题在新版本中重现。 |
| 203 | + |
| 204 | +Git分支和合并追踪可以解决这个问题。例如用 maint 分支跟踪最新的发行版, |
| 205 | +当确定里程碑tag v1.6.4 为最新发行版时,在 maint |
| 206 | +分支执行如下命令以切换到最新发行版: |
| 207 | + |
| 208 | + $ git checkout maint |
| 209 | + $ git merge --ff-only v1.6.4 |
| 210 | + |
| 211 | +如果合并成功,代表发行版 v1.6.4 包含了所有前一个发行版的提交。 |
| 212 | +反之说明前一个发行版某个或某些Hotfix提交尚未合并到最新发行版中。 |
| 213 | + |
| 214 | +### 更多的十条喜欢Git的理由 ### |
| 215 | + |
| 216 | +更多的十条喜欢Git的理由,参见 [《Git权威指南》](http://www.worldhello.net/gotgit/) 第11-21页。 |
| 217 | + |
| 218 | +* 异地协同工作。 |
| 219 | +* 现场版本控制。 |
| 220 | +* 重写提交说明。 |
| 221 | +* 无尽的后悔药。 |
| 222 | +* 更好用的提交列表。 |
| 223 | +* 更好的差异比较。 |
| 224 | +* 工作进度保存。 |
| 225 | +* 作为SVN前端实现移动办公。 |
| 226 | +* 无处不在的分页器。 |
| 227 | +* 快。 |
| 228 | + |
| 229 | +## 什么情况推荐使用SVN ## |
| 230 | + |
| 231 | +SVN具有的悲观锁的功能,能够实现一个用户在编辑时对文件进行锁定,阻止多人同时编辑 |
| 232 | +一个文件。这一悲观锁的功能是 Git 所不具备的。对于以二进制文件 |
| 233 | +(Word文档、PPT演示稿) 为主的版本库,为避免多人同时编辑造成合并上的困难, |
| 234 | +建议使用SVN做版本控制。 |
0 commit comments