# 機能設計書 41-リバート機能

## 概要

本ドキュメントは、OpenCodeにおけるリバート機能の設計を記述する。リバート機能はスナップショット機能の一部として、ファイル変更を以前の状態に復元するための機能を提供する。

### 本機能の処理概要

リバート機能は、Gitベースのスナップショット管理システムと連携し、AIエージェントやユーザーによって行われたファイル変更を、以前のスナップショット状態に安全に復元する機能を提供する。

**業務上の目的・背景**：AIエージェントが自動的にファイルを編集する際、意図しない変更や誤った修正が行われる可能性がある。リバート機能は、このような状況で素早く以前の安定した状態に戻すための安全網として機能し、開発者が安心してAIアシスタントを活用できる環境を提供する。また、複数のファイルにまたがる変更を一括で元に戻すことで、手動での復元作業の手間を大幅に削減する。

**機能の利用シーン**：
- AIエージェントによるコード修正が期待通りでなかった場合の取り消し
- セッション中の特定のメッセージ時点までの変更を巻き戻す場合
- 複数ファイルの変更を一括で復元する場合
- 実験的な変更を行った後、元の状態に戻したい場合

**主要な処理内容**：
1. スナップショットの追跡（track）：現在のワークツリー状態をGitリポジトリに記録
2. パッチの取得（patch）：指定したスナップショットからの変更ファイル一覧を取得
3. 復元（restore）：指定したスナップショットの完全な状態に復元
4. リバート（revert）：特定のパッチに含まれるファイルを個別に復元

**関連システム・外部連携**：GitコマンドラインツールをBunシェル経由で実行し、専用のGitディレクトリ（`.opencode/snapshot`）で変更履歴を管理する。

**権限による制御**：プロジェクトのVCSがGitである場合のみ機能が有効となる。設定ファイルで`snapshot: false`と指定することで無効化可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 26 | セッション | 補助機能 | メッセージのリバート・アンドゥ機能 |
| 2 | セッション画面 | 補助機能 | SessionReviewTabによる変更差分の表示、スナップショットベースの変更追跡 |

## 機能種別

データ復元処理 / バージョン管理連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| snapshot | string | Yes | 復元対象のスナップショットハッシュ | Gitのツリーハッシュ形式 |
| patches | Patch[] | Yes | リバート対象のパッチ配列 | 各パッチにhashとfilesが必要 |

### Patch型定義

```typescript
Patch = {
  hash: string,     // スナップショットハッシュ
  files: string[]   // 変更されたファイルの絶対パス配列
}
```

### 入力データソース

- セッション管理機能から渡されるスナップショットハッシュ
- ファイル変更追跡機能で記録されたパッチ情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| hash | string | 新しいスナップショットのハッシュ（track時） |
| patch | Patch | 変更ファイル情報（patch時） |

### 出力先

- ファイルシステム（復元されたファイル）
- Gitリポジトリ（スナップショット用）

## 処理フロー

### 処理シーケンス

```
1. リバート要求の受信
   └─ セッションまたはUIからパッチ配列を受信

2. ファイル単位の復元ループ
   └─ 各パッチのファイルに対して以下を実行
      ├─ 重複チェック（既に復元済みのファイルはスキップ）
      ├─ git checkout でファイルを復元
      └─ 失敗時の処理
          ├─ ファイルがスナップショットに存在した場合：エラーログ出力
          └─ ファイルがスナップショットに存在しなかった場合：ファイル削除

3. 復元完了
   └─ 復元されたファイルセットをログ出力
```

### フローチャート

```mermaid
flowchart TD
    A[リバート開始] --> B[パッチ配列を受信]
    B --> C{パッチが残っている?}
    C -->|Yes| D[次のパッチを取得]
    D --> E{ファイルが残っている?}
    E -->|Yes| F[次のファイルを取得]
    F --> G{既に復元済み?}
    G -->|Yes| E
    G -->|No| H[git checkout 実行]
    H --> I{成功?}
    I -->|Yes| J[復元済みセットに追加]
    I -->|No| K{ファイルはスナップショットに存在?}
    K -->|Yes| L[エラーログ出力]
    K -->|No| M[ファイル削除]
    J --> E
    L --> E
    M --> E
    E -->|No| C
    C -->|No| N[リバート完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-41-01 | VCS必須 | プロジェクトのVCSがGitでない場合は処理をスキップ | 常時 |
| BR-41-02 | 設定による無効化 | config.snapshot === false の場合は処理をスキップ | 設定ファイル確認時 |
| BR-41-03 | 重複回避 | 同一ファイルは1回のみ復元 | ファイル復元時 |
| BR-41-04 | 新規ファイル削除 | スナップショットに存在しないファイルは削除 | checkout失敗時 |

### 計算ロジック

スナップショット用Gitディレクトリのパス算出：
```
gitdir = {Global.Path.data}/snapshot/{project.id}
```

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

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

本機能はデータベースを直接操作しない。Gitリポジトリを通じたファイル管理を行う。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | checkout失敗 | git checkout コマンドが失敗 | スナップショット内のファイル存在確認を実施 |
| - | ファイル削除失敗 | unlink が失敗 | エラーを無視して継続 |

### リトライ仕様

リトライは実装されていない。エラー発生時はログ出力の上、次のファイル処理に進む。

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

ファイル単位での復元を行い、全体としてのトランザクションは提供しない。一部のファイル復元が失敗しても、他のファイルの復元は継続する。

## パフォーマンス要件

- 各ファイルの復元はGitコマンドの実行速度に依存
- 大量のファイル復元時は順次処理となる

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

- ワークツリー外のファイルへのアクセスは行わない（Instance.worktree を基準）
- Gitコマンドは専用ディレクトリで実行され、元のリポジトリには影響しない

## 備考

- ACPクライアント（Flag.OPENCODE_CLIENT === "acp"）では無効化される
- スナップショットのクリーンアップは1時間ごとに自動実行され、7日以上経過したデータが削除される

---

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

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

### 推奨読解順序

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

スナップショットとパッチのデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.ts | `packages/opencode/src/snapshot/index.ts` | Patch型定義（79-83行目）、FileDiff型定義（185-197行目）を確認 |

**読解のコツ**: Zodスキーマを使用した型定義パターン。`z.object()` で構造を定義し、`z.infer<typeof Patch>` で TypeScript型を導出している。

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

スナップショット管理のメイン関数を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/opencode/src/snapshot/index.ts` | revert関数（131-161行目）がリバートのメイン処理 |

**主要処理フロー**:
1. **131行目**: revert関数の開始、パッチ配列を受け取る
2. **132行目**: 復元済みファイルを追跡するSetを初期化
3. **133行目**: gitdir()でスナップショット用Gitディレクトリパスを取得
4. **134-160行目**: 二重ループでパッチとファイルを処理
5. **138-141行目**: git checkout でファイル復元を試行
6. **144-156行目**: 失敗時の処理（存在確認と削除）

#### Step 3: 関連処理を理解する

復元以外のスナップショット関連処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.ts | `packages/opencode/src/snapshot/index.ts` | track関数（51-77行目）: ワークツリー状態の記録 |
| 3-2 | index.ts | `packages/opencode/src/snapshot/index.ts` | restore関数（112-129行目）: 完全復元 |
| 3-3 | index.ts | `packages/opencode/src/snapshot/index.ts` | patch関数（85-110行目）: 差分ファイル取得 |

**主要処理フロー**:
- **51-77行目（track）**: `git write-tree` でツリーハッシュを生成
- **112-129行目（restore）**: `git read-tree` + `git checkout-index` で完全復元
- **85-110行目（patch）**: `git diff --name-only` で変更ファイル一覧取得

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

```
Session / UI
    │
    ├─ Snapshot.revert(patches)
    │      ├─ gitdir() - スナップショットディレクトリパス取得
    │      ├─ git checkout ${hash} -- ${file} - ファイル復元
    │      ├─ git ls-tree ${hash} -- ${relativePath} - 存在確認
    │      └─ fs.unlink(file) - 新規ファイル削除
    │
    ├─ Snapshot.restore(snapshot)
    │      └─ git read-tree + checkout-index - 完全復元
    │
    └─ Snapshot.track()
           ├─ add(git) - ファイルをステージング
           └─ git write-tree - ツリーハッシュ生成
```

### データフロー図

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

patches[] ───▶ revert()
                │
                ├─ git checkout ───▶ 復元されたファイル
                │
                └─ fs.unlink ───▶ 削除されたファイル

snapshot ───▶ restore()
                │
                └─ git read-tree
                   checkout-index ───▶ 完全復元されたワークツリー
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/opencode/src/snapshot/index.ts` | ソース | スナップショット管理のメインモジュール |
| instance.ts | `packages/opencode/src/project/instance.ts` | ソース | プロジェクトインスタンス管理、worktree情報提供 |
| config.ts | `packages/opencode/src/config/config.ts` | ソース | 設定情報の取得（snapshot設定） |
| global.ts | `packages/opencode/src/global.ts` | ソース | グローバルパス情報（data ディレクトリ） |
