前言
對于專案版本管理,你是否存在這樣的痛點:專案分支多而雜不好管理,git log界面commit資訊錯亂復雜無規范,版本回退不知道選擇什么版本合適……,
專案版本管理的最佳實踐系列,筆者將以兩篇文章的形式展開介紹(即基礎篇與進階篇),本文為gitflow版本管理的最佳實踐-基礎篇,基礎篇主要介紹git應用于生產的基本流程與怎么使用gitflow管理你的專案版本線(適用于敏捷迭代的專案管理場景下),進階篇 將著重介紹gitflow+jenkins+docker+DevOps+敏捷Scrum 完成專案持續構建與持續交付(CI/CD),閱讀本文需要有一定git基礎,基礎知識則不在本文展開,善用網上沖浪工具便可學習到許多Git的基礎知識,實際上,本文介紹的并不是純粹的gitflow,而是結合實際生產對gitflow的改造與最佳實踐,
Git的基本術語與簡寫
| 術語 | 解釋 |
|---|---|
| PR | 即pull request,拉取請求,請求git代碼管理員將你的代碼合并到倉庫的分支中,一般的PR由標題部分,描述部分與代碼部分組成, |
| code review | 在PR程序中代碼管理員對你提交的代碼進行代碼審查,即你的代碼是否符合規范、是否存在風格問題、安全問題等,對你代碼進行cr的同學并不一定是代碼管理員,成熟的敏捷團隊,每一個成員都是code owner,都可以對pr進行審閱, |
| squash | PR程序中,會將你的所有commit合并(榨取)成一個commit并提交到目標分支中,目的是減少冗余提交、規范主分支提交資訊(實際上是rebase的一種操作), |
| LGTM | look good to me(看起來很吊),一般存在于PR評論中,即對pr的內容沒有問題,同意合并到版本庫, |
一、分支規約
在我們的最佳實踐中,遠程版本庫永遠只存在三條長期且相互獨立的分支,他們分別為develop、release與master,三條分支對應三個環境,分別為開發環境(集成開發環境)、測驗環境(預發環境)與生產環境,三個分支分別都上權限,不可直接對其進行push與commit操作,即所有的修改均通過PR進行,以保證分支對應環境的安全與穩定,本地環境對應的遠程分支均會在PR通過之后,自動進行洗掉,以保證版本線的簡單,
| 環境 | 分支名 |
|---|---|
| 開發環境 | origin/develop |
| 測驗環境(預發環境) | origin/release |
| 生產環境 | origin/master |
| 本地環境功能分支 | develop_xxx (xxx為具體的開發成員或具體的功能描述, origin/develop_xxx,即feature分支下沉到本地,生命周期短,只存在于pr程序), |
二、版本號規約
在正式介紹gitflow之前,我們需要對版本號進行規范,方便接下來的行文展開,
在生產中,我們常用的版本號為三位數版本號(偶爾帶四位熱修復號),其構成如下:
V主版本號.次版本號.功能號(.${熱修復版本號}).環境
eg:V1.0.0.1.RELEASE、V1.1.0.DEVELOP、V1.0.0,(版本號并不以十進制,而是按照迭代規劃推送)
2.1 主版本號(首位版本號)
主版本號,也叫首位版本號、頂位版本號,即V后第一個版本號,主版本號一般代表專案的期數與產品方向,除非專案合同改變、大規模api不兼容、產品方向改變、底層架構升級等情況外不輕易更新,
另外,專案未正式發布、未正式范訓、未正式上線,則首位版本號為0,一期發布,則為V1,
2.2 次版本號(迭代號)
次版本號,也叫迭代號,一般代表某個迭代發布的功能集合(一個迭代發布會包含若干個功能更新),
如V1.1.0:第一期專案第一迭代發布版本、V1.2.0:第一期第二迭代發布版本,
2.3 功能號(PR號)
一般來說,提交到專案分支內的代碼均需要經過PR,而為了保證單個PR的簡潔性與純粹性,建議一個PR描述一個功能,因此第三位數的版本號也叫做PR號或功能號,用來描述單個提交到主分支內的功能或代碼修改,
如V0.0.1:第一迭代的第一個提交、V0.0.98:第一迭代的第98個PR,
2.4 熱修復號
四位數版本號是可選版本號,為熱修復版本號(也叫老爺保號hh),常規迭代與develop分支下并不會出現,而常出現在測驗環境對應的release分支與生產環境對應的master分支(develop分支對應的開發環境出現bug直接提交pr修復并在原來的版本號上+1便可),這個版本號常用于線上熱修復,測驗環境(預發環境)的熱修復,
值得注意的是,四位數版本號經過線上熱修復之后,要同步到本地develop環境的情況下,應當在develop分支下的三位數版本號上加一,
如:master的熱修復號為V1.0.0.4,develop分支當前版本為V1.1.8.DEVELOP,那么這個修復要同步回develop分支保證bug不重現,那么在develop上面的版本則為V1.1.9.DEVELOP
2.5 環境號
因為在git中的tag名稱是唯一的,那么在develop分支下出現了V1.0.0的tag,那么在release和master下便不可以再打一個tag叫V1.0.0,因此出現環境號來對分支版本進行區分(生產環境不加環境號),
| 環境 | 環境名 | 版本號(示例) |
|---|---|---|
| 開發環境 | DEVELOP | V1.0.0.DEVELOP |
| 測驗環境(預發環境) | RELEASE | V1.0.0.RELEASE |
| 生產環境 | MASTER | V1.0.0 |
三、Gitflow的最佳實踐
3.1 總體流程圖

