我習慣每篇部落格都有個開篇
還記得 Git 系列第一篇 Git 自我介紹的話嗎?其中有 Git 自己都贊同的三大特色
cheap local branching, convenient staging areas, and multiple workflows
輕量的本地分支, 方便的暫存,以及多工作流。其中因為有分支的存在,才構成了多工作流的特色,所以 Branch 不愧為 Git 的王牌特色。這篇部落格,主要和大家一起學習一下輕若鴻毛,帥到炸裂的分支兒。
Branch 的概念
分支的概念,在我看來,分兩個版本:
從使用場景上解釋,是這麼個概念
Git 的分支,就是開發過程中,要選擇的一條路,你可以選擇和其他小夥伴一起走同一條路,也可以自己走一條路,路與路之間相互沒有影響,作為路的主人,你也隨時可以讓兩條路合併。
深入一點的話,是這麼個概念:
Git 的分支,其實本質上僅僅是指向提交物件的可變指標,這個可變指標,指向路的終點。同時,還有一個比較特別的 HEAD 指標,用於記錄當前工作的位置,借用上面的例子,這個 HEAD 指標等於在路上走的你自己,你在哪,指標就在哪,你在哪個分支,HEAD 指標就指向哪個分支的指標。
實際上,當我們使用 Git 的時候,我們就使用了分支,因為 Git 的預設分支名字是 master,如果你有心的話,會發現執行 git init
後,命令列的輸出頭部已經預設在 master 分支了。 但是這個時候,還並未建立 master 分支,只有當有一個提交的時候,才會建立 master 分支。原因在於,分支的指標要指向提交的呀,突然明白了,當初看 Android Studio 的教程,為什麼每個都讓有一個初步提交了呢。
無圖無真相,不信的看下面:
玩轉 Branch 必備技能
有關分支的命令不多,無非是換著花樣的增刪改查,掌握好以下基本的命令,以後就可以在 Branch 的草原上策馬奔騰瀟瀟灑灑啦
建立分支
git branch <name>
複製程式碼
這個命令看起來,似乎簡單到你只需要想個分支的名字就好了。
但是在建立分支的時候,要想下,是否要從當前分支的內容基礎上去開闢一條新分支。
檢視分支
三個命令,讓你想看什麼分支就看什麼分支,就是這麼方便
git branch //檢視本地分支
git branch -r //檢視遠端分支
git branch -a //檢視本地和遠端的所有分支
複製程式碼
刪除分支
當本地分支刪除後,推動到遠端倉庫後,遠端倉庫並不能自動刪除遠端分支(原因,下回分解)。所以,分支的完全刪除是分兩個部分的,一個是本地,一個是遠端。
本地刪除操作需要加上 -d
或者 -D
引數,引數的名稱來自英語 delete
的縮寫,Remember it so easy!
兩者的區別在於-D
比-d
要粗暴一點。當被刪除分支有新內容沒有被合併的時候,使用-D
,會直接刪除, 使用-d
,會提示該分支有新內容沒有被合併,不執行刪除。刪除需謹慎,建議非特殊情況下,使用溫柔的-d
要好一點,以免小手一抖,眼淚長流。
git branch -d <name>
git branch -D <name> //強制刪除
複製程式碼
刪除遠端分支需要 push 操作。
git push origin :<name>
複製程式碼
重新命名分支
其實,這是個偽命題。
但是有很多人,包括我,都有過對分支名稱不滿意想該修改一下的想法,所以,就有了這個偽命題的存在。
事實上,分支不存在重新命名,沒有 rename 的這個命令。如果你起的名字不滿意,想重新起的話,那隻要建立一個和要修改分支內容一樣的分支,起上你喜歡的名字,然後再把之前的給刪掉。
檢出分支
這個檢出分支的“檢出”二字,算是個關於 Git 分支的專業術語了,你可以理解為切換當前分支。
本質上, checkout 操作是移動 HEAD 指標,將 HEAD 指標指向要切換的分支的指標處。
使用場景有兩個:
- 已經存在的分支,現在要切換過去。
git checkout <name>
複製程式碼
-
建立一個新分支,並切換到新分支,這個一步到位的話需要
-b
引數。以當前分支為基礎,建立一個新分支
git checkout -b <branch name> 複製程式碼
以指定的某一個提交,建立一個新分支
git checkout -b <branch name> <SHA1> 複製程式碼
合併分支
以上,是分支的增刪改查獨立操作,但是 Git 創造這個分支,並不只是為了讓它們自個兒和自個兒玩的,還需要它們之間的相互協作和配合。 就像寫專案的時候,分好開發任務,你和你的小夥伴新建了兩個分支,你寫你的 Anglela,他寫他的 baby,到開發完成之後,肯定要合在一起,才能成就 Anglelababy。合的這個動作,就涉及到了分支合併的概念。
分支合併使用到的命令是
git merge <branch name>
複製程式碼
分支合併相對分支的其他操作,是相對要複雜一點的,怎麼說也是多分支操作,至少要對得起它一聽就比較高階的名字吧,於是我決定把分支的合併作為一個大標題。
Branch 合併是大事
git 的兩種合併模式
分支的合併是非常智慧的,目前有兩種模式,兩種模式的選擇,不需要我們參與,而是 Git 根據分支情況不同,自行判斷選擇的。在我使用 Git 的過程中,執行分支合併,有時需要輸入提交資訊,有時不需要,作為小白的我懵的不知所以然,後來才知道是因為合併模式的問題。
兩種模式是:
- Fast-Forward(快進式)(PS:這個名字是官方的)
- Recursive Strategy Merge(策略合併式)(PS:這個名字非官方,我自己起的,有時也叫三方合併式)
- Fast-Forward(快進式)
如圖,有兩個分支,master 分支和 feature 分支。當這兩個分支處於上面的關係時,當進行合併操作時,就會出現 fast-forward。
原因是;由於當前 master 分支所指向的提交是 feature 分支的直接上游,所以 Git 只是簡單的將指標向前移動。 換句話說,當你試圖合併兩個分支時,如果順著一個分支走下去能夠到達另一個分支,那麼 Git 在合併兩者的時候,只會簡單的將指標向前推進(指標右移),因為這種情況下的合併操作沒有需要解決的分歧——這就叫做 “快進(fast-forward)”。
合併後的分支指標位置如下:
- Recursive Strategy Merge(策略合併式)
這個合併方式,是為補充 fast-forward 而出現的,因為你知道,在專案開發過程中,很多人開發的情況下,出現 fast-forward 的情況並不是很多,很多是類似下面這種。提交歷史是分叉的,無法滿足執行 fast-forward 的條件:
因為,master 分支所在提交併不是 feature 分支所在提交的直接祖先,Git 不得不做一些額外的工作。 出現這種情況的時候,Git 會使用兩個分支的末端所指的快照(C4 和 C5)以及這兩個分支的工作祖先(C3),做一個簡單的三方合併,生成一個新的提交(C6)。
實戰演練一下
說起來就是一堆理論,我自己都記不住,找個例子演示一下:
//建立一個資料夾,並初始化 Git
mkdir GitDemo
git init
//初次提交,建立 master 分支
touch master.txt
git add.
git commit -m '新增master檔案'
//從master分支末尾,建立並切換 featureA 分支,並建立一個提交
git checkout -b featureA
touch A.txt
git add.
git commit -m '新增A檔案'
//從master分支末尾,建立並切換 featureB 分支,並建立一個提交
git checkout master
git checkout -b featureB
touch B.txt
git add.
git commit -m '新增B檔案'
//切換 master 分支
git checkout master
//master 合併 featureA 分支
git merge featureA
//master 合併featureA 後再合併 featureB 分支
git merge featureB
複製程式碼
博主不想給你說話,並向你投擲了一大串命令,快去敲敲看啊,會看到兩種合併模式的!
master 分支合併 featureA 時,是快進式合併:
master 分支合併 featureA 後, 再合併 featureB 時,已經不滿足快進式條件了,此時合併會觸發一個三方合併,產生一個新的提交。所以,執行合併命令,會跳到下面的頁面,讓我們編輯這個新提交的提交資訊,預設提交資訊是“Merge branch 'branch name'”. 按 i
編輯提交資訊, :wq!
儲存並退出頁面。
合併成功後的提示資訊:
畫出上面小例子的分支合併,示意圖,如下:
和平解決 Branch 合併衝突
有人在的地方就有江湖,有分支在的地方,就有衝突。有時候合併操作不會如此順利。 如果你在兩個不同的分支中,對同一個檔案的同一個部分進行了不同的修改,Git 就沒法乾淨的合併它們,於是就會發生衝突。
如下,分別在 master 和 featureA ,在 master.txt 檔案第一行新增一句話,然後兩個分支合併,就會發生衝突。
衝突提示資訊中,指明衝突檔案為 master.txt。同時,也可以通過 git status
命令,檢視衝突的詳細資訊
需要說明的是,如果遇到衝突的話,git 就無法自動合併了,接下來要靠我們自己手動解決衝突,方法是:
- 檢視造成衝突的檔案,修改衝突部分
- 對修改後衝突檔案,執行
git add
操作 - 建立一個修改衝突的提交。
先知道一下思路,有個簡單的概念在腦子裡,接下來,一步一步仔細看~
第一步:檢視造成衝突的檔案,修改衝突部分
衝突檔案 master.txt 如下,git 雖然無法解決衝突, 但是已經幫我們幫到最後了,使用簡單的三個符號,標明瞭衝突的地方,以及衝突的兩個分支在該地方發生衝突內容。
符號 | 意義 |
---|---|
======= | 分隔符 |
<<<<<<< HEAD 至 ======= | master 分支中該地方的內容 |
======= 至 >>>>>>> featureA | featureA 分支中該地方為內容 |
接下來編輯 master.txt 檔案,完成合並,確認之後,把 git 衝突識別符號號給刪除掉即可。
第二步 & 第三步:修改後衝突檔案,add && commit
分支回滾, 有後悔路可以走
現實中,難免有些時候,你會有後悔的念頭。例如每天我遲到的時候,都會後悔為什麼第一遍鬧鐘響的時候沒有起床,但是這個世界,沒有後悔路可以走,我只能努力做到明天早起。
但是,Git 中有!因為神奇的 reset 和 revert 命令~,兩個命令都可以達到回滾的效果,兩者之間的區別以後會專門說,這裡我們使用 reset 。
提前宣告:回滾之前,不要忘記做好備份,有備無患吶。
本地分支回滾:
確定回滾到哪個提交,找到該提交的 commit id,執行以下命令,就好了
git reset --hard commit id
複製程式碼
遠端分支回滾
依舊是個偽命題。遠端分支不存在什麼回滾,要想達到回滾的效果,就是刪除之前的遠端分支,然後把本地回滾好的本地分支,push 到遠端。
git reset --hard commit id //本地分支回滾
git push origin :<name> //刪除遠端分支
git push origin <name> //用回滾後的本地分支重新建立遠端分支
複製程式碼
我習慣每篇部落格都有個結束語
這篇部落格用了我不少洪荒之力,希望對大家理解 Git 分支有所幫助,不對之處還請指出。
最近 Git 系列走起,下篇部落格見!