# 画面設計書 27-プロジェクトフォーク

## 概要

本ドキュメントは、GitLabのプロジェクトフォーク画面に関する設計書である。この画面は既存のプロジェクトをフォーク（複製）して、新しい名前空間に独自のコピーを作成するための機能を提供する。

### 本画面の処理概要

プロジェクトフォーク画面は、ユーザーが既存のプロジェクトを自分の名前空間（個人またはグループ）にコピーするためのインターフェースを提供する。フォーク先の名前空間を選択し、必要に応じてプロジェクト名やパス、可視性レベルを変更してフォークを実行できる。

**業務上の目的・背景**：フォークはオープンソース開発やコラボレーションにおいて重要な機能である。コントリビューターは元のプロジェクトに直接書き込み権限がなくても、フォークを作成して独自の変更を加え、後でマージリクエストを通じて変更を提案できる。また、既存のプロジェクトをベースに新しいプロジェクトを開始する場合にも活用される。

**画面へのアクセス方法**：
- プロジェクト詳細画面の「Fork」ボタンをクリック
- フォーク一覧画面の「Fork」ボタンをクリック
- 直接URL `/:namespace/:project/-/forks/new` にアクセス

**主要な操作・処理内容**：
1. フォーク先名前空間の選択 - 個人名前空間またはグループを選択
2. プロジェクト情報のカスタマイズ - 名前、パス、説明の変更（オプション）
3. 可視性レベルの選択 - フォーク先の可視性を設定
4. フォークの実行 - プロジェクトのコピーを作成

**画面遷移**：
- 遷移元：プロジェクト詳細画面、フォーク一覧画面
- 遷移先：フォーク成功時は新しいプロジェクト詳細画面、インポート中はインポート状態画面

**権限による表示制御**：
- 未ログインユーザー：アクセス不可（ログイン必須）
- ログインユーザー：`fork_project`権限がある場合にアクセス可能
- フォーク先権限：選択した名前空間への`create_projects`権限が必要

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 7 | プロジェクトフォーク | 主機能 | プロジェクトのフォーク実行 |

## 画面種別

登録

## URL/ルーティング

- URL: `/:namespace/:project/-/forks/new`
- ルーティング: `config/routes/project.rb` - `resources :forks, only: [:index, :new, :create]`
- コントローラ: `Projects::ForksController#new`, `Projects::ForksController#create`

## 入出力項目

| 項目名 | 入力/出力 | 必須 | データ型 | 説明 |
|--------|----------|------|----------|------|
| namespace_key | 入力 | 必須 | Integer | フォーク先名前空間のID |
| name | 入力 | - | String | フォーク先プロジェクト名（デフォルトは元プロジェクト名） |
| path | 入力 | - | String | フォーク先プロジェクトパス（デフォルトは元プロジェクトパス） |
| description | 入力 | - | Text | フォーク先プロジェクトの説明 |
| visibility | 入力 | - | Enum | フォーク先の可視性（デフォルトは元プロジェクトと同じ） |

## 表示項目

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| フォークイラスト | Image | フォーク操作を示すイラスト |
| 元プロジェクト情報 | Object | フォーク元プロジェクトの名前、パス |
| 名前空間セレクター | List | フォーク可能な名前空間一覧 |
| プロジェクト名入力 | Input | フォーク先プロジェクト名 |
| プロジェクトパス入力 | Input | フォーク先URLパス |
| プロジェクト説明 | Textarea | フォーク先プロジェクトの説明 |
| 可視性選択 | Radio | public/internal/private |
| デフォルトブランチ | Text | フォーク先のデフォルトブランチ |

## イベント仕様

### 1-ページ読み込み（HTML）

1. `Projects::ForksController#new` が呼び出される
2. `authenticate_user!` でログイン確認
3. `authorize_fork_project!` でフォーク権限チェック
4. `can_fork_to?(current_user.namespace)` で個人名前空間へのフォーク可否確認
5. Vue.jsマウントポイント `#fork-groups-mount-element` をレンダリング

### 2-ページ読み込み（JSON）

1. `Projects::ForksController#new` がJSONフォーマットで呼び出される
2. `load_namespaces_with_associations` でフォーク可能な名前空間を取得
3. `ForkNamespaceSerializer` で名前空間情報をシリアライズ
4. 既存のフォーク情報（`forked_projects_by_namespace`）を含めて返却

### 3-フォーク実行

1. フォームで名前空間を選択してサブミット
2. `Projects::ForksController#create` が呼び出される
3. `authorize_fork_namespace!` でフォーク先権限チェック
4. `Projects::ForkService` でフォーク実行
5. 成功時：新プロジェクト詳細画面にリダイレクト（またはインポート状態画面）
6. 失敗時：エラー画面を表示

### 4-既存フォークの確認

- 選択した名前空間に既にフォークが存在する場合は、既存フォークにリダイレクト
- 複数の名前空間にフォーク可能でない場合はフォークボタンが既存フォークへのリンクに変わる

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| フォーク実行 | projects | INSERT | フォーク先プロジェクト作成 |
| フォーク実行 | fork_network_members | INSERT | フォークネットワーク関係追加 |
| フォーク実行 | fork_networks | INSERT/SELECT | フォークネットワーク取得/作成 |
| フォーク実行 | project_features | INSERT | 機能設定の初期化 |

### テーブル別更新項目詳細

#### projects

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | name | 入力値またはデフォルト | フォーク先プロジェクト名 |
| INSERT | path | 入力値またはデフォルト | フォーク先パス |
| INSERT | namespace_id | 選択した名前空間ID | フォーク先名前空間 |
| INSERT | visibility_level | 入力値またはデフォルト | 可視性レベル |

#### fork_network_members

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | project_id | 新規プロジェクトID | フォーク先プロジェクト |
| INSERT | fork_network_id | 元プロジェクトのfork_network_id | フォークネットワーク |
| INSERT | forked_from_project_id | 元プロジェクトID | フォーク元プロジェクト |

## メッセージ仕様

| 種別 | メッセージID | メッセージ内容 | 表示条件 |
|------|------------|--------------|---------|
| 成功 | fork_success | The project 'xxx' was successfully forked. | フォーク成功時 |
| エラー | fork_error | Fork failed | フォーク失敗時 |
| 情報 | go_to_fork | Go to your fork | 既にフォーク済みの場合 |

## 例外処理

| 例外状況 | 処理内容 |
|---------|---------|
| ログインしていない | ログイン画面にリダイレクト |
| フォーク権限がない | 404エラー |
| フォーク先名前空間が無効 | アクセス拒否 |
| 既に同じパスのプロジェクトが存在 | バリデーションエラー |
| 可視性制限違反 | バリデーションエラー |

## 備考

- フォークはGitリポジトリの完全なコピーを作成
- 大きなリポジトリの場合、フォークはバックグラウンドで実行される
- フォーク元とフォーク先はフォークネットワークで関連付けられる
- 可視性レベルは元プロジェクトより高くできない（publicをprivateにはできるが、privateをpublicにはできない）
- `restricted_visibility_levels`設定により一部の可視性が制限される場合がある

---

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

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

### 推奨読解順序

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

フォークに関連するモデルとサービスを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | fork_network.rb | `app/models/fork_network.rb` | フォークネットワークの構造 |
| 1-2 | fork_network_member.rb | `app/models/fork_network_member.rb` | フォーク関係の関連付け |
| 1-3 | fork_service.rb | `app/services/projects/fork_service.rb` | フォークロジック |

**読解のコツ**: `Projects::ForkService#execute`メソッドでフォーク処理の全体フローを把握する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | forks_controller.rb | `app/controllers/projects/forks_controller.rb` | new/createアクション |

**主要処理フロー**:
1. **43-66行目**: `new` アクション（HTML/JSON両対応）
2. **46行目**: 個人名前空間へのフォーク可否確認
3. **51行目**: フォーク可能な名前空間の取得
4. **68-88行目**: `create` アクション
5. **69行目**: 既存フォークの確認
6. **73行目**: `fork_service.execute` でフォーク実行
7. **108-112行目**: `fork_service` メソッド - ForkServiceの初期化

#### Step 3: ビュー層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | new.html.haml | `app/views/projects/forks/new.html.haml` | メインビュー |

**主要処理フロー**:
- **3-15行目（new.html.haml）**: `#fork-groups-mount-element` Vue.jsマウントポイントとdata属性

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

```
Projects::ForksController#new
    │
    ├─ authenticate_user!
    │
    ├─ authorize_fork_project!
    │
    ├─ can_fork_to?(current_user.namespace)
    │      └─ ForkTargetsFinder.new(@project, current_user).execute
    │
    └─ render (HTML) / JSON (namespaces)
           │
           ├─ load_namespaces_with_associations
           │      └─ fork_service.valid_fork_targets(only_groups: true)
           │
           └─ ForkNamespaceSerializer.represent

Projects::ForksController#create
    │
    ├─ authorize_fork_namespace!
    │      └─ fork_service.valid_fork_target?
    │
    ├─ 既存フォークチェック
    │      └─ fork_namespace.projects.find_by(path: project.path)
    │
    └─ fork_service.execute
           │
           └─ Projects::ForkService#execute
                  │
                  ├─ create_project
                  │
                  ├─ GitリポジトリFork
                  │
                  └─ fork_network_member作成
```

### データフロー図

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

リクエスト ───▶ ForksController#new ───▶ Vue.jsアプリ
    │                     │
    │                     ├─ JSON API ───▶ 名前空間一覧
    │                     │
    └─ フォーム送信 ───▶ ForksController#create
                              │
                              ├─ Projects::ForkService
                              │      │
                              │      ├─ プロジェクト作成
                              │      │
                              │      └─ Gitリポジトリコピー
                              │
                              └─────────────────▶ リダイレクト
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| forks_controller.rb | `app/controllers/projects/forks_controller.rb` | コントローラ | リクエストハンドリング |
| new.html.haml | `app/views/projects/forks/new.html.haml` | テンプレート | メインビュー |
| fork_service.rb | `app/services/projects/fork_service.rb` | サービス | フォークロジック |
| fork_namespace_serializer.rb | `app/serializers/fork_namespace_serializer.rb` | シリアライザ | 名前空間情報シリアライズ |
| fork_targets_finder.rb | `app/finders/fork_targets_finder.rb` | ファインダー | フォーク可能先検索 |
| fork_network.rb | `app/models/fork_network.rb` | モデル | フォークネットワーク |
| fork_network_member.rb | `app/models/fork_network_member.rb` | モデル | フォーク関係 |
