From 76ee68d3e8e2fd9b66a6bec811ba9ab03d40e26f Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Wed, 29 Jul 2026 08:20:52 +0800 Subject: [PATCH] fix(diff): reconcile the diff-overlay work with the host-aware git refactor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rebase onto main lands this change on top of the remote-workspace work (#235, #242), which moved the git helpers into `tty7-core` and made every read take the pane's `Host`. Adapting rather than papering over: - `sidebar_diff_preview` moves to the core `Config`, where the struct now lives. - The diff and untracked reads go through `git_status::git(host, cwd, ..)`. - The shared probe, its in-flight set, and `install_diff_snapshot` key on (`HostId`, `PathBuf`) — the same path on two machines is two work trees — as does the Changes panel's `diff_pending`. - `diff_click_cwd` became generic over what identifies a repo, so the setting gate did not need to learn about hosts. - Main's newer card rounding reads `truncated`, which is an `Option` here now. The streaming diff read is deliberately absent at this commit: `Host::git` is buffered, so it is restored on top of a streaming host API in the next one. Refs #239. --- src/terminal/git_diff.rs | 7 +++---- src/ui/diff_overlay.rs | 6 +++--- src/ui/right_panel.rs | 2 +- src/ui/tab_sidebar.rs | 4 ++-- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/terminal/git_diff.rs b/src/terminal/git_diff.rs index 943af7b4..b51eedcd 100644 --- a/src/terminal/git_diff.rs +++ b/src/terminal/git_diff.rs @@ -295,10 +295,9 @@ pub fn probe(host: &dyn Host, cwd: &Path) -> Option { /// and the first hunk (modes, index, similarity) are simply skipped, so a git /// version printing extra headers degrades to "fewer facts", never a panic. /// -/// The whole-string form the tests drive the parser through; [`probe`] streams -/// the same [`DiffParser`] line by line off git's stdout instead, so no caller -/// in the app ever holds the full diff as one `String`. -#[cfg(test)] +/// The whole-string form: [`probe`] hands it the output of one `git diff` +/// invocation. [`DiffParser`] underneath is incremental, so a caller that can +/// feed lines as they arrive does not have to hold the whole diff at once. pub fn parse_unified(out: &str) -> Vec { let mut parser = DiffParser::default(); for line in out.lines() { diff --git a/src/ui/diff_overlay.rs b/src/ui/diff_overlay.rs index e608c5ca..8eaf9f98 100644 --- a/src/ui/diff_overlay.rs +++ b/src/ui/diff_overlay.rs @@ -184,7 +184,7 @@ impl Tty7App { // probe still flies below, so the seeded view is never the last word. // Read here, before the `&mut` borrow of the tab. let seed = match (&self.right_panel.diff_cwd, &self.right_panel.diff) { - (Some(panel_cwd), Some(Some(snap))) if *panel_cwd == cwd => { + (Some(panel_key), Some(Some(snap))) if *panel_key == (host, cwd.clone()) => { DiffLoad::Ready(Arc::clone(snap)) } _ => DiffLoad::Loading, @@ -740,7 +740,7 @@ impl Tty7App { // question: a binary file or a pure rename has no hunks and is not // truncated, so its body is empty and the header *is* the card. A // truncated file with no parsable hunks still renders the notice. - let has_body = expanded && (!file.hunks.is_empty() || file.truncated); + let has_body = expanded && (!file.hunks.is_empty() || file.truncated.is_some()); // The header paints a solid band flush into the card's corners, and the // card's `overflow_hidden` cannot round it — that clip is a square, @@ -858,7 +858,7 @@ impl Tty7App { .iter() .map(|hunk| (hunk, split_hunk(&hunk.lines))) .collect(); - let closing_row = if file.truncated { + let closing_row = if file.truncated.is_some() { None } else { hunks diff --git a/src/ui/right_panel.rs b/src/ui/right_panel.rs index 63ec887d..981cf954 100644 --- a/src/ui/right_panel.rs +++ b/src/ui/right_panel.rs @@ -1186,7 +1186,7 @@ impl Tty7App { if self.right_panel.diff_cwd.as_ref() != Some(&key) { self.right_panel.diff_cwd = Some(key); self.right_panel.diff = None; - self.spawn_right_panel_diff(cwd.clone(), cx); + self.spawn_right_panel_diff(host.clone(), cwd.clone(), cx); } else if self.right_panel.diff.is_none() && self.right_panel.diff_pending.is_none() { // Nothing cached and nothing in flight: a probe for a previous cwd // landed after we had already moved on and dropped its result, so diff --git a/src/ui/tab_sidebar.rs b/src/ui/tab_sidebar.rs index 0da4d819..2c9b47ae 100644 --- a/src/ui/tab_sidebar.rs +++ b/src/ui/tab_sidebar.rs @@ -1338,9 +1338,9 @@ mod tests { #[test] fn diff_click_target_needs_a_repo_either_way() { let mut cfg = Config::default(); - assert_eq!(diff_click_cwd(&cfg, None), None); + assert_eq!(diff_click_cwd::(&cfg, None), None); cfg.sidebar_diff_preview = false; - assert_eq!(diff_click_cwd(&cfg, None), None); + assert_eq!(diff_click_cwd::(&cfg, None), None); } /// Groups appear in first-appearance order with Scratch pinned last, and