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.

Safe to approve 2 follow-ups 46 files · +1,567 −701

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
Changed paths grouped by implementation area.
Review unit Files Change Review focus
1 · Git repository layer 4 +312 −30 Untracked files enter while the real index remains byte-identical.
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 static analysis of the orchestration file, so every source coordinate leads back to the exact patch.

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"
        );
The test reads the real index around all three preview computations and requires its bytes to match exactly.

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"
        );
After prewarm finishes, the off-screen worktree has no cached unified diff and the branch-only row does. This checks scheduling; selected-row latency remains unmeasured.

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();
Each call creates a temporary file, copies the real index through its open handle when present, and transfers ownership to a TempPath.

Evidence and remaining gaps

What the final revision demonstrates and what remains unmeasured.
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

  1. observation PR opened The first revision introduced the unified picker tab, shared diff preparation, and broad integration coverage.
  2. warning First review withheld approval It found divergent sparse handling, a predictable shared temp directory, startup-cost uncertainty, and documentation drift.
  3. intervention Temp indexes became random, exclusive files Sparse registration and worktree-local temp-directory exclusion gained focused tests.
  4. 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.
  5. observation Agent review recommends approval with follow-ups The shared TempIndex path and orchestration prose converged; selected-row latency and dry-run temp artifacts remained visible notes.