git分支概念和分支相關操作

分支是git中最容易被誤解的概念之一,雖然git分支並不難理解。使用分支時候是不有點不知所措,"老虎吃天,無處下爪"的感覺?還有那一系列的merge和rebase黑魔法操作,甚至是那些許的衝突都曾讓你頭痛不已?

本文中,蟲蟲將帶你回到git王國,通過實例理解git分支的概念及分支相關的操作,在你閱讀這篇文章後,希望能瞭解分支是怎麼一回事,知道怎麼靈活使用分支來解決工作中遇到的痛點,而不再是一頭霧水,懵懂不知所措。

什麼是分支?

要明白什麼是分支,我們從經典的git圖像開始,類似圖片估計大家都見到過:

git分支概念和分支相關操作

圖上顯示有三個分支Master、Dev和Chongchong。

其中Master是默認就存在的一個分支,我們叫它主分支。一般很多人會覺得這個Master系統保留的分支應該有其特殊之處,但是實際上Master和其他分支一樣沒有啥特殊之處,無非他是必須有的分支,這個名稱也可以通過git遠程倉庫修改默認配置,改為阿貓阿狗都沒有問題。這個分支也可以刪,但是需要特殊設置下。

分支其實上就是一個指針

簡而言之,在git中分支只是一個指向單個commit的指針。當然,這個指針只是用來表明這個commit和base分支的之間的偏差。從現在開始,本文中,當我們要指明指針的時候,我們將使用分支引用branch reference),而我們要指明偏差時候用分支。

我們說分支引用除了指定某個基本偏差的commit外,還有一些其他特性,可以讓我無縫高效實現一些git高級操作,比如merge和rebase。

在git中為什麼分支操作非常高效而無其他資源負擔的緣故就是因為他不過是個指針。這也是和svn等分支(獨立目錄)的根本區別之一。

git分支引用詳解

git的版本歷史通過一個的commit往前推進存儲。而分支引用則是相反從后王前引用的,如下圖所示:

git分支概念和分支相關操作

從圖上,我們可以看到每一個commit的引用,引用最先指向的是1。通過遍歷每個分支,我們可以看出最後的通常commit是4(然後分支出了branch-1)。

分支引用的其他特性?

有什麼東西在這裡失蹤了嗎?確定分支是對單個提交的引用,但是當我們進行commit或者在分支內hard reset時候,相應的分支引用會發生什麼變化呢?

下面我們實例展示一下,假設我們給branch-1做一個commit:

git checkout branch-1

echo "## 添加新的行" >> README.md

git add README.md

git commit -m "添加一個新行"

然我們用交互方式解釋一下以上各個命令:

"跳到branch-1分支"

"向README.md文件添加一行,內容為markdown二號標題添加新的行"

"將這些變化放入暫存區域"

"目前branch-1分支,提交變化給branch-1,並將引用指向新的commit"

另一方面,關於hard reset的操作則更容易:

> git checkout branch-1

> git reset --hard HEAD~1

這更容易解釋:

"我們在branch-1分支中,取消當前的提交,把引用指向前一個commit"。

Rebase操作

Rebase是我們在需要改變提交歷史時候用的操作,主要在以下兩種情況時候用:

1、改寫commit信息,增加squash,刪除或重新組織commit。當有意地將偏差點指向分支引用,則可以改變整個分支的歷史。

2、更改分支開始點,偏離點。改變後,整個分支提交歷史和偏差點都會改變,因為起點都變了。

下面我們以下圖的一個repo圖為實例,做操作演示

git分支概念和分支相關操作

改寫branch-1分支的基礎

我們以最簡單的場景開始,改變我們分支的基礎。我們的目標是使branch-1從主分支的最新提交(master/commit 8)開始。

為此,我們只需要指定一個commit或分支引用,然後分支的所有commit將被重新應用在這個提交的基礎上。

首先我們用git log命令看下當前的提交歷史

git log --pretty --graph --oneline --all

git分支概念和分支相關操作

切換到分支1

git checkout brach-1

以master為基礎rebase

git rebase master

git分支概念和分支相關操作

需要手動解決下衝突。

繼續

git rebase -continue

rebase後的提交歷史

git log --pretty --graph --oneline --all

git分支概念和分支相關操作

可以對比,rebase後可以看到comit 5和7的哈希值變了。因為rebase後commit 5的祖先發生了變化,並且由於提交是不可變的,所以我們必須創建一個新提交,同樣適用於以下提交。

改寫分支歷史

我們再實例展示下改變brach-1分支的提交歷史。

那麼讓我們來做一下互動式rebase

git checkout brach-1

git rebase -i 5b64297

git分支概念和分支相關操作

在這個例子中,我們將使用reword命令,其他各個命令建議自己測試練習。

r 5b64297 5

pick 369b38f 7

提交修改改的兩行,然後用

git commit --amend

chongchong

git分支概念和分支相關操作

git rebase -continue

完成rebase過程。

git log查看歷史,結果如下:

git分支概念和分支相關操作

我們通過上圖可以看到,每一次改變都相當於新建一次commit,而且以此為基礎繼承於它之後commit都會改變(本例的commit 7哈希變化了)。

Merge操作

Merge與rebase操作的處理方式完全不同,實際上merge比rebase更簡單。假設,我們想要想在分支branch-1添加master中的變化(6,8)變化,不用rebase提交歷史變為7結束。這種情況下使用merge,而在我們的日常實踐中也是merge更常用些。

merge有兩種方法:快進和非快進。快進是將分支的指針移動到我們想要合併的分支。快進方法有時候會不能用,比如我們上面的列子中就不能將branch-1移動到master提交,因為這樣會導致commit 5和7丟失。當然如果已經rebase分支到主分支頂部的,則可以用將快進方法將branch-1合併到主分支。

合併操作也很簡單,我們繼續前面的例子,實例展示:

git checkout master

git merge branch-1 -ff

快速合併之後的git圖形

git分支概念和分支相關操作

讓我們回到我們初始時候git記錄中

git分支概念和分支相關操作

讓我們使用快速merge 和並master合併branch-1中:

git分支概念和分支相關操作

git merge master --ff-only

git分支概念和分支相關操作

如預期的那樣是無法快進合併。我們用一般方法合併:

git merge master

git log --pretty --graph --oneline -all

git分支概念和分支相關操作

如上圖所示,改操作創建了一個新的commit(68476ab),這個提交很特別,它有2個祖先(4和8),它被稱為合併提交。

現在我們的git歷史圖示將如下所示:

git分支概念和分支相關操作

Merge vs Rebase

當我們想要反映另一個分支的變化時,我們是使用rebase還是merge呢?這個問題沒有統一的概念,按照個人喜好以及具體的工作場景:

如果你工作在公共分支中,你和基友們在同時協同工作,那麼就使用merge,因為它以時間順序保留了所有的commit,所有的變更都很清晰並將新變化最後疊加到最後一次提交的頂部。

如果你在你保持在你特有分支中工作,rebase會保持你的線性歷史,然後, 當將你的分支合併的時候,可以保持你的master更加清晰,簡練。


分享到:


相關文章: