3 1 Git 分支 分支簡介

2021-07-25 14:07:23 字數 4241 閱讀 6105

幾乎所有的版本控制系統都以某種形式支援分支。 使用分支意味著你可以把你的工作從開發主線上分離開來,以免影響開發主線。 在很多版本控制系統中,這是乙個略微低效的過程——常常需要完全建立乙個源**目錄的副本。對於大專案來說,這樣的過程會耗費很多時間。

有人把 git 的分支模型稱為它的「必殺技特性」,也正因為這一特性,使得 git 從眾多版本控制系統中脫穎而出。 為何 git 的分支模型如此出眾呢? git 處理分支的方式可謂是難以置信的輕量,建立新分支這一操作幾乎能在瞬間完成,並且在不同分支之間的切換操作也是一樣便捷。 與許多其它版本控制系統不同,git 鼓勵在工作流程中頻繁地使用分支與合併,哪怕一天之內進行許多次。 理解和精通這一特性,你便會意識到 git 是如此的強大而又獨特,並且從此真正改變你的開發方式。

為了真正理解 git 處理分支的方式,我們需要回顧一下 git 是如何儲存資料的。

或許你還記得 起步 的內容,git 儲存的不是檔案的變化或者差異,而是一系列不同時刻的檔案快照。

在進行提交操作時,git 會儲存乙個提交物件(commit object)。知道了 git 儲存資料的方式,我們可以很自然的想到——該提交物件會包含乙個指向暫存內容快照的指標。 但不僅僅是這樣,該提交物件還包含了作者的姓名和郵箱、提交時輸入的資訊以及指向它的父物件的指標。首次提交產生的提交物件沒有父物件,普通提交操作產生的提交物件有乙個父物件,而由多個分支合併產生的提交物件有多個父物件,

為了說得更加形象,我們假設現在有乙個工作目錄,裡面包含了三個將要被暫存和提交的檔案。 暫存操作會為每乙個檔案計算校驗和(使用我們在 起步 中提到的 sha-1 雜湊演算法),然後會把當前版本的檔案快照儲存到 git 倉庫中(git 使用 blob 物件來儲存它們),最終將校驗和加入到暫存區域等待提交:

$ git add readme test.rb license

$ git commit -m 'the initial commit of my project'

當使用 git commit 進行提交操作時,git 會先計算每乙個子目錄(本例中只有專案根目錄)的校驗和,然後在 git 倉庫中這些校驗和儲存為樹物件。 隨後,git 便會建立乙個提交物件,它除了包含上面提到的那些資訊外,還包含指向這個樹物件(專案根目錄)的指標。如此一來,git 就可以在需要的時候重現此次儲存的快照。

現在,git 倉庫中有五個物件:三個 blob 物件(儲存著檔案快照)、乙個樹物件(記錄著目錄結構和 blob 物件索引)以及乙個提交物件(包含著指向前述樹物件的指標和所有提交資訊)。

做些修改後再次提交,那麼這次產生的提交物件會包含乙個指向上次提交物件(父物件)的指標。

git 的分支,其實本質上僅僅是指向提交物件的可變指標。 git 的預設分支名字是 master。 在多次提交操作之後,你其實已經有乙個指向最後那個提交物件的 master 分支。 它會在每次的提交操作中自動向前移動。

note

git 的 「master」 分支並不是乙個特殊分支。 它就跟其它分支完全沒有區別。 之所以幾乎每乙個倉庫都有 master 分支,是因為 git init 命令預設建立它,並且大多數人都懶得去改動它。

git 是怎麼建立新分支的呢? 很簡單,它只是為你建立了乙個可以移動的新的指標。 比如,建立乙個 testing 分支, 你需要使用 git branch 命令:

$ git branch testing
這會在當前所在的提交物件上建立乙個指標。

那麼,git 又是怎麼知道當前在哪乙個分支上呢? 也很簡單,它有乙個名為 head 的特殊指標。 請注意它和許多其它版本控制系統(如 subversion 或 cvs)裡的 head 概念完全不同。 在 git 中,它是乙個指標,指向當前所在的本地分支(譯註:將 head 想象為當前分支的別名)。 在本例中,你仍然在 master 分支上。 因為 git branch 命令僅僅 建立 乙個新分支,並不會自動切換到新分支中去。

你可以簡單地使用 git log 命令檢視各個分支當前所指的物件。 提供這一功能的引數是 –decorate。

$ git log --oneline --decorate

f30ab (head, master, testing) add feature #32 - ability to

