關於Git中rebase和merge使用的一點小看法


關於Git中rebase和merge使用的一點小看法

前段時間看到一篇文章,標題大概是什麼 “還在用 git merge嗎,應該用 git rebase 替換掉了”,一股濃濃的標題黨氣息,我的第一感覺就是:兩個不同的命令,為什麼會有誰代替誰的說法?於是我決定寫下這篇文字,簡單談一談我個人對於這兩個命令的一點看法。

要了解 Git 命令功能的最簡單的方法就是 git --help,這裡我就不禁又要插一句閒話了,關於 Git 工具是使用命令行,還是使用GUI,這個問題就像“豆腐腦是鹹的好還是甜的好”一樣,之前還看過一個視頻推薦大家使用GUI,然後各種列舉命令行的劣勢。我個人覺得這個大可不必,作為一個工具,每個人要選擇自己最合適的方式使用,千萬不要存在鄙視鏈,就像代碼編輯器一樣,有人喜歡用IDE,有人喜歡vim/emacs,不存在誰比誰更高級的說法,只要能最高效的完成工作就可以了。但是有一點:如果你還不瞭解 Git 命令具體功能的時候,最好不要使用封裝度過高的GUI工具。因為這很容易讓初學者誤解 Git 的工作流程。

本文不針對這兩個命令的使用方法做過多闡述,主要針對兩者的區別和使用場景談一談個人看法。

Merge堅守陣地

很多關於 rebase 和 merge 相關的文章都會對這兩個命令做比較,而且也有很多帶節奏的人總會說要使用 rebase 而不要使用 merge,原因竟然是 rebase 的提交記錄線很流暢,看著舒服,而 merge 會產生一個額外的合併 commit,有強迫症的人看了會不舒服。(WTF???)

這裡我需要好好的槓一下,首先,使提交記錄看起來流暢明瞭確實是 rebase 命令的一個特點,但是這絕對不應該是我們使用它的原因。關於 merge 操作產生合併 commit 的問題,我們從下面幾個問題討論下:

01.merge 一定會產生一個合併的commit嗎?

相信大多數人肯定都遇到過,合併之後沒有產生合併 commit 的情況,所以 merge 操作並不是一定產生一個新的 commit,那麼什麼時候會產生新的 commit 呢?這裡需要介紹一個概念,fast-forward(快進模式),如果從 A 分支上出 B 分支,然後在 B 分支上工作,然後將 B 分支合併入 A 分支,如果此時 A 分支任然停留在出 B 分支時的狀態,即沒有新的 commit 加入,那麼此時 B 合併入 A 會以快進模式合併,即不產生新的合併 commit。

02.merge 產生 commit 真的不好嗎?

這裡我只想說我的觀點。git log 是開發者操作 git 的日誌記錄,也就是說每一條 log 都是有意義的,每一條 log 都可以追蹤到一個操作,比如合併產生的 commit,就是記錄了這裡是一個合併操作。其實有很多團隊的 Git 管理流程裡面是要求開發者在提交的時候加 --no-ff 參數的,也就是要求在合併時必須要寫清楚合併的內容;在 GitHub 或者 GitLab 中,大多數多人維護的項目也都是通過 PR 或者 MR 來合併的。而且合併分支的回滾也是通過 merge 的 commit 來進行操作的,所以說在很多情況下 merge 產生的 commit 是十分必要的。

03.這麼說 merge 在任何情況下也不會有問題嗎?

任何事情都有兩面性,merge 在很多情況下也會有問題。就拿 merge 產生的 commit 來說,有一些是可以避免的:比如在本地的分支 A 上開發完成後,要 push 到遠端,然後發現遠端已經有新的 commit 加入了,那麼這是如果 pull 操作,則會產生一個本地合併遠端的 commit。如果在本地 A 提交之前 pull 遠端 A,那麼這個 commit 是可以避免產生的,這也就是 rebase 這個命令的作用了。

還有就是跨分支合併的時候也會有問題:比如從 A 分支處 B 分支,然後在 B 分支上開發後,又在 B 分支上出 C 分支,C 分支開發完成後,如果優先於 B 分支合併到 A 分支,那麼這時候其實就是把不屬於 C 分支的內容合併到 A 分支了,此時也可能會出現問題。

Rebase閃亮登場

首先我們看這個命令叫 rebase,根據英文語法,re開頭的單詞一般都表示“重新...”;那麼如果要 rebase,首先得知道 base 是什麼。我們可以簡單的理解為:在 A 分支的 commit1 處出的分支 B,那麼 B 就是 base A 分支的 commit1 產生的分支。

下面我們簡單瞭解下 rebase 的工作原理,假設我們現有 A, B 兩分支,當前在 B 分支上,當我們執行 git rebase A 時,git 做了什麼呢?

  1. git 會以 A 分支為基礎,然後將 B 分支上和 A 分支有差異的 commit 一個接一個的合併到 A 分支上。
  2. 在 rebase 過程中,由於 B 分支上的 commit 是重新合併到 A 分支上的,所以 rebase 後的 commit ID 是重新生成了,跟合併前的 commit ID 是不同的。

瞭解了工作原理之後,我們可以上面 merge 可能存在的問題用 rebase 怎麼解決。假設在本地分支 A 開發完成提交以後,push 時發現遠程分支已經領先,那麼這裡我們可以在本地分支執行 git pull --rebase,這條命令等同於 git fetch && git rebase,此時就不會產生那條合併的 commit 了。

Rebase 真的是萬金油嗎

正如我們上面提到的,任何事物都有兩面性,能夠剔除掉一些無實際意義的 commit,這是rebase 最明顯的一個特點,但是 rebase 要想完全替代 merge,我個人任務這也是十分不現實的事情。從上面提到的 rebase 的工作原理中提到了,rebase 會將目標分支上的 commit 一個接一個的合併到 base 分支上,這裡就有一個有趣的問題,在 B 分支上執行 git rebase A,假設此時 A 上有新的改動,而且 B 分支上也對相同位置做了改動,那麼就會產生衝突,由於 rebase 會每個 commit 單獨進行合併,那麼也就是說需要每個commit 分別解決衝突,加入 B 分支上有10個 commit 都改了這個地方,那麼就需要解決10次衝突了....想想有點可怕。

那麼對於 rebase 的使用場景,我個人的習慣是,在本地分支同步遠程分支時使用 rebase;在本地分支的 base 分支有改動的情況下,使用 rebase 進行同步分支。其餘情況,使用 merge 進行合併。

最後有一點需要特別注意,如果當前分支存在基於這個分支的子分支,那麼千萬不要對這個分支進行rebase,為什麼?我舉一個簡單的栗子!你出門買菜,回來發現家搬走了....所以為了避免這種情況,通常建議工作分支要基於鎖定的分支出,這樣就可以避免你的分支到時候找不到家了。

小結

Git 作為一個開發的基礎工具,使用方法應該是每一個開發者必備的技能。由於 Git 的功能過於強大,導致很多人的使用方法都不是一樣的,而且每個公司或者每個團隊都有可能在日常工作中形成自己特有的 Git 管理流程,這些都是根據自己團隊的特點和工作方式制定的,並沒有孰優孰劣之分,畢竟是工具,能夠有效的幫助完成自己的任務才是最重要的。

歡迎大家能夠交流自己的一些問題和經驗,持續學習,共同成長!


分享到:


相關文章: