這一節(jié)主要是幾個概念的解釋:HEAD、master 以及 Git 中非常重要的一個概念: branch厦画。
引用:commit 的快捷方式
首先,再看一次 log
:
git log
第一行的 commit
后面括號里的 HEAD -> master, origin/master, origin/HEAD
滥朱,是幾個指向這個 commit
的引用根暑。在 Git 的使用中,經(jīng)常會需要對指定的 commit
進行操作徙邻。每一個 commit
都有一個它唯一的指定方式——它的 SHA-1 校驗和排嫌,也就是上圖中每個黃色的 commit
右邊的那一長串字符。兩個 SHA-1 值的重復概率極低鹃栽,所以你可以使用這個 SHA-1 值來指代 commit
躏率,也可以只使用它的前幾位來指代它(例如第一個 78bb0ab7d541…16b77
,你使用 78bb0ab
甚至 78bb
來指代它通常也可以)民鼓,但畢竟這種沒有任何含義的字符串是很難記憶的薇芝,所以 Git 提供了「引用」的機制:使用固定的字符串作為引用,指向某個 commit
丰嘉,作為操作 commit
時的快捷方式夯到。
HEAD:當前 commit 的引用
上一段里說到,圖中括號里是指向這個 commit
的引用饮亏。其中這個括號里的 HEAD
是引用中最特殊的一個:它是指向當前 commit
的引用耍贾。所謂當前 commit
這個概念很簡單,它指的就是當前工作目錄所對應的 commit
路幸。
例如上圖中的當前 commit
就是第一行中的那個最新的 commit
荐开。每次當有新的 commit
的時候,工作目錄自動與最新的 commit
對應简肴;而與此同時晃听,HEAD
也會轉(zhuǎn)而指向最新的 commit
。事實上砰识,當使用 checkout
能扒、reset
等指令手動指定改變當前 commit
的時候,HEAD
也會一起跟過去辫狼。
總之初斑,當前 commit
在哪里,HEAD
就在哪里膨处,這是一個永遠自動指向當前 commit
的引用见秤,所以你永遠可以用 HEAD
來操作當前 commit
。
branch
HEAD
是 Git 中一個獨特的引用真椿,它是唯一的秦叛。而除了 HEAD
之外,Git 還有一種引用瀑粥,叫做 branch
(分支)挣跋。HEAD
除了可以指向 commit
,還可以指向一個 branch
狞换,當它指向某個 branch
的時候避咆,會通過這個 branch
來間接地指向某個 commit
仓犬;另外蜂厅,當 HEAD
在提交時自動向前移動的時候,它會像一個拖鉤一樣帶著它所指向的 branch
一起移動周瞎。
例如上面的那張圖里黄琼,HEAD -> master
中的 master
就是一個 branch
的名字樊销,而它左邊的箭頭 ->
表示 HEAD
正指向它(當然,也會間接地指向它所指向的 commit
)。
如果我在這時創(chuàng)建一個 commit
围苫,那么 HEAD
會帶著 master
一起移動到最新的 commit
:
git commit
通過查看 log
裤园,可以對這個邏輯進行驗證:
git log
從圖中可以看出,最新的 commit
(提交信息:"Add feature1")被創(chuàng)建后剂府,HEAD
和 master
這兩個引用都指向了它拧揽,而在上面第一張圖中的后兩個引用 origin/master
和 origin/HEAD
則依然停留在原先的位置。
master: 默認 branch
上面的這個 master
腺占,其實是一個特殊的 branch
:它是 Git 的默認 branch
(俗稱主 branch
/ 主分支)淤袜。
所謂的「默認 branch」,主要有兩個特點:
- 新創(chuàng)建的 repository(倉庫)是沒有任何
commit
的衰伯。但在它創(chuàng)建第一個commit
時铡羡,會把master
指向它,并把HEAD
指向master
意鲸。
-
當有人使用
git clone
時蓖墅,除了從遠程倉庫把.git
這個倉庫目錄下載到工作目錄中,還會checkout
(簽出)master
(checkout
的意思就是把某個commit
作為當前commit
临扮,把HEAD
移動過去论矾,并把工作目錄的文件內(nèi)容替換成這個commit
所對應的內(nèi)容)。
另外杆勇,需要說一下的是贪壳,大多數(shù)的開發(fā)團隊會規(guī)定開發(fā)以 master
為核心,所有的分支都在一定程度上圍繞著 master
來開發(fā)蚜退。這個在事實上構(gòu)成了 master
和其它分支在地位上的一個額外的區(qū)別闰靴。
branch 的通俗化理解
盡管在 Git 中,branch
只是一個指向 commit
的引用钻注,但它有一個更通俗的理解:你還可以把一個 branch
理解為從初始 commit
到 branch
所指向的 commit
之間的所有 commit
s 的一個「串」蚂且。例如下面這張圖:
master
的本質(zhì)是一個指向 3
的引用,但你也可以把 master
理解為是 1
2
3
三個 commit
的「串」幅恋,它的起點是 1
杏死,終點是 3
。
這種理解方式比較符合 branch
這個名字的本意(branch 的本意是樹枝捆交,可以延伸為事物的分支)淑翼,也是大多數(shù)人對 branch
的理解。不過如果你選擇這樣理解 branch
品追,需要注意下面兩點:
- 所有的
branch
之間都是平等的玄括。
例如上面這張圖,`branch1` 是 `1` `2` `5` `6` 的串肉瓦,而不要理解為 `2` `5` `6` 或者 `5` `6` 遭京。其實胃惜,起點在哪里并不是最重要的,重要的是你要知道哪雕,所有 `branch` 之間是平等的船殉,`master` 除了上面我說的那幾點之外,并不比其他 `branch` 高級热监。這個認知的理解對于 `branch` 的正確使用非常重要。
換個角度來說饮寞,上面這張圖我可以用別的畫法來表達孝扛,它們的意思是一樣的:
通過這張動圖應該能夠?qū)Α钙降取惯@個概念更好地理解了吧?
-
branch
包含了從初始commit
到它的所有路徑幽崩,而不是一條路徑苦始。并且,這些路徑之間也是彼此平等的慌申。
像上圖這樣陌选,`master` 在合并了 `branch1` 之后,從初始 `commit` 到 `master` 有了兩條路徑蹄溉。這時咨油,`master` 的串就包含了 `1` `2` `3` `4` `7` 和 `1` `2` `5` `6` `7` 這兩條路徑。而且柒爵,這兩條路徑是平等的役电,`1` `2` `3` `4` `7` 這條路徑并不會因為它是「原生路徑」而擁有任何的特別之處。
如果你喜歡用「樹枝」的概念來理解 Git 的 branch
棉胀,一定要注意上面說的這兩點法瑟,否則在今后使用 branch
的時候就可能與出現(xiàn)理解偏差或者使用方式不當?shù)膯栴}。事實上我本人并不喜歡用這種方式來理解 branch
唁奢,因為覺得它有點舍近求遠的味道:我為了「直觀」地思考霎挟,給它了一個形象的比喻,但由于它的本質(zhì)含義其實更加簡單麻掸,導致我的這種比喻反而增加了思考它時的復雜度酥夭,未免有點畫蛇添足。不過這是我自己的感受脊奋,怎么理解 branch
是個個人偏好的問題采郎,這兩種理解方式你選一個喜歡的就好。
branch 的創(chuàng)建狂魔、切換和刪除
創(chuàng)建 branch
如果你想在某處創(chuàng)建 branch
蒜埋,只需要輸入一行 git branch 名稱
。例如你現(xiàn)在在 master
上:
你想在這個 commit
處創(chuàng)建一個叫做 "feature1" 的 branch
最楷,只要輸入:
git branch feature1
你的 branch
就創(chuàng)建好了:
切換 branch
不過新建的 branch
并不會自動切換整份,你的 HEAD
在這時依然是指向 master
的待错。你需要用 checkout
來主動切換到你的新 branch
去:
git checkout feature1
然后 HEAD
就會指向新建的 branch
了:
除此之外,你還可以用 git checkout -b 名稱
來把上面兩步操作合并執(zhí)行烈评。這行代碼可以幫你用指定的名稱創(chuàng)建 branch
后火俄,再直接切換過去。還以 feature1
為例的話讲冠,就是:
git checkout -b feature1
在切換到新的 branch
后瓜客,再次 commit
時 HEAD
就會帶著新的 branch
移動了:
git commit
而這個時候,如果你再切換到 master
去 commit
竿开,就會真正地出現(xiàn)分叉了:
git checkout master
...
git commit
刪除 branch
刪除 branch
的方法非常簡單:git branch -d 名稱
谱仪。例如要刪除 feature1
這個 branch:
git branch -d feature1
需要說明的有兩點:
HEAD
指向的branch
不能刪除。如果要刪除HEAD
指向的branch
否彩,需要先用checkout
把HEAD
指向其他地方疯攒。由于 Git 中的
branch
只是一個引用,所以刪除branch
的操作也只會刪掉這個引用列荔,并不會刪除任何的commit
敬尺。(不過如果一個commit
不在任何一個branch
的「路徑」上,或者換句話說贴浙,如果沒有任何一個branch
可以回溯到這條commit
(也許可以稱為野生commit
砂吞?),那么在一定時間后崎溃,它會被 Git 的回收機制刪除掉呜舒。)出于安全考慮,沒有被合并到
master
過的branch
在刪除時會失敱康臁(因為怕你誤刪掉「未完成」的branch
跋取):
這種情況如果你確認是要刪除這個 `branch` (例如某個未完成的功能被團隊確認永久斃掉了,不再做了)般婆,可以把 `-d` 改成 `-D`到腥,小寫換成大寫,就能刪除了蔚袍。
「引用」的本質(zhì)
所謂「引用」(reference)乡范,其實就是一個個的字符串。這個字符串可以是一個 commit
的 SHA-1 碼(例:c08de9a4d8771144cd23986f9f76c4ed729e69b0
)啤咽,也可以是一個 branch
(例:ref: refs/heads/feature3
)晋辆。
Git 中的 HEAD
和每一個 branch
以及其他的引用,都是以文本文件的形式存儲在本地倉庫 .git
目錄中宇整,而 Git 在工作的時候瓶佳,就是通過這些文本文件的內(nèi)容來判斷這些所謂的「引用」是指向誰的。
小結(jié)
這一節(jié)介紹了 Git 中的一些「引用」:HEAD
鳞青、master
霸饲、branch
为朋。這里總結(jié)一下:
-
HEAD
是指向當前commit
的引用,它具有唯一性厚脉,每個倉庫中只有一個HEAD
习寸。在每次提交時它都會自動向前移動到最新的commit
。 -
branch
是一類引用傻工。HEAD
除了直接指向commit
霞溪,也可以通過指向某個branch
來間接指向commit
。當HEAD
指向一個branch
時中捆,commit
發(fā)生時鸯匹,HEAD
會帶著它所指向的branch
一起移動。 -
master
是 Git 中的默認branch
轨香,它和其它branch
的區(qū)別在于:- 新建的倉庫中的第一個
commit
會被master
自動指向忽你; - 在
git clone
時幼东,會自動checkout
出master
臂容。
- 新建的倉庫中的第一個
-
branch
的創(chuàng)建、切換和刪除:- 創(chuàng)建
branch
的方式是git branch 名稱
或git checkout -b 名稱
(創(chuàng)建后自動切換)根蟹; - 切換的方式是
git checkout 名稱
脓杉; - 刪除的方式是
git branch -d 名稱
。
- 創(chuàng)建