3.2 最佳實踐舉例
這里要搬出兩位同學進行接下來的講解,他們是【弓行】同學與【阿康】同學,
3.2.1 遠程主干分支創建
版本的最開始(指V0.0.0),代碼管理員會初始化遠程倉庫,并基于master的初始版本創建三條分支,他們是:
origin/master(對應生產),origin/release(對應測驗環境),origin/develop(對應開發環境) 并為這三條分支設定保護策略,三條分支均不允許直接的commit與push修改,

代碼管理員將三個初始版本打上相應的TAG:(V0.0.0.DEVELOP、V0.0.0.RELEASE與V0.0.0),

3.2.2 本地分支創建
完成迭代計劃會議(迭代版本號為V0.1.0)之后,弓行與阿康他們分別認領了兩個任務:【開發功能】弓行,【開發功能2】阿康,
此時,弓行與阿康會將遠程倉庫克隆下來,并基于origin/develop 創建本地develop_gx分支與develop_kang分支,

3.2.3 創建PR
兩人認領任務后進行同步開發,一段時間后,弓行率先完成【開發功能1】的作業,因此他需要將當前開發版本提交到開發環境中進行自測與前后端聯調,但此時【origin/develop】是被保護的狀態無法被直接提交,因此,弓行需要對當前的開發的版本進行PR申請,即創建拉取請求,請求代碼管理員對代碼進行code review,通過后進行合并,
此處涉及的步驟大致如下:
1、push當前本地分支到origin,得到origin/develop_gx,
2、創建PR:即:origin/develop_gx 合并到 origin/develop 的拉取請求
3、等待代碼管理員(或小組內同學)進行code review,若需要修改,則直接在pr中提出注釋,作者修改后直接push到遠程分支中,繼續等待代碼管理員進行code review,
4、通過后,將當前commit list以squash的形式合并到origin/develop中,得到V0.0.1.DEVELOP 的commit
5、最后選擇洗掉origin/develop_gx的遠程分支

此時,弓行同學完成了第一個功能的開發,并在【origin/develop】分支上對自己的pr commit 進行tag操作:將此commit記錄為【V0.0.1.DEVELOP】
3.2.4 合并沖突提交版本
? 不久后,阿康同學也完成了【開發功能2】的開發,他也需要將代碼提交到origin/develop分支進行測驗與聯調,但此時,origin/develop已經與他的基版本不一樣了(基版本為V0.0.0.DEVELOP,遠程版本為V0.0.1.DEVELOP,領先一個版本)如果直接創建PR,可能因為代碼沖突的問題無法完成版本合并,如下圖,

此時阿康需要將origin/develop版本拉取到本地,并執行以下操作(推薦直接使用ide自帶的git工具,會方便不少)
//檢查遠程倉庫是否有新版本
git fetch origin
//發現新版本,需要拉取到本地解決沖突后進行代碼合并
//暫存本地修改
git stash
//拉取遠程版本
git pull origin/develop
//取出本地修改
git unstash
//手工解決沖突(推薦直接使用idea)
//提交修改
git commit -m'1、解決沖突合并版本'
使用ide自帶的沖突解決工具則如下圖

提交修改后(注意一定要和沖突代碼的作者商量代碼的變更),便可以創建PR,等待團隊內同學進行code review,團隊成員通過之后,阿康的修改便可以成功被合并到origin/develop中進行聯調與測驗了,阿康此時需要將改commit打上tag【V0.0.2.DEVELOP】,如下圖:

