Git 工作區 暫存區和版本庫

2021-07-07 10:53:52 字數 3566 閱讀 9383

暫存區(stage, index)是 git 最重要的概念之一,理解了這個概念很多 git 命令就不再那麼神秘了。 今天在寫這部分的內容,畫了乙個圖,看看有沒有什麼問題。 

理解 git 暫存區(stage) 把上面的「實踐二」從頭至尾走一遍,不知道您的感想如何?

在「實踐二」的過程中,我有意無意的透漏了「暫存區」的概念,為了避免使用者被新概念嚇壞,在暫存區出現的地方用同時使用了「提交任務」這一更易理解 的概念,但是暫存區(stage, 或稱為 index)才是其真正的名稱。我認為 git 暫存區(stage, 或稱為 index)的設計是 git 最成功的設計之一,也是最難理解的乙個設計。 在版本庫(.git)目錄下,有乙個 index 檔案,我們針對這個檔案做乙個有趣的試驗。 首先我們執行 "git checkout" 命令撤銷工作區中 

welcome.txt 檔案尚未提交的修改。

$ git checkout -- welcome.txt

$ git status -s

我們通過狀態輸出,看以看到工作區已經沒有改動了。我們檢視一下 

.git/index 檔案,注意該檔案的時間戳(19:37:44):

$ ls --full-time .git/index

-rw-r--r-- 1 jiangxin jiangxin 112 2010-11-29 19:37:44.625246224 +0800 .git/index

我們再次執行 "git status" 命令,然後顯示 

.git/index 檔案的時間戳(19:37:44),和上面的一樣。

$ git status -s

$ ls --full-time .git/index

-rw-r--r-- 1 jiangxin jiangxin 112 2010-11-29 19:37:44.625246224 +0800 .git/index

現在我們更改一下 welcome.txt 的時間戳,但是不改變它的內容。然後再執行 "git status" 命令,然後檢視 

.git/index 檔案時間戳(19:42:06)。

$ touch welcome.txt

$ git status -s

$ ls --full-time .git/index

-rw-r--r-- 1 jiangxin jiangxin 112 2010-11-29 19:42:06.980243216 +0800 .git/index

看到了麼,時間戳改變了! 這個試驗說明當執行 "git status" 命令掃瞄工作區改動的時候,先依據 

.git/index 檔案中記錄的(工作區跟蹤檔案的)時間戳、長度等資訊判斷工作區檔案是否改變。如果工作區的檔案時間戳改變,說明檔案的內容可能被改變了,需要要開啟檔案,讀取檔案內容,和更改前的原始檔案相比較,判斷檔案內容是否被更改。如果檔案內容沒有改變,則將該檔案新的時間戳記錄到 

.git/index 檔案中。因為判斷檔案是否更改,使用時間戳、檔案長度等資訊進行比較要比通過檔案內容比較要快的多,所以 git 這樣的實現方式可以讓工作區狀態掃瞄更快速的執行,這也是 git 高效的因素之一。

檔案 .git/index 實際上就是乙個包含檔案索引的目錄樹,像是乙個虛擬的工作區。在這個虛擬工作區的目錄樹中,記錄了檔名、檔案的狀態資訊(時間戳、檔案長度等),檔案的 內容並不儲存其中,而是儲存在 git 物件庫(.git/objects)中,檔案索引建立了檔案和物件庫中物件實體之間的對應。下面這個圖展示了工作區、版本庫中的暫存區和版本庫之間的關 系。

工作區、版本庫、暫存區原理圖

在這個圖中,我們可以看到部分 git 命令是如何影響工作區和暫存區(stage, index)的。

本文聊聊在使用git的過程中,如何撤銷乙個操作,這個操作可能是git add,git commit,或是git merge,或是其他的。接下來分情況討論:

情景一 檔案被修改,還沒有git add。

這時候檔案還沒有被新增到object database中,可以使用index中的版本來覆蓋工作區。? 1

git checkout -- [filename]

情景二 檔案被修改,且已經git add。

此時修改過的檔案已經作為乙個新的object新增到object databse中,可以使用當前head中的版本來覆蓋index。

這時候檔案還沒有被新增到object database中,可以使用index中的版本來覆蓋工作區。? 1

git reset head -- [filename]

如果還想重置工作區,可以繼續:

這時候檔案還沒有被新增到object database中,可以使用index中的版本來覆蓋工作區。? 1

git checkout -- [filename]

情景三 已經提交commit,需要撤銷commit。

這種情況又分為多種子情景,詳細討論,首先給出乙個模擬的提交歷史,從左到右一次提交,master執行提交h。

a---b---c---d---e---f---g---h

|master

子情形1. 要撤銷最近1個commit,要撤銷h。

可以使用2個命令:? 1

git reset --hard g

或 ?

1git revert h

二者區別在於git reset不會產生新的提交,而是將分支指向指定的commit,生成的提交歷史為:

a---b---c---d---e---f---g---h

| master

而git revert會產生乙個新的提交,生成的提交歷史為:

a---b---c---d---e---f---g---h---i

| master

子情形2. 要撤銷的提交不是最新的提交,即在錯誤提交之後又有若干個提交。

比如我們想撤銷e,f兩個提交,此時git reset已經無法勝任,因為我們在e,f後又有多個正常提交。這時可以使用git revert,但是要操作兩次。這種情況下,推薦使用git rebase:? 1

git rebase --onto d g^ h

情形四 執行merge操作,但是遇到conflict,想要撤銷merge操作。

此時並為生成新的提交,當前分支頭並未移動,因此可以簡單的:? 1

git reset --hard head

情形五 執行merge操作,且已生成新的提交,要撤銷merge操作。

這是簡單的git revert 已經不能勝任。因為merge生成的提交有2個父提交,git不知道該使用哪個作為主線。這時需要新增乙個額外的引數:? 1

git revert -m [parent-number] [refs]

-m指定要採用哪條主線(編號從1開始)。

Git 工作區 暫存區和版本庫

暫存區 stage,index 是 git 最重要的概念之一,理解了這個概念很多 git 命令就不再那麼神秘了。今天在寫這部分的內容,畫了乙個圖,看看有沒有什麼問題。理解 git 暫存區 stage 把上面的 實踐二 從頭至尾走一遍,不知道您的感想如何?在 實踐二 的過程中,我有意無意的透漏了 暫存...

Git 工作區 暫存區和版本庫

暫存區 stage,index 是 git 最重要的概念之一,理解了這個概念很多 git 命令就不再那麼神秘了。我認為 git 暫存區 stage,或稱為 index 的設計是 git 最成功的設計之一,也是最難理解的乙個設計。當執行 git status 命令掃瞄工作區改動的時候,先依據 git ...

Git 工作區 暫存區和版本庫

暫存區和版本區的目錄結構如下圖 下面這個圖展示了工作區 版本庫中的暫存區和版本庫之間的關係 圖中左側為工作區,右側為版本庫。在版本庫中標記為 index 的區域是暫存區 stage,index 標記為 master 的是 master 分支所代表的目錄樹。圖中我們可以看出此時 head 實際是指向 ...