分支簡介
在進(jìn)行提交操作時(shí),Git 會(huì)保存一個(gè)提交對(duì)象(commit object)荡灾。知道了 Git 保存數(shù)據(jù)的方式,我們可以很自然的想到——該提交對(duì)象會(huì)包含一個(gè)指向暫存內(nèi)容快照的指針哮内。 但不僅僅是這樣黍少,該提交對(duì)象還包含了作者的姓名和郵箱、提交時(shí)輸入的信息以及指向它的父對(duì)象的指針航徙。首次提交產(chǎn)生的提交對(duì)象沒有父對(duì)象如贷,普通提交操作產(chǎn)生的提交對(duì)象有一個(gè)父對(duì)象,而由多個(gè)分支合并產(chǎn)生的提交對(duì)象有多個(gè)父對(duì)象到踏,
Git 倉庫中有五個(gè)對(duì)象:三個(gè) blob 對(duì)象(保存著文件快照)杠袱、一個(gè)樹對(duì)象(記錄著目錄結(jié)構(gòu)和 blob 對(duì)象索引)以及一個(gè)提交對(duì)象(包含著指向前述樹對(duì)象的指針和所有提交信息)
首次提交對(duì)象及其樹結(jié)構(gòu)
做些修改后再次提交,那么這次產(chǎn)生的提交對(duì)象會(huì)包含一個(gè)指向上次提交對(duì)象(父對(duì)象)的指針窝稿。
提交對(duì)象及其父對(duì)象
Git 的分支,其實(shí)本質(zhì)上僅僅是指向提交對(duì)象的可變指針伴榔。 Git 的默認(rèn)分支名字是 master
纹蝴。 在多次提交操作之后,你其實(shí)已經(jīng)有一個(gè)指向最后那個(gè)提交對(duì)象的 master
分支踪少。 它會(huì)在每次的提交操作中自動(dòng)向前移動(dòng)塘安。
Git 的 “master” 分支并不是一個(gè)特殊分支。 它就跟其它分支完全沒有區(qū)別援奢。 之所以幾乎每一個(gè)倉庫都有 master 分支兼犯,是因?yàn)?git init
命令默認(rèn)創(chuàng)建它,并且大多數(shù)人都懶得去改動(dòng)它。
分支創(chuàng)建
分支及其提交歷史
Git 是怎么創(chuàng)建新分支的呢砸脊? 很簡單,它只是為你創(chuàng)建了一個(gè)可以移動(dòng)的新的指針纬霞。 比如凌埂,創(chuàng)建一個(gè) testing 分支, 你需要使用 git branch
命令:
$ git branch testing
這會(huì)在當(dāng)前所在的提交對(duì)象上創(chuàng)建一個(gè)指針诗芜。
兩個(gè)指向相同提交歷史的分支
那么,Git 又是怎么知道當(dāng)前在哪一個(gè)分支上呢绢陌? 也很簡單挨下,它有一個(gè)名為 HEAD
的特殊指針。 請(qǐng)注意它和許多其它版本控制系統(tǒng)(如 Subversion 或 CVS)里的 HEAD
概念完全不同脐湾。 在 Git 中臭笆,它是一個(gè)指針,指向當(dāng)前所在的本地分支(譯注:將 HEAD
想象為當(dāng)前分支的別名)秤掌。 在本例中愁铺,你仍然在 master
分支上。 因?yàn)?git branch
命令僅僅 創(chuàng)建 一個(gè)新分支闻鉴,并不會(huì)自動(dòng)切換到新分支中去茵乱。
HEAD 指向當(dāng)前所在的分支
你可以簡單地使用 git log
命令查看各個(gè)分支當(dāng)前所指的對(duì)象孟岛。 提供這一功能的參數(shù)是 --decorate
瓶竭。
$ git log --oneline --decorate
f30ab (HEAD, master, testing) add feature #32 - ability to add new
34ac2 fixed bug #1328 - stack overflow under certain conditions
98ca9 initial commit of my project
正如你所見,當(dāng)前 “master” 和 “testing” 分支均指向校驗(yàn)和以 f30ab
開頭的提交對(duì)象渠羞。
分支切換
要切換到一個(gè)已存在的分支斤贰,你需要使用 git checkout
命令。 我們現(xiàn)在切換到新創(chuàng)建的 testing
分支去:
$ git checkout testing
這樣 HEAD
就指向 testing
分支了次询。
HEAD 指向當(dāng)前所在的分支
那么,這樣的實(shí)現(xiàn)方式會(huì)給我們帶來什么好處呢屯吊? 現(xiàn)在不妨再提交一次:
$ vim test.rb
$ git commit -a -m 'made a change'
Figure 15. HEAD 分支隨著提交操作自動(dòng)向前移動(dòng)
如圖所示,你的 testing
分支向前移動(dòng)了盒卸,但是 master
分支卻沒有骗爆,它仍然指向運(yùn)行 git checkout
時(shí)所指的對(duì)象。 這就有意思了蔽介,現(xiàn)在我們切換回 master
分支看看:
$ git checkout master
檢出時(shí) HEAD 隨之移動(dòng)
這條命令做了兩件事糟需。 一是使 HEAD 指回 master
分支,二是將工作目錄恢復(fù)成 master
分支所指向的快照內(nèi)容谷朝。 也就是說,你現(xiàn)在做修改的話武花,項(xiàng)目將始于一個(gè)較舊的版本圆凰。 本質(zhì)上來講,這就是忽略 testing
分支所做的修改体箕,以便于向另一個(gè)方向進(jìn)行開發(fā)专钉。
分支切換會(huì)改變你工作目錄中的文件
在切換分支時(shí),一定要注意你工作目錄里的文件會(huì)被改變累铅。 如果是切換到一個(gè)較舊的分支跃须,你的工作目錄會(huì)恢復(fù)到該分支最后一次提交時(shí)的樣子。 如果 Git 不能干凈利落地完成這個(gè)任務(wù)娃兽,它將禁止切換分支菇民。
我們不妨再稍微做些修改并提交:
$ vim test.rb
$ git commit -a -m 'made other changes'
現(xiàn)在,這個(gè)項(xiàng)目的提交歷史已經(jīng)產(chǎn)生了分叉(參見 項(xiàng)目分叉歷史)投储。 因?yàn)閯偛拍銊?chuàng)建了一個(gè)新分支第练,并切換過去進(jìn)行了一些工作,隨后又切換回 master 分支進(jìn)行了另外一些工作玛荞。 上述兩次改動(dòng)針對(duì)的是不同分支:你可以在不同分支間不斷地來回切換和工作娇掏,并在時(shí)機(jī)成熟時(shí)將它們合并起來。 而所有這些工作勋眯,你需要的命令只有 branch
婴梧、checkout
和 commit
。
項(xiàng)目分叉歷史
你可以簡單地使用 git log
命令查看分叉歷史塞蹭。 運(yùn)行 git log --oneline --decorate --graph --all
,它會(huì)輸出你的提交歷史嚼酝、各個(gè)分支的指向以及項(xiàng)目的分支分叉情況浮还。
$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) made other changes
| * 87ab2 (testing) made a change
|/
* f30ab add feature #32 - ability to add new formats to the
* 34ac2 fixed bug #1328 - stack overflow under certain conditions
* 98ca9 initial commit of my project
由于 Git 的分支實(shí)質(zhì)上僅是包含所指對(duì)象校驗(yàn)和(長度為 40 的 SHA-1 值字符串)的文件,所以它的創(chuàng)建和銷毀都異常高效闽巩。 創(chuàng)建一個(gè)新分支就相當(dāng)于往一個(gè)文件中寫入 41 個(gè)字節(jié)(40 個(gè)字符和 1 個(gè)換行符)钧舌,如此的簡單能不快嗎?
這與過去大多數(shù)版本控制系統(tǒng)形成了鮮明的對(duì)比涎跨,它們?cè)趧?chuàng)建分支時(shí)洼冻,將所有的項(xiàng)目文件都復(fù)制一遍,并保存到一個(gè)特定的目錄隅很。 完成這樣繁瑣的過程通常需要好幾秒鐘撞牢,有時(shí)甚至需要好幾分鐘。所需時(shí)間的長短,完全取決于項(xiàng)目的規(guī)模屋彪。而在 Git 中所宰,任何規(guī)模的項(xiàng)目都能在瞬間創(chuàng)建新分支。 同時(shí)畜挥,由于每次提交都會(huì)記錄父對(duì)象仔粥,所以尋找恰當(dāng)?shù)暮喜⒒A(chǔ)(譯注:即共同祖先)也是同樣的簡單和高效。 這些高效的特性使得 Git 鼓勵(lì)開發(fā)人員頻繁地創(chuàng)建和使用分支蟹但。