1. 程式人生 > >Git 工作區、版本庫中的暫存區和版本庫之間的關系

Git 工作區、版本庫中的暫存區和版本庫之間的關系

再次 出現 它的 git rm div 理解 方式 ast 版本

下圖展示了 工作區、版本庫中的暫存區和版本庫之間的關系:

 技術分享圖片

1. 理解 Git 暫存區 (stage)

  在版本庫 .git 目錄下有一個 index 文件,下面針對這個文件做一個有趣的試驗。要說明的是:這個試驗用 1.7.3 版本的 Git 進行的,低版本的 Git 因為沒有針對 git status 命令進行優化設計,需要運行 git diff 命令才能看到 index 文件的日期戳變化,具體操作步驟如下。

  (1) 首先執行 git checkout 命令(後面會介紹此命令),撤銷工作區中 welcome.txt 文件尚未提交的修改。

git checkout -- welcome.txt
git status -s  #執行 git diff , 如果 git 版本號小於 1.7.3

  (2) 通過狀態輸出可以看到工作區已經沒有改動了。查看一下 .git/index 文件,註意該文件的時間戳為:19:37:44

ls --full-time .git/index
-rw-rw-r-- 1 git git 104 2018-05-03 19:37:44.492022847 +0800 .git/index

  (3) 再次執行 git status 命令,然後顯示 .git/index 文件的時間戳為19:37:44,與上面的一樣。

git status -s
ls --full-time .git/index
-rw-rw-r-- 1 git git 104 2018-05-03 19:37:44.492022847 +0800 .git/index

 (4) 現在更改一下welcome.txt 的時間戳,但是不改變它的內容。然後再執行 git status 命令,查看 .git/index 文件的時間戳為:19:42:06.

touch welcome.txt
git status -s
-rw-rw-r-- 1 git git 104 2018-05-03 19:42:06.830558718 +0800 .git/index  

 看到了嗎,時間戳改變了!

  這個試驗說明當執行 git status 命令 (或者 git diff 命令)掃描工作區改動的時候,先依據 .git/index 文件中記錄的(用於跟蹤工作與文件的)時間戳、長度等信息判斷工作區文件是否改變,如果工作區文件的時間戳改變了,說明文件的內容可能被改變了,需要打開文件,讀取文件內容,與更改前的原始文件相比較,判斷文件內容是否被更改。如果文件內容沒有改變,則將該文件新的時間戳記錄到 .git/index 文件中。因為如果要判斷文件是否更改,使用時間戳、文件長度等信息進行比較要比通過文件內容比較要快的多,所以 Git 這樣的實現方式可以讓工作區狀態掃描更快速地執行,這也是Git高效的原因之一。

  文件.git/index 實際上就是一個包含文件索引的目錄樹,像是一個虛擬的工作區。在這個虛擬工作區的目錄樹中,記錄了文件名和文件的狀態信息(時間戳和文件長度等)。文件的內容並沒有存儲在其中,而是保存在 Git 對象庫 .git/objects 目錄中,文件索引建立了文件和對象實體之間的對應。

技術分享圖片

從圖 5-1 中可以看到部分 Git 命令是如何影響工作區和暫存區的。這些命令的面紗將在接下來的幾個章節中徹底揭開,下面就對這些命令進行簡要說明:

  • 圖中左側為工作區,右側為版本庫。在版本庫中標記為 index 的區域就是暫存區,標記為 master 的是 master 分支所代表的目錄樹。
  • 圖中可以看出,此時HEAD 實際是指向 master 分支的一個“遊標”,所以圖示的命令中出現 HEAD 的地方可以用 master 來替換。 
  • 圖中的 objects 標識的區域為 Git 的對象庫,實際位於 .git/objects 目錄下,這一點會在後面的章節中重點介紹。
  • 當對工作區修改(或新增)的文件執行 git add 命令時,暫存區的目錄樹將被更新,同時工作區修改(或新增)的文件內容會被寫入到對象庫中的一個新的對象中,而該對象的ID 被記錄在暫存區的文件索引中。
  • 當執行提交操作(git commit)時,暫存區的目錄樹會寫到版本庫(對象庫)中,master 分支會做相應的更新,即master 最新指向的目錄樹就是提交時原暫存區的目錄樹。(即:master=暫存區)
  • 當執行 git reset HEAD 命令時,暫存區的目錄樹會被重寫,會被master 分支指向的目錄樹所替換,但是工作區不受影響。(即:暫存區=master)
  • 當執行 git rm --cached <file> 命令時,會直接從暫存區刪除文件,工作區則不做出改變。
  • 當執行 git checkout . 或 git checkout -- <file> 命令時,會用暫存區全部的文件或指定的文件替換工作區的文件。這個操作很危險,會清除工作區中未添加到暫存區的改動。
  • 當執行 git checkout HEAD .或 git checkout HEAD <file> 命令時,會用HEAD 指向的 master 分支中的全部或部分文件替換暫存區和工作區的文件。這個命令也是極具危險性的,因為不但會清除工作區中為提交的改動,也會清除暫存區中未提交的改動。

Git 工作區、版本庫中的暫存區和版本庫之間的關系