Skip to content

Commit cd627e5

Browse files
authored
Merge pull request #14 from wasabeef/perf/worktree-entry-latency
perf: optimize worktree entry latency
2 parents 105ebee + 8a89e5a commit cd627e5

File tree

12 files changed

+666
-19
lines changed

12 files changed

+666
-19
lines changed

docs/perf-worktree-entry-plan.md

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
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+
この単位なら、効果が大きく、差分も最小で済む。
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
pub use crate::infrastructure::git::{CommitInfo, GitWorktreeManager, WorktreeInfo};
1+
pub use crate::infrastructure::git::{
2+
BasicWorktreeInfo, CommitInfo, GitWorktreeManager, WorktreeInfo,
3+
};

src/adapters/git/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ pub mod git_worktree_repository;
22
pub mod repo_discovery;
33
pub mod worktree_lock;
44

5-
pub use git_worktree_repository::{CommitInfo, GitWorktreeManager, WorktreeInfo};
5+
pub use git_worktree_repository::{
6+
BasicWorktreeInfo, CommitInfo, GitWorktreeManager, WorktreeInfo,
7+
};
68
pub use repo_discovery::{open_repository_at_path, open_repository_from_env};
79
pub use worktree_lock::WorktreeLock;

src/domain/worktree.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
pub use crate::adapters::git::WorktreeInfo;
1+
pub use crate::adapters::git::{BasicWorktreeInfo, WorktreeInfo};

0 commit comments

Comments
 (0)