Note: このIssueは 2026-03-08 にレビュー結果(Stage 1, Stage 3 影響範囲レビュー, Stage 5 通常レビュー2回目, Stage 7 影響範囲レビュー2回目)を反映して更新されました。
詳細: dev-reports/issue/447/issue-review/
概要
ワークツリー詳細画面の左ペインタブ(履歴 / Files / CMATE)に「Git」タブを追加し、Git変更履歴の確認とファイルdiff表示を可能にする。
背景・課題
- 現在、Gitの変更履歴やdiffを確認するにはターミナルで
git log / git diff を実行する必要がある
- コーディングエージェントが行った変更内容を素早く確認したい
- コミット履歴と変更ファイルの関係をGUI上で直感的に把握したい
提案する解決策
Gitタブの追加
左ペインのタブに「Git」を追加し、以下の機能を提供する。
1. 変更履歴表示(デフォルト表示)
- Gitタブ選択時、コミット履歴を一覧表示(
git log 相当)
- デフォルト取得件数は最大50件(
--max-count=50)とし、大規模リポジトリでのタイムアウト・メモリ圧迫を防止する
- 各コミットにはコミットメッセージ、作成者、日時を表示
- コミットを選択すると、そのコミットで変更されたファイル一覧を表示(
git show --stat 相当)
- 将来拡張として「Load More」によるページネーション対応を検討する
2. Diff表示
- 変更ファイルを選択すると、右ペイン(ファイル内容表示領域)にdiffを表示
- unified diff形式を採用(コンテキスト行数はデフォルト3行)
- 追加行(緑)/ 削除行(赤)の色分け表示
- ファイル単位のdiff(
git diff 相当)
- 初期実装では各コミットの直前コミットとのdiff(
git show 相当)のみ対応。2コミット間比較やWorking tree changesの表示は将来拡張とする
3. モバイル対応
- モバイルビュー(MobileTabBar)ではGitタブを非表示とする(現在5タブで画面幅が逼迫しているため、6タブ化はUIの破綻リスクがある)
- モバイルでのGit履歴確認はターミナルタブからの
git log コマンドで代替する
- モバイルへのGitタブ追加は将来の別Issueとして検討する
APIエンドポイント設計
既存の /api/worktrees/[id]/xxx パターンに準拠する。
GET /api/worktrees/[id]/git/log
- クエリパラメータ:
limit(デフォルト: 50), offset(デフォルト: 0)
- レスポンス:
{ commits: CommitInfo[] }
CommitInfo: { hash: string, message: string, author: string, date: string }
- タイムアウト: git log用に専用のタイムアウト定数(
GIT_LOG_TIMEOUT_MS = 3000、推奨値: 3000-5000ms)を定義する。既存の GIT_COMMAND_TIMEOUT_MS(1000ms)はgit log実行には不足する可能性があるため、execGitCommand にタイムアウト引数を追加するか、git log専用定数を使用すること
GET /api/worktrees/[id]/git/show/[commitHash]
- レスポンス:
{ commit: CommitInfo, files: ChangedFile[] }
ChangedFile: { path: string, status: 'added' | 'modified' | 'deleted' | 'renamed' }
- バリデーション:
commitHash は [0-9a-f]{7,40} の正規表現でバリデーション
GET /api/worktrees/[id]/git/diff
- クエリパラメータ:
commit(コミットハッシュ), file(ファイルパス)
- レスポンス:
{ diff: string }(unified diff形式)
- バリデーション:
commit は [0-9a-f]{7,40} でバリデーション、file は isPathSafe() でバリデーション
セキュリティ要件
- すべてのAPIで
execFile を使用すること(exec 禁止)
'--' セパレータを使用してgitオプション注入を防止
- worktreePathはDB由来の信頼されたソースのみ使用(既存の方針を踏襲)
- すべての新規APIルートで
isValidWorktreeId(params.id) によるworktree IDバリデーションを実装すること(既存APIルートの実装パターンを踏襲。例: src/app/api/worktrees/[id]/route.ts の25行目参照)。なお、isValidWorktreeId は src/lib/auto-yes-manager.ts からimportする(既存の全7 APIルートと同じパターン)。import元のモジュール名は意味的に不自然だが、既存パターンの踏襲を優先する。将来的には validation専用モジュールへの移動を別Issueで検討する
実装タスク
タブ追加
API実装
UI実装
モバイル対応
テスト
受入条件
基本機能
エラーハンドリング・UI状態
モバイル対応
セキュリティ
テスト
スコープ外(将来拡張)
以下は本Issueのスコープ外とし、必要に応じて別Issueとして作成する:
- ページネーション(Load More / 無限スクロール)
- 2コミット間の任意比較
- Working tree changes(未コミット変更)の表示
- side-by-side diff表示
- モバイルビューへのGitタブ追加
- LeftPaneTabSwitcherのi18n化
- LeftPaneTab型の定義一元化(現在2箇所に重複)
- git log結果のキャッシュ(パフォーマンス問題発生時に検討。
tmux-capture-cache.ts のTTL+singleflightパターンが参考になる)
isValidWorktreeId のvalidation専用モジュールへの移動(現在 auto-yes-manager.ts に定義されており、意味的に不適切な依存が発生している)
概要
ワークツリー詳細画面の左ペインタブ(履歴 / Files / CMATE)に「Git」タブを追加し、Git変更履歴の確認とファイルdiff表示を可能にする。
背景・課題
git log/git diffを実行する必要がある提案する解決策
Gitタブの追加
左ペインのタブに「Git」を追加し、以下の機能を提供する。
1. 変更履歴表示(デフォルト表示)
git log相当)--max-count=50)とし、大規模リポジトリでのタイムアウト・メモリ圧迫を防止するgit show --stat相当)2. Diff表示
git diff相当)git show相当)のみ対応。2コミット間比較やWorking tree changesの表示は将来拡張とする3. モバイル対応
git logコマンドで代替するAPIエンドポイント設計
既存の
/api/worktrees/[id]/xxxパターンに準拠する。GET /api/worktrees/[id]/git/loglimit(デフォルト: 50),offset(デフォルト: 0){ commits: CommitInfo[] }CommitInfo:{ hash: string, message: string, author: string, date: string }GIT_LOG_TIMEOUT_MS = 3000、推奨値: 3000-5000ms)を定義する。既存のGIT_COMMAND_TIMEOUT_MS(1000ms)はgit log実行には不足する可能性があるため、execGitCommandにタイムアウト引数を追加するか、git log専用定数を使用することGET /api/worktrees/[id]/git/show/[commitHash]{ commit: CommitInfo, files: ChangedFile[] }ChangedFile:{ path: string, status: 'added' | 'modified' | 'deleted' | 'renamed' }commitHashは[0-9a-f]{7,40}の正規表現でバリデーションGET /api/worktrees/[id]/git/diffcommit(コミットハッシュ),file(ファイルパス){ diff: string }(unified diff形式)commitは[0-9a-f]{7,40}でバリデーション、fileはisPathSafe()でバリデーションセキュリティ要件
execFileを使用すること(exec禁止)'--'セパレータを使用してgitオプション注入を防止isValidWorktreeId(params.id)によるworktree IDバリデーションを実装すること(既存APIルートの実装パターンを踏襲。例:src/app/api/worktrees/[id]/route.tsの25行目参照)。なお、isValidWorktreeIdはsrc/lib/auto-yes-manager.tsからimportする(既存の全7 APIルートと同じパターン)。import元のモジュール名は意味的に不自然だが、既存パターンの踏襲を優先する。将来的には validation専用モジュールへの移動を別Issueで検討する実装タスク
タブ追加
LeftPaneTab型に'git'を追加(src/types/ui-state.ts)LeftPaneTab型に'git'を追加(src/components/worktree/LeftPaneTabSwitcher.tsx19行目) --- 同一の型定義が2箇所に存在するため、両方を同期的に更新する必要がある。片方のみ更新すると型不整合によるコンパイルエラーまたはランタイムエラーが発生する。理想的にはui-state.tsに一元化すべきだが、本Issueでは最低限両方の更新を必須とするWorktreeDetailRefactored.tsxの変更: (1) デスクトップ用leftPaneContentのuseMemo(2037行目付近)内にleftPaneTab === 'git'の条件分岐を追加し、新規GitPaneコンポーネントをレンダリング (2) 新規GitPaneコンポーネントのimport追加。なお、モバイル用renderMobileContentの switch文(880行目付近)にはGitケースを追加しない(MobileTabBarにGitタブがないため到達しない)LeftPaneTabSwitcher.tsxにGitタブを追加(ラベル:'Git'、ハードコード。i18n化は別Issue)useWorktreeUIStatereducerの対応確認(※ reducer側の変更は不要。SET_LEFT_PANE_TABアクションはLeftPaneTab型をペイロードとして受け取り、直接代入する設計のため、型定義の更新のみで対応完了)API実装
GET /api/worktrees/[id]/git/logエンドポイントの実装(git log --max-count=50 --format=...ベース)GET /api/worktrees/[id]/git/show/[commitHash]エンドポイントの実装GET /api/worktrees/[id]/git/diffエンドポイントの実装[0-9a-f]{7,40})の実装isPathSafe())の実装'--'セパレータによるgitオプション注入防止isValidWorktreeId(params.id)によるworktree IDバリデーションを実装(既存パターン踏襲。import { isValidWorktreeId } from '@/lib/auto-yes-manager'を使用)git-utils.tsのexecGitCommandをexportするか、新規関数(getGitLog,getGitShow,getGitDiff)をgit-utils.tsに追加して内部でexecGitCommandを再利用する設計とする。タイムアウト引数の追加(git log用: 3000-5000ms)も合わせて実装する。注意:execGitCommandにタイムアウト引数を追加する場合、オプショナル引数(timeout?: number、デフォルトGIT_COMMAND_TIMEOUT_MS= 1000ms)とし、既存のgetGitStatus内3箇所の呼び出しに影響しないよう後方互換性を維持することGIT_LOG_TIMEOUT_MS = 3000、推奨3000-5000ms)の定義UI実装
モバイル対応
MobileTab型)にはGitタブを追加しない(意図的な除外)テスト
LeftPaneTabSwitcher.test.tsxにGitタブのレンダリング・クリックテストを追加(既存テストは3タブ前提のため4タブに更新)git-utils.test.tsに新規関数(getGitLog/getGitShow/getGitDiff)のテストを追加受入条件
基本機能
エラーハンドリング・UI状態
モバイル対応
セキュリティ
isValidWorktreeId(params.id)によるworktree IDバリデーションが実装されているテスト
npm run test:unit)がパスするスコープ外(将来拡張)
以下は本Issueのスコープ外とし、必要に応じて別Issueとして作成する:
tmux-capture-cache.tsのTTL+singleflightパターンが参考になる)isValidWorktreeIdのvalidation専用モジュールへの移動(現在auto-yes-manager.tsに定義されており、意味的に不適切な依存が発生している)