本文主要講解下Git Rebase的基本概念用法、其內部原理以及咱們在真實項目中使用Git Rebase應該遵循的原則以及爲啥須要遵循這些原則。github
相信對於rebase確定不會陌生,就好像上圖描述的過程同樣,當你使用rebase命令的時候,即好像將你須要去rebase的分支拔下來而後從新插到另外一個分支上。官方對於rebase的描述爲:app
「git-rebase: Forward-port local commits to the updated upstream head」— git doc
翻譯一下,就是講你在某個分支上的全部提交記錄移花接木到另外一個分支上。這邊須要強調一個概念:reapply,使用rebase並非簡單地好像你用ctrl-x/ctrl-v進行剪切複製同樣,rebase會依次地將你所要操做的分支的全部提交應用到目標分支上。也就是說,實際上在執行rebase的時候,有兩個隱含的注意點:ui
在重放以前的提交的時候,Git會建立新的提交,也就是說即便你重放的提交與以前的如出一轍Git也會將之當作新的獨立的提交進行處理。spa
Git rebase並不會刪除老的提交,也就是說你在對某個分支執行了rebase操做以後,老的提交仍然會存放在.git文件夾的objects目錄下。若是你對於Git是如何存放你的提交不太瞭解的話能夠參考這篇文章:Understanding git for real by exploring the .git directory翻譯
基於以上表述,咱們能夠得出如下相對更準確的Git rebase的工做流程:指針
從上圖能夠看出,在對特徵分支進行rebase以後,其等效於建立了新的提交。而且老的提交也沒有被銷燬,只是簡單地不能再被訪問或者使用。在對於分支的章節咱們曾經說起,一個分支只是一個執行提交的指針。所以若是既沒有分支或者Tag指向某個提交,該提交將沒法再被訪問使用,可是該提交會一直存在於你的文件系統中,佔用着你的磁盤存儲。code
「No one shall rebase a shared branch」 — Everyone about rebaseblog
估計你也確定看過這個原則,不過可能表述不同罷了。本章節就是用實例的角度來探討下,爲啥不能再一個共享的分支上進行Git rebase操做。所謂共享的分支,便是指那些存在於遠端而且容許團隊中的其餘人進行Pull操做的分支。假設如今Bob和Anna在同一個項目組中工做,項目所屬的倉庫和分支大概是下圖這樣:開發
如今Bob爲了圖一時方便打破了原則,正巧這時Anna在特徵分支上進行了新的提交,此時的結構圖大概是這樣的:
當Bob打算推送本身的分支到遠端的時候,它收到了以下的警告:
Git嘗試着使用fast-forward來合併你的分支,具體的細節咱們會在其餘博客中進行討論,這邊只須要明白遠端的Git Server被Bob搞得一頭霧水,不知道應該如何去合併。此時Bob爲了推送他的本地的提交,只能選擇強行合併,即告訴遠端:不要再嘗試着合併我推送給你的和你已經有點提交,一切按照我推送過去的來。那麼Git會進行以下操做:
而後呢,當Anna也進行推送的時候,她會獲得以下的提醒:
這個消息很正常,沒啥特殊的,只是Git提醒Anna她本地的版本與遠程分支並不一致,在Anna提交以前,分支中的Commit序列是以下這樣的:
A--B--C--D' origin/feature // GitHub A--B--D--E feature // Anna
在進行Pull操做以後,Git會進行自動地合併操做,結果大概是這樣的:
這個第M個提交即表明着合併的提交,也就是Anna本地的分支與Github上的特徵分支最終合併的點,如今Anna解決了全部的合併衝突而且能夠Push她的代碼,在Bob進行Pull以後,每一個人的Git Commit結構爲:
到這裏,看到上面這個混亂的流線圖,相信你對於Rebase和所謂的黃金準則也有了更形象深刻的理解。這還只是僅有兩我的,一個特徵分支的項目由於誤用rebase產生的後果。若是你團隊中的每一個人都對公共分支進行rebase操做,那還不得一團亂麻。另外,相信你也注意到,在遠端的倉庫中存有大量的重複的Commit信息,這會大大浪費咱們的存儲空間。若是你還以爲這麼什麼,那咱們來假設下還有一哥們Emma,第三個開發人員,在他進行了本地Commit而且Push到遠端以後,倉庫變爲了: