-- 故國神游,多情應笑我,早生華發。
Git是什么?
Git是一個版本控制工具,代碼管理工具,團隊協作工具。它跟SVN等傳統工具實現同樣的目的;但從某種程度來說,它更快,更靈活。我想絕大多數讀者都已經在接觸這個工具了,並且用於日常的項目中去了。我的這篇文章,不是作為一個Git入門教程,也不是作為一本大塊頭的教科書。(說到教科書,我推薦下面的這本。這本書確實好,很全面。我的這篇文章,其實就是這本書的讀書筆記而已。)
Pro Git -- http://git.oschina.net/progit/
接着說。我的這篇文章,主旨在於啟發大家超越平時的使用局限,從另一種視角去看待Git和使用Git,讓人有一種“啊哈,Git也能這么用“的感覺。這有點王婆賣瓜的味道了,這我必須承認。
Git最基本的用法
很多項目組,對於剛入職的程序員,會像下面這樣對他們普及Git的知識:
#使用git clone克隆項目的代碼 git clone https://your-project-address #使用git add添加修改的文件 git add your-filename #或者使用git add --all添加所有修改的文件 git add --all #使用git commit提交代碼到本地的代碼庫 git commit -m some-description-message #使用git push提交代碼到遠程的代碼庫 git push
#使用git pull拉下遠程代碼庫中更新的代碼
git pull
僅使用這五個命令,確實就足夠進行團隊協作了。這是使用Git的一種模式,而且無可厚非。不過,這樣做有種僅僅把Git當成了純粹的代碼共享庫的感覺,多多少少顯得有點不大規范。其實,Git有更加豐富的能力,它能讓你的項目開發有更加規范的流程,整個項目歷史有更加清晰的追溯,分工協作上也更加井然有序。
Git的特色
Git的特色有哪些呢?
本地代碼庫
Git的最大特色可能就是作為一款分布式代碼管理工具了。Git的絕大部分操作都是在本地進行的,這使得Git更快,而且更靈活。更靈活是因為在你的改動被提交到遠程代碼庫之前,你可以在本地進行任意的操作。另一方面,這也意味着改動提交到遠程被共享之后,就不方便再改動舊的提交歷史了。所以,請慎重上傳改動到遠程。
歷史
代碼的提交歷史是一個項目開發歷程的記錄。這其中不僅僅是情懷的記錄。更重要的是,提交歷史給了我們項目回溯的能力,從而讓我們能夠找出問題代碼的來源,並修正它。Git的提交歷史很強大,我們可以去翻看歷史,還能附加上豐富的篩選條件;甚至可以修改有問題的歷史。為了更好地利用Git的歷史功能,我們要時刻保持提交歷史的細致與干凈。這意味着,我們要恰到好處地提交我們的代碼,以及給它一個恰到好處的描述。
分支
Git的另一特色就是分支了。簡單地說,分支就是不同的代碼故事。它們相互區別又互有聯系,漸行漸遠又能在某一處匯合。使用分支,我們能夠控制代碼的不同走向,從而很好地安排我們的開發工作。靈活使用分支可以簡化我們的很多工作。
簡單
說Git簡單,聽起來有點怪怪的。如果你要了解Git 內部原理,那確實夠復雜的。不過,Git的特色在於,即使你不了解這些原理,你仍然可以靈活運用它。只用去學Git的一些基本的概念就足夠了,例如倉庫,暫存,提交,歷史,分支等。說實話,我到現在是說不出這些概念的具體定義的,而且也只是淺要地了解了下這些概念的原理。但是,這並不影響我去使用它。Git的特色在於,學些基本的概念和原理;剩下的,去用就夠了。這讓我想起了很多的Unix工具,如Vim,LaTex等,以及Unix哲學。
Git哲學
所謂Git哲學,是指使用Git的思維方式。下面是我覺得的Git哲學中最要緊的幾點:
1. 保持細致清晰的提交歷史
如果說提交歷史是一個版本控制工具最核心的模塊,我覺得一點也不為過。代碼的提交歷史要涵蓋到具體的每一次改動,並且要有清晰一致的描述。這要求我們:
- 分成一個個小的提交,而不是一次大的提交
- 每次提交要給到一個簡潔明了的描述
每次的提交得是一個具體的整體。所謂具體的,是指你的提交不能是一次籠統的或概括的改動;所謂整體,是指你的提交是一個已經完成的改動,而不能是懸而未決的。其實,這兩點可以從你的提交說明中檢驗出來:
- 如果你的提交說明出現了像”完成用戶登錄以及實現網站首頁布局“這樣諸如以及、和這樣的詞匯,很可能就說明你的一次提交做了不止一件事情,應該分為兩次提交。
- 如果你的提交說明出現了像”完成用戶登錄(一)“、”完成用戶登錄(二)“、”完成用戶登錄(終)“等這樣連續關聯的幾個提交,說明你的這幾個提交都在做一件事情,應該壓縮為一個提交。
其實做到這一點也很容易 --
盡可能得多提交,盡可能得早提交,當完成一個小的功能點時就提交。
描述應該簡潔明了。簡潔是指能夠用一句話描述你做的事情,並且不要覆蓋其他不必要的信息;明了是指絕對不能含糊,不僅你能看得懂,也要讓局外人也能看得懂。我列了幾個要點:
- 如果不能做到簡潔,或者說不能用一句話描述你的提交,很有可能說明你的提交在做不止一件事情。解決辦法是分為多次提交。
- 不要使用含糊的或者概括的描述。像諸如”修復BUG“這樣的描述是要不得的,得說清楚修復了什么BUG。例如說”修復用戶登錄后無故崩潰的BUG“或者”修復ISSUE 553“。
- 不要附加多余的信息,例如Author,Date,Email這些信息不要附加在描述里面了,因為提交歷史已經自動包含這些信息了。
由於每個提交都是一個細致的改動單元,當項目進行到一定階段,整個提交歷史一定會顯得很長。這時候你可能會覺得提交很多很雜,會淹沒一些重要的版本發布提交,例如上線的版本v1.0,v1.1。我想這是不必要的擔心,認真地對待提交歷史,會讓整個提交歷史顯得多而不雜。而且,完全可以為重要的提交打標簽。請記住 git tag 命令,它可以為重要的提交打標簽。
Git的提交歷史是相當靈活的。你可以查看歷史;更厲害的,你還可以去修改它。所以,你可以在任意時刻去修正你的歷史。這里有個例外,就是你無法修改遠程倉庫的提交歷史。一方面,有的遠程代碼倉庫不支持提交歷史的修改;另一方面,即使支持修改歷史,這樣的修改也會對其他開發者造成混亂。關於遠程提交的准則是:
- 萬萬不得修改遠程倉庫的提交歷史
- 在將代碼提交到遠程倉庫之前,再大概檢查一下准備push的系列本地提交
2. 即使濫用,也好過從不使用多分支
-- 一個分支就是一個故事,一個劇情。使用分支是為了不讓第三者破壞完美的劇情。
要有意識地去使用多分支,而不是去忽略它。可以在下面的情況下考慮使用分支:
- 當在主線版本的基礎上要加上一個實驗性的特性的時候,為了不影響主線的開發進程,可以獨立地在另一個分支上開發。這時分開的兩個故事可以並行地發展,當實驗分支穩定后,兩分支可以合並為同一個分支。
- 當在主線版本的基礎上修復一個BUG,為了不影響主線的正常開發,可以獨立地另開一個分支進行。這時分開的兩個故事可以並行地發展,當BUG修復后,可以將BUG分支並到主線分支。
這兩個使用分支的策略都是本地臨時分支使用策略。無論是添加特性分支還是BUG修復分支,它們都有存在周期短的特點,並且都只是為解決某個特定問題而存在的。當這個問題被解決以后,它們就要被並入到主分支,這樣就完成了自己的歷史使命。使用分支的一種情況是:當你要同時進行多個任務的時候。
無論是主分支,還是上面的特性添加分支或者BUG修復分支,都是你自己一個人的任務。你要同時完成兩個或者三個任務。這時候才是分支發揮它最大作用的時候。不要在一個分支里同時做這幾件事情,這樣會違反第一哲學。記住:
每次只做一個任務;如果不得已要臨時切換到其他任務,請使用分支。
Git既存在本地分支也存在遠程分支;擅用分支,實際指的是靈活運用本地分支,而不是濫用遠程分支。你可以隨意地創建本地分支,頻繁地在多個分支的下來回工作。但不要把這一切牽扯進遠程分支。一般來說,遠程分支是長期分支,是作為項目不同的發展階段或者發展路線而存在的。例如穩定版、開發版、激進版等。這一般是項目決策者操心的事情。
再啰嗦一句:如果分支不再需要了,就刪除它。
3. 開發過程盡可能私有化
私有化意味着封裝。類似於面向對象上的封裝,團隊協作上也有封裝的概念。你可以在本地靈活地使用Git的各種操作,而不會對他人造成任何影響。不過這種靈活性應局限於本地,而不要將冗余公開給遠程的共享庫。這方面有些准則,其中有些是對上面兩個哲學的總結,如下:
- 為了簡化工作,要靈活使用Git的各種本地操作
- 本地提交歷史可以適當地混亂;但在提交到遠程倉庫前,一定要整理好
- 不要修改遠程倉庫的提交歷史;而且,雖然不大可能,不要隨意將新建分支推送到遠程
實踐Git
體現Git哲學的最好方式就是實踐它,用Git的方式去工作,學會使用Git思維。接下來我會涉及到具體的Git操作。
基本操作
【這一部分純當是復習。】
初始化一個新倉庫:
git init
從現有倉庫克隆:
git clone <your-project-address>
時刻檢查目錄狀態:
git status
.gitignore 文件可以聲明脫離版本控制的文件:
#haha
暫存修改:
git add <new-file-or-modified-file> git add --all
如果不小心 git add 了某個文件,使用 git rm 從暫存區移除(附帶--cached參數移除跟蹤但不刪除文件,以便稍后在 .gitignore
文件中補上):
git rm --cached <your-added-file>
想知道代碼做了哪些改動(會精確到行),使用:
git diff <some-file> git diff #這會顯示所有文件的改動
上面的命令是顯示工作目錄中當前文件和暫存區域快照之間的差異;如果想知道暫存區域與最近一次提交之間的差異,附加 --cached 參數:
git diff --cached <some-file> git diff --cached
提交更新:
git commit #這會為你打開一個文本編輯器
git commit -m <your-commit-message>
提交歷史
為了更好地說明用法,我使用了JFinal項目的Git源碼作為例子。
查看提交歷史
使用 git log 可以查看提交歷史。你應該看到類似於下面的結果:
$ git log
commit 121e247032be9e4d6a3c7eb8035914f59857c43d Author: James <jfinal@126.com> Date: Sun Jul 26 13:40:44 2015 +0800 修改變量名,actoin 改為 action commit d330532f9db493034578d5dac1ece43c65136569 Author: James <jfinal@126.com> Date: Sun Jul 26 11:00:55 2015 +0800 變量名修改
可以看到SHA-1 校驗和,作者的信息,時間以及提交說明。Git使用校驗和來指代每一次提交,例如輸出第一行的 121e247032be9e4d6a3c7eb8035914f59857c43d 。另外,如果不產生歧義的話,可以用校驗和的前幾個字符來指代提交,例如 121e247032be9e4d6a3c 和 121e247032 。
如果想看某一次具體的提交,指明它的校驗和即可:
$ git log d330532f9 commit d330532f9db493034578d5dac1ece43c65136569 Author: James <jfinal@126.com> Date: Sun Jul 26 11:00:55 2015 +0800 變量名修改 commit cdec1c3ceaceecfa4297c72efd8d8095640757f8 Author: James <jfinal@126.com> Date: Thu Jul 16 10:10:49 2015 +0800 修改 Cache.expireAt(...) 方法上的注釋
這會顯示從指定提交開始往前回溯的提交歷史。如果只是想顯示指定的這一個提交,用 -1 參數( -2 則顯示兩次提交,依次類推):
$ git log d330532f9 -1 commit d330532f9db493034578d5dac1ece43c65136569 Author: James <jfinal@126.com> Date: Sun Jul 26 11:00:55 2015 +0800 變量名修改
使用 git log --pretty=oneline 可以顯示更緊湊的提交信息,每次提交用一行來顯示:
$ git log --pretty=oneline 121e247032be9e4d6a3c7eb8035914f59857c43d 修改變量名,actoin 改為 action d330532f9db493034578d5dac1ece43c65136569 變量名修改 cdec1c3ceaceecfa4297c72efd8d8095640757f8 修改 Cache.expireAt(...) 方法上的注釋 749fbe0d8e9da0e33fe2b30085f7467e86881e17 JFinal 2.0 release ^_^ 52633ce2da704cf35a48aa9b8a1afe99d6c2ed1d JFinal 2.0 release ^_^ 1e00c07348bd7b1ed16bb5c224e0a0de67b9b13b JFinal 2.0 release ^_^ 0330d7ed5f3d742eaca201456927d9cecb40e215 JFinal 2.0 release ^_^ aa4a95af60a1dc12dfd649bd208de473dcfb369f jfinal 1.9 release ^_^ af6469eb6f49c23cba8215f2b0e9c8d51cd5f8c9 jfinal 1.9 release ^_^ 4ab71ce41cca8b7d16bef89655b51e6de6548d30 jfinal 1.9 release ^_^ 03a3c5a2bdb848ad2f9a30e29eba4f468176497f jfinal 1.9 release ^_^
使用 -p 選項展開每次提交所做的修改。例如我們使用下面的命令展開哈希前綴為 d330532f9 的那次提交的修改:
$ git log d330532f9 -p -1 commit d330532f9db493034578d5dac1ece43c65136569 Author: James <jfinal@126.com> Date: Sun Jul 26 11:00:55 2015 +0800 變量名修改 diff --git a/.gitignore b/.gitignore index 416db66..b1e58ba 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ integration-repo /build/ # IDEA metadata and output dirs +/.idea/^M *.iml *.ipr *.iws diff --git a/src/com/jfinal/config/Routes.java b/src/com/jfinal/config/Routes.ja index fab2604..8ad1b03 100644 --- a/src/com/jfinal/config/Routes.java +++ b/src/com/jfinal/config/Routes.java @@ -86,11 +86,11 @@ public abstract class Routes {
使用 --stat 選項僅顯示代碼修改的統計信息(僅顯示簡要的增刪行數統計,特別適合作代碼審查):
$ git log --stat commit 121e247032be9e4d6a3c7eb8035914f59857c43d Author: James <jfinal@126.com> Date: Sun Jul 26 13:40:44 2015 +0800 修改變量名,actoin 改為 action src/com/jfinal/core/ActionMapping.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) commit d330532f9db493034578d5dac1ece43c65136569 Author: James <jfinal@126.com> Date: Sun Jul 26 11:00:55 2015 +0800 變量名修改 .gitignore | 1 + src/com/jfinal/config/Routes.java | 6 +++--- src/com/jfinal/plugin/activerecord/Sqls.java | 4 ++-- src/com/jfinal/plugin/activerecord/tx/TxByMethods.java | 14 +++++++------- src/com/jfinal/plugin/redis/RedisInterceptor.java | 11 ++++++++++- 5 files changed, 23 insertions(+), 13 deletions(-)
我從《Pro Git》中摘出了一些選項及說明:
選項 說明 -p 按補丁格式顯示每個更新之間的差異。 --stat 顯示每次更新的文件修改統計信息。 --shortstat 只顯示 --stat 中最后的行數修改添加移除統計。 --name-only 僅在提交信息后顯示已修改的文件清單。 --name-status 顯示新增、修改、刪除的文件清單。 --abbrev-commit 僅顯示 SHA-1 的前幾個字符,而非所有的 40 個字符。 --relative-date 使用較短的相對時間顯示(比如,“2 weeks ago”)。 --graph 顯示 ASCII 圖形表示的分支合並歷史。 --pretty 使用其他格式顯示歷史提交信息。可用的選項包括 oneline,short,full,fuller 和 format(后跟指定格式)。
Git提交歷史的一大特色在於可以按照條件篩選歷史。例如篩選出最近兩周到最近一周的提交歷史:
$ git log --since=2.weeks --until=1.weeks commit cdec1c3ceaceecfa4297c72efd8d8095640757f8 Author: James <jfinal@126.com> Date: Thu Jul 16 10:10:49 2015 +0800 修改 Cache.expireAt(...) 方法上的注釋
我也列出了一些能夠設置篩選條件的一些選項,同樣來源於《Pro Git》:
選項 說明 -(n) 僅顯示最近的 n 條提交 --since, --after 僅顯示指定時間之后的提交。 --until, --before 僅顯示指定時間之前的提交。 --author 僅顯示指定作者相關的提交。 --committer 僅顯示指定提交者相關的提交。 -- 命令的最后一個選項,后跟路徑名表示只關心某一路徑下的提交歷史
修改提交歷史
Git提交歷史的另一大特色在於你可以修改它。不過格外注意的是,不要修改那些已經推送到遠程版本庫的提交歷史。
首先介紹如何撤銷。這多少與修改歷史無關,因為此時的歷史可能還未形成。
如果只是希望將某個文件的改動從暫存區拿出來,而保持文件原有內容不變,使用命令:
git reset HEAD <your-file>
其中HEAD指代最近一次提交(*)。你可以使用HEAD變量來避免使用哈希值。
下述命令會令整個項目的狀態回退(保留修改):
git reset HEAD
如果想將文件恢復到最近一次提交的版本,使用命令 git checkout 。這會使得其從暫存區拿出來(如果已經存入暫存區),並且文件內容恢復到最近一次提交的狀態:
git checkout <your-file>
如果想直接撤銷所有改動(包括修改),使用命令:
git reset --hard HEAD #恢復所有改動到最近一次提交
git reset --hard HEAD~1 #恢復所有改動到最近一次提交的上一次提交
git reset --hard HEAD~2 #恢復所有改動到最近一次提交的前兩次提交
git reset --hard d330532 #恢復到指定提交
帶有 --hard 選項的版本回退是一個不可恢復的操作,請謹慎用之。
當我們完成提交后,發現漏掉了某些改動(例如忘記添加相應的API注釋)。這時我們當然可以將改動作為一次新的提交提交一發。不過,還有其他的方案,即將漏掉的改動重新補回到對應的歷史提交中,畢竟漏掉的改動確實是那次提交的一部分。Git給了我們修改提交的能力。
如果只是要修改最近的一次提交,使用 git add 命令暫存相應的改動,然后輸入命令 git commit --amend 。這會把你代入修改提交說明的編輯器中。保存后退出,我們就完成了我們的提交修改。
如果只是想修改最近一次提交的提交說明,直接輸入 git commit --amend ,然后進入編輯器修改提交說明並保存退出即可。
要修改歷史中更早的提交,就要用到 git rebase -i 交互式的提交歷史修改工具。我在命令行中輸入:
git rebase -i HEAD~4
它會帶我進入一個文本編輯器中(在我的系統里意外地是VIM)。關注前四行,會看到我們可以修改最近的四次提交歷史( HEAD~4 指定)。其中提交由早到晚地排列,與 git log 顯示順序相反。
1 pick 749fbe0 JFinal 2.0 release ^_^ 2 pick cdec1c3 修改 Cache.expireAt(...) 方法上的注釋 3 pick d330532 變量名修改 4 pick 121e247 修改變量名,actoin 改為 action
這時我們可以修改某次提交,例如修改第2行的提交,只需將 pick 換成 edit 即可。然后保存並退出編輯器。
pick 749fbe0 JFinal 2.0 release ^_^ edit cdec1c3 修改 Cache.expireAt(...) 方法上的注釋 pick d330532 變量名修改 pick 121e247 修改變量名,actoin 改為 action
像之前一樣,使用 git commit --amend 修改提交;最后使用 git rebase --continue 完成修改任務。
我們也可以刪除某個提交,只需將相應的提交行刪掉即可。例如刪除第2行的提交:
pick 749fbe0 JFinal 2.0 release ^_^ pick d330532 變量名修改 pick 121e247 修改變量名,actoin 改為 action
重新排列提交也是OK的。只需要重新排列即可。例如下面的編輯把最近一次提交移到最前,最早的一次提交移到最近:
pick 121e247 修改變量名,actoin 改為 action pick cdec1c3 修改 Cache.expireAt(...) 方法上的注釋 pick d330532 變量名修改 pick 749fbe0 JFinal 2.0 release ^_^
一個有用的功能是壓制提交,即把多個提交合並成一次提交。例如下面的編輯將多個描述都為”JFinal 2.0 release ^_^“的提交合並為一個提交:
pick 0330d7e JFinal 2.0 release ^_^ squash 1e00c07 JFinal 2.0 release ^_^ squash 52633ce JFinal 2.0 release ^_^ squash 749fbe0 JFinal 2.0 release ^_^ pick cdec1c3 修改 Cache.expireAt(...) 方法上的注釋
相應地,可以拆分一個提交。它的技巧在於 edit 某次提交,然后調用 git reset HEAD^ 回到父提交,然后再多次 git commit 即可。
git reset HEAD^ git add file1 git commit -m "add file1" git add file2 git commit -m "add file2" git rebase --continue
當准備進入交互式rebase工具時,如果當前的工作區存在修改而沒有被提交,則會被禁止進入。此時我們可以先將修改提交;有時又不希望這么做,可能我們的代碼改動還不能構成一個完整的提交。我們只希望先保存自己的工作,然后在需要的時候釋放出來。這時就用到 git stash 命令了。
我們可以將改動保存在一個棧中,稱之為儲藏(Stashing):
git stash
然后查看棧狀態:
$ git stash list stash@{0}: WIP on master: 121e247 修改變量名,actoin 改為 action
因為是棧,可以多次調用 git stash ,然后查看棧狀態:
$ git stash list stash@{0}: WIP on master: 121e247 修改變量名,actoin 改為 action stash@{1}: WIP on master: 121e247 修改變量名,actoin 改為 action stash@{2}: WIP on master: 121e247 修改變量名,actoin 改為 action
然后在適當的時候,釋放一個儲藏。既可以釋放指定的儲藏,例如 stash@{0} 、 stash@{1} 、 stash@{2},也可以默認釋放最近的儲藏(stash@{0}):
git stash apply git stash apply stash@{2}
當儲藏不再需要時,刪除它:
git stash drop stash@{2}
或者立即釋放最近的儲藏並刪除它:
git stash pop
儲藏的典型應用場景是當工作區有未提交的修改時,要切回到舊提交去修改( git rebase -i )或者切換到其他分支( git checkout )。
分支
Git的分支是從某個分支引出的一個分叉。在Git中創建分支時,一定是以某個分支為基准,這個分支就是你當前工作的分支。
分支基本操作
要查看當前在哪個分支下工作,輸入命令:
git branch
此時會列出所有本地分支,行首標有*號的是當前工作分支。
創建分支:
git branch <new-branch-name>
切換到新分支:
git checkout <new-branch-name>
同時創建及切換分支:
git checkout -b <new-branch-name>
合並分支:
git checkout master
git merge <new-branch-name>
這能保證將新分支的改動合並到主分支。
刪除分支:
git branch -d <new-branch-name>
只有已被合並的分支才能順利刪除,否則會提示錯誤。
如果要強制刪除分支,使用命令:
git branch -D <new-branch-name>
分支思維
以上只是分支的基本操作命令。要想活用這些命令,就要知道分支的使用思維。分支的使用思維,我覺得就是一句話,重復之前的一句話:
當你要放下手中的任務,臨時切換到其他任務時,使用分支。
我這里想舉一個關於分支使用的簡單的不能再簡單的例子。
首先你在進行master上進行主線開發,實現功能點一。突然你臨時接到任務,完成功能點二,並馬上上線。此時你不得不放下手中的工作,投入到實現功能點二中去。
這時,你首先要做的是保存你正在進行的工作以便將來可以恢復。可以使用儲藏 git stash 或者臨時提交你的代碼以在將來通過 git commit --amend 修改你的歷史。這里假設你使用的是儲藏。
然后新建分支並切換到新分支工作:
git checkout -b feature2
當你完成功能點二的開發時,提交你的代碼:
git add --all git commit -m "finish feature 2"
之后切換到主分支,合並feature2分支:
git checkout master
git merge feature2
改動可以提交到遠程上線:
git push
然后你可以使用 git stash pop 恢復你的工作。
分支工作流程:
隊伍中的長期分支控制了不同的開發進度。一般來說,應該有一個穩定分支和開發分支:你可以將master作為穩定分支,並配有一個develop分支;或者反過來,將master作為開發分支,並配有一個stable分支。這里假設master是穩定分支,而develop是開發分支。develop分支一般比master要超前,並且當測試穩定后才會並入到master分支發布。所以一般的工作流程就是:
在develop分支開發,測試穩定后並入到master分支發布
另外,如果已發布的版本遭遇到一個緊急BUG亟待修復,這時你應該保存develop分支的工作,然后切換到master分支去修復。因為要緊急發布,你應當切換到穩定的master分支完成BUG修復,而不是基於不穩定develop分支。當修復完畢並發布后,再回到develop分支恢復工作。
Git找bug的能力:
利用分支,我們可以很好地分離了我們的不同工作,不讓它們相互干擾,從而減少bug的來源;利用歷史,我們可以追蹤代碼的變化,為我們找出問題代碼提供了途徑。其實Git也為我們提供了其他的好用的工具,利於我們調試。
git blame
:誰動了我的代碼
你是否會驚奇你的代碼為什么突然變成這副模樣?沒關系,使用 git blame 命令可以顯示你的代碼是誰最后修改的。命令格式:
git blame -L 12,22 simple.rb
可以顯示文件simple.rb的第12至22行這塊代碼最后是誰、什么時候修改並提交的。
二分查找
還有一個有趣的命令是 git bisect 。它可以以二分查找的策略逐步逼近你在意的壞代碼的來源。
首先輸入 git bisect start 來啟動二分查找。
輸入 git bisect bad 來告知當前的提交已經是問題提交了。
然后輸入 git bisect good <good-commit> 來告知你知道的最晚的正常提交。
接着就可以確定壞代碼的來源在<good-commit>和當前提交之間,二分查找策略就可以開啟了,這時Git監測處於中間的一個提交。假設從<good-commit>到當前提交一共有12個提交,記編號為0、1、…、11. 這時我們檢出的提交應該是6.
你可以輸入 git bisect good 來告知這個提交是正常的,這樣它會舍棄編號為0、1、…、6的提交;也可以輸入 git bisect bad 來告知問題依然存在,這樣它會舍棄編號為7、8、…、11的提交。然后繼續二分策略…一直到我們只剩下一個問題提交的時候。這個提交就是我們的問題提交的最初來源。
當你完成的時候,應該運行 git bisect reset 重新回到最初的地方。
Git使用總結:
- 在調用 git add --all 前,應該使用 git status 查看你要添加的改動,以免添加進不必要的改動。
- 要忽略的文件添加到 .gitignore 中去。
- git commit 時要給到一個簡潔明了的描述。
- git push 前要用 git log 檢查自己的提交歷史。
- 必要時要修改本地提交歷史,它們的命令是 git commit --amend 和 git rebase -i 。
- 不要修改遠程提交歷史,這往往意味着,你只能修改最近的幾個歷史。
- 要靈活地使用本地分支。它們的命令主要是 git branch 、 git checkout 等。
- 切換分支前注意保存自己的工作,用到的工具是 git stash 。
- 不要隨意將本地分支push到遠程。
超越Git
Git是偉大的,但我們依然要超越Git;畢竟Git只是一個工具,而我們是使用工具的人。Git的功能是代碼管理和版本控制,但Git的本質在於其重視團隊,重視協作的開發精神。它重視協作,但又不束縛個人的創造。它使得每個人都可以自由地編碼,同時又保持整個項目開發的協調一致。