Git Fast-forward Merge

IMG_20150327_103310

前一陣子,在 twitter 上看到 GitHub 在三月份要來臺灣做教育推廣,剛好中研院資訊所有老師在 03/23 也有邀請 GitHub 來演講。

03/23 當天是 GitHub 員工 John Britton 擔任講者,主要是介紹 Git 、GitHub 使用,自己雖然開發會用 Git 來管理程式碼,但是只對操作上比較熟悉,在整個演講中,又重新了解一些 Git 運作方式,收穫蠻多的,剛好也有人問到一個 Fast-forward 的問題,當下並沒有很清楚了解,因為,演講完之後,有 GitHub 貼紙可以拿,拿到貼紙之後,回到辦公室其實已經忘記了 XD,還好同事有問我知不知道 Fast-forward,立馬在辦公室就找了一下資料。

什麼是 Fast-forward?

簡單來說,Fast-forward 是 git merge 的一種方式,git merge 主要是將不同 branch 中的內容合併到某一個 branch 內。為什麼會特別區分這種方式呢?

用一張圖來解釋:

Fast-foward

Git merge 前

  1. 有一個 master branch,在 master branch 中,已經有了 3 個 commits 了
  2. m3 commit 後,建立了一個 develop branch
  3. develop branch 建立 2 個 commits

目前到這裡,是 git merge 前的操作,而 2 個 branches,分別有 2 個 pointer 指著 branches 的最新 commit 節點。

之後先切換到 master,將 develop branch 合併 (merge) 到 master branch:

$ git checkout master
$ git merge develop
Updating 5a51d31..129acb8
Fast-forward
 d1 | 0
 d2 | 0
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 d1
 create mode 100644 d2

develop 合併到 master 後會發現,其實 git 只是將原本指向 m3 commit 節點的指標,移到 d2 ,怎麼會這樣呢?

蠻容易可以了解的,因為 develop branch 建立之後,在 master branch 中並沒有再進行修改(也就是建立新的 commit),所以要把 develop 合併到 master 的話,就是 develop 最新的 commit 的內容了!

反之,develop branch 建立之後,如果 master branch 還有再被修改,之後的合併,就沒辦法使用 fast-forward 來進行合併。

從以下圖可以了解:

cannot-git-fast-forward-detail_sm

  1. 一開始一樣與 Git merge 前所敘述的一樣
  2. develop branch 建立後,在 master branch 中又進行了修改、建立 commits,所以指向 master branch 的指標,已經移到 m5 commit
  3. 接著,要將 develop branch 的內容合併到 master branch 中,此時,會發現無法使用 fast-forward 將指向 master 的指標指向 develop branch 的最新 commit 節點,因為,master branch 已經在建立 develop branch 之後被修改了,所以需要將 develop 的內容與 master 合併之後,建立一個新的 m6 commit

不使用 Fast-forward

也可以在 git merge 的時候,不使用 fast-forward 的方式來進行合併:

No fast-forward

  1. 不使用 fast-forward 的話,把 2 個 branches 內容合併之後,會建立一個新的 m4 commit 節點

如何不使用 fast-forward?

只要在 git merge 指令之後加上 --no-ff 即可:

$ git checkout master
$ git merge --no-ff develop
Merge made by the 'recursive' strategy.
 d1 | 0
 d2 | 0
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 d1
 create mode 100644 d2

為什麼不直接使用 fast-forward 要使用這種方式呢?

先來瞭解一下,有沒有 fast-forward,git log 看起來差別在哪:

有 fast-forward (develop 合併到 master 後):

commit 932154590bde6dd8c7329439870ae893e8b6bdf4
Author: YMHuang <MY_EMAIL@email.com>
Date: Sat Apr 4 14:58:34 2015 +0800

 d2

commit 129acb8246636279a28352f91a5c1a724a2d7a6d
Author: YMHuang <MY_EMAIL@email.com>
Date: Sat Apr 4 14:57:11 2015 +0800

 d1

commit adf8e08abdfe5d93acb24d9e0d43b89b4aa8d211
Author: YMHuang <MY_EMAIL@email.com>
Date: Sat Apr 4 14:45:17 2015 +0800

 m3

commit 5a51d311899f0b8520d16b276291e80735bf90b8
Author: YMHuang <MY_EMAIL@email.com>
Date: Sat Apr 4 14:43:19 2015 +0800

 m2

commit 09fd8ae6de5c0f718af12847d7c97364eb738fd2
Author: YMHuang <MY_EMAIL@email.com>
Date: Sat Apr 4 14:42:49 2015 +0800

 m1

沒 fast-forward (develop 合併到 master 後)

commit bc943817e068304f9de6285bd8c9f1a9a108e952
Merge: adf8e08 9321545
Author: YMHuang <MY_EMAIL@email.com>
Date: Sun Apr 5 12:56:40 2015 +0800

 Merge branch 'develop'

commit 932154590bde6dd8c7329439870ae893e8b6bdf4
Author: YMHuang <MY_EMAIL@email.com>
Date: Sat Apr 4 14:58:34 2015 +0800

 d2

commit 129acb8246636279a28352f91a5c1a724a2d7a6d
Author: YMHuang <MY_EMAIL@email.com>
Date: Sat Apr 4 14:57:11 2015 +0800

 d1

commit adf8e08abdfe5d93acb24d9e0d43b89b4aa8d211
Author: YMHuang <MY_EMAIL@email.com>
Date: Sat Apr 4 14:45:17 2015 +0800

 m3

commit 5a51d311899f0b8520d16b276291e80735bf90b8
Author: YMHuang <MY_EMAIL@email.com>
Date: Sat Apr 4 14:43:19 2015 +0800

 m2

commit 09fd8ae6de5c0f718af12847d7c97364eb738fd2
Author: YMHuang <MY_EMAIL@email.com>
Date: Sat Apr 4 14:42:49 2015 +0800

 m1

從上面的例子可以看到,如果沒有使用 fast-forward,能知道到哪些 commits 是從其他 branch 合併進來的,而且能在合併的時候加上 commit 訊息。

如果使用 fast-forward 合併,刪除被合併的 branch 後,原本的 commit 會不見嗎?

當然不會,以上面的例子來說,如果 develop branch 合併到 master branch 後,要將 develop branch 刪除,只是會把指向 develop 的指標移除,所以不會讓 develop branch 中的 commits 節點也被移除掉。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料