|
| 1 | +# Worktree Entry Latency Plan |
| 2 | + |
| 3 | +## Goal |
| 4 | + |
| 5 | +`create` と `batch delete` の初期レスポンスを改善する。 |
| 6 | + |
| 7 | +今回の主眼は「既存の挙動を壊さずに速くすること」であり、UI 文言や選択フローの変更は目的に含めない。 |
| 8 | + |
| 9 | +## Current Status |
| 10 | + |
| 11 | +以下は実装済み。 |
| 12 | + |
| 13 | +- Phase 1 |
| 14 | + - `create` は `has_linked_worktrees()` を使う |
| 15 | +- Phase 2 |
| 16 | + - `batch delete` は `list_worktrees_basic()` を使う |
| 17 | + - `is_branch_unique_to_worktree()` は軽量一覧ベース |
| 18 | +- Phase 3 |
| 19 | + - `rename / switch / search` も軽量一覧を使う |
| 20 | + |
| 21 | +以下は意図的に未変更。 |
| 22 | + |
| 23 | +- `list` |
| 24 | + - 詳細表示のため、引き続き `list_worktrees()` を使う |
| 25 | +- `WorktreeInfo` |
| 26 | + - public API と既存 test を壊さないため維持する |
| 27 | +- detailed status 取得 |
| 28 | + - `has_changes / last_commit / ahead_behind` は `list` 系のために残す |
| 29 | + |
| 30 | +## Observed Symptoms |
| 31 | + |
| 32 | +- `create` は worktree 数が増えると、画面に入って最初の prompt が出るまで遅くなる |
| 33 | +- `batch delete` は worktree 数が増えると、画面表示前と選択後の summary 生成で遅くなる |
| 34 | + |
| 35 | +## Root Cause Analysis |
| 36 | + |
| 37 | +### 1. `create` の入口が重い |
| 38 | + |
| 39 | +対象: |
| 40 | +- [create_worktree_with_ui](/Users/a12622/git/git-workers/src/usecases/create_worktree.rs:79) |
| 41 | + |
| 42 | +現状: |
| 43 | +- 冒頭で `manager.list_worktrees()?` を呼んでいる |
| 44 | +- 使い道は `has_worktrees = !existing_worktrees.is_empty()` のみ |
| 45 | + |
| 46 | +問題: |
| 47 | +- 件数確認しかしたくないのに、一覧表示向けの重い取得をしている |
| 48 | + |
| 49 | +### 2. `batch delete` の入口が重い |
| 50 | + |
| 51 | +対象: |
| 52 | +- [batch_delete_worktrees_internal](/Users/a12622/git/git-workers/src/usecases/delete_worktree.rs:240) |
| 53 | + |
| 54 | +現状: |
| 55 | +- 冒頭で `manager.list_worktrees()?` を呼んでいる |
| 56 | +- 候補生成には `name / branch / current` 程度しか使っていない |
| 57 | + |
| 58 | +問題: |
| 59 | +- 入口で full status を取りにいっている |
| 60 | + |
| 61 | +### 3. `batch delete` の後段も重い |
| 62 | + |
| 63 | +対象: |
| 64 | +- [is_branch_unique_to_worktree](/Users/a12622/git/git-workers/src/infrastructure/git.rs:1156) |
| 65 | + |
| 66 | +現状: |
| 67 | +- 内部で再び `self.list_worktrees()?` を呼んでいる |
| 68 | +- 選択した worktree 数ぶん呼ばれる |
| 69 | + |
| 70 | +問題: |
| 71 | +- 選択後の summary 生成が `O(selected_count * full_list_cost)` になる |
| 72 | + |
| 73 | +### 4. 重い本体 API |
| 74 | + |
| 75 | +対象: |
| 76 | +- [GitWorktreeManager::list_worktrees](/Users/a12622/git/git-workers/src/infrastructure/git.rs:240) |
| 77 | + |
| 78 | +現状: |
| 79 | +- 各 worktree ごとに以下を取得している |
| 80 | + - `Repository::open(path)` |
| 81 | + - branch 名 |
| 82 | + - `repo.statuses(...)` |
| 83 | + - last commit |
| 84 | + |
| 85 | +結論: |
| 86 | +- `list_worktrees()` は一覧画面には妥当だが、軽量画面の入口には重すぎる |
| 87 | + |
| 88 | +## Design Principle |
| 89 | + |
| 90 | +重い API を最適化して全部を一度に変えるのではなく、用途別に API を分ける。 |
| 91 | + |
| 92 | +### Keep |
| 93 | + |
| 94 | +- `list_worktrees()` |
| 95 | + - 一覧画面向けの詳細 API として残す |
| 96 | + |
| 97 | +### Add |
| 98 | + |
| 99 | +- `has_linked_worktrees()` |
| 100 | + - linked worktree の有無だけを確認する軽量 API |
| 101 | +- `list_worktrees_basic()` |
| 102 | + - 軽量一覧 API |
| 103 | + |
| 104 | +### Precise Semantics |
| 105 | + |
| 106 | +- `has_linked_worktrees()` |
| 107 | + - `main` worktree は数えない |
| 108 | + - `git worktree list` 上の linked worktree が 1 件以上あるかだけを見る |
| 109 | + - `create` の first-worktree 分岐と完全に同じ意味で使う |
| 110 | +- `list_worktrees_basic()` |
| 111 | + - 候補表示や選択解決に必要な最小情報だけ返す |
| 112 | + - `list_worktrees()` の軽量版であり、表示順と identity 解決規則は現行と同じにする |
| 113 | + |
| 114 | +## Proposed Data Model |
| 115 | + |
| 116 | +新規に lightweight な struct を追加する。 |
| 117 | + |
| 118 | +例: |
| 119 | + |
| 120 | +```rust |
| 121 | +pub struct BasicWorktreeInfo { |
| 122 | + pub name: String, |
| 123 | + pub git_name: String, |
| 124 | + pub path: PathBuf, |
| 125 | + pub branch: String, |
| 126 | + pub is_current: bool, |
| 127 | + pub is_locked: bool, |
| 128 | +} |
| 129 | +``` |
| 130 | + |
| 131 | +### Included |
| 132 | + |
| 133 | +- `name` |
| 134 | +- `git_name` |
| 135 | +- `path` |
| 136 | +- `branch` |
| 137 | +- `is_current` |
| 138 | +- `is_locked` |
| 139 | + |
| 140 | +### Excluded |
| 141 | + |
| 142 | +- `has_changes` |
| 143 | +- `last_commit` |
| 144 | +- `ahead_behind` |
| 145 | + |
| 146 | +理由: |
| 147 | +- `create / batch delete / rename / switch / search` の入口では不要 |
| 148 | + |
| 149 | +### Identity Contract |
| 150 | + |
| 151 | +- `name` |
| 152 | + - UI 表示名として使う |
| 153 | + - renamed worktree の表示も現行と同じくこの値を使う |
| 154 | +- `git_name` |
| 155 | + - destructive operation や内部解決に使う |
| 156 | + - `remove`, `rename`, orphaned branch 判定の対象解決はこの値を基準にする |
| 157 | +- renamed worktree が存在しても、現行の `name` と `git_name` の役割分担を変えない |
| 158 | +- `list_worktrees_basic()` と `list_worktrees()` は、同じ worktree に対して同じ `name / git_name` の組を返す |
| 159 | + |
| 160 | +## Safe Refactoring Scope |
| 161 | + |
| 162 | +### Phase 1 |
| 163 | + |
| 164 | +`create` のみ軽量化する。 |
| 165 | + |
| 166 | +- `manager.list_worktrees()?` をやめる |
| 167 | +- `manager.has_linked_worktrees()?` を導入して置き換える |
| 168 | +- この phase では `BasicWorktreeInfo` と `list_worktrees_basic()` はまだ導入しない |
| 169 | + |
| 170 | +期待効果: |
| 171 | +- 件数依存の初動遅延をかなり削減できる |
| 172 | + |
| 173 | +### Phase 2 |
| 174 | + |
| 175 | +`batch delete` を軽量化する。 |
| 176 | + |
| 177 | +- `manager.list_worktrees()?` を `manager.list_worktrees_basic()?` に置き換える |
| 178 | +- `is_branch_unique_to_worktree()` を軽量一覧ベースに切り替える |
| 179 | +- 候補順と選択 index の対応は現行のまま維持する |
| 180 | + |
| 181 | +期待効果: |
| 182 | +- 入口の待ち時間を削減 |
| 183 | +- summary 生成の二次的な遅延も削減 |
| 184 | + |
| 185 | +### Phase 3 |
| 186 | + |
| 187 | +同じ構造の他画面を軽量化する。 |
| 188 | + |
| 189 | +候補: |
| 190 | +- `rename` |
| 191 | +- `switch` |
| 192 | +- `search` |
| 193 | + |
| 194 | +状態: |
| 195 | +- 実装済み |
| 196 | + |
| 197 | +### Explicitly Out of Scope |
| 198 | + |
| 199 | +- `list` の軽量化 |
| 200 | +- 文言変更 |
| 201 | +- メニュー順変更 |
| 202 | +- hook の仕様変更 |
| 203 | +- path / branch の表示仕様変更 |
| 204 | + |
| 205 | +## Compatibility Rules |
| 206 | + |
| 207 | +以下は絶対に変えない。 |
| 208 | + |
| 209 | +- `create` |
| 210 | + - worktree 0 件のときだけ location 選択を出す |
| 211 | + - 1 件以上なら location 選択を出さない |
| 212 | + - ここでいう件数は linked worktree 数だけを意味する |
| 213 | +- `batch delete` |
| 214 | + - current worktree を候補に出さない |
| 215 | + - orphaned branch 判定の意味を変えない |
| 216 | + - 候補順を変えない |
| 217 | + - 選択 index と削除対象の対応を変えない |
| 218 | + - renamed worktree がある場合も、表示は `name`、削除対象解決は現行と同じ identity を使う |
| 219 | +- `rename / switch / search` |
| 220 | + - current 判定を変えない |
| 221 | + - 選択対象の path / branch を変えない |
| 222 | +- `list` |
| 223 | + - 表示内容は現状維持 |
| 224 | + |
| 225 | +## Test Plan |
| 226 | + |
| 227 | +### Existing Behavior Lock |
| 228 | + |
| 229 | +既存の以下を回帰として守る。 |
| 230 | + |
| 231 | +- `create` 系 test |
| 232 | +- `batch delete` 系 test |
| 233 | +- `switch / rename / search` 系 test |
| 234 | +- `custom_path_behavior_test` |
| 235 | +- `worktree lifecycle` 系 integration test |
| 236 | + |
| 237 | +### New Tests |
| 238 | + |
| 239 | +#### `create` |
| 240 | + |
| 241 | +- `has_linked_worktrees() == false` なら first-worktree location prompt が出る |
| 242 | +- `has_linked_worktrees() == true` なら first-worktree location prompt が出ない |
| 243 | +- `main` worktree しかない repo では location prompt が出る |
| 244 | +- linked worktree が 1 件以上ある repo では location prompt が出ない |
| 245 | + |
| 246 | +#### `batch delete` |
| 247 | + |
| 248 | +- `list_worktrees_basic()` ベースでも候補一覧が現状と一致する |
| 249 | +- `is_branch_unique_to_worktree()` の結果が現状と一致する |
| 250 | +- 候補順が現状と一致する |
| 251 | +- 選択 index と実際の削除対象の対応が現状と一致する |
| 252 | +- renamed worktree がある場合も `name / git_name` の解決が現状と一致する |
| 253 | + |
| 254 | +#### `basic vs full` |
| 255 | + |
| 256 | +- 同じ repo で |
| 257 | + - `name` |
| 258 | + - `git_name` |
| 259 | + - `path` |
| 260 | + - `branch` |
| 261 | + - `is_current` |
| 262 | + - `is_locked` |
| 263 | + が `list_worktrees_basic()` と `list_worktrees()` で一致する |
| 264 | + |
| 265 | +## Validation Commands |
| 266 | + |
| 267 | +最低限、各 phase ごとに以下を通す。 |
| 268 | + |
| 269 | +```bash |
| 270 | +cargo fmt --check |
| 271 | +cargo clippy --all-features -- -D warnings |
| 272 | +cargo test --all-features -- --test-threads=1 |
| 273 | +``` |
| 274 | + |
| 275 | +必要なら targeted 実行: |
| 276 | + |
| 277 | +```bash |
| 278 | +cargo test --all-features unit::commands::create -- --test-threads=1 |
| 279 | +cargo test --all-features unit::commands::delete -- --test-threads=1 |
| 280 | +``` |
| 281 | + |
| 282 | +## Success Criteria |
| 283 | + |
| 284 | +- `create` の初期 prompt までの待ち時間が件数増加に対して改善される |
| 285 | +- `batch delete` の画面表示前と summary 生成前の待ち時間が改善される |
| 286 | +- `rename / switch / search` の一覧表示前の待ち時間も改善される |
| 287 | +- full test が green |
| 288 | +- 観測可能な挙動差分がない |
| 289 | + |
| 290 | +## Implementation Notes |
| 291 | + |
| 292 | +- まず `infrastructure::git` に軽量 API を追加する |
| 293 | +- 既存の `WorktreeInfo` は残す |
| 294 | +- `BasicWorktreeInfo` から `WorktreeInfo` への安易な変換は作らない |
| 295 | + - 不要な status 取得を誘発しやすいため |
| 296 | +- `batch delete` の branch uniqueness は、一覧取得結果の再利用を優先する |
| 297 | +- `create` は `list_worktrees_basic()` を待たず、`has_linked_worktrees()` 単独で先に改善する |
| 298 | + |
| 299 | +## Recommended First Patch |
| 300 | + |
| 301 | +最初の 1 patch はここまでに限定する。 |
| 302 | + |
| 303 | +1. `GitWorktreeManager::has_linked_worktrees()` |
| 304 | +2. `create` を `has_linked_worktrees()` に切り替え |
| 305 | +3. first-worktree 分岐の回帰 test 追加 |
| 306 | + |
| 307 | +この単位なら、効果が大きく、差分も最小で済む。 |
0 commit comments