Git --everything-is-local

2.3 Git 基礎 - 檢視提交的歷史記錄

檢視提交的歷史記錄

在提交數個更新,或者複製已有一些歷史記錄的儲存庫後。 或許會想希望檢視之前發生過什麼事。 最基本也最具威力的工具就是 git log 命令。

以下採用非常簡單,名為 simplegit 的專案做展示。 欲取得此專案,執行以下命令:

git clone git://github.com/schacon/simplegit-progit.git

在此專案目錄內執行 git log,應該會看到類似以下訊息:

$ git log
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Sat Mar 15 16:40:33 2008 -0700

    removed unnecessary test code

commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Sat Mar 15 10:31:28 2008 -0700

    first commit

在未加任何參數情況下,git log以新到舊的順序列出儲存庫的提交的歷史記錄。 也就是說最新的更新會先被列出來。 同時也會列出每個更新的 SHA-1 查核值、作者大名及電子郵件地址、及提交時輸入的訊息。

git log命令有很多樣化的選項,供讀者精確指出想搜尋的結果。 接下來會介紹一些常用的選項。

最常用的選項之一為 -p,用來顯示每個更新之間差別的內容。 另外還可以加上 -2 參數,限制為只輸出最後兩個更新。

$ git log -p -2
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number

diff --git a/Rakefile b/Rakefile
index a874b73..8f94139 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,5 +5,5 @@ require 'rake/gempackagetask'
 spec = Gem::Specification.new do |s|
     s.name      =   "simplegit"
-    s.version   =   "0.1.0"
+    s.version   =   "0.1.1"
     s.author    =   "Scott Chacon"
     s.email     =   "schacon@gee-mail.com

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Sat Mar 15 16:40:33 2008 -0700

    removed unnecessary test code

diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index a0a60ae..47c6340 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -18,8 +18,3 @@ class SimpleGit
     end

 end
-
-if $0 == __FILE__
-  git = SimpleGit.new
-  puts git.show
-end
\ No newline at end of file

這個選項除了顯示相同的資訊外,還另外附上每個更新的差異。 這對於重新檢視或者快速的瀏覽協同工作伙伴新增的更新非常有幫助。

有時候用 word level 的方式比 line level 更容易看懂變化。在 git log -p 後面附加 --word-diff 選項,就可以取代預設的 line level 模式。當你在看原始碼的時候 word level 還挺有用的,還有一些大型文字檔,如書籍或論文就派上用場了,範例如下:

$ git log -U1 --word-diff
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number

diff --git a/Rakefile b/Rakefile
index a874b73..8f94139 100644
--- a/Rakefile
+++ b/Rakefile
@@ -7,3 +7,3 @@ spec = Gem::Specification.new do |s|
    s.name      =   "simplegit"
    s.version   =   [-"0.1.0"-]{+"0.1.1"+}
    s.author    =   "Scott Chacon"

如你所見,輸出範例中沒有列出新增與刪除的行,變動的地方用內嵌的方式顯示,你可以看到新增的字被包括在 {+ +} 內,而刪除的則包括在 [- -] 內,如果你想再減少顯示的資訊,將上述的三行再減少到只顯示變動的那行。你可以用 -U1 選項,就像上述的範例中那樣。

另外也可以使用git log提供的一系統摘要選項。 例如:若想檢視每個更新的簡略統計資訊,可使用 --stat 選項:

$ git log --stat
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number

 Rakefile |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Sat Mar 15 16:40:33 2008 -0700

    removed unnecessary test code

 lib/simplegit.rb |    5 -----
 1 file changed, 5 deletions(-)

commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Sat Mar 15 10:31:28 2008 -0700

    first commit

 README           |    6 ++++++
 Rakefile         |   23 +++++++++++++++++++++++
 lib/simplegit.rb |   25 +++++++++++++++++++++++++
 3 files changed, 54 insertions(+)

如以上所示,--stat選項在每個更新項目的下方列出被更動的檔案、有多少檔案被更動,以及有多行列被加入或移出該檔案。 也會在最後印出摘要的訊息。 其它實用的選項是 --pretty。 這個選項改變原本預設輸出的格式。 有數個內建的選項供讀者選用。 其中 oneline 選項將每一個更新印到單獨一行,對於檢視很多更新時很有用。 更進一步,shortfullfuller 選項輸出的格式大致相同,但會少一些或者多一些資訊。

$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 changed the version number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test code
a11bef06a3f659402fe7563abf99ad00de2209e6 first commit

最有趣的選項是 format,允許讀者指定自訂的輸出格式。 當需要輸出給機器分析時特別有用。 因為明確地指定了格式,即可確定它不會因為更新 Git 而被更動:

$ git log --pretty=format:"%h - %an, %ar : %s"
ca82a6d - Scott Chacon, 11 months ago : changed the version number
085bb3b - Scott Chacon, 11 months ago : removed unnecessary test code
a11bef0 - Scott Chacon, 11 months ago : first commit

表格2-1列出一些 format 支援的選項。

選項 選項的說明
%H 該更新的SHA1雜湊值
%h 該更新的簡短SHA1雜湊值
%T 存放該更新的根目錄的Tree物件的SHA1雜湊值
%t 存放該更新的根目錄的Tree物件的簡短SHA1雜湊值
%P 該更新的父更新的SHA1雜湊值
%p 該更新的父更新的簡短SHA1雜湊值
%an 作者名字
%ae 作者電子郵件
%ad 作者的日期 (格式依據 date 選項而不同)
%ar 相對於目前時間的作者的日期
%cn 提交者的名字
%ce 提交者的電子郵件
%cd 提交的日期
%cr 相對於目前時間的提交的日期
%s 標題

