logo-yuki

Yuki

Post

とうこう

git rebase vs. git merge 哪個好?

從不敢用 rebase 到常常用 rebase。

5 分鐘

rebase 感覺好複雜、好危險。我還是都用 merge 就好。

我以前也是這麼想。不過我後來漸漸發現了各有各的好。

git merge

在幹嘛?

把另一條 branch 的變更合進來,並產生一個 merge commit 記錄這次合併。

text
      A---B---C  feature
     /         \
D---E---F---G---H  dev  ← merge commit (H)

假設 feature branch 叫 feature/button-component、target branch 是 dev,英文可以說:

  • Merge feature/button-component into dev(將 feature branch 併入 dev)
  • Merge feature/button-component(合併 feature branch)

為什麼要特別英文教學呢?因為我曾遇過有主管 / 同事英文不好,看到 into 不知怎的以為是英文倒裝句,以為是有人把 dev 併入 feature branch 才導致程式壞掉,而不反思是自己有 bug ¯\(ツ)/¯

優點

  • 操作直覺,不會動到既有的 commit 歷史
  • conflict 一次解完,不用分批處理
  • 適合需要保留 合併記錄 的場合(e.g. PR / MR 的 merge)

缺點

  • 多人協作一段時間後,git log 會出現大量 merge commit
  • 歷史不是線性的,像蜘蛛網,不好讀
text
*   Merge branch 'feature/login'
|\
* |
| * feat: integrate login api
* |
|\* feat: build login form
|
*   Merge branch 'feature/registration'
|

git rebase

在幹嘛?

把你的 commit 一個個到 target branch 的最新點後面,重新接上去 aka 變基

text
      A---B---C  feature(rebase 前)

                A'--B'--C'  feature(rebase 後)
               /
D---E---F---G  dev

注意 A B C 變成了 A' B' C'。因為 base 換了,commit hash 隨之改變。

優點

  • git log 是乾淨的一條線,好讀好追
  • 同步 target branch 的最新變更,不會多出 merge commit
  • 搭配 rebase -i 可以整理 commit,squash 或改 message

缺點

  • 有 conflict 的話,要一個 commit 解一次,不像 merge 一次解完
  • commit hash 會改變,不適合用在已經 push 出去、別人也在用的 branch
  • 觀念沒到位前,操作容易出錯

為什麼大家會怕 rebase?

主要有三點:

conflicts 要分批解

merge 是把所有衝突集中在一次解決;

rebase 是每個 commit 都可能有衝突,要一個個處理,解完衝突後,手動 git add,再 git rebase --continue 繼續跑下一個 commit。

雖然麻煩,但換個角度想其實也還行——每次的 conflict 會鎖定在小範圍內,有時反而好處理。

要 force push

rebase 後 commit hash 變了,push 的時候 remote 會拒絕,必須 git push --forcegit push --force-with-lease

  • 如果確定這個 feature branch 只有自己在用,直接 --force / -f 就行。
  • 如果這個 feature branch 有別人跟你一起在用,那就要用 --force-with-lease

--force-with-lease 能幹嘛?一樣是 force push,但如果 remote 有你不知道的新 commit 會先擋下來,避免蓋掉別人的東西。

有必須遵守的鐵則

不要 rebase 已經 push 出去、有其他人在用的 branch。

也就是說,只有自己在用的 feature branch 隨便 rebase + force push 都沒問題;但如果在團隊沒有共識之下,擅自 rebase 了大家都在用的 maindev你跟他人一起協作開發的 branch,別人的 commit 歷史就會對不上了。

別人的 commit 歷史對不上會怎麼樣?你 rebase 之後 commit hash 全換了,但別人的 base 還停在舊的 commit hash,他們 pull 或 push 時 Git 會看到兩條分歧的歷史。輕則要手動修、重則搞不清楚哪些變更是真的、哪些是重複的,俗稱搞爛 repo

我的習慣

我曾經待過:

  • 統一用 git merge 的團隊
  • 自由、沒規範用哪個的團隊(結果習慣用 git merge 者佔多數、習慣用 git rebase 者會被議論說是有個人堅持
  • 統一用 rebase 的團隊(會看到 merge commit 基本上都是 PR / MR 被 merged 自動產生的。)

當然在公司、在團隊當中就是按照團隊共識來做。至於私人習慣,由於個人 side project 都是一人工作,基本上都在 dev 直接開發也不會發 PR / MR,就用 rebase 啦。

總結

哪個好沒有一定。有機會的話,不妨試試看沒用過的那個。

延伸閱讀