至此,V0.1.0所規劃的開發作業全部完成,
3.2.5 測驗環境版本發布
完成V0.1.0版本開發作業后,弓行同學認領了一個新任務:【V0.1.0版本提測】,正在其他進行其他功能開發作業的弓行同學此時需要將本地代碼stash起來,并將origin/develop分支的代碼與本地代碼進行合并(即git pull origin develop操作),并進行代碼沖突的解決作業,
因為要將代碼發布到origin/release分支進行版本提測,所以弓行同學需要同時將origin/release上的代碼與本地代碼進行合并操作(即git pull origin release操作) 并進行代碼沖突的解決作業,

完成git pull origin develop 與git pull origin release 之后,本地會形成一個新的commit版本,弓行同學需要將此commit版本通過pr的方式合并到 origin/release 上,方可完成release分支的測驗版本發布作業,因此弓行同學需要重復 3.2.3 步驟的PR創建程序,并通過release分支的分支管理員審批后,方可將版本發布到測驗環境,

3.2.6 版本標記
將commit通過pr的形式提交到release后,接下來就是對版本進行標記的程序,因為此release已經完成了版本的開發作業,因此,當前版本在release分支上會被標記為【V0.1.0.RELEASE】,又因為在develop分支上,V0.0.2.DEVELOP版本對應著release的V0.1.0.RELEASE版本,針對origin/develop的分支上的該commit,會被打上第二個tag:【V0.1.0.DEVELOP】,

而后,對于develop分支的tag處理,將會直接從V0.1.0.DEVELOP繼續往下走(如V0.1.1.DEVELOP等)
3.2.7 熱修復
origin/release分支對應著測驗環境,對于某些情況而言,測驗環境相當于專案的beta版本,有可能直接面對客戶,
那么版本提測之后,測驗同學針對該【V0.1.0.RELEASE】版本進行各種測驗后發現當前版本存在BUG,那么開發的同學就要針對改bug進行熱修復,
假設現在在測驗環境出現一個BUG,該BUG的修復作業依舊由弓行同學認領解決,那么此時弓行同學就需要將手頭上的開發作業暫停(git stash),然后拉取最新版本的origin/release分支到本地,然后進行bug修復作業,完成修復后,提交本地代碼到 origin/release_hotfix_gx 分支,對該分支進行PR操作,由release管理員進行code review并合并到release中,并將該修復版本記錄為【V.0.1.0.1.RELEASE】,

當然了,因為分支commit存在映射關系,出現在V0.1.0.RELEASE上的BUG,也一定會出現在V0.1.0.DEVELOP,那么此時修復了測驗環境的版本仍不夠,弓行需要將該修復合并到origin/develop上,因此弓行同學需要將新發的版本【V0.1.0.1.RELEASE】拉取到本地,然后對origin/develop進行版本提交作業,形成【V.0.1.1.DEVELOP】

至此完成熱修復的程序(master的熱修復也是同理,不過是將修復版本根據實際情況合并到release和develop上的不同罷了),
3.2.8 生產發布
完成release版本的提測作業、BUG修復作業后,弓行同學需要將release分支的版本發布到master上,完成生產環境版本的發布,實際上這個程序也與 3.2.5 并無太大差異,同學們可以結合自己實際情況,在這一步增加團隊code review、checklist檢查,發布風險控制等操作,對生產發布進行安全保障,

在完成origin/master的發布作業后,將master的tag更新到 V0.1.0 便完成了整個迭代的發布作業,
細心的同學讀到這里可能已經發現了,origin/develop、origin/release、origin/master 這三條分支在整個程序中都互相獨立,互不影響,因此本作業流程屬于三獨立分支模式的gitflow,同學們若為減少流程,release分支可優化掉,直接在develop分支上進行測驗,(也符合測驗驅動開發)
四、 雙周迭代制與gitflow
4.1 敏捷的雙周迭代制

