前端小微團隊的Gitlab實踐
- 2020 年 3 月 9 日
- 筆記
疫情期間我感覺整個人懶散了不少,慢慢有意識要振作起來了,恢復到正常的節奏。最近團隊程式碼庫從Gerrit遷移到了Gitlab,為了讓前端團隊日常開發工作有條不紊,高效運轉,開發歷史可追溯,我也查閱和學習了不少資料。參考業界主流的Git工作流,結合公司業務特質,我也梳理了一套適合自己團隊的Git工作流,在這裡做下分享。
分支管理
首先要說的是分支管理,分支管理是git工作流的基礎,好的分支設計有助於規範開發流程,也是CI/CD的基礎。
分支策略
業界主流的git工作流,一般會分為develop, release, master, hotfix/xxx, feature/xxx等分支。各個分支各司其職,貫穿了整個開發,測試,部署流程。我這裡也基於主流的分支策略做了一些訂製,下面用一張表格簡單概括下:
| 分支名 | 分支定位 | 描述 | 許可權控制 |
|---|---|---|---|
| develop | 開發分支 | 不可以在develop分支push程式碼,應新建feature/xxx進行需求開發。迭代功能開發完成後的程式碼都會merge到develop分支。 | Develper不可直接push,可發起merge request |
| feature/xxx | 特性分支 | 針對每一項需求,新建feature分支,如feature/user_login,用於開發用戶登錄功能。 | Develper可直接push |
| release | 提測分支 | 由develop分支合入release分支。ps: 應配置此分支觸發CI/CD,部署至測試環境。 | Maintainer可發起merge request |
| bug/xxx | 缺陷分支 | 提測後發現的bug,應基於develop分支創建bug/xxx分支修復缺陷,修改完畢後應合入develop分支等待回歸測試。 | |
| master | 發布分支 | master應處於隨時可發布的狀態,用於對外發布正式版本。ps: 應配置此分支觸發CI/CD,部署至生產環境。 | Maintainer可發起merge request |
| hotfix/xxx | 熱修復分支 | 處理線上最新版本出現的bug | Develper可直接push |
| fix/xxx | 舊版本修復分支 | 處理線上舊版本的bug | Develper可直接push |
一般來說,develop, release, master分支是必備的。而feature/xxx, bug/xxx, hotfix/xxx, fix/xxx等分支純屬一種語義化的分支命名,如果要簡單粗暴一點,這些分支可以不分類,都命名為issue/issue號,比如issue/1,但是要在issue中說明具體問題和待辦事項,保證開發工作可追溯。
保護分支
利用Protected Branches,我們可以防止開發人員錯誤地將程式碼push到某些分支。對於普通開發人員,我們僅對develop分支提供merge許可權。

具體操作案例請前往下面的實戰案例一節查看。
issue驅動工作
我們團隊採用的敏捷開發協作平台是騰訊的TAPD,日常迭代需求,缺陷等都會在TAPD上記錄。為了讓Gitlab程式碼庫能與迭代日常事務關聯上,我決定用Gitlab issues來做記錄,方便追溯問題。
里程碑
里程碑Milestone可以認為是一個階段性的目標,比如是一輪迭代計劃。里程碑可以設定時間範圍,用來約束和提醒開發人員。

里程碑可以拆解為N個issue,新建issue時可以關聯里程碑。比如這輪迭代一共5個需求,那麼就可以新建5個issue。在約定的時間範圍內,如果一個里程碑關聯的所有issue都Closed掉了,就意味著目標順利達成。

標籤
Gitlab提供了label來標識和分類issue,我覺得這是一個非常好的功能。我這裡列舉了幾種label,用來標識issue的分類和緊急程度。

issue分類
所有的開發工作應該通過issue記錄,包括但不限於需求,缺陷,開發自測試,用戶體驗等範疇。
需求&缺陷
這裡大概又分為兩種情況,一種是TAPD記錄在案的需求和缺陷,另一種是與產品或測試人員口頭溝通時傳達的簡單需求或缺陷(小公司會有這種情況...)。
對於TAPD記錄的需求和缺陷,創建issue時應附上鏈接,方便查閱(上文中已有提到)。
對於口頭溝通的需求和缺陷,我定了個規則,要求提出人本人在Gitlab上創建issue,並將需求或缺陷簡單描述清楚,否則口頭溝通的開發工作我不接(也是為了避免事後扯皮)。
ps:其實要求產品或者測試提issue,還不如上Tapd記錄。我定這麼個規則,其實就是借Gitlab找個說辭,杜絕口頭類需求或缺陷,哈哈。
開發自測試
開發者自己發現了系統缺陷或問題,此時應該通過issue記錄問題,並創建相應分支修改程式碼。

實戰案例
我前面也說了,我的原則是issue驅動開發工作。
下面用幾個例子來簡單說明基本的開發流程。小公司里整個流程比較簡單,沒有複雜的集成測試,多輪驗收測試,灰度測試等。我甚至連單元測試都沒做(捂臉...)。
公共庫和公共組件其實是很有必要做單元測試的,這裡立個flag,後面一定補上單元測試。
需求開發
feature/1,一個特性分支,對應issue 1
創建需求
正常的需求當然來源於產品經理等需求提出方,由於是通過示例說明,這裡我自己在TAPD上模擬著寫一個需求。

創建issue
創建Gitlab issue,鏈接到TAPD中的相關需求。


創建分支&功能開發
基於develop分支創建feature分支進行功能開發(要保證本地git倉庫當前處於develop分支,且與遠程倉庫develop分支同步)。
git checkout -b feature/1 或者直接以遠程倉庫的develop分支為基礎創建分支。
git checkout -b feature/1 origin/develop ps:我這裡用的feature/1作為分支名,其實這裡的1是用的issue號,並沒有用諸如feature/login_verify之類的名字,是因為我覺得用issue號可以更方便地找到對應的issue,更容易追蹤程式碼。
接著我們開始開發新功能......

commit & push
完成功能開發後,我們需要提交程式碼並同步到遠程倉庫。
PS D:projectsgitlabproject_xxx> git add . PS D:projectsgitlabproject_xxx> git cz [email protected], [email protected] ? Select the type of change that you're committing: feat: A new feature ? What is the scope of this change (e.g. component or file name): (press enter to skip) ? Write a short, imperative tense description of the change (max 94 chars): (9) 登錄校驗功能 ? Provide a longer description of the change: (press enter to skip) ? Are there any breaking changes? No ? Does this change affect any open issues? Yes ? If issues are closed, the commit requires a body. Please enter a longer description of the commit itself: - ? Add issue references (e.g. "fix #123", "re #123".): fix #1 git push origin HEAD git cz是利用了commitizen來替代git commit。詳情請點擊前端自動化部署的深度實踐深入了解。
fix #1用於關閉issue 1。
git push origin HEAD則代表推送到遠程倉庫同名分支。
創建Merge Request
開發人員發起Merge Request,請求將自己開發的功能特性合入develop分支。

接著Maintainer需要Review程式碼,確認無誤後同意Merge。然後這部分程式碼就在遠程Git倉庫入庫了,其他開發同學拉取develop分支就能看到了。
版本提測
issue/2,處理更新日誌,版本號等內容,對應issue 2
每個團隊的開發節奏都不同,有的團隊會每日持續集成發版本提測,有的可能兩天一次,這個就不深入討論了......
那麼當我們準備提測時,應該怎麼做呢?
通過上節的了解,我們已經知道,迭代內的功能需求都會通過feature/xxx分支合入到develop分支。
提測前,一般來說,還是要更新下CHANGELOG.md和package.json的版本號,可以由Maintainer或其他負責該項事務的同學執行。
主要是執行npm version major/minor/patch -m 'something done',具體操作可以參考前端自動化部署的深度實踐一文。
git checkout -b issue/2 origin/develop npm version minor -m '迭代1第一次提測' git push origin HEAD 然後發起merge request合入develop分支 此時,應以最新的develop分支程式碼在開發環境跑一遍功能,保證版本自測通過。
提測時,由Maintainer發起Merge Request,將develop分支程式碼合入release分支,此時自動觸發Gitlab CI/CD,自動構建並發布至測試環境。
版本提測後,各責任人應在TAPD上將相關需求和缺陷的狀態變更為「測試中」。
修複測試環境bug
bug/3,一個bug分支,對應issue 3
這裡說的是在迭代周期內由測試工程師發現的測試環境中的系統bug,這些bug會被記錄在敏捷開發協作平台TAPD上。修複測試環境bug的步驟與開發需求類似,這裡簡單說下步驟:
在Gitlab上創建issue
創建issue,並附上TAPD上的缺陷鏈接,方便追溯
創建分支&修復缺陷
基於
develop分支創建分支:// 3是issue號 git checkout -b bug/3 origin/develop接著改程式碼......
commit & push
PS D:projectsgitlabproject_xxx> git cz [email protected], [email protected] ? Select the type of change that you're committing: fix: A bug fix ? What is the scope of this change (e.g. component or file name): (press enter to skip) ? Write a short, imperative tense description of the change (max 95 chars): (11) 修復一個測試環境bug ? Provide a longer description of the change: (press enter to skip) ? Are there any breaking changes? No ? Does this change affect any open issues? Yes ? If issues are closed, the commit requires a body. Please enter a longer description of the commit itself: - ? Add issue references (e.g. "fix #123", "re #123".): fix #3 git push origin HEAD發起Merge Request
開發人員發起
Merge Request,請求將自己修復缺陷引入的程式碼合入develop分支。然後
Maintainer需要Review程式碼,同意本次Merge Request。等待回歸測試
該
bug將在下一次CI/CD後,進入回歸測試流程。級別高的測試環境Bug
如果是級別很高的
bug,比如影響了系統運行,導致測試人員無法正常測試的,應以release分支為基礎創建bug分支,修改完畢後合入release分支,再發起cherry pick合入develop分支。
發布至生產環境
經過幾輪持續集成和回歸測試後,一個迭代版本也慢慢趨於穩定,此時也迎來了激動人心的上線時間。我們要做的就是把通過了測試的release分支合入master分支。

