為什么說 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做版本控制。
原文出處:worldhello