@@ -3110,12 +3110,8 @@ impl BackgroundScannerState {
let repository = fs.open_repo(&dot_git_abs_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()?;
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> {
let contents = fs.load(abs_path).await?;
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) {
fsmonitor_parse_state = Some(FsMonitorParseState::FsMonitor);
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
} else {
fsmonitor_parse_state.take();
@@ -12,7 +12,13 @@ use pretty_assertions::assert_eq;
use rand::prelude::*;
use serde_json::json;
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};
#[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);
});
+ let path = PathBuf::from("/root/one/node_modules/c/lib");
+
// 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();
- fs.create_dir("/root/one/node_modules/c/lib".as_ref())
- .await
- .unwrap();
+ // When we open a directory, we check each ancestor whether it's a git
+ // repository. That means we have an fs.metadata call per ancestor that we
+ // need to subtract here.
+ let ancestors = path.ancestors().count();
+
+ fs.create_dir(path.as_ref()).await.unwrap();
cx.executor().run_until_parked();
+
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
);
}