一晚吞、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脊框,現(xiàn)在需要運行看效果,我們就要通過 qq 把自己修改的文件傳給對方践啄,然后手動覆蓋浇雹。有時我和他同時修改 index.jsp,就得讓他把他的文件發(fā)給我屿讽,我合并兩人的修改后昭灵,再把合并后的文件發(fā)給他。后來改動多了伐谈,我們也不記得我們修改了哪些東西烂完,完全混亂,于是就在同一臺機器上你敲一會兒诵棵,我嗑瓜子抠蚣,再我敲一會兒,你嗑瓜子履澳,嚴重影響了效率嘶窄。有時老板要檢查我們的進度,或者要幫助我們解決bug奇昙,我們就得將最新的版本打成壓縮包 qq 傳給總監(jiān)护侮。
后來我們用了 SVN敌完,這些麻煩就全沒有了储耐。要同步兩人的進度,只需要一人commit滨溉,另一人checkout什湘,根本不需要自己記改了哪些東西。解決沖突同樣是相對麻煩的事情晦攒,但是先 checkout,修改沖突的地方再提交脯颜,步驟清晰且有詳細記錄哟旗,比之前好了不知道多少。老板需要最新的代碼時栋操,也無需找我們要纠脾,直接從倉庫里下載下來就好啦背率!
3. 分支管理
分支在實際中有什么用呢?假設你準備開發(fā)一個新功能,但是需要兩周才能完成宗兼,第一周你寫了50%的代碼,如果立刻提交宿饱,由于代碼還沒寫完,不完整的代碼庫會導致別人不能干活了壹无。如果等代碼全部寫完再一次提交,又存在丟失每天進度的巨大風險「兴В現(xiàn)在有了分支斗锭,就不用怕了。
二失球、Git 與 SVN比較
1. 分布式與集中式
git 是分布式版本管理系統(tǒng)拒迅,SVN 是集中式版本管理系統(tǒng)。
集中式:
在沒有聯(lián)網的情況下不能 commit 操作她倘。只有中央服務器擁有完整倉庫璧微。
分布式:
每臺機器上安裝了同一套 git 程序,所以都能持有完整的倉庫硬梁∏傲颍可以方便的克隆倉庫,倉庫與倉庫之間可以互相同步荧止。
svn 并不能克隆倉庫屹电,只能從倉庫中下載文件,即使你用 TortoiseSVN 創(chuàng)建了一個本地倉庫跃巡,你和中央服務器的倉庫也無法進行關聯(lián)危号。
git 分布式為什么比 svn 集中式好?
svn 不聯(lián)網就不能 commit素邪。假設小明公司的中央服務器部署在內網外莲,回家了便無法訪問,但小明周末仍然勤奮地在家寫代碼兔朦。周六晚他想將這一天寫的東西 commit偷线,但是并不能夠。周天他將代碼改出bug來了沽甥,想回退到周六晚的版本声邦,事實殘忍地拒絕了他,并讓他手撕bug摆舟。
集中式中亥曹,中央服務器掛了,倉庫就掛了恨诱。分布式中媳瞪,掛了一臺機器,還能從另一臺機器恢復倉庫胡野。不過據(jù)最新消息證實材失,有些 svn 的客戶端現(xiàn)在也搞分布式技術,一個倉庫同時存在幾臺服務器上硫豆,一個服務器提交了修改龙巨,幾個服務器自動同步笼呆,但是這仍改不了我們工作的筆記本上沒有倉庫的事實。如果我們筆記本上倉庫也能和遠程同步旨别,等等诗赌,那不就和 git 的模式一模一樣了嗎?瞎折騰啥呢少年秸弛,不如一開始就用 git铭若。
git 中我們可以 commit 到本地倉庫而不 push 到遠程倉庫。假設我的 getDataService.java 只寫了一半递览,仍然會報錯叼屠,這時候用 svn 將修改提交,就會影響到隊友(隊友更新代碼 了以后說臥槽怎么 run 不起來了)绞铃。更可怕的是我修改的是已有的文件镜雨。但是不提交,我又面臨著和小明一樣丟失進度的風險儿捧。在 git 中只 commit 不 push 就能解決這個問題荚坞。在我的第一個實習公司,那時想自己改一改代碼玩菲盾,就自己在本地創(chuàng)建了一個分支颓影,隨便瞎折騰,只要不提交懒鉴,別人完全不知道我干了啥诡挂,不會影響到任何人。
2. 分支管理
git 的分支功能比 SVN 好用得多疗我。
其他版本控制系統(tǒng)如SVN等都有分支管理咆畏,但是用過之后你會發(fā)現(xiàn)南捂,這些版本控制系統(tǒng)創(chuàng)建和切換分支比蝸牛還慢吴裤,簡直讓人無法忍受,結果分支功能成了擺設溺健,大家都不去用麦牺。
SVN 使用分支,一般來說是我們直接手動管理文件夾鞭缭,見 trunk+-branches+-tags 目錄結構的使用剖膳。舉個例子,如果需要基于分支1新建一個分支2岭辣,我們就手動將分支1的文件夾拷貝一份命名為分支2吱晒。如果要切換分支,我們就切換文件夾沦童。類似這些操作仑濒,實際上是將文件夾“視作”分支叹话,用了 move、cd 等操作墩瞳,至于合并分支的 merge 命令驼壶,大概沒什么人敢用吧。
但Git的分支是與眾不同的喉酌,無論創(chuàng)建热凹、切換和刪除分支,Git在1秒鐘之內就能完成泪电!無論你的版本庫是1個文件還是1萬個文件般妙。
3. 權限控制
SVN 可以嚴格控制每個目錄的訪問權限(還有一套很變態(tài)的規(guī)則),git 只能控制每個倉庫分支的權限相速。SVN 可以 checkout 任意子目錄股冗,而 git 只能 clone 某個分支。
實際上和蚪,SVN的目錄權限控制和 checkout 子目錄主要是為了方便了基于trunk+-branches+-tags 結構的開發(fā)≈棺矗現(xiàn)在 git 既然已經有了好用的分支功能,目錄權限控制就沒有那么必要了攒霹。
補充:
- 在任意目錄checkout怯疤,意味著在任何位置都可能出現(xiàn).svn的隱藏文件夾,這樣對整個工程的代碼產生了污染催束。
- 也有非要像SVN那樣進行目錄權限管理的時候集峦,見知乎的這個問題:https://www.zhihu.com/question/20216542 在上面 SVN 總算占據(jù)了一點優(yōu)勢。
4. .gitignore 文件
git 可以忽略某些文件的提交抠刺,而SVN沒有這個功能塔淤。
5. 版本號
svn 的版本號是1、2速妖、3......的自然數(shù)高蜂,git 的版本號是一堆哈希碼,看起來非常反人類罕容。 其實 git 這樣設計和它的分布式結構有關备恤,作用是防止版本號沖突。
反例如下:如果 git 用自然數(shù)作為版本號锦秒,小明在本地提交了版本1露泊、2,小剛也在本地提交了版本1旅择、2惭笑,兩個倉庫同事 push 到公司的 gitlab上時,版本號就發(fā)生了沖突。而 svn 倉庫只有一個沉噩,不用與別的倉庫同步铺敌,版本也一定有個提交的先后順序,所以不會發(fā)生沖突屁擅。
工作區(qū)與暫存區(qū)
為什么要先 add 再 commit 呢偿凭?我覺得這是為了命令行的使用方便。有時一天的活兒干完派歌,修復了兩個bug弯囊,所以我要分兩次提交。那么先把第一次要提交的文件 add 到暫存區(qū)胶果,再將暫存區(qū)的所有文件進行commit匾嘱。寫命令行的時候,如果沒有 add 只有commit早抠,就要一口氣寫很多個文件霎烙,很不方便。但是圖形界面不存在這樣的不便蕊连,所以大多數(shù) git 的圖形化操作軟件都把 add 和 commit 合二為一操作悬垃。
當然,暫存區(qū)還有一些更靈活的使用甘苍,只要理解下圖尝蠕,便可以依照你自己的喜好使用。