pull request review · captured revision
Unified diff in the switch picker
Worktrunk PR #3865 added a unified-diff preview for the switch picker. Review this merged change as if the decision were still open, using its exact 46-file patch and the evidence below.
Review finding
PreparedDiff owns each complete Git diff invocation;
TempIndex includes untracked files without changing the real
index; and the picker loads off-screen worktrees only when selected.
Review found sparse-checkout divergence, a shared temporary directory, a symlink race while copying the index, and stale prewarming claims. The current revision corrects all four, and the review history dates each fix.
Change surface
Risk is concentrated in the Git repository layer and picker runtime. Tests, documentation, and benchmarks account for most of the remaining files.
- Files
- 46
- Hunks
- 191
- Commits
- 6
- Changed lines
- 2,268
- Patch lines
- 4,126
- Patch bytes
- 214 KB
| Review unit | Files | Change | Review focus |
|---|---|---|---|
| 1 · Git repository layer | 4 | +312 −30 |
Untracked files enter while the
|
| 2 · Picker runtime | 17 | +823 −466 |
Off-screen worktrees load on demand without breaking tab or row identity. |
| 3 · Integration tests | 8 | +308 −92 |
Tests observe index bytes and filesystem residue. |
| 4 · Other runtime | 6 | +42 −61 |
Prior diff callers move to the shared abstraction. |
| 5 · Docs and shipped skills | 9 | +69 −42 |
The Git 2.34 floor is discoverable in every published copy. |
| 6 · Benchmarks | 2 | +13 −10 |
Prewarm cost moves to selected-row demand. |
Path hierarchy · 46 files
max-sixty/worktrunk #3865
src/
commands/picker/ 17 files +823 −466
git/repository/ 4 files +312 −30
other 6 files +42 −61
tests/ 8 files +308 −92
docs + shipped skills
9 files +69 −42
benches/ 2 files +13 −10
What changes at runtime
The behavior view keeps the picker path fixed and changes the default
preview in place. The call view is a scoped
One picker path, changes in place
− old default unchanged boundary + new path
flowchart TB accTitle: Unified diff preparation accDescr: A selected row chooses a preview mode. Unified diff uses PreparedDiff for committed changes and TempIndex for staged, unstaged and untracked work; working and committed tabs remain available. Row[Selected row] --> Mode[Preview mode] Mode -. old default .-> Working["− working-tree diff"] Mode --> Unified["+ unified diff"] Mode --> Tabs[Working / committed tabs] Unified --> Prepared[PreparedDiff] Prepared --> History[Committed changes] Prepared --> Temp[TempIndex] Temp --> Staged[Staged] Temp --> Unstaged[Unstaged] Temp --> Untracked[Untracked] classDef removed fill:var(--del-tint),stroke:var(--danger),color:var(--danger) classDef added fill:var(--ok-tint),stroke:var(--ok),color:var(--ok-ink) class Working removed class Unified,Prepared,Temp added
One unchanged root, replacement calls in context
This is the exact semantic output of
calldiff@0.5.0 diff … --entry compute_combined_diff --locs
--max-depth 1. The stable compute_combined_diff root keeps its
neighboring calls while direct Git command assembly disappears and
PreparedDiff calls appear in the same branch. Comment on
any row, or follow a location into the authoritative patch.
The orchestration hunk the picker uses
This exact hunk from src/summary.rs replaces direct Git
argument assembly with the repository’s PreparedDiff API.
diff --git a/src/summary.rs b/src/summary.rs
index b856171b57..cfa4f0448e 100644
--- a/src/summary.rs
+++ b/src/summary.rs
@@ -258,16 +256,14 @@ pub(crate) fn compute_combined_diff(
// Working tree diff: uncommitted changes
if let Some(wt_path) = worktree_path {
- let path = wt_path.display().to_string();
- if let Ok(wt_stat) = repo.run_command(&["-C", &path, "diff", "HEAD", "--stat"])
+ let prepared = repo.worktree_at(wt_path).prepare_diff(["HEAD"]);
+ if let Ok(wt_stat) = prepared.capture(["--stat"])
&& !wt_stat.trim().is_empty()
{
stat.push_str(&wt_stat);
}
- let mut wt_diff_args = vec!["-C", &path];
- wt_diff_args.extend(DIFF_PREFIX_OVERRIDES);
- wt_diff_args.extend(["diff", "HEAD"]);
- if let Ok(wt_diff) = repo.run_command(&wt_diff_args)
+ if let Ok(wt_diff) =
+ prepared.capture_with_git_options(DIFF_PREFIX_OVERRIDES, std::iter::empty::<&str>())
&& !wt_diff.trim().is_empty()
{
diff.push_str(&wt_diff);
Review invariants
Exact excerpts from revision 9ca8f78b show the byte-equality
and scheduling tests, followed by the temporary-index implementation.
Real index byte equality
src/commands/picker/items.rs ·
unified_diff_is_net_change_and_subsidiary_diffs_include_untracked
let real_index = repo.current_worktree().git_dir().unwrap().join("index");
let index_before = std::fs::read(&real_index).unwrap();
let unified = PickerRow::compute_unified_diff_preview(&repo, &item, 80);
let working = PickerRow::compute_working_tree_preview(&repo, &item, 80);
let committed = PickerRow::compute_branch_diff_preview(&repo, &item, 80);
let index_after = std::fs::read(&real_index).unwrap();
assert_eq!(
index_after, index_before,
"preview computation must leave the real index byte-identical"
);
Off-screen worktree scheduling
src/commands/picker/preview_orchestrator.rs ·
initial_precompute_skips_offscreen_worktrees_but_warms_branches
orch.spawn_initial_precompute(
&orch.generation(),
&[first, Arc::clone(&offscreen), Arc::clone(&branch)],
(80, 24),
None,
);
orch.wait_for_idle();
assert!(
!orch
.cache
.contains_key(&("offscreen".to_string(), PreviewMode::UnifiedDiff)),
"off-screen worktree should wait for selected-row demand"
);
assert!(
orch.cache
.contains_key(&("branch-only".to_string(), PreviewMode::UnifiedDiff)),
"branch-only default should be prewarmed from the committed diff"
);
Temporary-index implementation evidence
src/git/repository/working_tree.rs ·
WorkingTree::temp_index
let mut temp_file = tempfile::Builder::new()
.prefix(TEMP_INDEX_PREFIX)
.tempfile()
.context("Failed to create temporary index")?;
let mut real_index_file = match std::fs::File::open(&real_index) {
Ok(file) => Some(file),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => None,
Err(error) => return Err(error).context("Failed to open index file"),
};
if let Some(real_index_file) = &mut real_index_file {
std::io::copy(real_index_file, temp_file.as_file_mut())
.context("Failed to copy index file")?;
}
let temp = temp_file.into_temp_path();
TempPath.Evidence and remaining gaps
| Claim | Evidence | Status | Review reading |
|---|---|---|---|
| Unified and subsidiary tabs show the intended changes |
unified_diff_is_net_change_and_subsidiary_diffs_include_untracked
|
passed | The same test requires the real index to remain byte-identical. |
| Sparse checkouts retain out-of-cone untracked files |
test_picker_dry_run_includes_untracked_outside_sparse_checkout
|
passed | Exercises the regression the first review found. |
| Worktree-local temp directories leave no index artifacts |
test_picker_dry_run_tempdir_inside_worktree_has_no_index_artifacts
|
passed | Also tests glob escaping with a bracketed path. |
| Off-screen worktrees are demand-loaded |
initial_precompute_skips_offscreen_worktrees_but_warms_branches
|
passed | Proves scheduling shape, not selected-row latency. |
| Selected-row demand remains fast | No latency benchmark | gap | The correctness test allows up to 60 seconds and cannot answer this. |
| Commit dry-run excludes its own temp index under local TMPDIR | No focused regression in this PR | gap |
The captured hunk moves the existing staging path onto
TempIndex without changing its git add
semantics; a reviewer reproduced the extra index and lock entries
with plain Git.
|
Exact PreparedDiff source at the current revision
This 169-line repository slice contains the source-owning abstraction and all three preparation entry points.
Exact patch · 46 files
GitHub’s captured patch for revision 9ca8f78b. All 46 files
start closed and remain independently commentable.
Review in place. Alt-click a file header or source line to comment on that whole target, or drag across an exact expression to quote it. Comments retain their file coordinate—and, for line comments, side, line, and quote—even when the file disclosure is closed.
Captured pull request
Review history
- observation PR opened The first revision introduced the unified picker tab, shared diff preparation, and broad integration coverage.
- warning First review withheld approval It found divergent sparse handling, a predictable shared temp directory, startup-cost uncertainty, and documentation drift.
- intervention Temp indexes became random, exclusive files Sparse registration and worktree-local temp-directory exclusion gained focused tests.
- intervention The index copy kept ownership of its pathname Writing through the still-open file closed the symlink race, and off-screen worktrees moved out of eager prewarm.
-
observation
Agent review recommends approval with follow-ups The
shared
TempIndexpath and orchestration prose converged; selected-row latency and dry-run temp artifacts remained visible notes.