git 使用詳解(4)—— commit -a -m/diff --staged/rm/mv
查看已暫存和未暫存的更新
實際上 git status
的顯示比較簡單,僅僅是 列出了(修改過的、新創建的、已經暫存但未提交的)文件,如果要查看具體修改了什麽地方,可以用git diff 命令。稍後我們會詳細介紹 git diff,不過現在,它已經能回答我們的兩個問題了:當前做的哪些更新還沒有暫存?有哪些更新已經暫存起來準備好了下次提交?git diff 會使用文件補丁的格式顯示具體添加和刪除的行。
假如再次修改 README 文件後暫存,然後編輯 benchmarks.rb 文件後先別暫存,運行 status 命令,會看到:
$ git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # new file: README # # Changed but not updated: # (use "git add <file>..." to update what will be committed) # # modified: benchmarks.rb #
要查看 尚未暫存 的文件更新了哪些部分,不加參數 直接輸入git diff
:
$ git diff diff --git a/benchmarks.rb b/benchmarks.rb index 3cb747f..da65585 100644 --- a/benchmarks.rb +++ b/benchmarks.rb @@ -36,6 +36,10 @@ def main @commit.parents[0].parents[0].parents[0] end + run_code(x, ‘commits 1‘) do + git.commits.size + end + run_code(x, ‘commits 2‘) do log = git.commits(‘master‘, 15) log.size
此命令比較的是 工作目錄中 當前文件
和暫存區域快照
之間的差異,也就是修改之後還沒有暫存起來的變化內容。
若要看 已經暫存起來 的文件
和 上次提交時的快照
之間的差異,可以用 git diff --cached
命令。(Git 1.6.1 及更高版本還允許使用git diff --staged
,效果是相同的,但更好記些。)來看看實際的效果:
$ git diff --cached diff --git a/README b/README new file mode 100644 index 0000000..03902a1 --- /dev/null +++ b/README2 @@ -0,0 +1,5 @@ +grit + by Tom Preston-Werner, Chris Wanstrath + http://github.com/mojombo/grit + +Grit is a Ruby library for extracting information from a Git repository
請註意,單單 git diff 不過是顯示還沒有暫存起來的改動
,而不是這次工作和上次提交之間的差異。所以有時候你一下子暫存了所有更新過的文件後,運行git diff 後卻什麽也沒有,就是這個原因。
像之前說的,暫存 benchmarks.rb 後再編輯,運行 git status 會看到暫存前後的兩個版本:
$ git add benchmarks.rb
$ echo ‘# test line‘ >> benchmarks.rb
$ git status
# On branch master
#
# Changes to be committed:
#
# modified: benchmarks.rb
#
# Changed but not updated:
#
# modified: benchmarks.rb
#
現在運行 git diff 看暫存前後的變化:
$ git diff
diff --git a/benchmarks.rb b/benchmarks.rb
index e445e28..86b2f7c 100644
--- a/benchmarks.rb
+++ b/benchmarks.rb
@@ -127,3 +127,4 @@ end
main()
##pp Grit::GitRuby.cache_client.stats
+# test line
然後用 git diff --cached 查看已經暫存起來的變化:
$ git diff --cached
diff --git a/benchmarks.rb b/benchmarks.rb
index 3cb747f..e445e28 100644
--- a/benchmarks.rb
+++ b/benchmarks.rb
@@ -36,6 +36,10 @@ def main
@commit.parents[0].parents[0].parents[0]
end
+ run_code(x, ‘commits 1‘) do
+ git.commits.size
+ end
+
run_code(x, ‘commits 2‘) do
log = git.commits(‘master‘, 15)
log.size
提交更新
現在的暫存區域已經準備妥當可以提交了。在此之前,請一定要確認還有什麽修改過的或新建的文件還沒有 git add
過,否則提交的時候不會記錄這些還沒暫存起來的變化。所以,每次準備提交前,先用git status 看下,是不是都已暫存起來了,然後再運行提交命令 git commit:
$ git commit
這種方式會啟動文本編輯器以便輸入本次提交的說明。(默認會啟用 shell 的環境變量 $EDITOR 所指定的軟件,一般都是 vim 或 emacs。當然也可以按照第一章介紹的方式,使用git config --global core.editor 命令設定你喜歡的編輯軟件。)
編輯器會顯示類似下面的文本信息(本例選用 Vim 的屏顯方式展示):
# Please enter the commit message for your changes. Lines starting
# with ‘#‘ will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: README
# modified: benchmarks.rb
~
~
~
".git/COMMIT_EDITMSG" 10L, 283C
可以看到,默認的提交消息包含最後一次運行 git status 的輸出,放在註釋行裏,另外開頭還有一空行,供你輸入提交說明。你完全可以去掉這些註釋行,不過留著也沒關系,多少能幫你回想起這次更新的內容有哪些。(如果覺得這還不夠,可以用-v 選項將修改差異的每一行都包含到註釋中來。)退出編輯器時,Git 會丟掉註釋行,將說明內容和本次更新提交到倉庫。
另外也可以用 -m 參數後跟提交說明的方式,在一行命令中提交更新:
$ git commit -m "Story 182: Fix benchmarks for speed"
[master]: created 463dc4f: "Fix benchmarks for speed"
2 files changed, 3 insertions(+), 0 deletions(-)
create mode 100644 README
好,現在你已經創建了第一個提交!可以看到,提交後它會告訴你,
- 當前是在哪個分支(master)提交的,
- 本次提交的完整 SHA-1 校驗和是什麽(463dc4f),
- 以及在本次提交中,有多少文件修訂過,多少行添改和刪改過。
記住,提交時記錄的是放在暫存區域的快照,任何還未暫存的仍然保持已修改狀態,可以在下次提交時納入版本管理。每一次運行提交操作,都是對你項目作一次快照,以後可以回到這個狀態,或者進行比較。
跳過使用暫存區域
盡管使用暫存區域的方式可以精心準備要提交的細節,但有時候這麽做略顯繁瑣。Git 提供了一個跳過使用暫存區域的方式,只要在提交的時候,給 git commit 加上 -a 選項
,Git 就會自動把所有已經跟蹤過的文件暫存起來一並提交,從而跳過git add 步驟:
$ git status
# On branch master
#
# Changed but not updated:
#
# modified: benchmarks.rb
#
$ git commit -a -m ‘added new benchmarks‘
[master 83e38c7] added new benchmarks
1 files changed, 5 insertions(+), 0 deletions(-)
看到了嗎?提交之前不再需要 git add 文件 benchmarks.rb 了。
移除文件
要從 Git 中移除某個文件,就必須要從已跟蹤文件清單中移除(確切地說,是從暫存區域移除),然後提交。可以用git rm
命令完成此項工作,並同時從工作目錄中刪除指定的文件,這樣以後就不會出現在未跟蹤文件清單中了。
如果只是簡單地從工作目錄中手工刪除文件
,運行 git status 時就會在 “Changed but not updated” 部分(也就是_未暫存_清單)看到:
$ rm grit.gemspec
$ git status
# On branch master
#
# Changed but not updated:
# (use "git add/rm <file>..." to update what will be committed)
#
# deleted: grit.gemspec
#
然後再運行 git rm 記錄此次移除文件的操作:
$ git rm grit.gemspec
rm ‘grit.gemspec‘
$ git status
# On branch master
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# deleted: grit.gemspec
#
最後提交的時候,該文件就不再納入版本管理了。如果刪除之前修改過並且已經放到暫存區域的話,則必須要用強制刪除選項 -f(譯註:即 force 的首字母),以防誤刪除文件後丟失修改的內容。
另外一種情況是,我們想把文件從 Git 倉庫中刪除(亦即從暫存區域移除),但仍然希望保留在當前工作目錄中。換句話說,僅是從跟蹤清單中刪除。比如一些大型日誌文件或者一堆.a 編譯文件,不小心納入倉庫後,要移除跟蹤但不刪除文件,以便稍後在 .gitignore 文件中補上,用--cached 選項即可:
$ git rm --cached readme.txt
後面可以列出文件或者目錄的名字,也可以使用 glob 模式。比方說:
$ git rm log/*.log
註意到星號 * 之前的反斜杠 \
,因為 Git 有它自己的文件模式擴展匹配方式,所以我們不用 shell 來幫忙展開(譯註:實際上不加反斜杠也可以運行,只不過按照 shell 擴展的話,僅僅刪除指定目錄下的文件而不會遞歸匹配。上面的例子本來就指定了目錄,所以效果等同,但下面的例子就會用遞歸方式匹配,所以必須加反斜杠。)。此命令刪除所有log/ 目錄下擴展名為 .log 的文件。類似的比如:
$ git rm \*~
會遞歸刪除當前目錄及其子目錄中所有 ~ 結尾的文件。
移動文件
不像其他的 VCS 系統,Git 並不跟蹤文件移動操作。如果在 Git 中重命名了某個文件,倉庫中存儲的元數據並不會體現出這是一次改名操作。不過 Git 非常聰明,它會推斷出究竟發生了什麽,至於具體是如何做到的,我們稍後再談。
既然如此,當你看到 Git 的 mv 命令時一定會困惑不已。要在 Git 中對文件改名,可以這麽做:
$ git mv file_from file_to
它會恰如預期般正常工作。實際上,即便此時查看狀態信息,也會明白無誤地看到關於重命名操作的說明:
$ git mv README.txt README
$ git status
# On branch master
# Your branch is ahead of ‘origin/master‘ by 1 commit.
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# renamed: README.txt -> README
#
其實,運行 git mv 就相當於運行了下面三條命令:
$ mv README.txt README
$ git rm README.txt
$ git add README
如此分開操作,Git 也會意識到這是一次改名,所以不管何種方式都一樣。當然,直接用 git mv 輕便得多,不過有時候用其他工具批處理改名的話,要記得在提交前刪除老的文件名,再添加新的文件名。
git 使用詳解(4)—— commit -a -m/diff --staged/rm/mv