這一步相對簡單,但是要特別注意許可權控制(防止生產環境事故),具體許可權控制可以回頭看第一章節分支管理。
與release分支類似,master分支自動觸發Gitlab CI/CD,自動構建並發布至生產環境。
線上回滾
revert/4,一個回滾分支,對應issue 4
程式碼升級到線上,但是發現報錯,系統無法正常運行。此時如果不能及時排查出問題,那麼只能先進行版本回退操作。
先說說慣性思維下,我的版本回退做法。
首先還是創建issue記錄下:

基於master分支創建revert/4分支
git checkout -b revert/4 origin/master 假設當前版本是1.1.0,我們想回退到上個版本1.0.3。那麼我們需要先查看下1.0.3版本的資訊。
PS D:tusiprojectsgitlabprojectname> git show 1.0.3 commit 90c9170a499c2c5f8f8cf4e97fc49a91d714be50 (tag: 1.0.3, fix/1.0.2_has_bug) Author: tusi <[email protected]> Date: Thu Feb 20 13:29:31 2020 +0800 fix:1.0.2 diff --git a/README.md b/README.md index ac831d0..2ee623b 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ 只想修改舊版本的bug,不改最新的master +1.0.2版本還是有個版本,再修復下 + 特性2提交 特性3提交 主要是取到1.0.3版本對應的commit id,其值為90c9170a499c2c5f8f8cf4e97fc49a91d714be50。
接著,我們根據commit id進行reset操作,再推送到遠程同名分支。
git reset --hard 90c9170a499c2c5f8f8cf4e97fc49a91d714be50 git push origin HEAD 接著發起Merge Request把revert/4分支合入master分支。

一般來說,這波操作沒什麼問題,能解決常規的回滾問題。
臨時變通
由於master分支是保護分支,設置了不可push。如果不想通過merge的方式回滾,所以只能先臨時設置Maintainer擁有push許可權,然後由Maintainer進行回滾操作。
git checkout master git pull git show 1.0.3 git reset --hard 90c9170a499c2c5f8f8cf4e97fc49a91d714be50 git push origin HEAD 完事後,還需要記得把master設置為不可push。
Q: 為什麼不讓
Maintainer一直擁有master的push許可權?A: 主要還是為了防止出現生產環境事故,給予臨時性許可權更穩妥!
git reset --hard存在什麼問題?
如題,git reset --hard確實是存在問題的。git reset --hard屬於霸道玩法,直接移動HEAD指針,會丟棄之後的提交記錄,如果不慎誤操作了也別慌,還是可以通過查詢git reflog找到commitId搶救回來的;git reset後還存在一個隱性的問題,如果與舊的branch進行merge操作時,會把git reset回滾的程式碼重新引入。那麼怎麼解決這些問題呢?

別慌,這個時候你必須掏出git revert了。
Q:
git revert的優勢在哪?A: 首先,
git revert是通過一次新的commit來進行回滾操作的,HEAD指針向前移動,這樣就不會丟失記錄;另外,git revert也不會引起merge舊分支時誤引入回滾的程式碼。最重要的是,git revert在回滾的細節控制上更加優秀,可解決部分回滾的需求。
舉個栗子,本輪迭代團隊共完成需求2項,而上線後發現其中1項需求有致命性缺陷,需要回滾這個需求相關的程式碼,同時要保留另一個需求的程式碼。