addnew

34ac2 fixed bug #1328 - stack overflow under certain conditions

98ca9 initial commit of my project

正如你所見,當前 「master」 和 「testing」 分支均指向校驗和以 f30ab 開頭的提交物件。

要切換到乙個已存在的分支,你需要使用 git checkout 命令。 我們現在切換到新建立的 testing 分支去:

$ git checkout testing
這樣 head 就指向 testing 分支了。

那麼,這樣的實現方式會給我們帶來什麼好處呢? 現在不妨再提交一次:

$ vim test.rb

$ git commit -a -m 'made a change'

如圖所示,你的 testing 分支向前移動了,但是 master 分支卻沒有,它仍然指向執行 git checkout 時所指的物件。 這就有意思了,現在我們切換回 master 分支看看:

$ git checkout master
這條命令做了兩件事。 一是使 head 指回 master 分支,二是將工作目錄恢復成 master 分支所指向的快照內容。 也就是說,你現在做修改的話,專案將始於乙個較舊的版本。 本質上來講,這就是忽略 testing 分支所做的修改,以便於向另乙個方向進行開發。

在切換分支時,一定要注意你工作目錄裡的檔案會被改變。 如果是切換到乙個較舊的分支,你的工作目錄會恢復到該分支最後一次提交時的樣子。 如果 git 不能乾淨利落地完成這個任務,它將禁止切換分支。

我們不妨再稍微做些修改並提交:

$ vim test.rb

$ git commit -a -m 'made other changes'

現在,這個專案的提交歷史已經產生了分叉(參見 figure 3-9)。 因為剛才你建立了乙個新分支,並切換過去進行了一些工作,隨後又切換回 master 分支進行了另外一些工作。 上述兩次改動針對的是不同分支:你可以在不同分支間不斷地來回切換和工作,並在時機成熟時將它們合併起來。 而所有這些工作,你需要的命令只有 branch、checkout 和 commit。

你可以簡單地使用 git log 命令檢視分叉歷史。 執行 git log –oneline –decorate –graph –all ,它會輸出你的提交歷史、各個分支的指向以及專案的分支分叉情況。

$ git log --oneline --decorate --graph --all

* c2b9e (head, master) made other changes

| * 87ab2 (testing) made a change

|/* f30ab add feature #32 - ability to

addnew formats to the

* 34ac2 fixed bug #1328 - stack overflow under certain conditions

* 98ca9 initial commit of my project

由於 git 的分支實質上僅是包含所指物件校驗和(長度為 40 的 sha-1 值字串)的檔案,所以它的建立和銷毀都異常高效。 建立乙個新分支就像是往乙個檔案中寫入 41 個位元組(40 個字元和 1 個換行符),如此的簡單能不快嗎?

這與過去大多數版本控制系統形成了鮮明的對比,它們在建立分支時,將所有的專案檔案都複製一遍,並儲存到乙個特定的目錄。 完成這樣繁瑣的過程通常需要好幾秒鐘,有時甚至需要好幾分鐘。所需時間的長短,完全取決於專案的規模。而在 git 中,任何規模的專案都能在瞬間建立新分支。 同時,由於每次提交都會記錄父物件,所以尋找恰當的合併基礎(譯註:即共同祖先)也是同樣的簡單和高效。 這些高效的特性使得 git 鼓勵開發人員頻繁地建立和使用分支。

接下來,讓我們看看為什麼你應該這麼做?

git 分支簡介

假設現在有乙個工作目錄,裡面包含了三個將要被暫存和提交的檔案。git add readme test.rb license git commit m the initial commit of my project 現在,git 倉庫中有五個物件 三個 blob 物件 儲存著檔案快照 乙個樹物件 記...

Git分支機制簡介

git分支只不過是乙個指向某次提交的輕量級的可移動指標,當你發起提交時,就有了乙個指向最後一次提交的名為master的分支。每次提交時,它都會自動向前移動。git預設的分支名稱為master,master分支其實並 乙個特殊的分支,它與其他分支沒有什麼區別。幾乎每個git倉庫都擁有該分支,這是因為g...

Git 合併分支 推送分支

1 本地倉庫與遠端倉庫同步,提取所有它獨有的資料到本地分支,供後續操作。git fetch origin2 把遠端倉庫中分支名為aimbranch中的 合併到本地倉庫中你當前所在的分支上。git merge no ff origin aimbranch3 把本地倉庫中你所在的分支中的 推送到遠端倉庫...