現在每個人都在學編程,可能你就是正在成為百萬碼農大軍中的一個。好吧,就算你沒有,那麼你也可能在你的日常工作中使用版本控制系統(也不是......好吧,那就忽略上面的兩句話)。做版本管理最流行的工具是Git,什麼,你是SVN!
好吧,遇到槓精,老子也是服了!
關於Git你可能用過,也知道基礎知識,比如git add,git commit,git push,git pull。你還懂其它的一些,比如git reset,git stash,git checkout。但是今天我們不講這些,如果你對他們有興趣,請給蟲蟲留言,或者自己去搜索好了。
今天我們要講的是四個鮮為人知的git命令,知道的人很少,但是很有用。
1、Git bisect
如果你處於一個組織良好的開發環境中,這個命令對你來說幾乎是無用的。但現實中的場景往是,你是宅在家裡一個人做小型項目,或者你的公司還沒要建立完善的CI/CD(持續的集成和發布)流程,更大的可能是你們的測試被狗吃了(不是@了狗的測試,雖然也差不多),並出現以下情況:
試想,當你在愉快的開發著你想像中的炫酷功能的時候,突然銷售妹紙(想得美,是個老爺們大胖子)給你打了一個緊急電話說你發布版本崩潰了,沒法工作。你試著看了看代碼和錯誤日誌,但沒有任何頭緒,在qq群詢問老司機後,老司機建議你找之前正常的版本,然後對比下。因此你現在問題是要找到引入錯誤的那次commite。該怎麼做?什麼,不會了把。
那麼這裡蟲蟲教你用二分法搜索來幫你尋找那次罪惡的commit。git提供了git bisect命令就是用來幹這事的。
想法很easy。你要使用git log從歷史中選擇一個commit,那次commit之前的版本你確定是沒有問題。於現在使用git bisect來執行一個二分搜索,找到引入錯誤的commit。如果聽起來太抽象,我們來實例展示:
假設我這有一個項目,是關於記單詞的,每天遞增把陌生的單詞存到github的項目中,以便以後複習和存儲你學習生詞的歷史。
下面是git repo中的最新版本:
我們開發了一個程序,getWord.pl,讓他解析這個文件,並且每次隨機的顯示一個單詞來顯示。現在我們發現以下內容:
似乎似有些格式除了問題,使解析出問題了。現在需要通過每次提交逐一驗證,找出引入錯誤的commit,這需要花費很長時間。幸運的是,我們有了法寶git bisect。
我們籤出第一次提交並確認它是正確的(通常選擇一個確認是沒有問題commit你的)。為了方便起見,我們將第一次提交標記為one,將我們的最新提交標記為last:
讓我們來啟動查詢:
1、git bisect start
git bisect good one
git bisect bad last
這樣git 會頭指針指向兩個版本之間的那次提交,這兒是08b9941
2、執行腳本,測試是不是還報錯,如果報錯,繼續搜索,這次只需輸入git bisect bad
3、如果測試正常了,輸入git bisect good
重複2,3,根據結果輸入git bisect bad或git bisect good,直到找到引入錯誤的確切那次commit。
ok,剩下的就是,你修改此次提交中引入的問題,必要時候可以通過git show獲得這次commit更多的信息(比如文件對比等)。
在我們的例子中是有大量為未識別的符號,用?代替了,主要是音標符不支持在簡單文本中顯示導致的。
git bisect reset 返回之前的版本,把錯誤作為一個補丁進行修改即可。
注意git bisect也支持圖形化界面搞git bisect visualize會運行gitk,讓你更方便的選擇commit。git bisect bad 和 git bisect good中的bad/good也可以用new/old取代。
2、Git blame
這可能是名單上最為人熟知的命令,但是我在git群裡面問過很多人,都沒聽說過,所以就把他給加進來了。
Git blame用對你文件的每行信息都進行注釋。你能看到關於該行修改的每一次commit的哈希標籤、作者和提交日期。
以從我們從前面的倉庫為例子:
效果如上圖,可以讓我快速的瀏覽整個倉庫的變化,提交時間,及作者,可以讓你快速找到,誰修改的,誰的鍋,罵丫的讓丫來扛。
關於git blame 還有以下兩點我們要牢記:
(1):如果一個提交哈希前面有^號,那麼自該文件創建以來,相關聯的行就沒有被修改過。
(2):Git還可以跟蹤跨文件的行內容變化。如果你要對一個大文件代碼重構或者你的配置文件重新發布到多個小文件中時,git會,那麼git會顯示大文件中的原始提交和大文件的名稱。你可通過-C選項來實現。
3、Git reflog
可能老司機警告過你要避免使用git reset。因為他是一種破壞性的操作。它不僅修改HEAD(--soft)和索引(--mixed),還可能修改你的工作目錄。如果你不會使用它的話,會把工程搞亂,搞的不正常(怎麼辦,你說,老司機說了,亂了就從新clone一個唄,有啥大不了的)。
其實上蟲蟲告訴你,在某些情況下做hard reset是必不可少,沒有啥好擔心的,用git reflog中你可以看到所有的變化。比如你一不小心誤刪除了個文件,你想把它找回來,因為還有變化未提交,所以你必須想辦法恢復回來。
reflog是一個本地結構,它記錄了HEAD和分支引用在過去指向的位置。reflog信息沒法與其他任何人共享,每個人都是自己特有的reflog。重要的一點是,它不是永久保存的,有一個可配置的過期時間,reflog中過期的信息會被自動刪除。
我們來看看我們目前的提交歷史。
注意:git reset的作用是將當前分支(和HEAD隱式地)的指針移動到一個新的提交,如果使用--mixed(如果沒有指定標誌,這是預設值),更新索引。如果使用—hard會更新索引以及工作目錄。出於某種原因,我們不滿最新的提交,因此我們將重置為前一個(你可以通過指定某種引用來重置為任何提交)
git reset HEAD^
git log --pretty=oneline
我們注意到git日誌輸出中的最新提交已經沒有了。但它並沒有被刪除,它只是在git log的等正常功能中不再可見,因為默認情況下它只是按照commit來繼承延續的。
現在突然意識到實際上我們reset的commit實際上是完美的,我要想希望恢復它。怎麼恢復呢?那麼我們可以使用git reset,並讓我們的分支再次指向之前提交,但是我們如何獲得對該提交的引用,因為它不再出現在git log中。ok這兒就需要git reflog大顯身手了:
上圖中,我們感興趣是前兩項。從輸出中我們可以發現HEAD指向哪個提交以及它現在指向的位置(HEAD@{0})。因此,在我們可以直接reset所需的commit哈希標(即b30981a)前,將分支移回原來的狀態。
注意這時候倉庫的狀態是落後於遠程倉庫的,所以一切pull push都會有問題。
現在HEAD指向(由HEAD @ {0}指示)。因此,在我們可以直接使用所需的commit哈希(即b30981a)或其引用(即HEAD@{1}),將分支移回原來的狀態。注意,不要重複提交引用,因為每次使用後引用指向都會變。reset重置倉庫:
git reset HEAD@{1}
正如我們所看到的,事情已經恢復正常。所以下次你因為git重置錯誤時候,不要恐慌,更不要做重新clone,你有reflog守護天使,可以幫你找回來。
注意:我們前面提到過,默認情況下,git log只會按照commit繼承鏈來顯示。你可以通過-g/--walk-reflogs標誌來讓它按reflog鏈顯示:
git log -g
4、commit範圍
最後蟲蟲要給說的是commit範圍, 如何使用三種方法來指定提交的範圍,每種方法都有其用途。在用於多分支管理時,它們特別方便。在我們深入研究之前,下面是我們將要研究的分支和commit樹的圖片。
兩個點(..)範圍
我估計你用過這個,即使是你不完全知道它到底在做什麼。它的典型用法是過濾從同一分支中commiy的範圍內的日誌,如下所示:
我們用本節開始的圖為例子說明則是:
git log --pretty=oneline B..F
F
D
我們注意到,他顯示了不包括最左對象(B)的指定範圍之間的提交,用數學中集合表示方法為(B,F]。我們把他嘗試用於不同的分支,結果大家可以看看:
git log --pretty=oneline G..F
F
D
git log --pretty=oneline F..G
G
E
git log --pretty=oneline C..G
G
E
git log --pretty=oneline G..C
C
如果你還沒有看出以上輸出是基於什麼規律的話,那麼就讓蟲蟲告訴你好了。使用這種表示法我們可以看出是不包含第一個commit範圍符(左邊..),但包含第二個commit(..右邊)的分支。
請自己核對圖片和例子,看看是不是這樣的呢?歡迎留言說明。
還有一個很有用的用法是在要將你的變化提交到遠程倉庫之前(git伺服器)執行下面的命令:
git log --pretty=oneline origin/master..HEAD
上述命令會輸出你現在本地當前倉庫和遠程倉庫之間的差異。
多分支範圍
兩個點的範圍很酷,解決很大的問題。但是如果要想指定多個分支時,該怎麼做?比如看到你想看到當前分支中沒有其他分支的commit的?
實際上,兩個點語法只是一種語法的縮寫。如果你在給引用指定了^字符或者—not選項,那麼git log將只顯示那些不能從指定的commit引用到達的分支。比如,以下命令是等效的:
git log --pretty=oneline G..F
git log --pretty=oneline ^G F
git log --pretty=oneline F --not G
舉一個多分支的例子則是這樣的:
git log --pretty=oneline G --not H
F
G
三個點(…)範圍
我們用於指定提交範圍的最後一個方法是…符號。你可以說它是包含所有兩個範圍commit兩邊的分支。通常來說,它是由包含兩個commit的所有提交和分支。
最後一個例子:
git log --pretty=oneline C...G
G
E
C
好了,蟲蟲今天要介紹的4個git命令都說完了,剩下的就是要你去理解和實操,多練練就都知道了。如果本文章請關注蟲蟲,並給我點讚。