# 機能設計書 22-差分表示（Diff）

## 概要

本ドキュメントは、GitLabにおける差分表示（Diff）機能の設計を定義する。コミット間、ブランチ間の差分を比較・表示し、コードの変更内容を視覚的に確認できる機能である。

### 本機能の処理概要

差分表示機能は、2つのGit参照（ブランチ、タグ、コミット）間のコード変更を比較し、追加・削除・変更された行を視覚的に表示する機能である。

**業務上の目的・背景**：コードレビューやマージリクエストの評価において、変更内容を正確に把握することは品質管理の基礎である。本機能により、レビュアーは変更箇所を効率的に特定し、問題のある変更を早期に発見できる。また、ブランチ統合前の事前確認や、リリース間の変更追跡にも活用される。開発チームの生産性向上とコード品質維持に不可欠な機能である。

**機能の利用シーン**：
- マージリクエスト作成前に変更内容を確認する
- コードレビュー時に変更箇所をレビューする
- 2つのブランチ間の差異を確認する
- リリースバージョン間の変更を調査する
- 特定のコミットでの変更内容を確認する

**主要な処理内容**：
1. ソース参照（from）とターゲット参照（to）の指定受付
2. 2つの参照間の差分計算（CompareService）
3. 差分のフォーマット（unified/side-by-side）選択
4. 変更ファイル一覧の表示
5. パッチ形式・diff形式でのダウンロード
6. コミット署名の検証と表示

**関連システム・外部連携**：Gitalyサーバーとの連携によるGit差分計算、CI/CDパイプラインとの連携（差分に関連するパイプライン状態表示）

**権限による制御**：リポジトリへの読み取り権限（read_code）が必要。プライベートプロジェクトではプロジェクトメンバーまたは適切な権限を持つユーザーのみアクセス可能。クロスプロジェクト比較の場合は両プロジェクトへの読み取り権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 42 | 比較画面 | 主機能 | ブランチ・タグ間の比較設定 |
| 43 | 比較結果 | 主機能 | 比較結果の差分表示 |
| 41 | コミット詳細 | 補助機能 | コミット差分の表示 |

## 機能種別

データ参照・表示処理（SELECT）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| from | String | Yes | 比較元の参照（ブランチ名、タグ名、SHA） | 有効なGit参照 |
| to | String | Yes | 比較先の参照（ブランチ名、タグ名、SHA） | 有効なGit参照 |
| from_project_id | Integer | No | 比較元プロジェクトID（フォーク間比較用） | 有効なプロジェクトID |
| straight | Boolean | No | 直接比較モード（3ドット/2ドット） | true/false |
| old_path | String | No | 旧ファイルパス（リネーム追跡用） | - |
| new_path | String | No | 新ファイルパス（リネーム追跡用） | - |
| file_path | String | No | 特定ファイルの差分表示用 | - |

### 入力データソース

- URL パス: `/project_namespace/project_name/-/compare/:from...:to`
- URL パス（2ドット）: `/project_namespace/project_name/-/compare/:from..:to`
- フォーム入力によるブランチ・タグ選択

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| compare | Compare | 比較結果オブジェクト |
| compare.commits | Array<Commit> | 差分に含まれるコミット一覧 |
| compare.diffs | Gitlab::Diff::FileCollection | 差分ファイルコレクション |
| diffs.diff_files | Array<Gitlab::Diff::File> | 差分ファイル一覧 |
| diff_file.old_path | String | 変更前のファイルパス |
| diff_file.new_path | String | 変更後のファイルパス |
| diff_file.diff | String | 差分内容（unified形式） |
| diff_file.added_lines | Integer | 追加行数 |
| diff_file.removed_lines | Integer | 削除行数 |

### 出力先

- HTML: ブラウザへのレンダリング（インライン/サイドバイサイド）
- Patch: パッチファイル形式でのダウンロード
- Diff: diff形式でのダウンロード

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信・認証確認
   └─ authorize_read_code!
2. 参照のバリデーション
   └─ validate_refs! で from/to の有効性確認
3. ターゲットプロジェクトの決定
   └─ フォーク間比較の場合は from_project_id を参照
4. 比較処理の実行
   └─ CompareService#execute で差分計算
5. 関連データの取得
   └─ コミット情報、マージリクエスト情報、環境情報
6. レスポンス形式に応じた出力
   └─ HTML/Patch/Diff形式でレンダリング
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{from/to が有効?}
    B -->|No| C[エラー表示・リダイレクト]
    B -->|Yes| D{from_project_id指定?}
    D -->|Yes| E[ターゲットプロジェクト取得]
    E --> F{読み取り権限あり?}
    F -->|No| G[ソースプロジェクト使用]
    F -->|Yes| H[指定プロジェクト使用]
    D -->|No| I[デフォルトターゲット使用]
    G --> J[CompareService実行]
    H --> J
    I --> J
    J --> K{比較結果あり?}
    K -->|No| L[空の結果表示]
    K -->|Yes| M{レスポンス形式}
    M -->|HTML| N[差分HTML表示]
    M -->|Patch| O[パッチダウンロード]
    M -->|Diff| P[Diffダウンロード]
    L --> Q[終了]
    N --> Q
    O --> Q
    P --> Q
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-22-01 | 3ドット比較 | マージベースからの差分を表示（デフォルト） | straight=false または未指定 |
| BR-22-02 | 2ドット比較 | 直接的な差分を表示 | straight=true |
| BR-22-03 | 参照検証 | 無効な参照はエラー表示とリダイレクト | 常時 |
| BR-22-04 | クロスプロジェクト | フォーク元/先間の比較が可能 | from_project_id指定時 |
| BR-22-05 | コミット表示制限 | 安全なサイズ以内のみパイプライン情報取得 | コミット数 < COMMITS_SAFE_SIZE |

