前端中小團隊 GitLab 最佳實踐(文末送書)
最近團隊代碼庫從Gerrit遷移到了Gitlab,為了讓前端團隊日常開發(fā)工作有條不紊,高效運轉,開發(fā)歷史可追溯,我也查閱和學習了不少資料。參考業(yè)界主流的Git工作流,結合公司業(yè)務特質,我也梳理了一套適合自己團隊的Git工作流,在這里做下分享。
分支管理
首先要說的是分支管理,分支管理是git工作流的基礎,好的分支設計有助于規(guī)范開發(fā)流程,也是CI/CD的基礎。
分支策略
業(yè)界主流的git工作流,一般會分為develop, release, master, hotfix/xxx, feature/xxx等分支。各個分支各司其職,貫穿了整個開發(fā),測試,部署流程。我這里也基于主流的分支策略做了一些定制,下面用一張表格簡單概括下:
| 分支名 | 分支定位 | 描述 | 權限控制 |
|---|---|---|---|
| develop | 開發(fā)分支 | 不可以在develop分支push代碼,應新建feature/xxx進行需求開發(fā)。迭代功能開發(fā)完成后的代碼都會merge到develop分支。 | Develper不可直接push,可發(fā)起merge request |
| feature/xxx | 特性分支 | 針對每一項需求,新建feature分支,如feature/user_login,用于開發(fā)用戶登錄功能。 | Develper可直接push |
| release | 提測分支 | 由develop分支合入release分支。ps: 應配置此分支觸發(fā)CI/CD,部署至測試環(huán)境。 | Maintainer可發(fā)起merge request |
| bug/xxx | 缺陷分支 | 提測后發(fā)現的bug,應基于develop分支創(chuàng)建bug/xxx分支修復缺陷,修改完畢后應合入develop分支等待回歸測試。 | |
| master | 發(fā)布分支 | master應處于隨時可發(fā)布的狀態(tài),用于對外發(fā)布正式版本。ps: 應配置此分支觸發(fā)CI/CD,部署至生產環(huán)境。 | Maintainer可發(fā)起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中說明具體問題和待辦事項,保證開發(fā)工作可追溯。
保護分支
利用Protected Branches,我們可以防止開發(fā)人員錯誤地將代碼push到某些分支。對于普通開發(fā)人員,我們僅對develop分支提供merge權限。

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

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

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

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

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

創(chuàng)建issue
創(chuàng)建Gitlab issue,鏈接到TAPD中的相關需求。


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

commit & push
完成功能開發(fā)后,我們需要提交代碼并同步到遠程倉庫。
PS D:\projects\gitlab\project_xxx> git add .
PS D:\projects\gitlab\project_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則代表推送到遠程倉庫同名分支。
創(chuàng)建Merge Request
開發(fā)人員發(fā)起Merge Request,請求將自己開發(fā)的功能特性合入develop分支。

接著Maintainer需要Review代碼,確認無誤后同意Merge。然后這部分代碼就在遠程Git倉庫入庫了,其他開發(fā)同學拉取develop分支就能看到了。
版本提測
issue/2,處理更新日志,版本號等內容,對應issue 2
每個團隊的開發(fā)節(jié)奏都不同,有的團隊會每日持續(xù)集成發(fā)版本提測,有的可能兩天一次,這個就不深入討論了......
那么當我們準備提測時,應該怎么做呢?
通過上節(jié)的了解,我們已經知道,迭代內的功能需求都會通過feature/xxx分支合入到develop分支。
提測前,一般來說,還是要更新下CHANGELOG.md和package.json的版本號,可以由Maintainer或其他負責該項事務的同學執(zhí)行。
主要是執(zhí)行npm version major/minor/patch -m 'something done',具體操作可以參考前端自動化部署的深度實踐一文。
git checkout -b issue/2 origin/develop
npm version minor -m '迭代1第一次提測'
git push origin HEAD
然后發(fā)起merge request合入develop分支
此時,應以最新的develop分支代碼在開發(fā)環(huán)境跑一遍功能,保證版本自測通過。
提測時,由Maintainer發(fā)起Merge Request,將develop分支代碼合入release分支,此時自動觸發(fā)Gitlab CI/CD,自動構建并發(fā)布至測試環(huán)境。
版本提測后,各責任人應在TAPD上將相關需求和缺陷的狀態(tài)變更為**“測試中”**。
修復測試環(huán)境bug
bug/3,一個bug分支,對應issue 3
這里說的是在迭代周期內由測試工程師發(fā)現的測試環(huán)境中的系統(tǒng)bug,這些bug會被記錄在敏捷開發(fā)協(xié)作平臺TAPD上。修復測試環(huán)境bug的步驟與開發(fā)需求類似,這里簡單說下步驟:
在Gitlab上創(chuàng)建issue
創(chuàng)建issue,并附上TAPD上的缺陷鏈接,方便追溯
創(chuàng)建分支&修復缺陷
基于
develop分支創(chuàng)建分支:// 3是issue號
git checkout -b bug/3 origin/develop接著改代碼......
commit & push
PS D:\projects\gitlab\project_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) 修復一個測試環(huán)境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發(fā)起Merge Request
開發(fā)人員發(fā)起
Merge Request,請求將自己修復缺陷引入的代碼合入develop分支。然后
Maintainer需要Review代碼,同意本次Merge Request。等待回歸測試
該
bug將在下一次CI/CD后,進入回歸測試流程。級別高的測試環(huán)境Bug
如果是級別很高的
bug,比如影響了系統(tǒng)運行,導致測試人員無法正常測試的,應以release分支為基礎創(chuàng)建bug分支,修改完畢后合入release分支,再發(fā)起cherry pick合入develop分支。
發(fā)布至生產環(huán)境
經過幾輪持續(xù)集成和回歸測試后,一個迭代版本也慢慢趨于穩(wěn)定,此時也迎來了激動人心的上線時間。我們要做的就是把通過了測試的release分支合入master分支。

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