讀者可能會好奇 作者提交者 之間的差別。 作者 是完成該工作的人,而 提交者 則是最後將該工作提交出來的人。 因此,若讀者將某個專案的修補檔送出,而且該專案的核心成員中一員套用該更新,則讀者與該成員皆會被列入該更新。 讀者即 作者,而該成員則是 提交者。 在第五章會提到較多之間的差別。

onelineformat 選項對於另一個名為 --graph 的選項特別有用。 該選項以 ASCII 畫出分支的分歧及合併的歷史。 可參考我們的 Grit 的儲存庫:

$ git log --pretty=format:"%h %s" --graph
* 2d3acf9 ignore errors from SIGCHLD on trap
*  5e3ee11 Merge branch 'master' of git://github.com/dustin/grit
|\
| * 420eac9 Added a method for getting the current branch.
* | 30e367c timeout code and tests
* | 5a09431 add timeout protection to grit
* | e1193f8 support for heads with slashes in them
|/
* d6016bc require time for xmlschema
*  11d191e Merge branch 'defunkt' into local

這些只是一些簡單的 git log 的選項,還有許多其它的。 表格2-2列出目前我們涵蓋的及一些可能有用的格式選項,以及它們如何更動 log 命令的輸出格式。

選項 選項的說明
-p 顯示每個更新與上一個的差異。
--word-diff 使用 word diff 格式顯示 patch 內容。
--stat 顯示每個更新更動的檔案的統計及摘要資訊。
--shortstat 僅顯示--stat提供的的訊息中關於更動、插入、刪除的文字。
--name-only 在更新的訊息後方顯示更動的檔案列表。
--name-status 顯示新增、更動、刪除的檔案列表。
--abbrev-commit 僅顯示SHA1查核值的前幾位數,而不是顯示全部的40位數。
--relative-date 以相對於目前時間方式顯示日期(例如:“2 weeks ago”),而不是完整的日期格式。
--graph 以 ASCII 在 log 輸出旁邊畫出分支的分歧及合併。
--pretty 以其它格式顯示更新。 可用的選項包含oneline、short、full、fuller及可自訂格式的format。
--oneline --pretty=oneline --abbrev-commit 的簡短用法。

限制 log 的輸出範圍

除了輸出格式的選項,git log也接受一些好用的選項。 也就是指定只顯示某一個子集合的更新。 先前已介紹過僅顯示最後兩筆更新的 -2 選項。 實際上可指定 -<n>,而 n 是任何整數,用來顯示最後的 n 個更新。 不過讀者可能不太會常用此選項,因為 Git 預設將所有的輸出導到分頁程式,故一次只會看到一頁。

然而,像 --since--until 限制時間的選項就很有用。 例如,以下命令列出最近兩週的更新:

$ git log --since=2.weeks

此命令支援多種格式。 可指定特定日期(如:“2008-01-15”)或相對的日期,如:“2 years 1 day 3 minutes ago”。

使用者也可以過濾出符合某些搜尋條件的更新。 --author 選項允許使用者過濾出特定作者,而 --grep 選項允許以關鍵字搜尋提交的訊息。(注意:若同時使用作者名字及字串比對,該命令會列出同時符合二個條件的更新。)

若希望比對多個字串,需要再加上 --all-match;否則只會列出符合任一條件的更新。

最後一個有用的選項是過濾路徑。 若指定目錄或檔案名稱,可僅印出更動到這些檔案的更新。 這選項永遠放在最後,而且一般來說會在前方加上 -- 以資區別。

在表格2-3,我們列出這些選項以及少數其它常見選項以供參考。

選項 選項的說明文字
-(n) 僅顯示最後 n 個更新
--since, --after 列出特定日期後的更新。
--until, --before 列出特定日期前的更新。
--author 列出作者名稱符合指定字串的更新。
--committer 列出提交者名稱符合指定字串的更新。

例如:若想檢視 Git 的原始碼中,Junio Hamano 在 2008 年十月提交且不是合併用的更新。 可執行以下命令:

$ git log --pretty="%h - %s" --author=gitster --since="2008-10-01" \
   --before="2008-11-01" --no-merges -- t/
5610e3b - Fix testcase failure when extended attribute
acd3b9e - Enhance hold_lock_file_for_{update,append}()
f563754 - demonstrate breakage of detached checkout wi
d1a43f2 - reset --hard/read-tree --reset -u: remove un
51a94af - Fix "checkout --track -b newbranch" on detac
b0ad11e - pull: allow "git pull origin $something:$cur

Git 原始碼的更新歷史接近二萬筆更新,本命令顯示符合條件的六筆更新。

使用圖形界面檢視歷史

若讀者較偏向使用圖形界面檢視歷史,或許會想看一下隨著 Git 發佈的,名為 gitk 的 Tcl/Tk 程式。 Gitk 基本上就是 git log 的圖形界面版本,而且幾乎接受所有 git log 支援的過濾用選項。 若在專案所在目錄下執行 gitk 命令,將會看到如圖2-2的畫面。


圖2-2。 gitk檢視歷史程式。

在上圖中可看到視窗的上半部顯示相當棒的更新歷史圖。 視窗下半部則顯示當時被點選的更新引入的變更。