你根本不懂rebase-使用rebase打造可读的git graph
git graph 可讀指什么?
這里的可讀,主要指的是能夠通過看git graph了解每一次版本更迭,每一次hotfix的修改記錄.反映到分支上面,有兩個要求:
- 每個分支的歷史修改可讀(單個分支的層面)
- 每個分支的分叉合并可讀(多個分支的層面)
rebase是什么,它是更優雅的merge嗎?
rebase翻譯做變(re)基(base).
講rebase的文章經常會引用三張圖:
原本的兩個分支 通過merge的結果 通過rebase的結果用來說明git rebase和git merge的區別的時候確實是足夠了,但是 git rabase的用途并非是合并分支,它與merge根本不是同樣的性質.(注意,這里的說法是并非是,不是并非只是,因為雖然有時rebase替代了merge的工作,但其原理和性質完全不一樣.)
rebase還有以下幾種用處:
- git pull —-rebase處理同一分支上的沖突(如果你能理解其實這是git fetch&&git rebase兩個操作,并且理解遠程分支和本地分支的區分的話,那么其實他跟單純的rebase用法沒什么區別,但是因為其場景不一樣,所以單獨拆分出來講)
- git rebase -i修改commit記錄
實質上:
- merge是對目前分叉的兩條分支的合并
- rebase是對當前分支記錄基于任何commit節點(不限于當前分支上的節點)的變更.
rebase的base不能理解為分叉的基點,而是整個git庫中存在的所有commit節點:
- 在git pull —-rebase的時候,這個當前分支是本地分支,commit節點是遠程分支的head
- 在git rebase master的時候,這個當前分支是feature分支,commit節點是master分支的head
- 在git rebase -i的時候,這個當前分支就是當前工作分支,commit節點是在 -i后注明的commit
rebase是怎么工作的?
上面我們已經說到了:
rebase是對當前分支記錄基于任何commit節點(不限于當前分支上的節點)的變更.
怎么做到呢?我沒有深入研究它真的是如何實現的,以下步驟一定是不對的,但足夠讓你理解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更可讀.目的和原則我們都已經說過了,沒必要再重新說一遍.
多有謬誤之處,還望不吝賜教!
總結
以上是生活随笔為你收集整理的你根本不懂rebase-使用rebase打造可读的git graph的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 异形方向盘有隐患吗?汽车博主:既不方便也
- 下一篇: 判断两条链表是否交叉,若有交叉,返回交叉