Git 暫存區的使用
一個健康的版本庫,是可閱讀,可追溯的。對於每一次提交,我們需要有清晰的把握。 Git 的暫存區(index),就是我們的得力工具。暫存區,就像是超市裏的購物車。 我們在貨架(工作區)上挑選商品(文件的修改),並帶到收銀臺結帳(提交)。 有了暫存區,我們可以對一大堆修改進行分門別類,先後有序的提交。
基本命令
對暫存區的操作,主要使用三個命令 add、reset、checkout。
它們的作用分別爲:
-
add將修改加入暫存區 -
reset將修改從暫存區移出 -
checkout放棄未加入暫存區的修改(從暫存區同步到工作區) 其中resset和checkout都是多用途的命令,分別還有另一個用途。reset還可用於重置當前分支,checkout還可用於重置 HEAD 並檢出文件。 區別在於若命令後跟提交(hash)或指向提交的指針(分支、tag 等), 則是重置分支和 HEAD 的功能; 命令後跟文件,或沒有參數,則是本文所說的用途。
精细操作 --patch
add、reset、checkout 三個命令都可以加 --patch 選項,簡寫爲 -p。
遍歷每一處修改進行精細處理。
後面可以跟文件名作爲參數;也可以不加參數,表示對所有修改進行處理。
三個帶 -p 的命令組合使用,我給它起了個俗名,叫“3P 大法”。
是目前處理 git 暫存區的最理想方案。
執行命令將進入一個交互界面。大致如下:
diff --git a/index.md b/index.md
index 921ba51..1c0d98b 100644
--- a/index.md
+++ b/index.md
@@ -3,5 +3,5 @@
layout: home
-list_title: foo
+list_title: bar
---
Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,s,e,?]? 最後一行的內容三個命令個有不同,分別爲:
add
Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,s,e,?]?
reset
Unstage this hunk [y,n,q,a,d,k,K,j,J,g,/,s,e,?]?
checkout
Discard this hunk from worktree [y,n,q,a,d,k,K,j,J,g,/,s,e,?]? 說明了各自的用途。
這裏可見,git 展示了我們對 index.md 文件作出的一處修改。
並提供了 y,n,q,a,d,k,K,j,J,g,/,s,e,? 十一個可用的操作。分別表示什麼呢?
別的不清楚,但 ? 想必是用來提供幫助。我們輸入 ? Enter。
得到提示信息:
add
y - stage this hunk
n - do not stage this hunk
q - quit; do not stage this hunk or any of the remaining ones
a - stage this hunk and all later hunks in the file
d - do not stage this hunk or any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print helpreset
y - unstage this hunk
n - do not unstage this hunk
q - quit; do not unstage this hunk or any of the remaining ones
a - unstage this hunk and all later hunks in the file
d - do not unstage this hunk or any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print helpcheckout
y - discard this hunk from worktree
n - do not discard this hunk from worktree
q - quit; do not discard this hunk or any of the remaining ones
a - discard this hunk and all later hunks in the file
d - do not discard this hunk or any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help初次看簡直眼花繚亂,讓我們整理一下:
處理命令:
`y` yes 要(加入/移出/丟棄);
`n` no 不要動;
`a` all 此文件的修改,全都要(加入/移出/丟棄);
`d` don't 此文件的修改,全都不要;
`e` edit 編輯,後面詳細說明;
`s` split 分割,有時候 git 會把幾個修改併在一起,若想分開處理,用此操作;
導航命令(只能在本文件中導航):
`g` goto 跳轉到第 n 個修改,如 `g 1`,`g 2`,需要注意序號從 1 開始;
`/` search 搜索,和 vim、less 等軟件的搜索差不多,在大文件中有大量修改的時候很方便;
`j` next 下一處沒處理的修改;
`J` Next 下一處修改;
`k` previous 上一處沒處理的修改;
`K` Previous 上一處修改;
其它命令:
`q` quit 退出;
`?` help 幫助;
基本使用只需要掌握 y、n、s、q,高級使用就再加上個 e。
程序員一般有 y、n、s、e 就夠用了。
導航命令則對於字典維護人員,webpack 配置師這樣的工種比較有用。
下面我們詳細講一下這個 e。既然是 edit,自然需要 editor。
和不帶 -m 選項的 commit 一樣,它會調起編輯器,vim、nano 或是其它,依據你的配置。
內容如下:
# Manual hunk edit mode -- see bottom for a quick guide
@@ -20,4 +20,5 @@
><ul
><li>foo</li
- ><li>bar</li
+ ><li>baz</li
+ ><li>qux</li
></ul
# ---
# 這裏會有一些操作說明,可以作爲參考,我就不粘貼了。
以上展示了一處修改,我們刪除了 bar 行,並增加了 baz、qux 兩行。
@@ ... @@ 中的內容展示了修改所在的行號和修改前後的行數。
緊接着是內容,在每行的行首第一個字符,表示行的類型,可以是 空格、-、+。
空格表示普通行,此行沒有修改;- 表示刪除的行,而 + 則表示新加的行。
注意,此空格不是縮進。若你使用縮進符,則非常清晰;若使用空格符代替縮進符,則要仔細了,要多一個空格。
此時,若保存退出編輯器,則表示應用(加入/移出/丟棄)此修改,相當於直接敲 y。
若我們將修改還原,即刪除所有新加的行,即把刪除的行取消刪除,即將- 改爲空格,
則等於不應用此修改,相當於直接敲 n。
若我們修改內容,則可以得到更多的效果。要注意不是所有修改都是允許的。
在 add 時,情況比較容易理解。例如我們修改爲如下的樣子:
><ul
- ><li>foo</li
+ ><li>qux</li
><li>bar</li
+ ><li>Baz</li
></ul表示我不刪除 bar 行,而刪除 foo 行。
baz 行的內容進行修改。而 qux 行則換個位置插入。
理解起來並不困難。
我們可以總結出規律:在 add 時,空格行和 - 行可以相互轉換,但的內容不能修改;
而 + 行則可隨意修改,刪除,還可以在任意地方插入。
而 reset 時,情況就和 add 相反了。空格行和 + 行可以相互轉換,但的內容不能修改;
而 - 行則可隨意修改,刪除,還可以在任意地方插入。
這讓人十分費解,還是用例子說明吧。還是上面的內容,我們作如下編輯:
><ul
+ ><li>foo</li
- ><li>Bar</li
+ ><li>baz</li
><li>qux</li
- ><li>ET</li
></ul我們得以“放棄”,“取消”這樣的字眼來分析。
我們將空格行 foo 改爲 + 行,表示取消對 foo 行的添加。
可 foo 行並非添加的,而是原來就在,所以就是刪除 foo 行囉。
刪除的 bar 行改爲 Bar 行,表示不放棄對 bar 行的刪除,而取消對 Bar 行的刪除。
那麼 bar 行還是要刪,然後添加 Bar 行。取消不存在的刪除,等於添加。即 bar 行改爲 Bar 行。
最後,放棄對 ET 行的刪除,也等於新插入一個 ET 行。
有點繞腦。有一個囫圇吞棗的辦法:reset 時,把 + 當 -,把 - 當 + 就行了。
checkout 時,由於方向和 reset 一致,因此允許的修改和效果,都和 reset 一致,是反的。
區別只是 reset 改的是暫存區,而 checkout 改的是文件內容。
因此,可以 checkout 來做做實驗。躬行所悟,可比紙上所得要深刻百倍。
需要注意,對於新文件,add -p 是無效的,它會跳過這些文件,而 reset -p 則沒有這個問題。
對於新文件的 add,必須整個文件進行操作。
整個文件操作和全局操作
通常,用 -p 瀏覽一遍到底提交了些什麼總是好的。
但也會有需要整文件操作,甚至全局操作的時候。
只是勿圖方便而濫用。
-
git add file將文件中的全部修改加入暫存區 -
git reset file將文件中的全部修改從暫存區移出 -
git checkout file放棄文件中的全部未加入暫存區的修改 -
git add .將文件中的全部修改加入暫存區 -
git reset將暫存區中的全部修改移出(清空購物車) -
git checkout全部未加入暫存區的修改
未完善的終極武器 add --interactive
git add 中,還有一個更加新潮的選項:--interactive,簡寫爲 -i。
顧名思義,這也是一個交互界面。樣子如下:
staged unstaged path
1: unchanged +0/-1 TODO
2: unchanged +1/-1 index.html
*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now> 上方顯示 status 內容,與 git status 得到得主體內容差不多,列表得方式看起來更加清晰。
下方顯示可用的操作,一共有 8 個,此外還有個隱藏的 ?,分別爲:
1 status 顯示主界面,通常用於刷新
2 update 加入暫存區,相當於 add file
3 revert 移出暫存區,相當於 reset file
4 add untracked 新文件加入暫存區
5 patch 相當於 add -p file
6 diff 查看暫存區中的內容
7 quit 退出
8 help 幫助,關於以上操作
? 幫助,關於交互界面實際使用時,敲數字或首字母即可,無需鍵入完整命令。
其中 u r a p d 爲核心操作,執行後,會進入到二級界面,我們看到命令提示符由 > 變爲 >>。
這時候,我們可以選取要操作的文件。可用序號選擇,也可輸入文件名(前面一段,沒有重複即可)。
目前,文件名還不支持分段匹配、模糊匹配、* ?,也不支持 tab 鍵輸入提示,和上下鍵輸入歷史,希望將來能有。
但序號也夠用了。在所選的序號或文件名前加 - 則表示取消選擇。第一次回車,執行選擇,然後可以繼續選擇。
第二次回車,執行操作。
add --interactive 給了我們一個很棒的操作方式,我們可流暢地操作,不必一而再再而三地執行 git status。
就操作方式而言,可以說是終極武器。然而這個未來的終極武器還有需多不夠完善之處。
它有 add file、reset file 以及 add -p file 卻缺少了 reset -p file、checkout file、checkout -p file。
可以說只整合了一半的功能。最關鍵的 add -p file 和 reset -p file,就少了一個。
我們只能先 reset file 再 add -p file。若文件中有很多修改,這會相當麻煩。
因此,這個命令還需要觀望。目前最佳方案,還是三個 patch,俗稱 3P 大法。