git: Make worktrees work for bare git repositories (#21596)
Fixes #21210 by ensuring that Zed can open worktrees of bare git repositories. Co-authored-by: Peter Tripp <peter@zed.dev>
This commit is contained in:
parent
6ebd6c2893
commit
2d43ad12e6
2 changed files with 44 additions and 11 deletions
|
@ -3110,12 +3110,8 @@ impl BackgroundScannerState {
|
||||||
let repository = fs.open_repo(&dot_git_abs_path)?;
|
let repository = fs.open_repo(&dot_git_abs_path)?;
|
||||||
|
|
||||||
let actual_repo_path = repository.path();
|
let actual_repo_path = repository.path();
|
||||||
let actual_dot_git_dir_abs_path: Arc<Path> = Arc::from(
|
|
||||||
actual_repo_path
|
|
||||||
.ancestors()
|
|
||||||
.find(|ancestor| ancestor.file_name() == Some(&*DOT_GIT))?,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
let actual_dot_git_dir_abs_path = smol::block_on(find_git_dir(&actual_repo_path, fs))?;
|
||||||
watcher.add(&actual_repo_path).log_err()?;
|
watcher.add(&actual_repo_path).log_err()?;
|
||||||
|
|
||||||
let dot_git_worktree_abs_path = if actual_dot_git_dir_abs_path.as_ref() == dot_git_abs_path
|
let dot_git_worktree_abs_path = if actual_dot_git_dir_abs_path.as_ref() == dot_git_abs_path
|
||||||
|
@ -3161,6 +3157,31 @@ impl BackgroundScannerState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn is_git_dir(path: &Path, fs: &dyn Fs) -> bool {
|
||||||
|
if path.file_name() == Some(&*DOT_GIT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're in a bare repository, we are not inside a `.git` folder. In a
|
||||||
|
// bare repository, the root folder contains what would normally be in the
|
||||||
|
// `.git` folder.
|
||||||
|
let head_metadata = fs.metadata(&path.join("HEAD")).await;
|
||||||
|
if !matches!(head_metadata, Ok(Some(_))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let config_metadata = fs.metadata(&path.join("config")).await;
|
||||||
|
matches!(config_metadata, Ok(Some(_)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn find_git_dir(path: &Path, fs: &dyn Fs) -> Option<Arc<Path>> {
|
||||||
|
for ancestor in path.ancestors() {
|
||||||
|
if is_git_dir(ancestor, fs).await {
|
||||||
|
return Some(Arc::from(ancestor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
async fn build_gitignore(abs_path: &Path, fs: &dyn Fs) -> Result<Gitignore> {
|
async fn build_gitignore(abs_path: &Path, fs: &dyn Fs) -> Result<Gitignore> {
|
||||||
let contents = fs.load(abs_path).await?;
|
let contents = fs.load(abs_path).await?;
|
||||||
let parent = abs_path.parent().unwrap_or_else(|| Path::new("/"));
|
let parent = abs_path.parent().unwrap_or_else(|| Path::new("/"));
|
||||||
|
@ -3967,7 +3988,7 @@ impl BackgroundScanner {
|
||||||
} else if fsmonitor_parse_state == Some(FsMonitorParseState::Cookies) && file_name == Some(*FSMONITOR_DAEMON) {
|
} else if fsmonitor_parse_state == Some(FsMonitorParseState::Cookies) && file_name == Some(*FSMONITOR_DAEMON) {
|
||||||
fsmonitor_parse_state = Some(FsMonitorParseState::FsMonitor);
|
fsmonitor_parse_state = Some(FsMonitorParseState::FsMonitor);
|
||||||
false
|
false
|
||||||
} else if fsmonitor_parse_state != Some(FsMonitorParseState::FsMonitor) && file_name == Some(*DOT_GIT) {
|
} else if fsmonitor_parse_state != Some(FsMonitorParseState::FsMonitor) && smol::block_on(is_git_dir(ancestor, self.fs.as_ref())) {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
fsmonitor_parse_state.take();
|
fsmonitor_parse_state.take();
|
||||||
|
|
|
@ -12,7 +12,13 @@ use pretty_assertions::assert_eq;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use settings::{Settings, SettingsStore};
|
use settings::{Settings, SettingsStore};
|
||||||
use std::{env, fmt::Write, mem, path::Path, sync::Arc};
|
use std::{
|
||||||
|
env,
|
||||||
|
fmt::Write,
|
||||||
|
mem,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
use util::{test::temp_tree, ResultExt};
|
use util::{test::temp_tree, ResultExt};
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
@ -532,14 +538,20 @@ async fn test_open_gitignored_files(cx: &mut TestAppContext) {
|
||||||
assert_eq!(fs.read_dir_call_count() - prev_read_dir_count, 1);
|
assert_eq!(fs.read_dir_call_count() - prev_read_dir_count, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let path = PathBuf::from("/root/one/node_modules/c/lib");
|
||||||
|
|
||||||
// No work happens when files and directories change within an unloaded directory.
|
// No work happens when files and directories change within an unloaded directory.
|
||||||
let prev_fs_call_count = fs.read_dir_call_count() + fs.metadata_call_count();
|
let prev_fs_call_count = fs.read_dir_call_count() + fs.metadata_call_count();
|
||||||
fs.create_dir("/root/one/node_modules/c/lib".as_ref())
|
// When we open a directory, we check each ancestor whether it's a git
|
||||||
.await
|
// repository. That means we have an fs.metadata call per ancestor that we
|
||||||
.unwrap();
|
// need to subtract here.
|
||||||
|
let ancestors = path.ancestors().count();
|
||||||
|
|
||||||
|
fs.create_dir(path.as_ref()).await.unwrap();
|
||||||
cx.executor().run_until_parked();
|
cx.executor().run_until_parked();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
fs.read_dir_call_count() + fs.metadata_call_count() - prev_fs_call_count,
|
fs.read_dir_call_count() + fs.metadata_call_count() - prev_fs_call_count - ancestors,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue