合併與變基

sizeofio發表於2019-02-27

我們正在開發某個專案,為了實現某個專案,我們建立了分支task_113,正在這個分支上開展工作。這個時候收到反饋說有個很嚴重的bug需要立即處理。這個時候,你應該立即切換到線上分支,為這個bug建立新的分支issue_005,並在這個分支上修復它。在測試通過後,切換到線上分支來合併issue_005,最後將改動推到線上。然後切換回task_113上繼續做新的需求。

merge 合併

enter image description here

開始在task 113上工作:

在沒開始做任務前,我們倉庫的結構如上。現在我們開始建立task_113分支:

$ git checkout -b task_113
Switched to a new branch 'task_113'

這個時候我們的git倉庫結構如下: enter image description here

這個時候我們在當前分支上做了些工作,並且提交到了版本庫後,我們的git結構如下: enter image description here

收到緊急問題issue 005

這個時候我們收到反饋的bug後,我們立即切到master分支上,從master分支建立新的issue_005分支開始解決線上的bug:

在切換到master之前,需要確認所有修改已經提交到版本庫中。

# 切換到master分支
git checkout master
Switched to branch 'master'

# 建立issue_005分支處理bug
git checkout -b issue_005
Switched to a new branch 'issue_005'

下面的圖是我們現在git倉庫的狀態: enter image description here 當issue_005分支上的修改測試完成後,我們需要到master上將其合併。

git checkout master
Switched to branch 'master'

git merge issue_005
Updating 04132ad..86e4899
Fast-forward
 about.html | 1 +
 1 file changed, 1 insertion(+)

Fast-forward: 表示的是在合併兩個分支的時候,如果順著一個分支走下去能夠到達另一個分支,那麼Git在合併兩者的時候,只會簡單的將指標向前推進(指標右移),因為這種情況沒有需要解決的分歧,這就叫做快進。

這個緊急問題處理完成後,我們就可以將issue_005這個分支刪除,因為我們再也用不上它了。刪除完成後,我們繼續回到task_113分支上繼續我們的工作:

git branch -d issue_005
Deleted branch issue_005 (was 86e4899).

git checkout task_113
Switched to branch 'task_113'

git commit -a -v -m 'update index.html[task 113]'
[task_113 76e0e88] update index.html[task 113]
 1 file changed, 1 insertion(+)

這個時候我們再來看看我們的倉庫結構: enter image description here

合併分支

task_113的需求我們已經完成了,並且測試通過,這個時候我們應該開始將它合併到master上準本釋出到線上環境了。

git checkout master
Switched to branch 'master'

git merge task_113
Merge made by the 'recursive' strategy.
 index.html | 2 ++
 1 file changed, 2 insertions(+)

這次的合併並沒有像上次合併那樣,出現Fast-forward。因為master分支所在提交併不是task_113分支所提交的直接祖先,在這種情況下,Git會使用兩個分支末端所指的快照(c4和c5)以及這兩個分支的共同祖先c2來一個簡單的三方合併。合併過後的倉庫結構為: enter image description here

這個時候我們再把task_113分支刪除了就好了。我們再看看git log --graph的結果: enter image description here

從上圖可以看出,和我們畫的結構圖完全一致,接下來我們再介紹下另外一種方法變基

變基

enter image description here 上圖是我們當前專案結構,如果這個時候用合併的方式來整合分支的話,最終結構為: enter image description here 整合分支最容易的方法就是合併分支。它會把兩個分支的最新快照以及二者之間共同祖先(如果能過快進的話,則直接快進)進行三方合併來生成新的快照。

而變基則是提取出C4中引入的補丁和修改,在C3的基礎上應用一次。可以使用rebase命令將提交的某一分支上的所有修改都移至另一分支上。

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command

這個時候我們回到master上,進行一次快進合併:

git merge work
Updating 3462092..ece0ba5
Fast-forward
 work.html | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 work.html

合併過後,我們的倉庫結構為: enter image description here

還是用git log --graph來看看最後的結構: enter image description here

其實變基和合並一樣,最終都生成了一次新的提交,但是使用變基我們讓提交歷史變得更加簡介,你可以看到經過變基的分支的歷史記錄中,儘管開發工作是並行的,但是它們看起來就好像是穿行一樣,提交歷史是一條直線沒有分叉。在簡單的專案中,可能有點分叉也沒什麼,但是在一個多人維護的大型專案中,分叉多了專案的維護工作就會變得越來越複雜。

一般情況下我們用變基也就是為了確保向遠端分支推送時能夠保持提交歷史的簡潔:當我們在github上修復別人專案的bug時,我們首先在自己的分支裡開發,當開發完成後,你需要將你的程式碼變基到origin/master上,然後再向主專案提交修改。這樣的話,該專案的維護者就不再需要進行整合工作,而是快速合併即可。

變基的風險

使用變基的時候一定要記住一條規則:不要對在你的倉庫外有副本的分支執行變基。 變基操作本質上是丟棄一些現有的提交,然後相應的建一些內容一樣但實際不同的提交。如果你將提交推送到遠端倉庫上,而其他人也從該倉庫拉取提交併基於你的分支建立了新的分支做後續工作,這個時候你用git rebase命令重新整理了提交再此推送,其他人不得不再次將它們手頭的工作與你的提交進行整合,如果他們還要拉去並整合他們修改過的提交,整個開發工作就會變得異常的混亂。想想去年美國因為同事頻繁使用git push -f推送到遠端分支而被同事槍擊的新聞吧,這樣你就能記住這條規則了。

相關文章