### 計算ロジック

**3ドット比較（A...B）**：
- AとBの共通祖先（マージベース）からBまでの差分を表示
- マージリクエストで通常使用される比較方式

**2ドット比較（A..B）**：
- AからBへの直接的な差分を表示
- コミットの実際の違いを確認する際に使用

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 差分計算 | - | - | Gitalyから直接取得（DBアクセスなし） |
| MR検索 | merge_requests | SELECT | 同一ブランチのオープンMRを検索 |
| 環境取得 | environments | SELECT | 関連する環境情報を取得 |
| パイプライン取得 | ci_pipelines | SELECT | コミットに紐づくパイプライン情報 |

### テーブル別操作詳細

#### merge_requests

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | source_branch, target_branch等 | source_project, source_branch=head_ref, target_branch=start_ref | オープン状態のMRのみ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 302 | Redirect | from/to が未指定または無効 | 比較フォームにリダイレクト |
| 404 | NotFound | 比較結果がない場合（Patch/Diff） | エラーページ表示 |
| Flash Alert | ValidationError | 無効なブランチ名 | フラッシュメッセージ表示 |

### リトライ仕様

特になし（読み取り専用操作）

## トランザクション仕様

読み取り専用のため、トランザクション管理は不要

## パフォーマンス要件

- 差分計算はGitalyに委譲し、アプリケーション層での負荷を軽減
- コミット数が大きい場合はパイプライン情報取得をスキップ（COMMITS_SAFE_SIZE制限）
- RapidDiffsプレゼンターによる効率的な差分表示

## セキュリティ考慮事項

- authorize_read_code! による読み取り権限の確認
- クロスプロジェクト比較時は両プロジェクトへのアクセス権確認
- URLパラメータのエスケープ処理（Addressable::URI.unescape）

## 備考

- 大きな差分は折り畳み表示
- バイナリファイルは差分非表示（サイズのみ表示）
- シンタックスハイライト対応

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

比較結果を格納するデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | compare.rb | `app/models/compare.rb` | 比較結果を表すモデル、commits/diffs アクセサ |
| 1-2 | file.rb | `lib/gitlab/diff/file.rb` | 差分ファイルを表すクラス |

**読解のコツ**: Compare オブジェクトがどのようにコミット一覧と差分情報を保持するかを理解する。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | compare_controller.rb | `app/controllers/projects/compare_controller.rb` | コントローラーの主要アクション |

**主要処理フロー**:
1. **28-30行目**: index - 比較パラメータ設定画面
2. **32-54行目**: show - 差分表示のメインアクション
3. **56-60行目**: diff_for_path - 特定ファイルの差分取得
4. **62-74行目**: create - 比較実行とリダイレクト
5. **142-146行目**: compare - CompareService呼び出し

#### Step 3: 比較サービスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | compare_service.rb | `app/services/compare_service.rb` | 差分計算のコアロジック |

**主要処理フロー**:
- **15-21行目**: execute - リポジトリ間の比較実行
- compare_source_branch メソッドでGitalyに差分計算を依頼

#### Step 4: ターゲットプロジェクト決定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | target_projects.rb | `app/controllers/concerns/projects/target_projects.rb` | フォーク間比較のプロジェクト取得 |

**主要処理フロー**:
- **115-131行目**: target_project - 比較元プロジェクトの決定ロジック

### プログラム呼び出し階層図

```
CompareController#show
    │
    ├─ validate_refs!
    │      └─ valid_ref? - 参照の有効性確認
    │
    ├─ target_project
    │      └─ from_project_id からプロジェクト取得
    │
    ├─ compare
    │      └─ CompareService#execute
    │             └─ Repository#compare_source_branch
    │                    └─ Gitaly::CommitService
    │
    ├─ define_commits
    │      ├─ compare.commits
    │      └─ with_latest_pipeline
    │
    ├─ define_environment
    │      └─ EnvironmentsByDeploymentsFinder
    │
    └─ respond_to (HTML/Patch/Diff)
           ├─ send_git_patch
           └─ send_git_diff
```

### データフロー図

```
[入力]                    [処理]                         [出力]

URL パラメータ    ───▶  CompareController       ───▶  HTML/Patch/Diff
(from, to)              │
                        ▼
                   validate_refs!
                   (参照検証)
                        │
                        ▼
                   CompareService#execute
                        │
                        ▼
                   Gitaly Server ──────────────────▶  差分情報
                   (Git差分計算)                      コミット一覧
                        │
                        ▼
                   RapidDiffs::ComparePresenter ────▶  整形された差分表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| compare_controller.rb | `app/controllers/projects/compare_controller.rb` | コントローラー | 差分表示のエントリーポイント |
| compare_service.rb | `app/services/compare_service.rb` | サービス | 差分計算ロジック |
| compare.rb | `app/models/compare.rb` | モデル | 比較結果モデル |
| target_projects.rb | `app/controllers/concerns/projects/target_projects.rb` | Concern | ターゲットプロジェクト取得 |
| diff_helper.rb | `app/helpers/diff_helper.rb` | ヘルパー | 差分表示ヘルパー |
| compare_helper.rb | `app/helpers/compare_helper.rb` | ヘルパー | 比較画面ヘルパー |
| file.rb | `lib/gitlab/diff/file.rb` | ライブラリ | 差分ファイルクラス |
| compare_presenter.rb | `app/presenters/rapid_diffs/compare_presenter.rb` | プレゼンター | 差分表示プレゼンター |
| show.html.haml | `app/views/projects/compare/show.html.haml` | ビュー | 比較結果表示テンプレート |
| index.html.haml | `app/views/projects/compare/index.html.haml` | ビュー | 比較フォームテンプレート |