以上圖為例,一個敏捷團隊中有三種垂直的職能角色:開發、測驗、與Scrum master(專案),我們假定當前迭代為N,下個迭代為N+1,上個迭代為N-1,
雙周迭代制,即一個沖刺迭代設定為兩周(或若干周),在這兩周中的第一周,這幾位垂直職能角色可以如下分工:
· 開發:進行N迭代 (當前迭代) 的開發與N-1版本 (上個版本) 的hotfix作業,并在每周五進行統一提測;
· 測驗:進行N迭代 (當前迭代) 的develop環境測驗與N-1迭代 (上個迭代) 的 release 環境測驗,在每周五前完成N-1版本的測驗作業;
· 專案:進行N+1 (下個迭代) 的迭代規劃作業與上兩個迭代(N-2)的迭代發布作業
如此一來,N-1版本,N版本,N+1版本便可實作交錯進行,有條不紊(需求源源不斷地來hhh)當然了,迭代開發時間與測驗時間可以適當變動(如 開發:測驗 = 6:4 或7:3),
采用雙周迭代的好處在于:
· 開發同學有充足和彈性的時間進行迭代的開發作業與bug修復作業與需求理解
· 測驗同學有充足的時間進行測驗作業以保證專案質量 (在develop環境上一個功能測一個功能,并在release環境可以完成充分的功能測驗)
· 專案有更多時間去規劃專案的迭代與分解具體需求做更完善的設計(瘋狂規劃迭代),
試想一個場景:開發在迭代最后一天完成開發作業,測驗只有最后2小時進行測驗便令人十分抓狂,
4.2 雙周迭代結合gitflow的最佳實踐
基于雙周迭代制的gitflow版本管理,即在迭代中:
· 開發在origin/develop上進行開發,進行 3.2.4步驟的開發作業,與上個release版本的hotfix作業;
· 測驗緊跟開發進行develop分支的測驗與上個release版本的測驗;
· 第一周結束,統一發版本到origin/release,測驗在第二周開始當前版本release環境的功能測驗;
· 第三周的周一,專案進行版本發版作業(即發布到origin/master)
五、FAQ
Q1 : 微服務架構下,每個專案獨立一個版本庫怎么做到版本號統一,是每個微服務單獨編制版本號還是全域統一版本號?
A1 : 對于微服務架構下(或者分布式架構專案)的每個微服務獨立版本庫的情況,建議全域編制版本號,即同一個發布視窗,對所有的目標發布分支與的二位數版本號進行編制(即全域統一迭代號或者產品號),對于沒有更新的微服務,可直接在原release的commit上進行Tag發布,
Q2 : 三分支版本線相對獨立,對于版本合并比較痛苦,
A2 : 這個問題是切實存在的,建議固定發布人,在本地分支保留release分支、master分支與develop分支的合并記錄,防止沖突過多,
Q3 : 對于目標發布的功能,若功能在發布前存在風險,則無法下有風險的分支,
A3 : 問題切實存在,可以配合開關配置做發布,
Q4 : 專案處于快速開發階段,大家一直往develop分支上面提PR,但是沒有人做code review,
A4 : 可以嘗試PR標題帶上tag資訊作為commit title,即:V0.0.1 xxx功能開發 V0.0.2 XXX功能開發,這樣一來,就相當于做了資源鎖,大家都想往develop上面提pr,但是上一個人把三位數版本號占用了,那么就需要有人把這個pr處理掉,自己才能使用下一個版本號,直到團隊code review習慣成熟,如下圖(develop的版本線是不是很清晰),

Q5 : 允許origin/develop、origin/release與origin/master三條分支之間互相合并嗎?
A5 : 不允許,只能通過PR形式進行分支版本合并
Q6 : 使用此種gitflow后,三個分支的版本線會是什么樣子的?
A6 : 如下圖,無論是develop分支還是release分支與master分支,分支永遠只有一條直線,不會有分支之間進行合并的情況,所以顯得版本線十分干凈整潔,

Q7 : 好像整個程序都沒有看到feature分支
A7 : 是的,在此種版本管理中,feature分支其實已經下沉到了每個人的本地版本庫中,不直接在origin庫中體現,
Q8 : 遠程feature分支可以不洗掉嗎?
A8 : 為了保證git log干凈,建議個人分支合并到develop分支后便執行洗掉,但不洗掉遠程feature也是可以的,可以嘗試使用同一個feature合并到release_${version}中,然后執行release_${version}->release的PR,
以上便是專案版本管理的最佳實踐:gitflow生產實踐篇的所有內容,歡迎在下方評論區討論與提出改進意見!
另外筆者在公眾號內對后端技術堆疊知識做了系統分類,并且在持續更新內容:
- JDK底層原始碼
- Spring生態
- 分布式高可用
- 多執行緒高并發
- 性能調優
- 資料結構與演算法
- 云部署與運維
長按識別下方二維碼,關注公眾號:奇客時間,回復:面試寶典,領取收錄的2020年的美團,阿里,騰訊,滴滴等互聯網公司各個業務部們面試題,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/175721.html
標籤:其他
下一篇:學習第54天
