Worktree paths in git panel, take 2 (#26047)
Modified version of #25950. We still use worktree paths, but repo paths with a status that lie outside the worktree are not excluded; instead, we relativize them by adding `..`. This makes the list in the git panel match what you'd get from running `git status` (with the repo's worktree root as the working directory). - [x] Implement + test new unrelativization logic - [x] ~~When collecting repositories, dedup by .git abs path, so worktrees can share a repo at the project level~~ dedup repos at the repository selector layer, with repos coming from larger worktrees being preferred - [x] Open single-file worktree with diff when activating a path not in the worktree Release Notes: - N/A
This commit is contained in:
parent
330e799293
commit
1763dd714b
23 changed files with 724 additions and 184 deletions
|
@ -58,7 +58,7 @@ use std::{
|
|||
future::Future,
|
||||
mem::{self},
|
||||
ops::{Deref, DerefMut},
|
||||
path::{Path, PathBuf},
|
||||
path::{Component, Path, PathBuf},
|
||||
pin::Pin,
|
||||
sync::{
|
||||
atomic::{self, AtomicU32, AtomicUsize, Ordering::SeqCst},
|
||||
|
@ -212,7 +212,11 @@ impl RepositoryEntry {
|
|||
self.work_directory.relativize(path)
|
||||
}
|
||||
|
||||
pub fn unrelativize(&self, path: &RepoPath) -> Option<Arc<Path>> {
|
||||
pub fn try_unrelativize(&self, path: &RepoPath) -> Option<Arc<Path>> {
|
||||
self.work_directory.try_unrelativize(path)
|
||||
}
|
||||
|
||||
pub fn unrelativize(&self, path: &RepoPath) -> Arc<Path> {
|
||||
self.work_directory.unrelativize(path)
|
||||
}
|
||||
|
||||
|
@ -491,7 +495,7 @@ impl WorkDirectory {
|
|||
}
|
||||
|
||||
/// This is the opposite operation to `relativize` above
|
||||
pub fn unrelativize(&self, path: &RepoPath) -> Option<Arc<Path>> {
|
||||
pub fn try_unrelativize(&self, path: &RepoPath) -> Option<Arc<Path>> {
|
||||
match self {
|
||||
WorkDirectory::InProject { relative_path } => Some(relative_path.join(path).into()),
|
||||
WorkDirectory::AboveProject {
|
||||
|
@ -504,6 +508,33 @@ impl WorkDirectory {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn unrelativize(&self, path: &RepoPath) -> Arc<Path> {
|
||||
match self {
|
||||
WorkDirectory::InProject { relative_path } => relative_path.join(path).into(),
|
||||
WorkDirectory::AboveProject {
|
||||
location_in_repo, ..
|
||||
} => {
|
||||
if &path.0 == location_in_repo {
|
||||
// Single-file worktree
|
||||
return location_in_repo
|
||||
.file_name()
|
||||
.map(Path::new)
|
||||
.unwrap_or(Path::new(""))
|
||||
.into();
|
||||
}
|
||||
let mut location_in_repo = &**location_in_repo;
|
||||
let mut parents = PathBuf::new();
|
||||
loop {
|
||||
if let Ok(segment) = path.strip_prefix(location_in_repo) {
|
||||
return parents.join(segment).into();
|
||||
}
|
||||
location_in_repo = location_in_repo.parent().unwrap_or(Path::new(""));
|
||||
parents.push(Component::ParentDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display_name(&self) -> String {
|
||||
match self {
|
||||
WorkDirectory::InProject { relative_path } => relative_path.display().to_string(),
|
||||
|
@ -1422,6 +1453,19 @@ impl Worktree {
|
|||
worktree_scan_id: scan_id as u64,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn dot_git_abs_path(&self, work_directory: &WorkDirectory) -> PathBuf {
|
||||
let mut path = match work_directory {
|
||||
WorkDirectory::InProject { relative_path } => self.abs_path().join(relative_path),
|
||||
WorkDirectory::AboveProject { absolute_path, .. } => absolute_path.as_ref().to_owned(),
|
||||
};
|
||||
path.push(".git");
|
||||
path
|
||||
}
|
||||
|
||||
pub fn is_single_file(&self) -> bool {
|
||||
self.root_dir().is_none()
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalWorktree {
|
||||
|
@ -5509,7 +5553,7 @@ impl BackgroundScanner {
|
|||
|
||||
let mut new_entries_by_path = SumTree::new(&());
|
||||
for (repo_path, status) in statuses.entries.iter() {
|
||||
let project_path = repository.work_directory.unrelativize(repo_path);
|
||||
let project_path = repository.work_directory.try_unrelativize(repo_path);
|
||||
|
||||
new_entries_by_path.insert_or_replace(
|
||||
StatusEntry {
|
||||
|
|
|
@ -3412,6 +3412,43 @@ async fn test_private_single_file_worktree(cx: &mut TestAppContext) {
|
|||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_unrelativize() {
|
||||
let work_directory = WorkDirectory::in_project("");
|
||||
pretty_assertions::assert_eq!(
|
||||
work_directory.try_unrelativize(&"crates/gpui/gpui.rs".into()),
|
||||
Some(Path::new("crates/gpui/gpui.rs").into())
|
||||
);
|
||||
|
||||
let work_directory = WorkDirectory::in_project("vendor/some-submodule");
|
||||
pretty_assertions::assert_eq!(
|
||||
work_directory.try_unrelativize(&"src/thing.c".into()),
|
||||
Some(Path::new("vendor/some-submodule/src/thing.c").into())
|
||||
);
|
||||
|
||||
let work_directory = WorkDirectory::AboveProject {
|
||||
absolute_path: Path::new("/projects/zed").into(),
|
||||
location_in_repo: Path::new("crates/gpui").into(),
|
||||
};
|
||||
|
||||
pretty_assertions::assert_eq!(
|
||||
work_directory.try_unrelativize(&"crates/util/util.rs".into()),
|
||||
None,
|
||||
);
|
||||
|
||||
pretty_assertions::assert_eq!(
|
||||
work_directory.unrelativize(&"crates/util/util.rs".into()),
|
||||
Path::new("../util/util.rs").into()
|
||||
);
|
||||
|
||||
pretty_assertions::assert_eq!(work_directory.try_unrelativize(&"README.md".into()), None,);
|
||||
|
||||
pretty_assertions::assert_eq!(
|
||||
work_directory.unrelativize(&"README.md".into()),
|
||||
Path::new("../../README.md").into()
|
||||
);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_git_statuses(snapshot: &Snapshot, expected_statuses: &[(&Path, GitSummary)]) {
|
||||
let mut traversal = snapshot
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue