git 多人在同一分支上迭代開發時,如何保證分支提交歷史保持線性

三國夢迴發表於2022-03-29

背景

最近我們組幾個同事都投入到了一個新專案,互相之間的功能耦合比較緊密,因此,是打算從master上新拉一個分支,可以理解為我們幾個人的開發分支,以develop代替。

一開始,我們是打算像svn那樣用的,幾個人就把這個新分支develop當做唯一的主幹分支,幾個人互相快速提交/拉取,回到了用svn的快樂日子。

不過,大家用svn也知道,經常呢,我們為了保證程式碼不丟,會經常性地往分支提交,即使某個功能寫了一半,一個功能,n次commit記錄,且和同事的commit交錯在一起;另外,我們提交的程式碼,有時候會導致同事那裡跑不起來。

簡而言之,就是commit有點碎;另外,可能阻塞其他同事。

我們組長提了另外一種思路,就是,每個人基於這個開發分支develop,再自己單獨拉取一個分支出來,如develop-zhangsan,develop-lisi。每個人在自己的單獨的分支上開發,開發了一個較為完整的功能後,再提一個pull request給develop,此時,可以對這個較完整的功能做程式碼review,review通過後,即合併到develop分支。但此時,怎麼才是最佳實踐呢,且能保證開發分支develop的提交歷史成為優雅的一條線呢?

這裡假設有張三、李四兩個人,基於gitlab、github、gitee等進行開發,最終,主要有以下幾個分支:

遠端 本地
origin/master master
origin/develop develop
origin/develop-zhangsan develop-zhangsan
origin/develop-lisi develop-lisi

實戰環境準備

我這邊已經準備好了實戰案例,已經把上面的幾個分支都拉好了。

https://gitee.com/ckl111/git-rebase-test

假設我先在遠端,把這幾個分支先建好,我是在gitee操作的。

目前,zhangsan、lisi分支,是基於develop拉出來的,所以最新提交都是一樣的。

模擬張三開發

大家看上圖,張三來了一頓操作,切到了自己的分支,改了點東西,做了一次提交,不過提交還沒推送到遠端自己的分支。

模擬李四開發

修改、推送

李四也是個猛人啊,上來一頓cv,commit、push一氣呵成。

遠端狀態

此時,可以看到,遠端分支裡,只有lisi這娃兒的分支狀態有變化。此時,按照標準流程,李四需要在遠端發起一個到develop的pull request。

發起pr

此時,是可以檢視這次pr的內容,包括提交內容,檔案修改差異。具體每個平臺不一樣,但是功能應該類似。

此時,假設經過程式碼review,認為沒有問題,那麼可以合併到develop去了。

合併後,develop的情況

可以看到,除了把lisi分支的commit拿過來了,還加了個表示本次合併的commit。

ok,李四的工作,第一階段就算結束了。

模擬張三拉取李四程式碼

張三一看,李四這小夥子太快了,cv666。假設張三就依賴李四程式碼,此時,應該要把李四程式碼拉下來。

其實,這裡有個操作上的問題,當前張三在自己的分支上,他現在需要做的是:拉取develop程式碼最新程式碼,然後將develop的程式碼合到自己這裡來。

這個步驟的話,其實有些工具做得比較好,我用的intelj idea就有相關功能。這一步如果工具不趁手的話,非常要命。因為我們可能開發到一半,要去切換到其他分支,結果本分支有程式碼沒提交,還得先提交或者stash,切過去到develop,pull最新程式碼。然後再切回來自己分支。

很累人。

這塊回頭我講講idea裡面的實戰操作,其他ide工具大家可以自行探索。

這次先只介紹命令列版本,我先用笨辦法,切過去,pull,再切回來的方式吧。

模擬張三合併/rebase李四程式碼

要保證develop的commit保持線性,這裡有個重點,我們要以rebase的方式去合併develop的程式碼,而不是merge的方式。

rebase呢,這裡簡單說下,

這裡就是rebase的大體流程圖,其實,我剛有個想法,最近拿起了以前的電視劇,新三國。裡面呂布不就換了幾位義父嗎,這裡的rebase,換的也是parent啊,感覺rebase也是相當神似。

當然了,忘了一點,進行rebase那些的commit,hashcode會發生變化,和以前不一樣了。

我們這邊實際操作,看看效果:

這裡主要幾個操作,

1 git rebase develop -------因為和lisi改了同一行,需要解決衝突
2 我這邊習慣用小烏龜git,解決衝突
3 git add .
4 git rebase --continue

形象一點,也就是前面那個圖,不過新的rebase後的commit的hash變了

模擬張三push程式碼到遠端,遠端發起pull request

push

pull req

遠端develop log檢視

可以看看,遠端的develop分支,log是非常好看的。

第二輪開始

可以看到,第一輪已經差不多結束了,張三李四各提交了一次。假設現在輪到李四了,李四發現張三有push程式碼,就準備拉下來,就像之前的張三一樣。

李四切換到develop,拉取最新develop程式碼,並rebase

然後,我們基於develop,進行rebase(也就是,以develop為base)。本來為了模擬效果,是應該先本地搞點提交,再rebase的,我搞忘了。不過不重要,過程和前面張三差不多。

李四修改程式碼、commit、push

李四遠端發起pull request

檢查遠端develop分支的commit 記錄

依然是漂亮的commit記錄。

張三在此期間,已經做了修改、commit、push

張三這期間,暫時不依賴李四程式碼,就自己commit、push了(為啥push,怕程式碼丟嘛,多個備份)

張三切換到develop、拉取最新develop、rebase

張三此時終於準備合併李四的程式碼。

省略了張三這次解決了衝突的過程,我依然用了小烏龜。

張三此時的log情況

張三,由於rebase,導致自己本地之前的那次commit,被rebase了。rebase後,hashcode也變了。

此時,張三的本地分支,和張三遠端分支之間,出現了分叉。

張三rebase後,面臨分叉,強行push,覆蓋遠端分支

強制push後,張三遠端分支的log

張三遠端發起pull request

遠端develop分支log,線性日誌

總結

兩輪實戰結束。大家學會了沒?

2點要點:

1、總是rebase的方式去合併develop分支

2、rebase的時候,就是會面臨分叉的情況,此時強制push遠端分支,讓遠端分支的log和本地一致。

相關文章