你根本不懂rebase-使用rebase打造可讀的git graph

git graph 可讀指什麼?

這裏的可讀,主要指的是可以經過看git graph瞭解每一次版本更迭,每一次hotfix的修改記錄.反映到分支上面,有兩個要求:git

  • 每一個分支的歷史修改可讀(單個分支的層面)
  • 每一個分支的分叉合併可讀(多個分支的層面)

rebase是什麼,它是更優雅的merge嗎?

rebase翻譯作變(re)基(base).安全

講rebase的文章常常會引用三張圖:數據結構

本來的兩個分支

經過merge的結果

經過rebase的結果

用來講明git rebase和git merge的區別的時候確實是足夠了,可是 git rabase的用途並不是是合併分支,它與merge根本不是一樣的性質.(注意,這裏的說法是並不是是,不是並不是只是,由於雖然有時rebase替代了merge的工做,但其原理和性質徹底不同.)fetch

rebase還有如下幾種用處:翻譯

  • git pull —-rebase處理同一分支上的衝突(若是你能理解其實這是git fetch&&git rebase兩個操做,而且理解遠程分支和本地分支的區分的話,那麼其實他跟單純的rebase用法沒什麼區別,可是由於其場景不同,因此單獨拆分出來說)
  • git rebase -i修改commit記錄

實質上:code

  • merge是對目前分叉的兩條分支的合併
  • rebase是對當前分支記錄基於任何commit節點(不限於當前分支上的節點)的變動.

rebase的base不能理解爲分叉的基點,而是整個git庫中存在的全部commit節點:cdn

  • git pull —-rebase的時候,這個當前分支是本地分支,commit節點是遠程分支的head
  • git rebase master的時候,這個當前分支是feature分支,commit節點是master分支的head
  • git rebase -i的時候,這個當前分支就是當前工做分支,commit節點是在 -i後註明的commit

rebase是怎麼工做的?

上面咱們已經說到了:對象

rebase是對當前分支記錄基於任何commit節點(不限於當前分支上的節點)的變動.blog

怎麼作到呢?我沒有深刻研究它真的是如何實現的,如下步驟必定是不對的,但足夠讓你理解rebase幹了什麼.工作流

咱們標註出了兩個重點,當前分支commit節點.

  • 當前分支branch-A從頭至尾列出來,從數據結構的角度來講這是一個鏈表
  • commit節點所在的分支branch-B從頭至尾列出來,一樣是一個鏈表
  • 找到這兩個鏈表最近相同的節點n
  • 把A在n以後的全部節點拆下來構成L
  • 把B在n以後的全部節點中存在的diff信息都彙總起來構成d
  • 對於L中的每個節點,把他的diff信息拿出來,看看d中有沒有衝突,若是有無法自動處理的衝突拋出錯誤,等待用戶本身處理
  • 可選地,對於rebase -i來講,還能夠一次取多個節點或者按照不一樣順序取,你有更大的處理自由
  • 沒衝突和處理完衝突的節點,改一個hash放到branch-B的commit節點以後 你能夠把以前咱們說到的三種rebase用處套在以上步驟看看,是否可以理解.

rebase很危險對嗎?

對,很危險.

不過就像小馬過河同樣,光聽別人說是沒用的,咱們須要明白爲何有人說危險,有人說不危險.我看到不少文章說rebase有問題,但他們的說法其實並不讓人信服,不少時候只是他們不會用.

不少人據說過一個golden rule,在文末有連接,可是不多有人會明白真正的緣由.讓咱們一層層地剖析:

  • 其餘人git push的時候會對比較本地分支和遠程分支的區別,把不一樣的地方推上去
  • 若是遠程分支被修改了,那麼其餘人的本地分支和遠程分支就會出現分叉(另外還可能形成其餘人以前已經推送的工做被覆蓋)
  • 當出現分叉的時候,意味着其餘人須要處理衝突,也就是說,你對於遠程歷史記錄的修改使得衝突擴散到了其餘人身上
  • 因此咱們儘可能不能修改遠程分支,不能把別人fetch回去的改掉,由於他們的工做就是基於fetch回去的分支開展的(往前推動是必須的,其實也修改了遠程分支,因此纔會merge產生衝突,可是這個衝突是沒法避免的)
  • 針對上面說的這一條,git也作了限制,若是你觸犯了上面的原則,會在push的時候被阻擋,可是經過加一個-f能夠強推

實際上不止rebase這樣,任何修改遠程分支歷史的操做都會形成衝突,而且這個衝突須要全部人都解決一遍.

可是分析仍是太長了,記不住怎麼辦?

只須要記住-f,只要你不使用-f,那麼就是安全的.

不過僅是安全,並不能保證優雅,若是要使git graph可讀,那你還得多想一想:

  • 怎麼讓本身的commit歷史清晰(每一個commit反應了一個單位的工做,先後順序合理)
  • 怎麼讓每次hotfix和feature所作的工做和順序清晰

rebase如何讓git graph可讀?

咱們仍是說回以前提到的三個用法:

git rebase master

在把分支合併回master的時候,用git rebase master代替git merge master.(注意,只在合併以前使用,不然多人協做會遇到衝突)

