第六章:Pull Request 工作流程詳解 – 現代協作的核心機制

Pull Request(在 GitLab 中稱為 Merge Request)是現代軟體開發中最重要的協作機制之一。作為一位資深工程師,我可以說 Pull Request 不僅僅是合併程式碼的工具,更是知識分享、品質把關、和團隊學習的平台。今天我們要深入了解如何有效地使用這個強大的協作工具。

什麼是 Pull Request?

Pull Request 就像是向專案維護者「請求拉取」你的變更。想像你是一位作家,想要為一本書貢獻新的章節。你不能直接修改原書,而是要將你的章節提交給編輯審查,編輯同意後才會將你的內容加入到正式版本中。

Pull Request 的核心價值:

  • 程式碼審查:讓其他開發者檢視你的程式碼
  • 討論平台:針對具體的程式碼進行討論
  • 品質控制:確保進入主分支的程式碼符合標準
  • 知識分享:團隊成員互相學習的機會
  • 歷史記錄:保留決策過程和討論內容

Pull Request 的完整生命週期

graph LR
    A[建立功能分支] --> B[開發功能]
    B --> C[推送分支]
    C --> D[建立 PR]
    D --> E[Code Review]
    E --> F{需要修改?}
    F -->|是| G[修改程式碼]
    G --> H[推送更新]
    H --> E
    F -->|否| I[核准合併]
    I --> J[合併到主分支]
    J --> K[清理分支]

建立 Pull Request 的標準流程

步驟 1:準備工作環境

# 1. 確保主分支是最新的
git switch main
git pull origin main

# 2. 建立功能分支
git switch -c feature/user-profile-enhancement

# 3. 確認分支狀態
git status
git branch

步驟 2:開發和提交

# 進行開發工作
echo "實作使用者個人資料頁面" > profile.js
echo "個人資料頁面樣式" > profile.css
echo "個人資料 API 端點" > profile-api.js

# 分階段提交(推薦)
git add profile.js
git commit -m "新增使用者個人資料頁面組件"

git add profile.css  
git commit -m "新增個人資料頁面樣式設計"

git add profile-api.js
git commit -m "實作個人資料 API 端點"

步驟 3:推送分支並建立 PR

# 推送功能分支
git push -u origin feature/user-profile-enhancement

# 輸出會包含建立 PR 的連結
# remote: Create a pull request for 'feature/user-profile-enhancement' on GitHub by visiting:
# remote:      https://github.com/username/repo/pull/new/feature/user-profile-enhancement

步驟 4:在平台上建立 Pull Request

在 GitHub 上建立 PR 時需要填寫:

標題範例:

新增使用者個人資料管理功能

描述範例:

## 功能說明
實作使用者個人資料管理功能,包括:
- 個人資料顯示頁面
- 編輯個人資料功能  
- 頭像上傳功能
- 響應式設計

## 變更內容
- 新增 `ProfileComponent` 組件
- 實作個人資料 API 端點
- 新增個人資料頁面樣式
- 加入單元測試

## 測試
- [ ] 單元測試通過
- [ ] 整合測試通過  
- [ ] 手動測試完成
- [ ] 響應式設計測試完成

## 截圖
![個人資料頁面](screenshot.png)

## 相關議題
Closes #123
Related to #456

## 檢查清單
- [x] 程式碼遵循專案風格指南
- [x] 新增適當的註解
- [x] 更新相關文件
- [x] 加入或更新測試
- [x] 所有測試通過

Fork 工作流程詳解

Fork 工作流程是參與開源專案的標準方式,特別適合不熟悉的專案或大型開源社群。

完整的 Fork 流程

# 1. 在 GitHub 上 Fork 目標專案
# 點選專案頁面的 "Fork" 按鈕

# 2. 複製你 Fork 的版本
git clone git@github.com:yourusername/awesome-project.git
cd awesome-project

# 3. 新增原始專案為 upstream
git remote add upstream git@github.com:original-owner/awesome-project.git

# 4. 確認遠端設定
git remote -v
# origin    git@github.com:yourusername/awesome-project.git (fetch)
# origin    git@github.com:yourusername/awesome-project.git (push)  
# upstream  git@github.com:original-owner/awesome-project.git (fetch)
# upstream  git@github.com:original-owner/awesome-project.git (push)