基于master分支創(chuàng)建revert/4分支
git checkout -b revert/4 origin/master
假設當前版本是1.1.0,我們想回退到上個版本1.0.3。那么我們需要先查看下1.0.3版本的信息。
PS D:\tusi\projects\gitlab\projectname> 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
接著發(fā)起Merge Request把revert/4分支合入master分支。

一般來說,這波操作沒什么問題,能解決常規(guī)的回滾問題。
臨時變通
由于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: 主要還是為了防止出現生產環(huán)境事故,給予臨時性權限更穩(wěn)妥!
git reset --hard存在什么問題?
如題,git reset --hard確實是存在問題的。git reset --hard屬于霸道玩法,直接移動HEAD指針,會丟棄之后的提交記錄,如果不慎誤操作了也別慌,還是可以通過查詢git reflog找到commitId搶救回來的;git reset后還存在一個隱性的問題,如果與舊的branch進行merge操作時,會把git reset回滾的代碼重新引入。那么怎么解決這些問題呢?

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

首先我們查看下日志,找下這兩個需求的commitId
PS D:\tusi\projects\test\git_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
感覺還是菜菜的,如果大佬們有更優(yōu)雅的解決方案,求指導??!
修復線上bug
hotfix/5,一個線上熱修復分支,對應issue 5
比如線上出現了系統(tǒng)無法登錄的bug,測試工程師也在TAPD提交了缺陷記錄。那么修復線上bug的步驟是什么呢?
創(chuàng)建
issue,標題可以從TAPD中的Bug單中copy過來,而描述就貼上Bug單的鏈接即可。基于
master分支創(chuàng)建分支hotfix/5。git checkout -b hotfix/5 origin/master擼代碼,修復此bug......
修復完此
bug后,提交該分支代碼到遠程倉庫同名分支git push origin HEAD然后發(fā)起
cherry pick合入到master和develop分支,并在master分支打上新的版本tag(假設當前最大的tag是1.0.0,那么新的版本tag應為1.0.1)。
修復線上舊版本bug
fix/6,一個線上舊版本修復分支,對應issue 6
某些項目產品可能會有多個線上版本同時在運行,那么不可避免要解決舊版本的bug。針對線上舊版本出現的bug,修復步驟與上節(jié)類似。
創(chuàng)建
issue,描述清楚問題假設當前版本是
1.0.0,而0.9.0版本出了一個bug,應基于tag 0.9.0創(chuàng)建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分支代碼到遠程倉庫同名分支,然后發(fā)起cherry pick合入到master,此時很大可能存在沖突,需要解決沖突。
cherry pick
cherry pick
在了解到cherry pick之前,我一直認為只有git merge可以合并代碼,也好幾次遇到合入了不想要的代碼的問題。有了cherry pick,我們就可以合并單次提交記錄,解決git merge時合并太多不想要的內容的煩惱,在解決bug時特別有用。
git rebase
經過這段時間的使用,我發(fā)現使用git merge合并分支時,會讓git log的Graph圖看起來有點吃力。
PS D:\tusi\projects\gitlab\projectname> 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還是有很多隱藏的坑的,使用起來要慎重!在這里先挖個坑吧,后面搞懂了再填坑......
注意事項
一般而言,自己發(fā)起的 Merge Request必須由別的同事Review并同意合入,這樣更有利于發(fā)現代碼問題。對了, TAPD還支持與Gitlab協(xié)同的。詳情見源碼關聯指引[2]。
結語
實踐證明,這套Git工作流目前能覆蓋我項目開發(fā)過程中的絕大部分場景。不過要注意的是,適合自己的才是最好的,盲目采用別人的方案有時候是會水土不服的。
以上所述純屬前端小微團隊內部的Gitlab實踐,必然存在著很多不足之處,如有錯誤之處還請指正,歡迎交流。
參考資料
TAPD: https://www.tapd.cn/
[2]源碼關聯指引: https://www.tapd.cn/help/view#1120003271001001346
最近上市的新書《前端函數式演進》是一部寫給前端工程師的函數式編程實戰(zhàn)指南,由阿里本地生活企業(yè)訂餐前端負責人撰寫。
作者結合自己在前端領域的大量工程實踐經驗,首先從前端開發(fā)者的角度介紹了函數式編程在前端領域的演進,以及前端必備的函數式編程知識;然后對React和RxJS等流行前端工具的函數式編程功能和思想進行了深入剖析,既可以幫助讀者更好地理解和使用這些工具,又能給予讀者技術選型方面的建議。此外,書中提供了平臺級別的前端開發(fā)示例的多種實現,方便讀者結合書中的理論進行印證和思考。
這本書還是挺不錯的,推介一下 ,最后給本公眾號粉絲送出 12 個小禮物(在下大出血),包括 3 本前端函數式演進。
廢話少說,抽獎在下面:
