上一篇已經做到:
找到 Task
↓
建立自己的 Branch
↓
Visual Studio Fetch
↓
Checkout
↓
確認目前 Branch
↓
開始開發
接下來假設功能終於改完了。
程式:
可以啟動
Build 沒錯
功能也測過
以前自己寫練習專案時,做到這裡大概就是:
Commit
Push
結束
但公司專案不是。
因為我的程式現在還只是:
我自己的工作 Branch
最後還要經過:
檢查修改
↓
Commit
↓
Push
↓
Pull Request
↓
Reviewer
↓
Pipeline
↓
Approve
↓
Merge
才能真的進到團隊的主要版本。
所以這篇就把「功能寫完之後」的流程完整走一次。
程式改完,我現在不會直接按:
Commit
而是先打開:
檢視
↓
Git 變更
也就是:
Git Changes
Visual Studio 會列出目前有哪些檔案跟 Git 原本記錄的版本不同。
例如:
Changes
UserController.cs
UserService.cs
UserDetailViewModel.cs
Detail.cshtml
這時第一個問題不是:
Commit Message 要寫什麼?
而是:
這四支真的是我這次應該修改的檔案嗎?
看到檔案名稱還不夠。
例如:
UserService.cs
我知道它改過。
但:
到底改了哪幾行?
就要打開:
Diff
看修改前後的差異。
例如原本:
Name = user.Name,
Email = user.Email
改成:
Name = user.Name,
Email = user.Email,
DepartmentName = user.DepartmentName
這樣我才能確認:
對。
這確實是這次需求需要的修改。
我目前會先檢查:
1. 只有這次工作真正需要修改的檔案
2. 沒有帳號、密碼、Token、Connection String 等敏感資料
3. 沒有測試用假資料
4. 沒有暫時 Debug 用的程式
5. 沒有不必要的大量格式變更
6. 沒有不該進版控的編譯產物
例如:
bin
obj
通常是 Build 過程產生的內容,一般會透過:
.gitignore
排除。
但這裡也不能看到:
.dll
就全部刪掉。
因為如果某些第三方 DLL 原本就由專案納入版本控制,可能是專案必要的依賴。
所以重點不是:
DLL 都不能 Commit
而是:
這是不是這次 Build 自動產生、原本就不該被版本控制的檔案?
這個很容易被忽略。
假設我真正只改:
3 行
但不小心 Format Document。
結果:
整支檔案 300 行
都出現在 Diff
後面 Reviewer 打開 PR:
???
要在幾百行格式變更中找那三行真正的修改。
所以 Commit 前看到:
怎麼整支檔案都是紅紅綠綠?
我就會先確認:
這是真的有改?
還是只有縮排、空白、換行被動到?
這時我通常還會再做:
Build
以及必要的:
功能測試
因為有可能:
功能原本測成功
↓
開始整理 Diff
↓
刪掉 Debug Code
↓
整理程式
↓
結果少刪一個括號
所以正式 Commit 前再確認:
Build 可以過
功能還正常
比較安全。
Commit 前我也會再看一次:
目前 Branch
例如:
AP04-E1087-20240126-1200
確認:
這就是現在 Task 對應的工作 Branch
再提交。
因為如果做到最後才發現:
蛤?
我怎麼在 main?
事情會麻煩很多。
檢查完成後,就可以:
Commit
Commit 可以先理解成:
把這次修改正式建立成一筆本機 Git 版本紀錄。
例如 Commit Message:
新增使用者詳細頁部門名稱顯示
接著:
Commit
這時修改就從:
Working Directory
變成:
Local Repository 裡的一筆 Commit
所以:
Commit
≠
Push
Commit 完:
Azure DevOps 不一定看得到。
因為目前只是:
存在我的 Local Repository
Visual Studio 有些環境可能提供:
Copilot
協助產生 Commit Message。
可以使用它幫忙整理修改內容,但最後還是要自己確認:
它寫的是不是這次真正做的事情?
Commit Message 至少應該讓人看得懂:
這筆版本主要改了什麼?
例如:
新增會員詳細頁部門資訊
比:
update
好很多。
例如:
修正查詢條件錯誤
也比:
fix
清楚。
以後不管自己或同事回頭看:
Commit History
才知道每筆紀錄在幹嘛。
Git Changes 裡可能會看到:
Commit All
全部提交
概念就是:
把目前選定要提交的變更建立成一筆 Local Commit。
所以:
全部提交
↓
Local Git
還沒有:
Push
到 Azure DevOps。
Visual Studio 也可能提供:
Commit All and Push
全部提交並推送
它等於:
Commit
↓
Push
一次完成。
也就是:
修改
↓
建立 Local Commit
↓
把 Commit 推到 Remote Branch
如果想把每一步拆開確認,也可以:
先 Commit
↓
再 Push
對剛開始學 Git 來說,我反而比較喜歡拆開。
因為比較知道:
現在到底做到哪一步。
還可能看到:
Commit All and Sync
全部提交並同步
這個要比:
Commit and Push
再小心一點。
Sync 通常是要讓:
Local
和:
Remote
進行同步。
過程可能包含:
取得 Remote 更新
↓
再把 Local Commit 推出去
實際行為會受到 Visual Studio 與 Git 設定影響。
所以我不會把:
Sync
理解成:
比較厲害的 Push
如果目前只需要:
把自己的 Commit 推到自己的 Branch
我會先搞清楚 Remote 有沒有新內容,再決定怎麼同步。
Git Changes 裡還可能看到:
Stash
隱藏
例如:
Stash All
白話可以先理解成:
我現在做到一半,還不想 Commit,但想先把這些修改暫時收起來。
它不是:
刪除
也不是:
Commit
比較像:
先收進抽屜
等等再拿回來。
可能還會看到:
--include-untracked
表示連尚未被追蹤的新檔案一起收起來。
或者:
--keep-index
保留目前已經放進 Stage 的修改。
這些比較進階。
目前至少先知道:
Stash
是暫時收工作
不是交版本
就夠了。
如果這支 Branch:
只有我自己使用
而且 Remote 沒有其他人修改它。
流程可能很單純:
檢查修改
↓
Commit
↓
Push
但如果:
有人也在同一支 Branch Push 新 Commit
就不能完全當成:
Remote 永遠沒變
所以可以先:
Fetch
看看遠端目前有沒有新版本。
上一篇有講:
Fetch
↓
取得 Remote 最新資訊
Pull
↓
把 Remote 更新整合進目前 Local Branch
Push
↓
把 Local Commit 送到 Remote
如果這支 Branch 只有自己使用,通常可能就是:
Commit
↓
Push
但如果 Remote Branch 已經有新的 Commit:
Fetch
↓
發現 Remote 比我新
↓
Pull / Merge
↓
處理衝突
↓
重新測試
↓
Push
這樣比較合理。
即使自己的 Branch 只有自己使用,準備 Pull Request 時還有另一件事:
main 有沒有更新?
假設我建立 Branch 時:
main
A
B
C
我開始開發。
這幾天別人的 PR 已經合進 main:
main
A
B
C
D
E
但我的 Branch 還是從:
C
開始。
準備 PR 前,依照團隊流程可能需要把最新:
main
整合到自己的 Branch。
例如概念上:
更新最新 main
↓
Merge 到自己的 Branch
↓
解 Conflict
↓
Build
↓
Test
↓
Push
這樣可以提前發現:
我的功能和最新 main
會不會互相衝突?
而不是到 PR 最後才突然發現。
實際要用 Merge 還是 Rebase,要依團隊規範。
我們目前的流程主要是:
準備 PR 前
先確認 main 是否需要同步進工作 Branch
可以整理成:
修改完成
↓
檢查 Git Changes
↓
逐一看 Diff
↓
Build / Test
↓
Commit
↓
Fetch
↓
確認 Remote 是否有更新
↓
需要時 Pull / Merge
↓
重新 Build / Test
↓
Push
Push 完成後:
Local Branch
的 Commit 才真正進:
Azure DevOps Remote Branch
接著才有辦法:
Create Pull Request
這個非常重要。
假設目前:
我的 Branch
AP04-E1087-20240126-1200
有:
Commit D
Push 後:
Azure DevOps
AP04-E1087-20240126-1200
↓
有 Commit D
但是:
main
可能還是:
A
B
C
所以:
Push
≠
Merge 到 main
Push 只是:
把我的工作 Branch
送到 Remote。
接下來才要開:
Pull Request
Pull Request:
PR
可以先理解成:
我這支 Branch 已經完成了,現在請團隊 Review,確認沒問題後,再合併到目標 Branch。
例如:
AP04-E1087-20240126-1200
要進:
main
PR 就是在提出:
我想把這支 Branch 的修改
合併進 main。
進到 Azure DevOps 建立 Pull Request 時,最重要的第一件事:
Source Branch
Target Branch
例如:
AP04-E1087-20240126-1200 → main
意思是:
Source
我的工作 Branch
↓
Target
main
也就是:
把我的工作內容,合併進 main。
如果方向選反:
main → 我的 Branch
那意思完全不一樣。
所以我現在看到 PR 第一件事就是確認:
誰要進誰?
接著會填:
Title
Description
Title 應該簡單說明:
這個 PR 做了什麼
例如:
新增使用者詳細頁部門名稱顯示
Description 可以再補:
修改內容
測試方式
注意事項
相關需求
讓 Reviewer 不需要完全靠猜。
例如:
修改內容:
1. 詳細頁新增部門名稱
2. Service 補上部門資料
3. ViewModel 增加 DepartmentName
測試:
已確認詳細頁可正常顯示部門名稱
不一定每家公司格式都一樣。
有些團隊會有固定 PR Template。
就照團隊規範。
PR 還可以跟:
Work Item
建立關聯。
前面一路是:
Task
↓
Branch
現在又可以:
Branch
↓
PR
所以整條鏈可以變成:
Task
↓
Branch
↓
Commit
↓
Pull Request
之後有人打開 Task,就可以追到:
這個需求最後是哪個 PR 完成的?
這就是一開始把 Work Item 綁好的價值。
建立 PR 時可能看到:
Optional reviewers
以及:
Required reviewers
可以先理解成:
希望他幫忙 Review
但是否一定需要他的核准,要看團隊 Policy。
可以先理解成:
這個 Reviewer 是必要審查者。
在有相對應 Branch Policy 的情況下,通常必須符合必要 Reviewer 的要求,PR 才能完成。
在我們目前的公司流程裡:
Optional Reviewers
留空
Required Reviewers
填指定審查人員
而且:
不要把自己加入 Reviewer
這是我們目前的團隊規範。
其他公司可能不同。
PR 裡可能看到:
Set auto-complete
這個功能的概念是:
當 PR 所需的 Policy 條件都滿足後,自動完成 PR。
例如:
Required Reviewer 核准
↓
Build Validation 通過
↓
其他必要 Policy 通過
↓
自動 Complete
Azure DevOps 有這個功能。
但是我們目前公司的流程是:
不要勾 Set auto-complete
由人員確認全部內容後:
手動 Complete
所以要分清楚:
Azure DevOps 有這個功能
≠
我們公司要求使用這個功能
建立成功後,可以看到:
Overview
Files
Commits
Updates
等資訊。
Reviewer 最重要的其中一個地方就是:
Files
因為這裡可以直接看:
這個 PR 到底改了哪些檔案
哪些行新增
哪些行刪除
其實跟我們 Commit 前看的:
Git Diff
非常像。
差別是:
Commit 前
我自己 Review 自己
PR
團隊 Review 我的修改
不是只看:
程式有沒有紅字
可能還要確認很多事情。
依不同專案,檢查項目會不同。
例如我們目前的檢查內容可能包含:
版號是否需要更新
程式能不能正常啟動
Coding Guideline
命名與排版
UI 是否符合系統標準
共用參數是否有影響
資料庫 Transaction / Rollback
設定檔是否正確
Connection String 是否被誤改
Debug Mode 是否正確
如果是特定系統,可能還會有:
版本號
需要另外確認。
這些都屬於:
專案 / 公司自己的 Review Checklist
不是所有 Azure DevOps PR 天生都會幫你檢查。
Review Diff 時,也可能遇到:
整支檔案看起來都是亂碼
或中文字:
怪怪的
這時不一定是程式邏輯壞掉。
也可能是:
檔案編碼
出了問題。
例如原本專案使用特定編碼,修改後被編輯器存成另一種編碼。
這時就需要確認:
專案原本使用什麼 Encoding
必要時再用:
Visual Studio
VS Code
Notepad++
等工具調整。
不要看到 Azure DevOps Diff 一整片不同,就直接 Approve。
可以。
這個我一開始也會疑惑。
假設 Reviewer 說:
這裡條件有問題,請修改。
不用:
把 PR 關掉
↓
重開一張
通常是回到:
原本那支 Branch
繼續修改。
流程:
Visual Studio
↓
修改
↓
Build / Test
↓
Commit
↓
Push
只要 Push 到:
同一支 Source Branch
原本的 PR 就會更新。
所以:
PR
是跟 Branch 的修改連在一起
不是:
建立 PR 當下的一張靜態截圖。
PR 開出來後,我們公司還會經過:
Pipelines
再檢查一次程式。
例如 Pipeline 可能:
取得程式碼
↓
Restore
↓
Build
↓
執行其他檢查
實際跑什麼,要看專案設定。
所以:
我在自己電腦 Build 成功
不代表:
PR Pipeline 一定成功
因為 Pipeline 使用的:
環境
設定
步驟
可能跟我的本機不同。
如果 Pipeline 出現:
紅色叉叉
Failed
就要點進去看:
到底是哪個 Step 失敗?
再請開發者修正。
不要只看到:
Failed
就重新跑十次 XD
先進去找:
第一個真正失敗的 Step
例如:
Restore Failed
Build Failed
Test Failed
再看:
Error Message
這其實跟 Day 25 講的:
Error List
Output
Exception
概念很像。
差別只是:
Day 25
看本機 Visual Studio
Day 29
開始看 CI Pipeline 的 Log
不同團隊設定可能略有不同。
一般 PR Review 可能會看到:
Approve
以及其他 Review 狀態。
Reviewer 也可以直接:
Comment
指出哪一行有問題。
例如:
這裡是否需要 null 檢查?
或者:
這個命名不符合規範
開發者修改後:
Commit
↓
Push
Reviewer 再重新確認。
這就是:
Code Review
真正開始發生的地方。
假設 Required Reviewer 已經:
Approve
也不代表:
下一秒 main 自動更新
還要看:
Branch Policy
Build Validation
其他必要條件
以及:
團隊採用手動 Complete
還是 Auto-complete
我們目前的流程是:
檢查完成
↓
Approve
↓
由相關人員手動 Complete PR
PR 最後的:
Complete
可以先理解成:
確認這個 Pull Request 完成,正式把 Source Branch 的修改整合到 Target Branch。
例如:
我的 Branch
↓
PR
↓
Complete
↓
main
這時才真正走到:
Merge
按 Complete 時,可能還會看到不同的 Merge 選項。
例如:
Merge
Squash commit
Rebase
實際能看到哪些選項,要看 Azure DevOps 和 Branch Policy 設定。
我們目前公司的流程使用:
Squash commit
假設我的工作 Branch 上有:
Commit A
建立畫面
Commit B
修正查詢
Commit C
修改 Reviewer 意見
Commit D
再修一次
如果使用 Squash:
A
B
C
D
在合併到 main 時,可以整理成:
一筆新的整合 Commit
例如:
Merged PR 1234:新增使用者部門名稱
所以 main 的歷史比較乾淨。
可以簡單理解成:
工作 Branch
A
B
C
D
↓
Squash
↓
main
一筆整合後的 Commit
而不是把開發過程中:
fix
fix2
test
再修一次
全部攤在 main History。
另一種可能看到:
Merge
no fast-forward
它通常會保留 Branch 合併的歷史關係。
所以 History 可能會看到更多:
Branch
Commit
Merge Commit
不是:
這個方式錯了
而是 Git 歷史呈現方式不同。
我們目前公司的規範是:
使用 Squash Commit
所以開 PR 完成時就照公司的流程。
不要把:
Squash 是唯一正確方式
當成 Git 通用規則。
我們目前的流程是:
這張工作完成
↓
PR Merge
↓
這支工作 Branch 任務結束
下一張新的 Task:
重新建立新的 Branch
不要:
舊 Branch
↓
繼續做下一張 Task
不然會讓:
Task
Branch
Commit
PR
全部開始混在一起。
這點也很容易忘。
假設 Azure DevOps:
PR Complete
↓
main 更新
我的 Visual Studio 本機:
main
不會因為網頁上 Merge 完就自動變最新。
所以後面還需要:
Fetch
↓
取得 Remote 最新資訊
再依流程:
Pull
更新本機 main。
所以:
PR Merge
≠
我的電腦自動更新。
還有一個實際很容易遇到的情況。
我剛:
Commit
完。
結果發現:
Commit Message 寫錯
或
有個檔案不該 Commit
如果:
還沒有 Push
修改通常比較容易。
例如在 Git History 對前一個版本:
Reset
並選擇:
保留變更
可以讓最後一筆 Local Commit 消失,但把程式修改保留在工作目錄。
這樣就能重新整理後再 Commit。
但這裡有個很重要的界線:
只存在 Local 的 Commit
和
已經 Push 給別人使用的 Commit
是兩回事。
如果已經 Push 到 Remote,尤其多人共同使用,就不要隨便重寫 History。
因為可能會影響其他人的版本。
現在可以把今天整篇串起來:
功能修改完成
↓
確認目前 Branch
↓
Git Changes
↓
逐一查看 Diff
↓
移除 Debug / Test / 不相關修改
↓
Build
↓
Test
↓
Commit
↓
Local Repository
↓
Fetch
↓
確認 Remote 更新
↓
必要時 Pull / Merge
↓
重新 Build / Test
↓
Push
↓
Remote Branch
↓
確認最新 main
↓
必要時整合 main
↓
Push
↓
Create Pull Request
↓
確認 Source → Target
↓
Title / Description
↓
Work Item
↓
Required Reviewer
↓
Create
↓
Pipeline / Build Validation
↓
Code Review
↓
有問題
→ 修改 → Commit → Push
↓
Reviewer Approve
↓
Complete
↓
Squash Commit
↓
Merge 到 main
這已經不是:
寫完程式按一下 Push
而是一整套:
把自己的修改安全交進團隊版本
的流程。
我會先看:
Branch 對不對
修改檔案對不對
Diff 對不對
沒有敏感資訊
沒有測試資料
沒有 Debug Code
沒有不必要格式變更
沒有 bin / obj 等不該提交內容
Build 正常
功能測試正常
接著:
Remote Branch 是最新的嗎?
main 有沒有更新?
需不需要把最新 main 整合進來?
重新測試過了嗎?
Source Branch 對嗎?
Target Branch 對嗎?
Work Item 對嗎?
Reviewer 對嗎?
Title / Description 清楚嗎?
然後才真的:
Create Pull Request
以前自己寫 React:
寫完
↓
Commit
↓
Push GitHub
可能就差不多結束。
但進入團隊開發後:
Push
其實只代表:
我的 Branch 已經到 Remote。
還沒有進:
main
真正的團隊流程是:
Git Changes
↓
Diff
↓
Commit
↓
Push
↓
Pull Request
↓
Reviewer
↓
Pipeline
↓
Approve
↓
Complete
↓
Merge
而且有幾件事情要特別分清楚:
Commit
≠
Push
Push
≠
Merge
PR 建立
≠
PR 已通過
Approve
≠
一定已經 Merge
Remote main 更新
≠
我的 Local main 自動更新
到這裡,從:
工作單
一路走到:
Pull Request
的流程差不多都拆完了。
最後一天就不再加新的名詞。
直接把這 30 天學的東西全部串起來:
收到工作
↓
找到 Task
↓
建立 Branch
↓
找到 MVC 程式
↓
F12 追 Service
↓
Debug
↓
修改
↓
Build
↓
Git Changes
↓
Commit
↓
Push
↓
PR
↓
Review
↓
Merge
下一篇:
從工作單到 PR 合併:我現在怎麼走完一次 Azure DevOps 企業開發流程。