From 01bcbf3b0d0c50022c7a2db1759aa56d402f19c6 Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Thu, 6 Feb 2025 10:13:56 -0500 Subject: [PATCH] Fix missing diff hunks in single-file worktrees (#24377) Release Notes: - Fixed diff hunks not appearing when opening a single file within a larger repository --- crates/project/src/project_tests.rs | 65 +++++++++++++++++++++++++++++ crates/worktree/src/worktree.rs | 20 +++++---- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index d2f36ae960..dfd5a5dc56 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -5832,6 +5832,71 @@ async fn test_uncommitted_changes_for_buffer(cx: &mut gpui::TestAppContext) { }); } +#[gpui::test] +async fn test_single_file_diffs(cx: &mut gpui::TestAppContext) { + init_test(cx); + + let committed_contents = r#" + fn main() { + println!("hello from HEAD"); + } + "# + .unindent(); + let file_contents = r#" + fn main() { + println!("hello from the working copy"); + } + "# + .unindent(); + + let fs = FakeFs::new(cx.background_executor.clone()); + fs.insert_tree( + "/dir", + json!({ + ".git": {}, + "src": { + "main.rs": file_contents, + } + }), + ) + .await; + + fs.set_head_for_repo( + Path::new("/dir/.git"), + &[("src/main.rs".into(), committed_contents)], + ); + + let project = Project::test(fs.clone(), ["/dir/src/main.rs".as_ref()], cx).await; + + let buffer = project + .update(cx, |project, cx| { + project.open_local_buffer("/dir/src/main.rs", cx) + }) + .await + .unwrap(); + let uncommitted_changes = project + .update(cx, |project, cx| { + project.open_uncommitted_changes(buffer.clone(), cx) + }) + .await + .unwrap(); + + cx.run_until_parked(); + uncommitted_changes.update(cx, |uncommitted_changes, cx| { + let snapshot = buffer.read(cx).snapshot(); + assert_hunks( + uncommitted_changes.diff_hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot), + &snapshot, + &uncommitted_changes.base_text.as_ref().unwrap().text(), + &[( + 1..2, + " println!(\"hello from HEAD\");\n", + " println!(\"hello from the working copy\");\n", + )], + ); + }); +} + async fn search( project: &Entity, query: SearchQuery, diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index 8c3bd04657..da48baf095 100644 --- a/crates/worktree/src/worktree.rs +++ b/crates/worktree/src/worktree.rs @@ -372,15 +372,19 @@ impl WorkDirectory { /// of the project root folder, then the returned RepoPath is relative to the root /// of the repository and not a valid path inside the project. pub fn relativize(&self, path: &Path) -> Result { - if let Some(location_in_repo) = &self.location_in_repo { - Ok(location_in_repo.join(path).into()) + let repo_path = if let Some(location_in_repo) = &self.location_in_repo { + // Avoid joining a `/` to location_in_repo in the case of a single-file worktree. + if path == Path::new("") { + RepoPath(location_in_repo.clone()) + } else { + location_in_repo.join(path).into() + } } else { - let relativized_path = path - .strip_prefix(&self.path) - .map_err(|_| anyhow!("could not relativize {:?} against {:?}", path, self.path))?; - - Ok(relativized_path.into()) - } + path.strip_prefix(&self.path) + .map_err(|_| anyhow!("could not relativize {:?} against {:?}", path, self.path))? + .into() + }; + Ok(repo_path) } /// This is the opposite operation to `relativize` above