保持 Fork 同步

# 定期同步上游變更(建議每天或每週)
git fetch upstream
git switch main
git merge upstream/main
git push origin main

# 或使用 rebase 保持線性歷史
git fetch upstream  
git switch main
git rebase upstream/main
git push origin main

Fork 貢獻流程

# 1. 同步上游
git fetch upstream
git switch main
git merge upstream/main

# 2. 建立功能分支
git switch -c fix/documentation-typo

# 3. 進行修改
echo "修正文件中的拼寫錯誤" > docs/README.md
git add docs/README.md
git commit -m "修正 README 中的拼寫錯誤"

# 4. 推送到你的 Fork
git push -u origin fix/documentation-typo

# 5. 在原始專案建立 Pull Request
# 從 yourusername:fix/documentation-typo
# 到   original-owner:main

Code Review 最佳實踐

Code Review 是 Pull Request 流程中最重要的環節,它不僅能提高程式碼品質,還能促進團隊學習。

作為 PR 作者的準則

1. 撰寫清楚的 PR 描述

## 問題描述
使用者無法在行動裝置上正確查看個人資料頁面

## 解決方案
- 修正 CSS 媒體查詢
- 調整響應式斷點
- 優化觸控互動

## 測試方式
在以下裝置測試:
- iPhone 12 (Safari)
- Samsung Galaxy S21 (Chrome)
- iPad Air (Safari)

## 影響範圍
只影響前端顯示,不涉及後端 API 變更

2. 保持 PR 規模適中

# 好的做法:單一目的的小 PR
git log --oneline
# a1b2c3d 修正行動裝置個人資料頁面佈局
# e4f5g6h 調整響應式斷點設定
# h7i8j9k 優化觸控按鈕大小

# 避免:包含多個不相關變更的大 PR
git log --oneline  
# a1b2c3d 修正個人資料頁面 + 新增支付功能 + 重構認證系統

3. 自我審查

在建立 PR 前進行自我審查:

# 查看即將提交的所有變更
git diff main..feature/my-branch

# 查看每個檔案的變更
git diff main..feature/my-branch --name-only | xargs -I {} git diff main..feature/my-branch {}

# 確保沒有調試程式碼
grep -r "console.log\|debugger\|TODO" src/

作為 Reviewer 的準則

1. 審查的重點領域

功能正確性

// 審查:這個函數是否正確處理邊界情況?
function calculateDiscount(price, discountPercent) {
  // ✅ 好:有輸入驗證
  if (price <= 0 || discountPercent < 0 || discountPercent > 100) {
    throw new Error('Invalid input parameters');
  }
  
  return price * (1 - discountPercent / 100);
}

程式碼風格和一致性

// ❌ 不一致的命名風格
const user_name = getUserName();
const userAge = getUserAge(); 

// ✅ 一致的命名風格
const userName = getUserName();
const userAge = getUserAge();

效能和安全性

// ❌ 潛在的安全問題
app.get('/api/user/:id', (req, res) => {
  const query = `SELECT * FROM users WHERE id = ${req.params.id}`;
  // SQL 注入風險
});

// ✅ 使用參數化查詢
app.get('/api/user/:id', (req, res) => {
  const query = 'SELECT * FROM users WHERE id = ?';
  db.query(query, [req.params.id], (err, result) => {
    // 安全的查詢方式
  });
});

2. 提供建設性的回饋

好的回饋範例:

## 建議改善
在 `calculateTax` 函數中,建議加入輸入驗證:

```javascript
function calculateTax(amount) {
  if (typeof amount !== 'number' || amount < 0) {
    throw new Error('Amount must be a positive number');
  }
  return amount * 0.1;
}
</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>這樣可以避免在傳入無效值時產生意外結果。</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">優點</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>使用 async/await 讓非同步程式碼更易讀,很棒的改善!</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>
**避免的回饋方式:**
```markdown
❌ "這個程式碼有問題"
❌ "不對"  
❌ "重寫"

3. 使用 GitHub 的審查功能