這樣的好處有兩個:

  • log裏不會出現一個Merge branch 'master' into hotfix/xxx的節點
  • master分支上在此次merge以前已經被提交的上一次工做和這一次工做的順序更清晰,由於rebase會讓此次feature的分叉節點改到上一次工做後.對於master分支來講,咱們並不關心checkout新的feature的順序,咱們更關心merge新的feature的順序.

好比這裏,使用merge master致使的紫色的分叉在提交以前與master多了一次鏈接,並且主線上在紫色分叉合併以前還經歷了一次合併,這個時間順序並不清晰.

那麼在master分支上合併也用rebase嗎?不是.由於咱們須要master上的分叉讓咱們更明白master上的改變(因此使用-no-ff).實際上,無論你採用任何git flow模型,我都建議你對不過重要的分支合併採用rebase,對重要的分支合併採用merge.這樣會讓主幹的更改更清晰,而分支不會擴散地太遠.

git pull —-rebase

多人在同一分支上工做的時候(包含master分支和多人合做的feature等分支),在git pull的時候會遇到衝突,git pull的默認行爲是git fetch&git merge,merge的對象是遠程分支和本地分支.

它的好處基本上與上一條無異,還多了一條:

  • 使用merge行爲的pull會將其餘人的工做做爲外來的分叉,從而在graph上產生一個新的分叉, 而且其餘人這一段時間所作的全部的工做都會在graph上被擡升出去,若是這段時間其餘人作的工做不少,graph的主線會變得喪失了主線的意義(由於它太單薄了,不少工做根本沒反應上來).

好比這裏,原本左數第二條玫紅色的纔是主線,由於不規範地在master上直接提交了一次commit而且採用merge方式的pull作了合併致使主線被擡升到了外層.而此次不規範的commit卻成了主線.

git rebase -i

使用這條命令能夠修改分支的記錄,好比以爲以前的commit修改內容不夠單元化,像是修改了文案1爲文案2,修改了文案2爲文案3,這種記錄對於master分支來講是不必關注的信息,最好經過git commit --amend或者rebase的方式修改掉.

不過並不推薦在提交以前手動作一次整個分支的squash,若是是rebase方式合併的話,也許更有意義.工蜂(騰訊內部的code平臺)提供了merge request的標題和內容功能,因此不必作squash,徹底能夠沒必要太聚合,以便反應真實的信息.

爲了避免影響別人,只用它修改未push的commit,或者若是一條分支只有一我的,你也能夠修改已經push的commit.

對於這條命令的更多功能,能夠再去查閱其餘文章.

可讀的graph應該長什麼樣?

先說一個原則,看graph要先看主線,主線要清晰,再看分叉上信息,這與咱們的工做流程是一致的.

綠色的hotfix或者feature分支每次不是隻容許提交一次commit,只是這一段都是一些小更改.

這看起來有點好笑,一點都不高級.說了這麼多作了這麼多難道只是爲了獲得這麼簡單的圖?

沒錯,爲了讓東西變簡單,原本就要付出不少代價,咱們所作的就是要讓東西變簡單,好比努力工做是爲了讓賺錢變簡單,努力提高是爲了讓工做變簡單.讓事情變複雜只會讓事情不可控.

固然具體如何仍是要取決於你採用的git flow,可是原則很簡單:

  • 每一個分叉的子分叉儘可能是一個串聯一個,內部儘可能不要再有本身的提交.

爲何我認爲這樣的git graph可讀性好,由於它把咱們的工做也拍平了,不在意每一個工做的開始時間和持續時間,只關心這個工做的完成時間.

假如一個項目需求1是1月1號啓動,2月1號上線,需求2是1月20號啓動,2月10號上線.1月10號修了一個bug,2月3號修了一個bug. 聽起來是否是很繞?

若是你的git graph顯示的也是這樣的信息,可讀性必定很差,因此咱們要作的git graph應該反應的是以下信息:

  • 1月10號修補bug
  • 2月1號上線需求1
  • 2月3號修補bug
  • 2月10號上線需求2

rebase的缺點是什麼?

(這裏並不討論rebase可能帶來的衝突問題,有不少文章都會講,上面也已經提到了rebase的危險性,這裏只討論rebase對於git graph的缺點.實際上,衝突只是rebase不恰當使用致使的問題,而非rebase自己的問題.)

固然也有人會說,工做的開始時間也很重要呀,由於它反映了當時工做開展的基礎條件.對,這是rebase master的弊端.他讓記錄清晰,也讓記錄丟失了一些信息.記錄的加工讓可讀性變得更好,也讓信息量變少了.

git rebase 讓git graph發生了變化,每次分叉的檢出和併入之間不會再有任何節點.(由於合併到master採起的是merge行爲.不然根本沒有分叉)

也就是這種狀況不會再出現.由於每次老是rebase master,把本身的起點擡了上去.git rebase實際上讓檢出信息沒有意義,換取了主分支分叉的清晰.

若是rebase沒有缺點,那麼也就沒有爭議.是否使用rebase也要看真實的需求是什麼.

這篇文章要幹什麼?

經過rebase讓git graph更可讀.目的和原則咱們都已經說過了,不必再從新說一遍.

多有謬誤之處,還望不吝賜教!

相關文章
相關標籤/搜索