Git 分支(一)
理解Git暫存區
檔案.git/index是一個包含檔案索引的目錄樹,像是一個虛擬的工作區。在這個虛擬工作區的目錄樹中,記錄了檔名和檔案的狀態資訊。以便快速檢測檔案的變化。 索引中還包含所有Blob型別的SHA-1識別符號。檔案的內容沒有儲存在其中, 而是儲存在Git物件庫.git/objects目錄中,檔案索引建立了檔案和物件庫中物件實體之間的對應, 如圖,展示了工作區、版本庫中的暫存區和版本庫之間的關係。
- 圖中左側為工作區,右側為版本庫。在版本庫中標記為index的區域是暫存區,標記為master的是master分支代表的目錄樹。
- HEAD實際是指向master分支的一個“遊標”,所以圖示的命令中出現的HEAD的地方可以用master來替代。
- objects標誌的區域為Git的物件庫,實際位於.git/objects目錄下。
- 工作區修改(或新增)檔案執行git add命令時,暫存區的目錄樹將被更新,同時工作區修改(或新增)的檔案內容會被寫入到物件庫中的一個新的物件中,而該物件的ID被記錄在暫存區的索引檔案中。
- 執行提交操作(git commit)時,暫存區的目錄樹會寫到版本庫(物件庫)中,master分支會做相應的更新,即master最新指向的目錄樹就是提交時原暫存區的目錄樹。
- 執行git reset HEAD命令時,暫存區的目錄樹會被重寫,會被master分支指向的目錄樹替換,但是工作區不受影響。
- 執行 git rm --cached <file>命令時,會直接從暫存區刪除檔案,工作區不做改變。
- 執行 git checkout. 或 git checkout -- <file>命令時,會用暫存區全部的檔案或指定的檔案替換工作區的檔案。這個操作很危險,會清除工作區中未新增到暫存區的改動。
- 執行 git checkout HEAD ,或 git checkout HEAD <file> 命令時,會用HEAD指向的master分支中的全部或部分檔案替換暫存區和工作區中的檔案,這個命令也是極具危險性的,因為不但會清除工作 區中未提交的改動,也會清楚暫存區中為提交的改動。
分支
Git 在進行提交操作時,會建立一個提交物件(commit object)。該提交物件會包含一個指向提交內容快照的指標、作者的姓名和郵箱、提交時輸入的資訊以及
指向它的父物件的指標。首次提交產生的提交物件沒有父物件,普通提交操作產生的提交物件有一個父物件,而由多個分支合併產生的提交物件有多個父物件。
假設一個工作目錄,包含了三個將要被暫存和提交的檔案。 暫存操作會為每一個檔案計算校驗和(使用我們在 起步 中提到的 SHA-1 雜湊演算法),
然後把當前版本的檔案快照儲存到Git 倉庫中(objects,Git 使用 blob 物件來儲存它們),將校驗和加入到暫存區域等待提交:
當使用 git commit 進行提交操作時,Git 會先計算每一個子目錄(本例中只有專案根目錄)的校驗和,然後在Git 倉庫中這些校驗和儲存為樹物件。
Git 會建立一個提交物件,它除了包含上面提到的那些資訊外,還包含指向這個樹物件(專案根目錄)的指標。如此一來,Git 就可以在需要的時候重現此次儲存的快照。
現在,Git 倉庫中有五個物件:三個 blob 物件(儲存著檔案快照)、一個樹物件(記錄著目錄結構和 blob 物件索引)以及一個提交物件(包含著指向前述樹物件的指標 和所有提交資訊)。
Git 的分支,其實本質上僅僅是指向提交物件的可變指標。 Git 的預設分支名字是 master。 在多次提交操作之後,你其實已經有一個指向最後
那個提交物件的 master 分支。 它會在每次的提交操作中自動向前移動。
分支建立
$ git branch testing #建立一個 testing 分支
建立Testing分支會在當前所在的提交物件上建立一個指標。
可以簡單地使用 git log 命令檢視各個分支當前所指的物件。 提供這一功能的引數是 --decorate。
正如你所見,當前 “master” 和 “testing” 分支均指向校驗和以 f30ab 開頭的提交物件。
分支切換
要切換到一個已存在的分支:
這樣 HEAD 就指向 testing 分支了。
現在不妨再提交一次:
如圖所示, testing 分支向前移動了,但是 master 分支卻沒有,它仍然指向執行 git checkout 時所指的物件。 這就有意思了,
現在我們切換回 master 分支看看:
這條命令做了兩件事。 一是使 HEAD 指回 master 分支,二是將工作目錄恢復成 master 分支所指向的快照內容。
我們不妨再稍微做些修改並提交:
你可以簡單地使用 git log 命令檢視分支歷史。 執行 git log --oneline --decorate --graph--all ,它會輸出你的提交歷史、各個分支的指向以及專案的分支分支情況。
由於 Git 的分支實質上僅是包含所指物件校驗和(長度為 40 的 SHA-1 值字串)的檔案,所以它的建立和銷燬都異常高效。建立一個新分支就相當於往一個檔案
中寫入 41 個位元組(40 個字元和 1 個換行符),如此的簡單能不快嗎?
這與過去大多數版本控制系統形成了鮮明的對比,它們在建立分支時,將所有的專案檔案都複製一遍,並儲存到一個特定的目錄。 完成這樣繁瑣的過程通常需要
好幾秒鐘,有時甚至需要好幾分鐘。所需時間的長短,完全取決於專案的規模。而在 Git 中,任何規模的專案都能在瞬間建立新分支。 同時,由於每次提交都會記錄父物件,
所以尋找恰當的合併基礎(譯註:即共同祖先)也是同樣的簡單和高效。 這些高效的特性使得 Git 鼓勵開發人員頻繁地建立和使用分支。