fix(diff): reconcile the diff-overlay work with the host-aware git refactor

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.
This commit is contained in:
l0ng-ai
2026-07-29 16:10:47 +08:00
parent 4c27f59f77
commit 76ee68d3e8
4 changed files with 9 additions and 10 deletions
+3 -4
View File
@@ -295,10 +295,9 @@ pub fn probe(host: &dyn Host, cwd: &Path) -> Option<DiffSnapshot> {
/// 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<FileDiff> {
let mut parser = DiffParser::default();
for line in out.lines() {
+3 -3
View File
@@ -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
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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::<PathBuf>(&cfg, None), None);
cfg.sidebar_diff_preview = false;
assert_eq!(diff_click_cwd(&cfg, None), None);
assert_eq!(diff_click_cwd::<PathBuf>(&cfg, None), None);
}
/// Groups appear in first-appearance order with Scratch pinned last, and