git 查看提交、撤消

查看提交歷史

在提交了若干更新之后,又或者克隆了某個項目,想回顧下提交歷史,可以使用 git log 命令查看。

接下來的例子會用我專門用于演示的 simplegit 項目,運行下面的命令獲取該項目源代碼:

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

然后在此項目中運行 git log,應(yīng)該會看到下面的輸出:

 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

默認不用任何參數(shù)的話,git log 會按提交時間列出所有的更新,最近的更新排在最上面??吹搅藛?,每次更新都有一個 SHA-1 校驗和、作者的名字和電子郵件地址、提交時間,最后縮進一個段落顯示提交說明。

git log 有許多選項可以幫助你搜尋感興趣的提交,接下來我們介紹些最常用的。

我們常用 -p 選項展開顯示每次提交的內(nèi)容差異,用 -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,7 +5,7 @@ require 'rake/gempackagetask'
 spec = Gem::Specification.new do |s|
-    s.version   =   "0.1.0"
+    s.version   =   "0.1.1"
     s.author    =   "Scott Chacon"

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

在做代碼審查,或者要快速瀏覽其他協(xié)作者提交的更新都作了哪些改動時,就可以用這個選項。此外,還有許多摘要選項可以用,比如 –stat,僅顯示簡要的增改行數(shù)統(tǒng)計:

 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 files changed, 1 insertions(+), 1 deletions(-)

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 files changed, 0 insertions(+), 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(+), 0 deletions(-)

每個提交都列出了修改過的文件,以及其中添加和移除的行數(shù),并在最后列出所有增減行數(shù)小計。還有個常用的 –pretty 選項,可以指定使用完全不同于默認格式的方式展示提交歷史。比如用 oneline 將每個提交放在一行顯示,這在提交數(shù)很大時非常有用。另外還有 short,full 和 fuller 可以用,展示的信息或多或少有些不同,請自己動手實踐一下看看效果如何:

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

但最有意思的是 format,可以定制要顯示的記錄格式,這樣的輸出便于后期編程提取分析,像這樣:

 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 列出了常用的格式占位符寫法及其代表的意義。

選項  說明
%H  提交對象(commit)的完整哈希字串
%h  提交對象的簡短哈希字串
%T  樹對象(tree)的完整哈希字串
%t  樹對象的簡短哈希字串
%P  父對象(parent)的完整哈希字串
%p  父對象的簡短哈希字串
%an 作者(author)的名字
%ae 作者的電子郵件地址
%ad 作者修訂日期(可以用 -date= 選項定制格式)
%ar 作者修訂日期,按多久以前的方式顯示
%cn 提交者(committer)的名字
%ce 提交者的電子郵件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式顯示
%s  提交說明

你一定奇怪作者(author)和提交者(committer)之間究竟有何差別,其實作者指的是實際作出修改的人,提交者指的是最后將此工作成果提交到倉庫的人。所以,當你為某個項目發(fā)布補丁,然后某個核心成員將你的補丁并入項目時,你就是作者,而那個核心成員就是提交者。我們會在第五章再詳細介紹兩者之間的細微差別。

用 oneline 或 format 時結(jié)合 –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 還列出了一些其他常用的選項及其釋義。

選項  說明
-p  按補丁格式顯示每個更新之間的差異。
–stat   顯示每次更新的文件修改統(tǒng)計信息。
–shortstat  只顯示 –stat 中最后的行數(shù)修改添加移除統(tǒng)計。
–name-only  僅在提交信息后顯示已修改的文件清單。
–name-status    顯示新增、修改、刪除的文件清單。
–abbrev-commit  僅顯示 SHA-1 的前幾個字符,而非所有的 40 個字符。
–relative-date  使用較短的相對時間顯示(比如,“2 weeks ago”)。
–graph  顯示 ASCII 圖形表示的分支合并歷史。
–pretty 使用其他格式顯示歷史提交信息。可用的選項包括 oneline,short,full,fuller 和 format(后跟指定格式)。

限制輸出長度

除了定制輸出格式的選項之外,git log 還有許多非常實用的限制輸出長度的選項,也就是只輸出部分提交信息。之前我們已經(jīng)看到過 -2 了,它只顯示最近的兩條提交,實際上,這是 -<n> 選項的寫法,其中的 n 可以是任何自然數(shù),表示僅顯示最近的若干條提交。不過實踐中我們是不太用這個選項的,Git 在輸出所有提交時會自動調(diào)用分頁程序(less),要看更早的更新只需翻到下頁即可。

另外還有按照時間作限制的選項,比如 –since 和 –until。下面的命令列出所有最近兩周內(nèi)的提交:

git log --since=2.weeks

你可以給出各種時間格式,比如說具體的某一天(“2008-01-15”),或者是多久以前(“2 years 1 day 3 minutes ago”)。

還可以給出若干搜索條件,列出符合的提交。用 –author 選項顯示指定作者的提交,用 –grep 選項搜索提交說明中的關(guān)鍵字。(請注意,如果要得到同時滿足這兩個選項搜索條件的提交,就必須用 –all-match 選項。否則,滿足任意一個條件的提交都會被匹配出來)