首先我們查看下日誌,找下這兩個需求的commitId
PS D:tusiprojectstestgit_test> git log --oneline 86252da (HEAD -> master, origin/master, origin/HEAD) 解決衝突 e3cef4e (origin/release, release) Merge branch 'develop' into 'release' f247f38 (origin/develop, develop) 需求2 89502c2 需求1 我們利用git revert回滾需求1相關的程式碼
git revert -n 89502c2 這時可能要解決衝突,解決完衝突後就可以push到遠程master分支了。
git add . git commit -m '回滾需求1的相關程式碼,並解決衝突' git push origin master 感覺還是菜菜的,如果大佬們有更優雅的解決方案,求指導啊!
修複線上bug
hotfix/5,一個線上熱修復分支,對應issue 5
比如線上出現了系統無法登錄的bug,測試工程師也在TAPD提交了缺陷記錄。那麼修複線上bug的步驟是什麼呢?
創建
issue,標題可以從TAPD中的Bug單中copy過來,而描述就貼上Bug單的鏈接即可。基於
master分支創建分支hotfix/5。git checkout -b hotfix/5 origin/master擼程式碼,修復此bug......
修復完此
bug後,提交該分支程式碼到遠程倉庫同名分支git push origin HEAD然後發起
cherry pick合入到master和develop分支,並在master分支打上新的版本tag(假設當前最大的tag是1.0.0,那麼新的版本tag應為1.0.1)。
修複線上舊版本bug
fix/6,一個線上舊版本修復分支,對應issue 6
某些項目產品可能會有多個線上版本同時在運行,那麼不可避免要解決舊版本的bug。針對線上舊版本出現的bug,修復步驟與上節類似。
創建
issue,描述清楚問題假設當前版本是
1.0.0,而0.9.0版本出了一個bug,應基於tag 0.9.0創建fix分支。git checkout -b fix/6 0.9.0修復缺陷後,應打上新的
tag 0.9.1,並推送到遠程。git tag 0.9.1 git push origin tag 0.9.1如果此
bug也存在於最新的master分支,則需要git push origin HEAD提交該fix分支程式碼到遠程倉庫同名分支,然後發起cherry pick合入到master,此時很大可能存在衝突,需要解決衝突。
cherry pick
在了解到cherry pick之前,我一直認為只有git merge可以合併程式碼,也好幾次遇到合入了不想要的程式碼的問題。有了cherry pick,我們就可以合併單次提交記錄,解決git merge時合併太多不想要的內容的煩惱,在解決bug時特別有用。
git rebase
經過這段時間的使用,我發現使用git merge合併分支時,會讓git log的Graph圖看起來有點吃力。
PS D:tusiprojectsgitlabprojectname> git log --pretty --oneline --graph * 7f513b0 (HEAD -> develop) Merge branch 'issue/55' into 'release' | | * 1c94437 (origin/issue/55, issue/55) fix: 【bug】XXX1 | * c84edd6 Merge branch 'release' of host:project_repository into release | | | |/ |/| * | 115a26c Merge branch 'develop' into 'release' | | * 60d7de6 Merge branch 'issue/30' into 'develop' | | | | * | 27c59e8 (origin/issue/30, issue/30) fix: 【bug】XXX2 | | | * ea17250 Merge branch 'release' of host:project_repository into release | | | | | |_|_|/ |/| | | * | | | 9fd704b Merge branch 'develop' into 'release' | | |/ / / | * | | a774d26 Merge branch 'issue/30' into 'develop' | | | | |/ / 接著我就了解到了git rebase,變基,哈哈哈。由於對rebase了解不深,目前也不敢輕易改用rebase,畢竟rebase還是有很多隱藏的坑的,使用起來要慎重!在這裡先挖個坑吧,後面搞懂了再填坑......
注意事項
- 一般而言,自己發起的
Merge Request必須由別的同事Review並同意合入,這樣更有利於發現程式碼問題。 - 對了,
TAPD還支援與Gitlab協同的。詳情見源碼關聯指引。
結語
實踐證明,這套Git工作流目前能覆蓋我項目開發過程中的絕大部分場景。不過要注意的是,適合自己的才是最好的,盲目採用別人的方案有時候是會水土不服的。
以上所述純屬前端小微團隊內部的Gitlab實踐,必然存在著很多不足之處,如有錯誤之處還請指正,歡迎交流。


