30分鐘教你輕松使用Git做代碼管理
2017-06-20
Git的起源
(故事性內容,不喜跳過,干貨往下看)
git的由來說白了,就是一個天才程序員,因為不喜歡傳統的代碼版本控制工具(集中式)或者喜歡的工具不開源,就自己花了半個月的時間寫了這么一個流行至今的“分布式版本控制系統”。
當年,Linux的發明人Linus Torvalds將Linux的源碼放置開源社區后,開始接收來自世界各地的志愿者們貢獻的代碼,并手工合并。可想而知,隨著linux社區日益活躍,Linux的源碼也更加龐大,這樣的手工合并代碼的方式越來越困難,也越來越不像程序員的做事風格。
不是早就存在像SVN和CVS這樣的自動化代碼版本控制工具嗎?然而Linus表示,堅決抵制集中式版本控制器。因為集中式的工具不但速度慢,而且如果中央服務器突然宕機,或者沒有網絡,團隊幾乎無法提交工作。那Bitkeeper這樣強大的分布式版本控制工具呢?事實上,從2002年開始,linus就開始用Bitkeeper管理代碼,自此,Linux的發展步伐加快了一倍。
然而,Bitkeeper是一款商業版軟件,雖然為開發開源軟件的兄弟們提供使用許可,但是同時也限制開源社區不可以開發具備相同功能的軟件。但程序員們哪有幾個是安份的?開源社區的兄弟們更是如此。Linux和Bitkeeper交往的大好時光持續到2005年,開發Samba的Andrew試圖破解BitKeeper的協議,卻被Bitmover公司(Bitkeeper的研發公司)發現了,從此收回了在開源社區使用Bitkeeper的許可。
激動人心的時刻來了,Linus和Bitmover公司的CEO Larry McVoy是好朋友,本來可以為社區的兄弟們低個頭,向Bitmover認個錯,以前怎么哥倆好,今后繼續怎么搞。
但是天才就是天才,Linus隨后花了半個月的時間用C寫了一個分布式版本控制器,并在一個月內將Linux的代碼完全由其托管,這個工具就是Git。當然,開源的。
時至今日,Git已經成為了最為流行的代碼管理工具。隨著Github網站的上線,更是紅得一發不可收拾。
Tips:據說去年,Bitkeeper也開源了。
Git vs SVN
上面說了這么多,無非是想表達的對天才程序員的敬佩之情和對開源精神的推崇,雖然自己還只是一個Coder,但也會努力成為一個厲害的Programmer的。
為什么Linus抵制像SVN這樣的集中式版本控制呢?Git是分布式的,和集中式的區別究竟在哪呢?我們接下來就來探討下。上圖
可以看出,所有的開發者都是通過連接中央服務器進行代碼獲取或者代碼提交的。開發人員之間,必須通過中央服務器來合作開發。中央服務器中保存著完整的版本庫,開發者如果要進行版本控制,需要連接網絡。若服務器宕機,或者網絡中斷,開發工作將無法開展。

而對于分布式的版本控制器,每個人的電腦上都可以保存一份完整的版本庫。從remote pull下來最新的代碼,如果網絡中斷,開發人員完全可以在本地做版本回滾和修改提交,再在聯網的時候推送到遠程倉庫。若兩名開發人員合作開發一個功能,可以不經過遠端服務器就可以相互之間推送修改,合并沖突后,再由某一人推送代碼。大大得提高了開發效率。而且如果服務器壞了,數據丟失,沒關系,每個人都有完整的版本庫,從其他人那里clone一份就ok了。當然,信息安全的問題也就來了。
當然,對于一個項目的開發,選擇集中式或者分布式的版本控制并不是關鍵問題,我認為提高版本控制的決定因素在于良好的分支管理策略,這里還是比較講究的。有興趣的同學可以參考《Git權威指南》。
Git初體驗
講了這么多,我們來實際演練下git的操作吧。由于習慣了命令行操作,雖然公司推薦小烏龜,但我還是很傲嬌的用git bash
環境搭建
window10(win7也ok,64bit或者32bit無關緊要,都有對應的git版本)
git的win版官網下載地址 下載最新版的可以的。30M+,一會就下好了。
github遠端倉庫,登陸官網注冊,創建一個就好
安裝
看到以下畫面前一路next(注意設置安裝路徑)

