如何理解git rebase?

趁你還年輕233發表於2018-12-02

如何理解git rebase?

在merge PR的過程中,rebase and merge會產生衝突,因此需要補充一下Git rebase的知識點。

Understanding Rebase (And Merge) in Git

merge 是Git中最簡單也是最常用的整合change的方法,但是這並不是唯一的一種方式。 Rebase是另外一種可選的但是略微高階的整合方式。

合併提交的case

通常情況下,一個由人類認真建立的commit,是一個有意義的單元:它僅僅包含相關的change並且每個commit都伴隨著一個comment。 有一種merge commit可以讓所有comments丟失:Git自動建立的commit,並且由Git填充所有的differ change。沒有語義,沒有主題。當然,這些獨立commits的內容是保留的。但是history分成了註釋的,分離的有意義的commit,由於這個原因,在一次merge commit中不會保留。 這就是為什麼有些人不喜歡merge,喜歡rebase的原因。

Rebase之美

與一籠統把commits塞到一個commit不同,一個rebase會保留原始的commits。 專案的歷史是一條單的,直線。沒有任何跡象表明它在某個時候拆分出一個分支來。

image
在一次rebase後,看起來就像development從來沒有被拆分成不同的分支。

我們來一步一步拆分一個rebase操作。方案很簡單:我們想用rebase整合branch-B到branch-A。

image
一個rebase之前的方案。

命令很簡單:

git rebase branch-B
複製程式碼

首先,線條開始分支後,Git將"undo"所有的branch-A上的commits(在共同的祖提交後)。當然,它不會丟棄它們,而是臨時將它們存了起來。

image

其次,它會應用我們想整合的來自branch-B的commits。此使,兩個分支是相同的。

image

最後,branch-A的新commits重新被應用,但是在一個新的位置,在branch-B的後面。(they are rebased)。 結果就是development在一條直線上開發。不是一個commit包含了所有的改變,而是讓原始commit結構保持原樣。

image

下面嘗試開BranchA,BranchB兩個分支,然後基於webstorm的Version Control,觀察git rebase操作會不會有上述的變化。

BranchA

image

BranchB

image

Rebase BranchA onto branchB

image
其實就是將branchB的母分支branchA進行了integrate changes,也就是把branchB的2次commit,放在共同的起點與branchA的新commit之間,或者也可以理解成將branchA的新commit,移動到了branchB的2次commits之後。

rebase的是誰,就修改的是誰 onto的是誰,誰就是被rebase的分支的新commits

其實,rebase只做了一件事:更新base branch!(重點!重點!重點!)

而想將誰的更新內容作為新的base branch的提交,就將作為topicBranch。

非常重要的命令。

git checkout baseBranch
git rebase topicBranch
複製程式碼

再說的通俗一點,其實就是:挑了一個branch,把它的特性拿過來,放在我的新特性之前。

Merging vs. Rebasing

看完上面這篇文章後,並沒有搞清楚rebase做了什麼操作,所以還是需要多讀一些文章。

  • 對於初學者來說,git rebase命令就像一個magic voodoo
  • merge和rebase都是用來從一個分支到另一個分支integrate changes的,只是方式不同

image

Merge Option

git checkout feature
git merge master
複製程式碼
git merge master feature
複製程式碼

會有一個'merge commit', 但是merge是非常安全的,不會像rebase有很多陷阱。 但是若master非常活躍,每次merge都會有要給'merge commit',會導致feature的commit history很髒。

image

Rebase Option

git checkout feature
git rebase master
複製程式碼
  • feature從master tip處開始合併master上的commits

  • 重寫project的history

    image

  • rebase後,project的history更加乾淨了。沒了多餘的'merge commit',並且成了一條線。

  • rebase 需要遵循Golden Rule of Rebasing,否則會導致災難性的合作workflow。

  • rebase 會丟失掉merge commit,導致看不到之後合併到feature的commit。

靈活一點的Rebasing

  • 選擇特定的commits移動到新分支,加一個i選項
  • fixup某一個提交
git checkout feature
git rebase -i master
複製程式碼
pick 33d5b7a Message for commit #1
pick 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3
複製程式碼
pick 33d5b7a Message for commit #1
fixup 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3
複製程式碼

image

Golden Rule of Rebasing

永遠不要在public 分支上使用git rebase!

image
每次使用git rebase前,問自己"有沒有人也正在基於這個branch寫程式碼?"若是的話,就老老實實用merge,不要嘗試rebase。 若有gitflow的經驗,其實就是當你開了一個feature/foo時,若同事也開了一個feature/bar,而且你們是同時基於develop checkout出來的分支,那麼當develop有hotfix merge進去時,若你想拉去最新的develop程式碼,就不能用git rebase,只能用merge,否則會導致同事的develop分支與我們的develop分支不同,而此時她想再與我們保持同步是很複雜的。

Force-Pushing

若想將rebased的master分支推到遠端倉庫,Git 將會阻止你,因為它與遠端的master分支衝突了。但是,你可以force push。

# 這個命令一定要小心使用
git push --force
複製程式碼
  • 只有100%確定自己在做什麼時再force,否則會讓團隊的人很困惑
  • 若是想將某個feature遠端分支徹底替換掉,可以這樣做。

下面嘗試開master,feture兩個分支,然後基於webstorm的Version Control,觀察git rebase操作會不會有上述的變化。

master

image

feature

image

git checkout feature
git rebase master
# resolve conflict1
git rebase --continue
# resolve confict2
git rebase --continue
複製程式碼

Rebase feature onto master

image

webstorm 的 Rebase Current onto selected什麼操作?

可以理解成下圖這樣。

Rebase feature onto master

image

image

Current在WebStorm中指右下角的branch,selected一般指的original branch。

rebase and merge 一個Pull request做了什麼操作?

image
相當於:

git checkout feature
git rebase master
複製程式碼

像下圖這樣:

image

image

相關文章