iT邦幫忙

2026 iThome 鐵人賽

DAY 29
0
佛心分享-IT 人自學之術

學過一點 React,卻被撈進 C#:新手用 AI 硬啃 MVC 企業專案的 3 個月實錄系列 第 29

Day 29|程式改完怎麼送出去?Git Changes、Commit、Push 到 Pull Request 一次走完

  • 分享至 

  • xImage
  •  

上一篇已經做到:

找到 Task
↓
建立自己的 Branch
↓
Visual Studio Fetch
↓
Checkout
↓
確認目前 Branch
↓
開始開發

接下來假設功能終於改完了。

程式:

可以啟動
Build 沒錯
功能也測過

以前自己寫練習專案時,做到這裡大概就是:

Commit
Push
結束

但公司專案不是。

因為我的程式現在還只是:

我自己的工作 Branch

最後還要經過:

檢查修改
↓
Commit
↓
Push
↓
Pull Request
↓
Reviewer
↓
Pipeline
↓
Approve
↓
Merge

才能真的進到團隊的主要版本。

所以這篇就把「功能寫完之後」的流程完整走一次。


第一件事不是 Commit,是先看 Git Changes

程式改完,我現在不會直接按:

Commit

而是先打開:

檢視
↓
Git 變更

也就是:

Git Changes

Visual Studio 會列出目前有哪些檔案跟 Git 原本記錄的版本不同。

例如:

Changes

UserController.cs
UserService.cs
UserDetailViewModel.cs
Detail.cshtml

這時第一個問題不是:

Commit Message 要寫什麼?

而是:

這四支真的是我這次應該修改的檔案嗎?

每一支檔案都要看 Diff

看到檔案名稱還不夠。

例如:

UserService.cs

我知道它改過。

但:

到底改了哪幾行?

就要打開:

Diff

看修改前後的差異。

例如原本:

Name = user.Name,
Email = user.Email

改成:

Name = user.Name,
Email = user.Email,
DepartmentName = user.DepartmentName

這樣我才能確認:

對。

這確實是這次需求需要的修改。

Commit 前我會檢查哪些東西?

我目前會先檢查:

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 和測一次

這時我通常還會再做:

Build

以及必要的:

功能測試

因為有可能:

功能原本測成功
↓
開始整理 Diff
↓
刪掉 Debug Code
↓
整理程式
↓
結果少刪一個括號

所以正式 Commit 前再確認:

Build 可以過
功能還正常

比較安全。


確認現在還是在自己的 Branch

Commit 前我也會再看一次:

目前 Branch

例如:

AP04-E1087-20240126-1200

確認:

這就是現在 Task 對應的工作 Branch

再提交。

因為如果做到最後才發現:

蛤?

我怎麼在 main?

事情會麻煩很多。


Commit 到底做了什麼?

檢查完成後,就可以:

Commit

Commit 可以先理解成:

把這次修改正式建立成一筆本機 Git 版本紀錄。

例如 Commit Message:

新增使用者詳細頁部門名稱顯示

接著:

Commit

這時修改就從:

Working Directory

變成:

Local Repository 裡的一筆 Commit

所以:

Commit
≠
Push

Commit 完:

Azure DevOps 不一定看得到。

因為目前只是:

存在我的 Local Repository

Commit Message 要寫什麼?

Visual Studio 有些環境可能提供:

Copilot

協助產生 Commit Message。

可以使用它幫忙整理修改內容,但最後還是要自己確認:

它寫的是不是這次真正做的事情?

Commit Message 至少應該讓人看得懂:

這筆版本主要改了什麼?

例如:

新增會員詳細頁部門資訊

比:

update

好很多。

例如:

修正查詢條件錯誤

也比:

fix

清楚。

以後不管自己或同事回頭看:

Commit History

才知道每筆紀錄在幹嘛。


Visual Studio 的「全部提交」是什麼?

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 有沒有新內容,再決定怎麼同步。


Stash 是什麼?

Git Changes 裡還可能看到:

Stash
隱藏

例如:

Stash All

白話可以先理解成:

我現在做到一半,還不想 Commit,但想先把這些修改暫時收起來。

它不是:

刪除

也不是:

Commit

比較像:

先收進抽屜
等等再拿回來。

可能還會看到:

--include-untracked

表示連尚未被追蹤的新檔案一起收起來。

或者:

--keep-index

保留目前已經放進 Stage 的修改。

這些比較進階。

目前至少先知道:

Stash
是暫時收工作
不是交版本

就夠了。


Commit 完,下一步是不是直接 Push?

如果這支 Branch:

只有我自己使用

而且 Remote 沒有其他人修改它。

流程可能很單純:

檢查修改
↓
Commit
↓
Push

但如果:

有人也在同一支 Branch Push 新 Commit

就不能完全當成:

Remote 永遠沒變

所以可以先:

Fetch

看看遠端目前有沒有新版本。


Fetch、Pull、Push 在這時候怎麼配合?

上一篇有講:

Fetch
↓
取得 Remote 最新資訊
Pull
↓
把 Remote 更新整合進目前 Local Branch
Push
↓
把 Local Commit 送到 Remote

如果這支 Branch 只有自己使用,通常可能就是:

Commit
↓
Push

但如果 Remote Branch 已經有新的 Commit:

Fetch
↓
發現 Remote 比我新
↓
Pull / Merge
↓
處理衝突
↓
重新測試
↓
Push

這樣比較合理。


準備開 PR 前,還要看 main 有沒有更新

即使自己的 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

Push 完不代表 main 已經有我的程式

這個非常重要。

假設目前:

我的 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 到底是在做什麼?

Pull Request:

PR

可以先理解成:

我這支 Branch 已經完成了,現在請團隊 Review,確認沒問題後,再合併到目標 Branch。

例如:

AP04-E1087-20240126-1200

要進:

main

PR 就是在提出:

我想把這支 Branch 的修改
合併進 main。

建立 PR 前先確認方向

進到 Azure DevOps 建立 Pull Request 時,最重要的第一件事:

Source Branch
Target Branch

例如:

AP04-E1087-20240126-1200 → main

意思是:

Source
我的工作 Branch

↓

Target
main

也就是:

把我的工作內容,合併進 main。

如果方向選反:

main → 我的 Branch

那意思完全不一樣。

所以我現在看到 PR 第一件事就是確認:

誰要進誰?

Title 和 Description 要寫什麼?

接著會填:

Title
Description

Title 應該簡單說明:

這個 PR 做了什麼

例如:

新增使用者詳細頁部門名稱顯示

Description 可以再補:

修改內容
測試方式
注意事項
相關需求

讓 Reviewer 不需要完全靠猜。

例如:

修改內容:
1. 詳細頁新增部門名稱
2. Service 補上部門資料
3. ViewModel 增加 DepartmentName

測試:
已確認詳細頁可正常顯示部門名稱

不一定每家公司格式都一樣。

有些團隊會有固定 PR Template。

就照團隊規範。


Work Items 也要確認

PR 還可以跟:

Work Item

建立關聯。

前面一路是:

Task
↓
Branch

現在又可以:

Branch
↓
PR

所以整條鏈可以變成:

Task
↓
Branch
↓
Commit
↓
Pull Request

之後有人打開 Task,就可以追到:

這個需求最後是哪個 PR 完成的?

這就是一開始把 Work Item 綁好的價值。


Optional Reviewer 和 Required Reviewer 差在哪?

建立 PR 時可能看到:

Optional reviewers

以及:

Required reviewers

Optional Reviewer

可以先理解成:

希望他幫忙 Review
但是否一定需要他的核准,要看團隊 Policy。

Required Reviewer

可以先理解成:

這個 Reviewer 是必要審查者。

在有相對應 Branch Policy 的情況下,通常必須符合必要 Reviewer 的要求,PR 才能完成。

在我們目前的公司流程裡:

Optional Reviewers
留空

Required Reviewers
填指定審查人員

而且:

不要把自己加入 Reviewer

這是我們目前的團隊規範。

其他公司可能不同。


Set auto-complete 是什麼?

PR 裡可能看到:

Set auto-complete

這個功能的概念是:

當 PR 所需的 Policy 條件都滿足後,自動完成 PR。

例如:

Required Reviewer 核准
↓
Build Validation 通過
↓
其他必要 Policy 通過
↓
自動 Complete

Azure DevOps 有這個功能。

但是我們目前公司的流程是:

不要勾 Set auto-complete

由人員確認全部內容後:

手動 Complete

所以要分清楚:

Azure DevOps 有這個功能

≠

我們公司要求使用這個功能

PR 建立後會看到什麼?

建立成功後,可以看到:

Overview
Files
Commits
Updates

等資訊。

Reviewer 最重要的其中一個地方就是:

Files

因為這裡可以直接看:

這個 PR 到底改了哪些檔案
哪些行新增
哪些行刪除

其實跟我們 Commit 前看的:

Git Diff

非常像。

差別是:

Commit 前
我自己 Review 自己

PR
團隊 Review 我的修改

Reviewer 到底在看什麼?

不是只看:

程式有沒有紅字

可能還要確認很多事情。

依不同專案,檢查項目會不同。

例如我們目前的檢查內容可能包含:

版號是否需要更新

程式能不能正常啟動

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。


PR 開完後,程式還可以改嗎?

可以。

這個我一開始也會疑惑。

假設 Reviewer 說:

這裡條件有問題,請修改。

不用:

把 PR 關掉
↓
重開一張

通常是回到:

原本那支 Branch

繼續修改。

流程:

Visual Studio
↓
修改
↓
Build / Test
↓
Commit
↓
Push

只要 Push 到:

同一支 Source Branch

原本的 PR 就會更新。

所以:

PR
是跟 Branch 的修改連在一起

不是:

建立 PR 當下的一張靜態截圖。

Pipeline 為什麼會出現在 PR?

PR 開出來後,我們公司還會經過:

Pipelines

再檢查一次程式。

例如 Pipeline 可能:

取得程式碼
↓
Restore
↓
Build
↓
執行其他檢查

實際跑什麼,要看專案設定。

所以:

我在自己電腦 Build 成功

不代表:

PR Pipeline 一定成功

因為 Pipeline 使用的:

環境
設定
步驟

可能跟我的本機不同。

如果 Pipeline 出現:

紅色叉叉
Failed

就要點進去看:

到底是哪個 Step 失敗?

再請開發者修正。


Pipeline Failed 要怎麼想?

不要只看到:

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

Reviewer 可以怎麼回應?

不同團隊設定可能略有不同。

一般 PR Review 可能會看到:

Approve

以及其他 Review 狀態。

Reviewer 也可以直接:

Comment

指出哪一行有問題。

例如:

這裡是否需要 null 檢查?

或者:

這個命名不符合規範

開發者修改後:

Commit
↓
Push

Reviewer 再重新確認。

這就是:

Code Review

真正開始發生的地方。


Approved 不代表立刻自動 Merge

假設 Required Reviewer 已經:

Approve

也不代表:

下一秒 main 自動更新

還要看:

Branch Policy
Build Validation
其他必要條件

以及:

團隊採用手動 Complete
還是 Auto-complete

我們目前的流程是:

檢查完成
↓
Approve
↓
由相關人員手動 Complete PR

Complete 是什麼?

PR 最後的:

Complete

可以先理解成:

確認這個 Pull Request 完成,正式把 Source Branch 的修改整合到 Target Branch。

例如:

我的 Branch
↓
PR
↓
Complete
↓
main

這時才真正走到:

Merge

Merge Strategy 又是什麼?

按 Complete 時,可能還會看到不同的 Merge 選項。

例如:

Merge
Squash commit
Rebase

實際能看到哪些選項,要看 Azure DevOps 和 Branch Policy 設定。

我們目前公司的流程使用:

Squash commit

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) 又是什麼?

另一種可能看到:

Merge
no fast-forward

它通常會保留 Branch 合併的歷史關係。

所以 History 可能會看到更多:

Branch
Commit
Merge Commit

不是:

這個方式錯了

而是 Git 歷史呈現方式不同。

我們目前公司的規範是:

使用 Squash Commit

所以開 PR 完成時就照公司的流程。

不要把:

Squash 是唯一正確方式

當成 Git 通用規則。


PR 完成後,舊 Branch 還繼續用嗎?

我們目前的流程是:

這張工作完成
↓
PR Merge
↓
這支工作 Branch 任務結束

下一張新的 Task:

重新建立新的 Branch

不要:

舊 Branch
↓
繼續做下一張 Task

不然會讓:

Task
Branch
Commit
PR

全部開始混在一起。


PR 合併後,本機還不是最新 main

這點也很容易忘。

假設 Azure DevOps:

PR Complete
↓
main 更新

我的 Visual Studio 本機:

main

不會因為網頁上 Merge 完就自動變最新。

所以後面還需要:

Fetch
↓
取得 Remote 最新資訊

再依流程:

Pull

更新本機 main。

所以:

PR Merge
≠
我的電腦自動更新。

如果本機 Commit 想修改怎麼辦?

還有一個實際很容易遇到的情況。

我剛:

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

而是一整套:

把自己的修改安全交進團隊版本

的流程。


我現在 Commit 前會檢查什麼?

我會先看:

Branch 對不對

修改檔案對不對

Diff 對不對

沒有敏感資訊

沒有測試資料

沒有 Debug Code

沒有不必要格式變更

沒有 bin / obj 等不該提交內容

Build 正常

功能測試正常

我現在 PR 前又會檢查什麼?

接著:

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 企業開發流程。


上一篇
Day 28|工作單怎麼變成我的 Branch?從 Azure DevOps 建分支到 Visual Studio Checkout
系列文
學過一點 React,卻被撈進 C#:新手用 AI 硬啃 MVC 企業專案的 3 個月實錄29
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言