另一個真正實用的git log選項是路徑(path),如果只關(guān)心某些文件或者目錄的歷史提交,可以在 git log 選項的最后指定它們的路徑。因為是放在最后位置上的選項,所以用兩個短劃線(–)隔開之前的選項和后面限定的路徑名。

表 2-3 還列出了其他常用的類似選項。

選項  說明
-(n)    僅顯示最近的 n 條提交
–since, –after  僅顯示指定時間之后的提交。
–until, –before 僅顯示指定時間之前的提交。
–author 僅顯示指定作者相關(guān)的提交。
–committer  僅顯示指定提交者相關(guān)的提交。

來看一個實際的例子,如果要查看 Git 倉庫中,2008 年 10 月期間,Junio Hamano 提交的但未合并的測試腳本(位于項目的 t/ 目錄下的文件),可以用下面的查詢命令:

 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 項目有 20,000 多條提交,但我們給出搜索選項后,僅列出了其中滿足條件的 6 條。

撤消操作

任何時候,你都有可能需要撤消剛才所做的某些操作。接下來,我們會介紹一些基本的撤消操作相關(guān)的命令。請注意,有些撤銷操作是不可逆的,所以請務(wù)必謹慎小心,一旦失誤,就有可能丟失部分工作成果。

修改最后一次提交

有時候我們提交完了才發(fā)現(xiàn)漏掉了幾個文件沒有加,或者提交信息寫錯了。想要撤消剛才的提交操作,可以使用 –amend 選項重新提交:

$ git commit --amend

此命令將使用當前的暫存區(qū)域快照提交。如果剛才提交完沒有作任何改動,直接運行此命令的話,相當于有機會重新編輯提交說明,但將要提交的文件快照和之前的一樣。

啟動文本編輯器后,會看到上次提交時的說明,編輯它確認沒問題后保存退出,就會使用新的提交說明覆蓋剛才失誤的提交。

如果剛才提交時忘了暫存某些修改,可以先補上暫存操作,然后再運行 –amend 提交:

$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend

上面的三條命令最終只是產(chǎn)生一個提交,第二個提交命令修正了第一個的提交內(nèi)容。

取消已經(jīng)暫存的文件

接下來的兩個小節(jié)將演示如何取消暫存區(qū)域中的文件,以及如何取消工作目錄中已修改的文件。不用擔心,查看文件狀態(tài)的時候就提示了該如何撤消,所以不需要死記硬背。來看下面的例子,有兩個修改過的文件,我們想要分開提交,但不小心用 git add . 全加到了暫存區(qū)域。該如何撤消暫存其中的一個文件呢?其實,git status 的命令輸出已經(jīng)告訴了我們該怎么做:

$ git add .
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   README.txt
#       modified:   benchmarks.rb
#

就在 “Changes to be committed” 下面,括號中有提示,可以使用 git reset HEAD <file>... 的方式取消暫存。好吧,我們來試試取消暫存 benchmarks.rb 文件:

$ git reset HEAD benchmarks.rb
benchmarks.rb: locally modified
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   README.txt
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   benchmarks.rb
#

這條命令看起來有些古怪,先別管,能用就行?,F(xiàn)在 benchmarks.rb 文件又回到了之前已修改未暫存的狀態(tài)。

取消對文件的修改

如果覺得剛才對 benchmarks.rb 的修改完全沒有必要,該如何取消修改,回到之前的狀態(tài)(也就是修改之前的版本)呢?git status 同樣提示了具體的撤消方法,接著上面的例子,現(xiàn)在未暫存區(qū)域看起來像這樣:

# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   benchmarks.rb
#

在第二個括號中,我們看到了拋棄文件修改的命令(至少在 Git 1.6.1 以及更高版本中會這樣提示,如果你還在用老版本,我們強烈建議你升級,以獲取最佳的用戶體驗),讓我們試試看:

$ git checkout -- benchmarks.rb
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   README.txt
#

可以看到,該文件已經(jīng)恢復(fù)到修改前的版本。你可能已經(jīng)意識到了,這條命令有些危險,所有對文件的修改都沒有了,因為我們剛剛把之前版本的文件復(fù)制過來重寫了此文件。所以在用這條命令前,請務(wù)必確定真的不再需要保留剛才的修改。如果只是想回退版本,同時保留剛才的修改以便將來繼續(xù)工作,可以用下章介紹的 stashing 和分支來處理,應(yīng)該會更好些。

記住,任何已經(jīng)提交到 Git 的都可以被恢復(fù)。即便在已經(jīng)刪除的分支中的提交,或者用 –amend 重新改寫的提交,都可以被恢復(fù)(關(guān)于數(shù)據(jù)恢復(fù)的內(nèi)容見第九章)。所以,你可能失去的數(shù)據(jù),僅限于沒有提交過的,對 Git 來說它們就像從未存在過一樣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容