一、git 解決的問題
1. 版本控制
沒有 git 的時候......假如你寫了一篇文章,要刪除某一個段落,為了防止以后需要找回這個段落,你需要為這個文件備份。
如果你第一次刪除了段落1,第二次修改了段落2,第三次添加了段落4,則需要為每一個版本都建立一個備份。(還有一個辦法是注釋掉修改的代碼,在下面寫添加的代碼,但是時間久了就弄不清楚了)你的文件可能看上去是這個樣子:
版本多了,你想恢復修改前的段落2,卻不記得是那個備份,所以你需要一個一個文件去翻閱查找,非常麻煩。
這時你可能會寫一個說明文檔,文檔中記錄了:副本(1)——刪除段落1;副本(2)——修改段落2;副本(3)添加段落3...... 有了這些摘要你能很快定位到需要的文件。
可是修改了第112行,或者是修改了114、118和119行呢?你不可能有耐心記錄下每個版本修改了哪幾行,所以這些修改很難定位,找起來特別費神。
版本管理可以解決以上述所有麻煩(這些麻煩是我在未接觸版本管理時寫代碼真實遇到的)。每次有新的修改,添加版本說明并提交。從 git log 信息中你可以看到所有版本和版本摘要,看起來就像下面這樣(這就不用手動備份文件和寫摘要文檔了):
版本 | 用戶 | 說明 | 日期 |
---|---|---|---|
1 | 張三 | 刪除段落1 | 2017.1.1 |
2 | 李四 | 添加段落4 | 2017.1.3 |
對于任意兩個版本,用 git diff 命令,可以比較兩個文件的不同,這樣就解決了修改定位的問題。完全自動化,Cool!
2. 代碼同步
我曾經和同學 H 合作寫一個小插件,剛開始沒有使用 git 或者 svn,一度覺得自己沒辦法干活了。比如我編輯MainServlet.java,他編輯 index.jsp,現在需要運行看效果,我們就要通過 qq 把自己修改的文件傳給對方,然后手動覆蓋。有時我和他同時修改 index.jsp,就得讓他把他的文件發給我,我合并兩人的修改后,再把合并后的文件發給他。后來改動多了,我們也不記得我們修改了哪些東西,完全混亂,于是就在同一臺機器上你敲一會兒,我嗑瓜子,再我敲一會兒,你嗑瓜子,嚴重影響了效率。有時老板要檢查我們的進度,或者要幫助我們解決bug,我們就得將最新的版本打成壓縮包 qq 傳給總監。
后來我們用了 SVN,這些麻煩就全沒有了。要同步兩人的進度,只需要一人commit,另一人checkout,根本不需要自己記改了哪些東西。解決沖突同樣是相對麻煩的事情,但是先 checkout,修改沖突的地方再提交,步驟清晰且有詳細記錄,比之前好了不知道多少。老板需要最新的代碼時,也無需找我們要,直接從倉庫里下載下來就好啦!
3. 分支管理
分支在實際中有什么用呢?假設你準備開發一個新功能,但是需要兩周才能完成,第一周你寫了50%的代碼,如果立刻提交,由于代碼還沒寫完,不完整的代碼庫會導致別人不能干活了。如果等代碼全部寫完再一次提交,又存在丟失每天進度的巨大風險。現在有了分支,就不用怕了。
二、Git 與 SVN比較
1. 分布式與集中式
git 是分布式版本管理系統,SVN 是集中式版本管理系統。
集中式:
在沒有聯網的情況下不能 commit 操作。只有中央服務器擁有完整倉庫。
分布式:
每臺機器上安裝了同一套 git 程序,所以都能持有完整的倉庫。可以方便的克隆倉庫,倉庫與倉庫之間可以互相同步。
svn 并不能克隆倉庫,只能從倉庫中下載文件,即使你用 TortoiseSVN 創建了一個本地倉庫,你和中央服務器的倉庫也無法進行關聯。
git 分布式為什么比 svn 集中式好?
svn 不聯網就不能 commit。假設小明公司的中央服務器部署在內網,回家了便無法訪問,但小明周末仍然勤奮地在家寫代碼。周六晚他想將這一天寫的東西 commit,但是并不能夠。周天他將代碼改出bug來了,想回退到周六晚的版本,事實殘忍地拒絕了他,并讓他手撕bug。
集中式中,中央服務器掛了,倉庫就掛了。分布式中,掛了一臺機器,還能從另一臺機器恢復倉庫。不過據最新消息證實,有些 svn 的客戶端現在也搞分布式技術,一個倉庫同時存在幾臺服務器上,一個服務器提交了修改,幾個服務器自動同步,但是這仍改不了我們工作的筆記本上沒有倉庫的事實。如果我們筆記本上倉庫也能和遠程同步,等等,那不就和 git 的模式一模一樣了嗎?瞎折騰啥呢少年,不如一開始就用 git。
git 中我們可以 commit 到本地倉庫而不 push 到遠程倉庫。假設我的 getDataService.java 只寫了一半,仍然會報錯,這時候用 svn 將修改提交,就會影響到隊友(隊友更新代碼 了以后說臥槽怎么 run 不起來了)。更可怕的是我修改的是已有的文件。但是不提交,我又面臨著和小明一樣丟失進度的風險。在 git 中只 commit 不 push 就能解決這個問題。在我的第一個實習公司,那時想自己改一改代碼玩,就自己在本地創建了一個分支,隨便瞎折騰,只要不提交,別人完全不知道我干了啥,不會影響到任何人。
2. 分支管理
git 的分支功能比 SVN 好用得多。
其他版本控制系統如SVN等都有分支管理,但是用過之后你會發現,這些版本控制系統創建和切換分支比蝸牛還慢,簡直讓人無法忍受,結果分支功能成了擺設,大家都不去用。
SVN 使用分支,一般來說是我們直接手動管理文件夾,見 trunk+-branches+-tags 目錄結構的使用。舉個例子,如果需要基于分支1新建一個分支2,我們就手動將分支1的文件夾拷貝一份命名為分支2。如果要切換分支,我們就切換文件夾。類似這些操作,實際上是將文件夾“視作”分支,用了 move、cd 等操作,至于合并分支的 merge 命令,大概沒什么人敢用吧。
但Git的分支是與眾不同的,無論創建、切換和刪除分支,Git在1秒鐘之內就能完成!無論你的版本庫是1個文件還是1萬個文件。
3. 權限控制
SVN 可以嚴格控制每個目錄的訪問權限(還有一套很變態的規則),git 只能控制每個倉庫分支的權限。SVN 可以 checkout 任意子目錄,而 git 只能 clone 某個分支。
實際上,SVN的目錄權限控制和 checkout 子目錄主要是為了方便了基于trunk+-branches+-tags 結構的開發。現在 git 既然已經有了好用的分支功能,目錄權限控制就沒有那么必要了。
補充:
- 在任意目錄checkout,意味著在任何位置都可能出現.svn的隱藏文件夾,這樣對整個工程的代碼產生了污染。
- 也有非要像SVN那樣進行目錄權限管理的時候,見知乎的這個問題:https://www.zhihu.com/question/20216542 在上面 SVN 總算占據了一點優勢。
4. .gitignore 文件
git 可以忽略某些文件的提交,而SVN沒有這個功能。
5. 版本號
svn 的版本號是1、2、3......的自然數,git 的版本號是一堆哈希碼,看起來非常反人類。 其實 git 這樣設計和它的分布式結構有關,作用是防止版本號沖突。
反例如下:如果 git 用自然數作為版本號,小明在本地提交了版本1、2,小剛也在本地提交了版本1、2,兩個倉庫同事 push 到公司的 gitlab上時,版本號就發生了沖突。而 svn 倉庫只有一個,不用與別的倉庫同步,版本也一定有個提交的先后順序,所以不會發生沖突。
工作區與暫存區
為什么要先 add 再 commit 呢?我覺得這是為了命令行的使用方便。有時一天的活兒干完,修復了兩個bug,所以我要分兩次提交。那么先把第一次要提交的文件 add 到暫存區,再將暫存區的所有文件進行commit。寫命令行的時候,如果沒有 add 只有commit,就要一口氣寫很多個文件,很不方便。但是圖形界面不存在這樣的不便,所以大多數 git 的圖形化操作軟件都把 add 和 commit 合二為一操作。
當然,暫存區還有一些更靈活的使用,只要理解下圖,便可以依照你自己的喜好使用。