前端小微團隊的Gitlab實踐

疫情期間我感覺整個人懶散了不少,慢慢有意識要振作起來了,恢復到正常的節奏。最近團隊代碼庫從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可以認為是一個階段性的目標,比如是一輪迭代計劃。里程碑可以設定時間範圍,用來約束和提醒開發人員。

milestones

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

創建issue

標籤

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

標籤管理

issue分類

所有的開發工作應該通過issue記錄,包括但不限於需求缺陷開發自測試用戶體驗等範疇。

需求&缺陷

這裡大概又分為兩種情況,一種是TAPD記錄在案的需求和缺陷,另一種是與產品或測試人員口頭溝通時傳達的簡單需求或缺陷(小公司會有這種情況…)。

對於TAPD記錄的需求和缺陷,創建issue時應附上鏈接,方便查閱(上文中已有提到)。

對於口頭溝通的需求和缺陷,我定了個規則,要求提出人本人在Gitlab上創建issue,並將需求或缺陷簡單描述清楚,否則口頭溝通的開發工作我不接(也是為了避免事後扯皮)。

ps:其實要求產品或者測試提issue,還不如上Tapd記錄。我定這麼個規則,其實就是借Gitlab找個說辭,杜絕口頭類需求或缺陷,哈哈。

開發自測試

開發者自己發現了系統缺陷或問題,此時應該通過issue記錄問題,並創建相應分支修改代碼。

自測試issue

實戰案例

我前面也說了,我的原則是issue驅動開發工作。

下面用幾個例子來簡單說明基本的開發流程。小公司里整個流程比較簡單,沒有複雜的集成測試,多輪驗收測試,灰度測試等。我甚至連單元測試都沒做(捂臉…)。

公共庫和公共組件其實是很有必要做單元測試的,這裡立個flag,後面一定補上單元測試。

需求開發

feature/1,一個特性分支,對應issue 1

創建需求

正常的需求當然來源於產品經理等需求提出方,由於是通過示例說明,這裡我自己在TAPD上模擬着寫一個需求。

TAPD創建需求

創建issue

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

創建issue

一個issue

創建分支&功能開發

基於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分支。

創建Merge Request

接着Maintainer需要Review代碼,確認無誤後同意Merge。然後這部分代碼就在遠程Git倉庫入庫了,其他開發同學拉取develop分支就能看到了。

版本提測

issue/2,處理更新日誌,版本號等內容,對應issue 2

每個團隊的開發節奏都不同,有的團隊會每日持續集成發版本提測,有的可能兩天一次,這個就不深入討論了……

那麼當我們準備提測時,應該怎麼做呢?

通過上節的了解,我們已經知道,迭代內的功能需求都會通過feature/xxx分支合入到develop分支。

提測前,一般來說,還是要更新下CHANGELOG.mdpackage.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的步驟與開發需求類似,這裡簡單說下步驟:

  1. 在Gitlab上創建issue

    創建issue,並附上TAPD上的缺陷鏈接,方便追溯

  2. 創建分支&修復缺陷

    基於develop分支創建分支:

    // 3是issue號  git checkout -b bug/3 origin/develop

    接着改代碼……

  3. 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
  4. 發起Merge Request

    開發人員發起Merge Request,請求將自己修復缺陷引入的代碼合入develop分支。

    然後Maintainer需要Review代碼,同意本次Merge Request

  5. 等待回歸測試

    bug將在下一次CI/CD後,進入回歸測試流程。

  6. 級別高的測試環境Bug

    如果是級別很高的bug,比如影響了系統運行,導致測試人員無法正常測試的,應以release分支為基礎創建bug分支,修改完畢後合入release分支,再發起cherry pick合入develop分支。

發佈至生產環境

經過幾輪持續集成和回歸測試後,一個迭代版本也慢慢趨於穩定,此時也迎來了激動人心的上線時間。我們要做的就是把通過了測試的release分支合入master分支。

release合入master

這一步相對簡單,但是要特別注意權限控制(防止生產環境事故),具體權限控制可以回頭看第一章節分支管理

release分支類似,master分支自動觸發Gitlab CI/CD,自動構建並發佈至生產環境

線上回滾

revert/4,一個回滾分支,對應issue 4

代碼升級到線上,但是發現報錯,系統無法正常運行。此時如果不能及時排查出問題,那麼只能先進行版本回退操作。

先說說慣性思維下,我的版本回退做法。

首先還是創建issue記錄下:

創建記錄回滾的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 Requestrevert/4分支合入master分支。

回滾分支合入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一直擁有masterpush權限?

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的步驟是什麼呢?

  1. 創建issue,標題可以從TAPD中的Bug單中copy過來,而描述就貼上Bug單的鏈接即可。

  2. 基於master分支創建分支hotfix/5

    git checkout -b hotfix/5 origin/master
  3. 擼代碼,修復此bug……

  4. 修復完此bug後,提交該分支代碼到遠程倉庫同名分支

    git push origin HEAD
  5. 然後發起cherry pick合入到masterdevelop分支,並在master分支打上新的版本tag(假設當前最大的tag1.0.0,那麼新的版本tag應為1.0.1)。

修複線上舊版本bug

fix/6,一個線上舊版本修復分支,對應issue 6

某些項目產品可能會有多個線上版本同時在運行,那麼不可避免要解決舊版本的bug。針對線上舊版本出現的bug,修復步驟與上節類似。

  1. 創建issue,描述清楚問題

  2. 假設當前版本是1.0.0,而0.9.0版本出了一個bug,應基於tag 0.9.0創建fix分支。

    git checkout -b fix/6 0.9.0
  3. 修復缺陷後,應打上新的tag 0.9.1,並推送到遠程。

    git tag 0.9.1  git push origin tag 0.9.1
  4. 如果此bug也存在於最新的master分支,則需要git push origin HEAD提交該fix分支代碼到遠程倉庫同名分支,然後發起cherry pick合入到master,此時很大可能存在衝突,需要解決衝突。

    cherry pick

cherry pick

在了解到cherry pick之前,我一直認為只有git merge可以合併代碼,也好幾次遇到合入了不想要的代碼的問題。有了cherry pick,我們就可以合併單次提交記錄,解決git merge時合併太多不想要的內容的煩惱,在解決bug時特別有用。

git rebase

經過這段時間的使用,我發現使用git merge合併分支時,會讓git logGraph圖看起來有點吃力。

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還是有很多隱藏的坑的,使用起來要慎重!在這裡先挖個坑吧,後面搞懂了再填坑……

注意事項

  1. 一般而言,自己發起的Merge Request必須由別的同事Review並同意合入,這樣更有利於發現代碼問題。
  2. 對了,TAPD還支持與Gitlab協同的。詳情見源碼關聯指引

結語

實踐證明,這套Git工作流目前能覆蓋我項目開發過程中的絕大部分場景。不過要注意的是,適合自己的才是最好的,盲目採用別人的方案有時候是會水土不服的。

以上所述純屬前端小微團隊內部的Gitlab實踐,必然存在着很多不足之處,如有錯誤之處還請指正,歡迎交流。

歡迎關注&交流