From 6a02ab254bd05a2814d32a172bbe343dec747347 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Tue, 8 Sep 2026 11:00:41 +0700 Subject: [PATCH] fix flashing issue --- crates/workspace/src/views/repo_detail/mod.rs | 127 ++++++++++++++++-- 1 file changed, 118 insertions(+), 9 deletions(-) diff --git a/crates/workspace/src/views/repo_detail/mod.rs b/crates/workspace/src/views/repo_detail/mod.rs index 0c9e384..0446d16 100644 --- a/crates/workspace/src/views/repo_detail/mod.rs +++ b/crates/workspace/src/views/repo_detail/mod.rs @@ -492,16 +492,8 @@ impl RepoDetailView { if refresh_generation != this.ref_generation { return; } - if let Ok(Some((moved, branches, tags, current_branch, head_commit))) = refresh { - if moved { - // The mirror caught up with the remote. - // E.g. the push of an owned checkout just landed. - // Rebuild the explorer, previews and commit list from the worktree. - this.reload_worktree(cx); - cx.notify(); - return; - } + if let Ok(Some((moved, branches, tags, current_branch, head_commit))) = refresh { let branches: Vec = branches.iter().map(Into::into).collect(); let tags: Vec = tags.iter().map(Into::into).collect(); @@ -528,6 +520,10 @@ impl RepoDetailView { this.load_all_commits(cx); } + if moved { + this.catch_up_worktree(cx); + } + cx.notify(); } })?; @@ -1225,6 +1221,119 @@ impl RepoDetailView { self.tasks.push(task); } + /// Refresh the file explorer, previews and commit list after the mirror + /// caught up with the remote. + /// + /// The checked-out branch fast-forwarded in place, so unlike + /// [`Self::reload_worktree`] this keeps the panel's selection and previews: + /// it rebuilds the tree, drops previews of files the refresh removed and + /// re-renders the README when it is on screen. + fn catch_up_worktree(&mut self, cx: &mut Context) { + let Some(worktree) = self.worktree.clone() else { + return; + }; + + let task = cx.spawn(async move |this, cx| { + let result = cx + .background_spawn(async move { + let snapshot = signed_git::worktree_snapshot(&worktree)?; + let tree = build_tree_items(&snapshot.entries); + Ok::<_, Error>((snapshot, tree)) + }) + .await; + + this.update(cx, |this, cx| { + match result { + Ok((snapshot, tree)) => { + let head_changed = snapshot.head_commit.as_ref().map(|c| &c.id) + != this.head_commit.as_ref().map(|c| &c.id); + + this.head_commit = snapshot.head_commit; + this.tree_state.update(cx, |state, cx| { + state.set_items(tree_items(tree, false), cx); + }); + + // Drop previews of files the refresh removed from the worktree, + // everything else stays put. + let present: HashSet = snapshot + .entries + .iter() + .map(|path| path.to_string_lossy().into_owned()) + .collect(); + + let mut previewed: Vec = Vec::new(); + previewed.extend(this.files.keys().cloned()); + previewed.extend(this.selected_file.clone().map(|p| p.to_string())); + + if let Some(path) = this.md.as_ref().and_then(|md| md.path.clone()) { + previewed.push(path.to_string()); + } + + if let Some(path) = this.code.as_ref().map(|code| code.path.clone()) { + previewed.push(path.to_string()); + } + + previewed.sort(); + previewed.dedup(); + + for path in previewed { + if !present.contains(&path) { + this.drop_preview_of(&path); + } + } + + // Re-render the README when it is on screen, i.e. when no file preview is open. + if this.selected_file.is_none() { + match snapshot.readme_path.zip(snapshot.readme) { + Some((path, bytes)) => { + this.readme_name = Some(path.to_string_lossy().into()); + if let Ok(text) = String::from_utf8(bytes) { + this.set_markdown(None, &text, cx); + } + } + None => { + this.md = None; + this.readme_name = None; + } + } + } + + if head_changed { + this.all_commits = None; + this.loading_all_commits = false; + this.load_all_commits(cx); + } + } + Err(error) => { + this.error = Some(error.to_string().into()); + } + } + cx.notify(); + })?; + + Ok(()) + }); + + self.tasks.push(task); + } + + /// Drop the cached preview, editor and commit state of `path`. + fn drop_preview_of(&mut self, path: &str) { + if let Some(FileContent::Text(text)) = self.files.remove(path) { + self.preview_bytes -= text.len(); + } + self.commits.remove(path); + if self.selected_file.as_deref() == Some(path) { + self.selected_file = None; + } + if self.md.as_ref().and_then(|md| md.path.as_deref()) == Some(path) { + self.md = None; + } + if self.code.as_ref().map(|code| code.path.as_ref()) == Some(path) { + self.code = None; + } + } + /// Drop the oldest previews beyond the cache caps. /// Keep the currently selected file. /// An evicted file's parsed editor state drops with its entry.