然后按上圖進行單選,接著一路next,安裝就好了。在新建一個文件夾,進入,在空白處右擊,會發現右鍵菜單多了兩個選項,Git GUI here,Git Bash here。選擇第二個。進入如下界面

這個就是git的命令行界面了。
我利用git提交代碼的流程
git在window的安裝需要200M+的空間,而在Linux上安裝包卻非常小,很有可能是因為windows上啟動git,首先會啟動一個輕量級Linux系統,然后再在其中啟動git,你會發現在git bash界面上,你能夠使用很多Linux下的代碼,包括vim編輯器,有點小激動。
Tips:C://Users/[你的用戶名]/目錄下面有一個.bashrc文件,這個文件和Linux的.bashrc一樣,在啟動時加載,配置bash的環境變量,你可以在這里配置java的環境變量,這樣就能在git bash使用java命令了。其他軟件也是如此。
假設你剛進公司,公司在github上托管了一個開源項目(我剛剛創建一個空的項目),你需要在上面開發,你首先需要從github上clone一份完整的最新代碼。復制下圖鏈接

然后在git bash敲入如下命令git clone [剛剛復制的鏈接]
,并進入目錄。

查看code1.txt的內容

你已經或得了完整的代碼了,現在你可以嘗試進行開發。一般這個時候,我會選擇新建一個分支,在那個分支上做開發,完成后切換回主分支,合并新建的分支,再提交代碼。那什么是分支呢?參考這里吧!
敲入git checkout -b dev
來新建一個名為dev的分支。

git checkout
這個命令是用來切換分支的,如果帶上了-b
選項,就表示新建一個分支。git branch
是查看所有分支,分支名前帶分號的表示當前所在分支。-a
選項表示羅列所有分支,包括本地和遠程追蹤分支。
**Tips: **在命令后設置-h
可以查看命令幫助,--help
可以啟動瀏覽器查看更加詳細的幫助文檔。
好了,分支也建好了,我們可以嘗試開發了,我們做如下操作:
- 在code1.txt中修改第一行,并增加若干行。
- 新建一個文件,寫入一些內容


你在項目中添加了新的文件,并且更改了舊的文件,可以通過git diff
命令查看修改

你發現,對于code1.txt中原本一行的修改,git簡單粗暴的記錄為刪除后,再添加。對于新增的內容記錄為增加。是的,git是以行(hang)為單位記錄文件的修改。等等,你發現我們剛剛新建的mycode1.txt文件并沒有被顯示。使用git status
命令查看當前工作空間狀態

mycode1.txt是Untracked files,這個文件未被git追蹤,你需要通過git add .
將你的所有修改、新增、刪除添加進工作區的暫存區stage,什么是git的暫存區呢,參考
敲入git add .

再用git status
可以看到,能夠看到所有文件的修改了。mycode.txt為新增文件。
現在你完成了你的工作,準備提交所有的修改,敲入git commit -m "一些信息"

將代碼的變化提交到本地倉庫。現在的分支是dev,我們切換回master

可以看出,切換回master分支后,你會發現目錄下只有code1.txt文件了,自己新建的文件不復存在。(注意:如果在dev分支上沒有commit修改,在master分支上能夠看到在dev分支上的修改。)
我們以圖來分析

這是最開始的狀態,只有一個master分支,HEAD指向當前分支的最新版本。

新建一個分支后,同時也切換到dev分支上,此時HEAD指向dev,master和dev的最新版本是一樣的。

現在在dev分支上修改,并提交代碼,你會發現,dev開始和master分開了,

多次提交代碼后,切換回master,此時master的版本早已落后于dev,通過git merge dev
命令將dev分支合并進master分支中,以保證版本一致.


現在,你可以在將master分支推送的遠端倉庫的master分支了。

**Tips: **截圖中的命令很仔細的制定了本地分支和遠程分支的名字git push origin [local branch]:[remote branch]
,事實上,如果使用了git branch --set-upstream [本地分支] [遠程分支]
關聯本地和遠端分支后,可以直接簡寫git push
推送代碼。git branch -vv
可以查看本地和遠端分支的關聯。
git branch -D dev
刪除dev分支

這大概是我每天最常的一套提交代碼的流程,只要沒有人碰我修改了的文件,就不會發生代碼沖突,一般能夠順利提交(后面會演示沖突的產生和解決辦法)。此外,你不會直接提交修改給遠端master分支,一般會設置評審分支,日常的代碼先提交到評審分支,再發起合并請求,請相關大佬review代碼,再合并進master分支。這個還是比較簡單的分支管理策略,對于持續發布的項目,分支管理是更加復雜的。具體可以查閱阮一峰的博客,或者搜索“git flow”,"gitlab flow","github flow"關鍵字。
代碼沖突
在多人協作開發的情況下,代碼沖突是常有的事,每次辛辛苦苦地修改完bug,增加feature,滿心歡喜的推送代碼,等著下班回家陪媳婦,結果看到由于代碼沖突而導致merge失敗的錯誤,真的有點想shi.
親親抱抱舉高高,沒什么大不了。下面我就以我的經驗,演示下沖突的產生,以及解決辦法。
情況1:研發A修改了code1.txt文件并合并到master,此時研發B修改了mycode1.txt文件,想要合并到master(A已經合并)

在code1.txt中增加新內容

提交修改,并切換回master分支,merge devA

checkout devB分支,修改mycode1.txt內容

再提交修改,并切換回master,合并devB
合并成功,這是不會發生沖突的。因此,如果每個人修改的不同的代碼文件都,合并是不會發生沖突的。
情況2:研發A和研發B合作開發新功能,A修改了code1.txt文件并合并到master,此時B在他的分支上也修改了code1.txt文件,想要合并到master(A已經合并)

修改完code1.txt后,devA提交修改,并切換到master,合并devA

切換到devB,由于沒有更新代碼,你發現devB分支上的code1.txt文件沒有devA之前修改的痕跡,同樣的,devA分支上的mycode1.txt也沒有devB的修改。修改code1.txt,增加一行內容。

切換回master,進行合并發現代碼沖突,合并失敗。此時右下角顯示merging狀態。激動人心的時刻到了!

這個時候使用git status
命令可以看到,code1.txt被同時修改了,所以產生沖突。繼續使用git diff
命令查看code1.txt中的沖突

命令行中看到上面這樣的內容,第一反應是崩潰的,本能的就會抵制繼續往下看,而開始尋找好用的GUI工具。這就是人們對未知事物的本能反應,抗拒。其實讀懂這些符號就沒啥了。這些內容是實實在在得寫在了文件中的,在實際的工程中,如果有實時編譯的IDE,立馬就會報錯。我們用vim編輯code1.txt,以圖解決沖突。

第一部分是沖突中HEAD的修改,現在在master分支,自然是指master的修改。第二部分就是devB的修改。

簡單粗暴的刪除git自動添加的符號,完成沖突解決。注意:實際開發中,沖突的解決還是的小心一點,兩個人當面核對代碼沖突還是很有必要的。如果不能見面溝通,最好是在理解對方的修改后,再解決沖突。否則弄丟了對方辛辛苦苦修改的代碼,還是很崩潰的。

之后將修改add進暫存區,然后commit。merge會自動完成。git log
可以查看版本歷史
一般在工程中,推送代碼前,重新從遠端倉庫pull一份新的代碼,和本地分支做合并,在本地解決沖突后,再推送到遠程倉庫。