Git鮮為人知的四個命令:bisect,blame,reflog和提交範圍

2020-12-13 蟲蟲搜奇

現在每個人都在學編程,可能你就是正在成為百萬碼農大軍中的一個。好吧,就算你沒有,那麼你也可能在你的日常工作中使用版本控制系統(也不是......好吧,那就忽略上面的兩句話)。做版本管理最流行的工具是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命令都說完了,剩下的就是要你去理解和實操,多練練就都知道了。如果本文章請關注蟲蟲,並給我點讚。

相關焦點

  • 你應該知道的10個Git命令(附連結)
    在本文中,我們將討論作為開發人員、數據科學家或產品經理應該了解的各種Git命令,如何使用Git進行檢查、刪除和整理,以及如何通過Bash別名和Git編輯器配置來逃避Vim並節省時間。這裡有10個要知道的命令和一些常見的標誌。每個命令都連結到該命令的Atlassian Bitbucket指南。
  • 幾個常用的 Git 高級命令
    相關的書籍和教程網上琳琅滿目,它們多數都詳細的介紹其基本的使用和命令。本人根據自己的經驗,整理出幾個較為高級而常用的命令。推薦資料 Git Book。https://git-scm.com/book/en/v2Git blameGit blame 可以查詢每一行代碼的 commit ID、提交者和提交日期。
  • 如何用Git工具發現和解決開發項目中的痛點
    而且儘管通過管制和審核流程似乎應該是完美無瑕的代碼,但是實際上並不一定會帶來功能良好的系統。軟體開發的過程不僅涉及開發人員之間以及開發人員與他人團隊之間的交互,如何快速的無聲的項目的痛點這是個問題。如果你的開發項目是採用git管理,那麼Git本身就能給我們很多好用的工具,本文蟲蟲就給大家講講git中自帶哪些解決痛點的工具。
  • Git命令的用法小結
    提交 (Commit)一次提交的表示一組針對工作目錄內容的修改或操作。例如新增了5個文件,刪除了2個,修改3個。這些修改信息一次性納入git倉庫中,形成了對工作目錄整體的新的快照。在git中一次提交即對應於新的快照,即可看作一個臨時版本。「提交」總是從緩衝區發出。
  • 你可能不太會用的 10 個 Git 命令
    我們將了解該如何用 Git 進行檢查、刪除和整理操作。我們還將介紹如何用 Bash 別名和 Git 編輯器配置來逃避 Vim 以節省時間。如果你不熟悉基本的 git 命令,在閱讀本文前可以先參考我之前寫的關於 Git 工作流程的文章。
  • 【Git】616- git命令的進階和複習(帶動圖效果)
    learnGit在線學習猴子都能懂的git小姐姐用動畫圖解Git個人比較推薦第二個Git學習網站猴子都能懂的git,圖文結合,簡單明了,本文將介紹一些常用 Git 指令,作為一個學習總結git rebasegit mergegit resetgit revertgit cherry-pick
  • ​你可能不太會用的10個Git命令
    我們將了解該如何用 Git 進行檢查、刪除和整理操作。我們還將介紹如何用 Bash 別名和 Git 編輯器配置來逃避 Vim 以節省時間。如果你不熟悉基本的 git 命令,在閱讀本文前可以先參考我之前寫的關於 Git 工作流程的文章。
  • 你可能不太會用的10個Git命令
    如果你不熟悉基本的 git 命令,在閱讀本文前可以先參考我之前寫的關於 Git 工作流程的文章。地址:https://towardsdatascience.com/learn-enough-git-to-be-useful-281561eef959本文介紹了 10 個常見的命令以及它們的一些參數。
  • Git命令的動畫展示,讓我們學習Git事半功倍
    交互式基礎也可以在您當前正在使用的分支上使用,並且希望修改某些提交。我們可以對基於基準的提交執行6個操作:reword:更改提交消息edit:修改此提交squash:將提交合併到上一個提交中fixup:將提交合併到先前的提交中,而不保留提交的日誌消息exec:對要重新設置基準的每個提交運行命令drop:刪除提交太棒了!這樣,我們可以完全控制提交。
  • git命令的進階和複習(帶動圖效果)
    連結:https://juejin.im/post/5e9e49356fb9a03c917fe7fd個人學習途徑主要以下幾個:learnGit在線學習猴子都能懂的git小姐姐用動畫圖解Git個人比較推薦第二個Git學習網站猴子都能懂的git,圖文結合,簡單明了,本文將介紹一些常用 Git 指令,作為一個學習總結
  • git環境配置和.gitconfig配置文件詳解
    剛開始只需學一些簡單的命令:clone、add,commit,checkout,push,pull,status和diff然後就足矣應付你日常的工作了。隨著工作的深入,你可能需要學習分支、tag標籤、等操作和概念。
  • 這才是真正的Git——Git實用技巧
    在這篇文章裡我會使用操作錄屏的方式來介紹例子,希望這種方式可以讓你更直觀的了解命令的使用方法。將幾個commit壓縮成一個另外如果要合併的是最近的幾個commit,我們還可以用git reset --soft HEAD~3 && git commit -m 'xxx'來實現。對這個有問題的同學可以參照Git內部原理強調的可視化方法思考一下。
  • Git 常用命令及使用
    Git 常用命令使用1)、本地庫初始化 git init2)、設置籤名作用:區分不同開發人員的身份。說明:這裡設置的籤名和登錄遠程庫(代碼託管中心)的帳戶沒有關係。[郵箱地址]籤名信息位置:cd ~ 、cat .gitconfig3)、基本操作a)、查看狀態: git status(查看工作區、暫存區的狀態)b)、添加操作: git add 文件名(將工作區新建/修改的內容添加到暫存區)c)、提交操作: git commit -m 「commit message」 文件名(將暫存區的內容提交到本地庫
  • 您必須知道的 Git 分支開發規範,附 Git 常用命令大全!
    編寫良好的 Commit messages 可以達到3個重要的目的:目前,社區有多種 Commit message 的寫法規範。來自 Github 上的 Angular 規範是目前使用最廣的寫法,比較合理和系統化。如下圖:Commit messages 的基本語法規範。
  • Git常用命令總結及其用法說明
    解決方法就是先把本地緩存刪除(改變成未track狀態),然後再提交:git rm -r --cached .git add .>git reset --hard commit_id //回退到指定版本7、查看命令歷史git reflog8、拉取暫存區文件並將其替換成工作區文件gitcheckout-- <file>
  • Git版本控制常用命令
    Git常用命令集git使用命令1、查看git配置信息git config --list2、查看git用戶名git config user.name3、查看郵箱配置git config user.email4、全局配置用戶名git config --global user.name "nameVal"5、全局配置郵箱git config --global user.email
  • 7000+ 字帶你全面搞懂 Git 命令+原理!
    當你執行git commit,文件改動就到本地倉庫來了~Remote:遠程倉庫,就是類似github,碼雲等網站所提供的倉庫,可以理解為遠程數據交換的倉庫~Git的工作流程上一小節介紹完Git的四大工作區域,這一小節呢,介紹Git的工作流程咯,把git的操作命令和幾個工作區域結合起來,個人覺得更容易理解一些吧,哈哈,
  • Git命令總結,總結收藏
    等命令自動著色 git config --global color.status auto git config --global color.diff auto git config --global color.branch auto git config --global color.interactive auto git
  • 工作流一目了然,看小姐姐用動圖展示10大Git命令
    merge、git rebase、git reset、git revert、git fetch、git pull、git reflog……你知道這些 git 命令執行的究竟是什麼任務嗎?在本文中,熟知 JavaScript、TypeScript、GraphQL、Serverless、AWS、Docker 和 Golang 的 21 歲年輕軟體顧問 Lydia Hallie 通過動圖形式直觀地介紹了這些常用 git 命令的工作過程,包你過目不忘。
  • Git 常用命令清單筆記
    $ git pull -p # 等同於下面的命令  $ git fetch --prune origin $ git fetch -p  更改pull只需要更改config文件裡,那三個url的順序即可,fetch-url會直接對應排行第一的那個utl連接。