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