git: Support git repos with .git folder above project root (#11550)

TODOs:

- [x] Add assertions to the test to ensure that the git status is
propagated
- [x] Get collaboration working
- [x] Test opening a git repository inside another git repository
- [x] Test opening the sub-folder of a repository that itself contains
another git repository in a subfolder

Fixes:
- Fixes https://github.com/zed-industries/zed/issues/10154
- Fixes https://github.com/zed-industries/zed/issues/8418
- Fixes https://github.com/zed-industries/zed/issues/8275
- Fixes https://github.com/zed-industries/zed/issues/7816
- Fixes https://github.com/zed-industries/zed/issues/6762
- Fixes https://github.com/zed-industries/zed/issues/4419
- Fixes https://github.com/zed-industries/zed/issues/4672
- Fixes https://github.com/zed-industries/zed/issues/5161

Release Notes:

- Added support for opening subfolders of git repositories and treating
them as part of a repository (show git status in project panel, show git
diff in gutter, git blame works, ...)
([#4672](https://github.com/zed-industries/zed/issues/4672)).

Demo video:


https://github.com/zed-industries/zed/assets/1185253/afc1cdc3-372c-404e-99ea-15708589251c
This commit is contained in:
Thorsten Ball 2024-05-14 18:34:51 +02:00 committed by GitHub
parent 9f0a20241b
commit db89353193
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 331 additions and 72 deletions

View file

@ -7536,13 +7536,12 @@ impl Project {
.flatten()
.chain(current_buffers)
.filter_map(|(buffer, path, abs_path)| {
let (work_directory, repo) =
snapshot.repository_and_work_directory_for_path(&path)?;
let repo_entry = snapshot.get_local_repo(&repo)?;
Some((buffer, path, abs_path, work_directory, repo_entry))
let (repo_entry, local_repo_entry) = snapshot.repo_for_path(&path)?;
Some((buffer, path, abs_path, repo_entry, local_repo_entry))
})
.map(|(buffer, path, abs_path, work_directory, repo_entry)| {
.map(|(buffer, path, abs_path, repo, local_repo_entry)| {
let fs = fs.clone();
let snapshot = snapshot.clone();
async move {
let abs_path_metadata = fs
.metadata(&abs_path)
@ -7557,8 +7556,11 @@ impl Project {
{
None
} else {
let relative_path = path.strip_prefix(&work_directory).ok()?;
repo_entry.repo().lock().load_index_text(relative_path)
let relative_path = repo.relativize(&snapshot, &path).ok()?;
local_repo_entry
.repo()
.lock()
.load_index_text(&relative_path)
};
Some((buffer, base_text))
}
@ -7931,24 +7933,17 @@ impl Project {
.context("worktree was not local")?
.snapshot();
let (work_directory, repo) = match worktree
.repository_and_work_directory_for_path(&buffer_project_path.path)
{
Some(work_dir_repo) => work_dir_repo,
None => anyhow::bail!(NoRepositoryError {}),
};
let (repo_entry, local_repo_entry) =
match worktree.repo_for_path(&buffer_project_path.path) {
Some(repo_for_path) => repo_for_path,
None => anyhow::bail!(NoRepositoryError {}),
};
let repo_entry = match worktree.get_local_repo(&repo) {
Some(repo_entry) => repo_entry,
None => anyhow::bail!(NoRepositoryError {}),
};
let relative_path = repo_entry
.relativize(&worktree, &buffer_project_path.path)
.context("failed to relativize buffer path")?;
let repo = repo_entry.repo().clone();
let relative_path = buffer_project_path
.path
.strip_prefix(&work_directory)?
.to_path_buf();
let repo = local_repo_entry.repo().clone();
let content = match version {
Some(version) => buffer.rope_for_version(&version).clone(),
@ -7962,7 +7957,7 @@ impl Project {
let (repo, relative_path, content) = blame_params?;
let lock = repo.lock();
lock.blame(&relative_path, content)
.with_context(|| format!("Failed to blame {relative_path:?}"))
.with_context(|| format!("Failed to blame {:?}", relative_path.0))
})
} else {
let project_id = self.remote_id();