# 在程式碼行上留言
// 點選行號左邊的 "+" 號

# 建議具體的程式碼變更
```suggestion
const isValid = validateInput(userInput);
if (!isValid) {
  return { error: 'Invalid input' };
}

總結性的審查意見

Overall, this is a solid implementation. Just a few minor suggestions above. LGTM! (Looks Good To Me)


## Pull Request 範本

建立 `.github/pull_request_template.md` 檔案:

```markdown
## 變更類型
- [ ] 新功能 (feature)
- [ ] 錯誤修復 (bug fix)  
- [ ] 文件更新 (documentation)
- [ ] 樣式修改 (formatting, missing semi colons, etc)
- [ ] 重構 (refactoring)
- [ ] 測試 (adding missing tests, refactoring tests)
- [ ] 其他 (請說明):

## 變更描述
簡要描述這個 PR 的目的和變更內容

## 相關議題
- Closes #(issue number)
- Related to #(issue number)

## 測試
說明如何測試這些變更:
- [ ] 單元測試
- [ ] 整合測試  
- [ ] 手動測試
- [ ] 其他測試 (請說明):

## 檢查清單
- [ ] 我的程式碼遵循專案的風格指南
- [ ] 我已進行自我審查
- [ ] 我已為程式碼加入註解,特別是複雜的邏輯
- [ ] 我已更新相關文件
- [ ] 我的變更不會產生新的警告
- [ ] 我已加入測試,且新舊測試都通過
- [ ] 相關的測試都已在本地通過

## 螢幕截圖 (如適用)
請加入螢幕截圖或 GIF 來說明變更

## 其他資訊
任何其他相關資訊

自動化檢查設定

GitHub Actions 工作流程

建立 .github/workflows/pr-checks.yml

name: Pull Request Checks

on:
  pull_request:
    branches: [ main, develop ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run linter
      run: npm run lint
    
    - name: Run tests
      run: npm run test:coverage
    
    - name: Check code coverage
      run: |
        coverage=$(npm run test:coverage:json | grep -o '"total":{"lines":{"pct":[0-9.]*' | grep -o '[0-9.]*$')
        if (( $(echo "$coverage < 80" | bc -l) )); then
          echo "Code coverage is below 80%: $coverage%"
          exit 1
        fi
    
    - name: Build project
      run: npm run build
    
    - name: Check bundle size
      run: npm run bundlesize

分支保護規則

在 GitHub 專案設定中啟用:

# Branch protection rules for 'main'
- Require pull request reviews before merging
  - Required approving reviews: 2
  - Dismiss stale reviews when new commits are pushed
  - Require review from code owners
  - Restrict who can dismiss reviews

- Require status checks to pass before merging
  - Require branches to be up to date before merging
  - Required status checks:
    - test
    - lint
    - build

- Require conversation resolution before merging
- Require signed commits
- Require linear history
- Include administrators in restrictions

處理 PR 回饋的流程

回應審查意見

# 1. 拉取最新的變更(如果有其他協作者)
git pull origin feature/my-branch

# 2. 根據回饋進行修改
echo "根據審查意見修改的內容" > updated-file.js

# 3. 提交修改
git add updated-file.js
git commit -m "根據 Code Review 意見修正驗證邏輯"

# 4. 推送更新
git push origin feature/my-branch

# PR 會自動更新,觸發新的檢查

處理衝突

# 當 PR 與主分支產生衝突時
git fetch origin
git switch feature/my-branch
git merge origin/main

# 解決衝突後
git add .
git commit -m "解決與 main 分支的合併衝突"
git push origin feature/my-branch

壓縮提交(如果需要)

# 使用互動式 rebase 整理提交歷史
git rebase -i origin/main

# 在編輯器中:
# pick a1b2c3d 第一個提交
# squash e4f5g6h 第二個提交  
# squash h7i8j9k 第三個提交

# 強制推送(因為改寫了歷史)
git push --force-with-lease origin feature/my-branch

大型團隊的 PR 策略

程式碼擁有者 (CODEOWNERS)

建立 .github/CODEOWNERS 檔案:

# 全域規則
* @team-leads

# 前端程式碼
/frontend/ @frontend-team @ui-team
*.css @ui-team
*.scss @ui-team

# 後端程式碼  
/backend/ @backend-team
/api/ @api-team

# 資料庫相關
/migrations/ @database-team @senior-devs
/models/ @backend-team @database-team

# 基礎設施
/docker/ @devops-team
/k8s/ @devops-team
*.yml @devops-team
*.yaml @devops-team

# 文件
/docs/ @tech-writers @team-leads
README.md @team-leads

# 安全相關(需要額外審查)
/auth/ @security-team @senior-devs
/payment/ @security-team @backend-team

多階段審查流程

# .github/workflows/staged-review.yml
name: Staged Review Process

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  initial-checks:
    runs-on: ubuntu-latest
    steps:
      - name: Automated Tests
        run: npm test
      - name: Code Quality
        run: npm run lint && npm run type-check
  
  security-scan:
    needs: initial-checks
    if: contains(github.event.pull_request.changed_files, 'auth/') || contains(github.event.pull_request.changed_files, 'payment/')
    runs-on: ubuntu-latest
    steps:
      - name: Security Scan
        run: npm audit && npm run security-scan
  
  performance-check:
    needs: initial-checks  
    if: contains(github.event.pull_request.changed_files, 'frontend/')
    runs-on: ubuntu-latest
    steps:
      - name: Bundle Size Check
        run: npm run bundlesize
      - name: Lighthouse CI
        run: npm run lighthouse-ci

PR 標籤和里程碑管理

自動標籤分配

建立 .github/workflows/auto-label.yml

name: Auto Label PR

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  label:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/labeler@v4
      with:
        repo-token: ${{ secrets.GITHUB_TOKEN }}
        configuration-path: .github/labeler.yml

建立 .github/labeler.yml

# 根據檔案路徑自動加標籤
frontend:
  - frontend/**/*
  - '**/*.css'
  - '**/*.scss'
  - '**/*.js'
  - '**/*.jsx'
  - '**/*.ts'
  - '**/*.tsx'

backend:
  - backend/**/*
  - api/**/*
  - '**/*.py'
  - '**/*.java'
  - '**/*.go'

documentation:
  - docs/**/*
  - '**/*.md'
  - '**/*.rst'

tests:
  - '**/*test*'
  - '**/*spec*'
  - tests/**/*

security:
  - auth/**/*
  - security/**/*
  - '**/*auth*'
  - '**/*security*'

PR 性能優化

減少 PR 大小

# 使用 git 查看 PR 的統計資訊
git diff --stat origin/main..feature/my-branch

# 輸出範例:
# src/components/UserProfile.js    | 145 ++++++++++++++++++++++++++++++++++++
# src/styles/profile.css          |  89 +++++++++++++++++++++++  
# tests/UserProfile.test.js       |  67 ++++++++++++++++++
# 3 files changed, 301 insertions(+)

# 如果變更過多,考慮拆分:
git switch -c feature/user-profile-component
git cherry-pick commit1 commit2
git push -u origin feature/user-profile-component

git switch -c feature/user-profile-styles  
git cherry-pick commit3 commit4
git push -u origin feature/user-profile-styles

並行審查策略

# 對於大型功能,建立多個相關的 PR
# PR 1: 基礎組件
feature/user-profile-base

# PR 2: 樣式設計 (依賴 PR 1)
feature/user-profile-styles  

# PR 3: 互動功能 (依賴 PR 1)
feature/user-profile-interactions

# PR 4: 單元測試 (依賴 PR 1-3)
feature/user-profile-tests

常見 PR 問題和解決方案

問題 1:PR 過大難以審查

解決方案:

# 將大型 PR 拆分成多個小 PR
git log --oneline feature/large-feature

# 建立多個針對性的分支
git switch -c feature/part1 
git cherry-pick commit1 commit2

git switch -c feature/part2
git cherry-pick commit3 commit4

問題 2:審查意見太多難以追蹤

解決方案:

## 審查意見處理清單

### 已處理
- [x] 修正輸入驗證邏輯 (John的建議)
- [x] 更新測試案例 (Sarah的建議)
- [x] 改善錯誤處理 (Mike的建議)

### 處理中  
- [ ] 重構 API 介面 (預計明天完成)
- [ ] 優化資料庫查詢 (需要與 DBA 討論)

### 待討論
- [ ] 是否需要快取機制?(性能 vs 複雜度的權衡)

問題 3:CI/CD 檢查失敗

解決方案:

# 本地重現 CI 環境
docker run -it --rm -v $(pwd):/app node:18-alpine sh
cd /app
npm ci
npm run test
npm run lint
npm run build

# 修復問題後重新推送
git add .
git commit -m "修復 CI 檢查失敗問題"
git push origin feature/my-branch

PR 合併策略

Merge Commit

# 保留完整的分支歷史
git switch main
git merge --no-ff feature/user-profile
# 建立明確的合併提交

Squash and Merge

# 將功能分支的所有提交壓縮成一個
git switch main  
git merge --squash feature/user-profile
git commit -m "新增使用者個人資料功能

- 實作個人資料顯示頁面
- 新增編輯個人資料功能  
- 加入頭像上傳功能
- 完整的單元測試覆蓋

Closes #123"

Rebase and Merge

# 保持線性歷史,但保留個別提交
git switch feature/user-profile
git rebase main
git switch main
git merge --ff-only feature/user-profile

開源專案的 PR 特殊考慮

貢獻者協議 (CLA)

# .github/CONTRIBUTING.md

## 貢獻協議
在提交 Pull Request 前,請確保:

1. 您已閱讀並同意我們的貢獻者許可協議
2. 您的程式碼遵循專案的授權條款  
3. 您有權限貢獻所提交的程式碼

## 首次貢獻者
歡迎首次貢獻者!請查看標有 `good first issue` 的議題。

## 程式碼風格
請執行 `npm run lint` 確保程式碼符合我們的風格指南。

社群互動範本

# .github/workflows/welcome.yml
name: Welcome New Contributors

on:
  pull_request_target:
    types: [opened]

jobs:
  welcome:
    if: github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/first-interaction@v1
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}
          pr-message: |
            感謝您的第一個 Pull Request!🎉
            
            請確保:
            - 您已閱讀我們的貢獻指南
            - 所有測試都通過
            - 程式碼遵循專案風格
            
            我們會盡快審查您的 PR。歡迎加入我們的社群!

小結

Pull Request 是現代軟體開發協作的核心,掌握 PR 工作流程讓你能夠:

核心技能回顧:

  • PR 建立:清楚的描述、適當的規模、完整的測試
  • Code Review:建設性的回饋、關注重點領域、促進學習
  • Fork 流程:參與開源專案的標準方式
  • 自動化檢查:CI/CD 整合、品質把關、效率提升
  • 團隊協作:程式碼擁有者、多階段審查、衝突解決

重要原則:

  1. 小而頻繁:保持 PR 規模適中
  2. 清楚溝通:詳細的描述和建設性的回饋
  3. 品質第一:程式碼審查不僅是找錯誤,更是知識分享
  4. 自動化助力:善用工具提高效率和品質
  5. 持續改進:根據團隊需求調整流程

最佳實踐檢查清單:

## PR 作者檢查清單
- [ ] PR 規模適中(<400 行變更)
- [ ] 清楚的標題和描述
- [ ] 相關測試已加入和通過
- [ ] 自我審查完成
- [ ] 文件已更新
- [ ] CI 檢查通過

## Reviewer 檢查清單  
- [ ] 功能符合需求
- [ ] 程式碼品質良好
- [ ] 安全性考慮
- [ ] 效能影響評估
- [ ] 測試覆蓋充足
- [ ] 文件準確完整

在下一篇文章中,我們將探討多人協作的分支策略與規範,學習如何在大型團隊中建立有效的 Git 工作流程,包括 Conventional Commits 規範和進階的分支管理策略。


記住,優秀的 Pull Request 不僅能提高程式碼品質,更是建立團隊知識共享文化的重要工具。投入時間學習和實踐 PR 最佳實踐,將大大提升你和團隊的開發效率。

404NOTE
404NOTE
文章: 40

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *