Skip Git tracking for invisible worktrees (#55760)

Release Notes:

- N/A or Added/Fixed/Improved ...
This commit is contained in:
Lukas Wirth authored and GitHub committed 2026-05-06 06:57:06 +00:00
1 parent 2ae08181de
commit f0bdd3c4af
3 files changed
+109 -52

No files matched your search

-7
View File
@@ -1495,13 +1495,6 @@ impl GitStore {
else {
return;
};
if !worktree.read(cx).is_visible() {
log::debug!(
"not adding repositories for local worktree {:?} because it's not visible",
worktree.read(cx).abs_path()
);
return;
}
self.update_repositories_from_worktree(
*worktree_id,
project_environment.clone(),
+65 -45
View File
@@ -414,9 +414,13 @@ impl Worktree {
None
};
let root_repo_common_dir = discover_root_repo_common_dir(&abs_path, fs.as_ref())
.await
.map(SanitizedPath::from_arc);
let root_repo_common_dir = if visible {
discover_root_repo_common_dir(&abs_path, fs.as_ref())
.await
.map(SanitizedPath::from_arc)
} else {
None
};
Ok(cx.new(move |cx: &mut Context<Worktree>| {
let mut snapshot = LocalSnapshot {
@@ -1147,6 +1151,7 @@ impl LocalWorktree {
let next_entry_id = self.next_entry_id.clone();
let fs = self.fs.clone();
let scanning_enabled = self.scanning_enabled;
let track_git_repositories = self.visible;
let settings = self.settings.clone();
let (scan_states_tx, mut scan_states_rx) = mpsc::unbounded();
let background_scanner = cx.background_spawn({
@@ -1185,6 +1190,7 @@ impl LocalWorktree {
share_private_files,
settings,
watcher,
track_git_repositories,
is_single_file,
};
@@ -3936,6 +3942,7 @@ struct BackgroundScanner {
watcher: Arc<dyn Watcher>,
settings: WorktreeSettings,
share_private_files: bool,
track_git_repositories: bool,
/// Whether this is a single-file worktree (root is a file, not a directory).
/// Used to determine if we should give up after repeated canonicalization failures.
is_single_file: bool,
@@ -3961,7 +3968,7 @@ impl BackgroundScanner {
// If the worktree root does not contain a git repository, then find
// the git repository in an ancestor directory. Find any gitignore files
// in ancestor directories.
let repo = if scanning_enabled {
let repo = if scanning_enabled && self.track_git_repositories {
let (ignores, exclude, repo) =
discover_ancestor_git_repo(self.fs.clone(), &root_abs_path).await;
let mut state = self.state.lock().await;
@@ -3989,6 +3996,7 @@ impl BackgroundScanner {
let containing_git_repository = if let Some((ancestor_dot_git, work_directory)) = repo
&& scanning_enabled
&& self.track_git_repositories
{
maybe!(async {
self.state
@@ -4015,6 +4023,7 @@ impl BackgroundScanner {
let mut global_gitignore_events = if let Some(global_gitignore_path) =
&global_gitignore_file
&& scanning_enabled
&& self.track_git_repositories
{
let is_file = self.fs.is_file(&global_gitignore_path).await;
self.state.lock().await.snapshot.global_gitignore = if is_file {
@@ -4352,14 +4361,16 @@ impl BackgroundScanner {
let mut dot_git_paths = None;
for ancestor in abs_path.as_path().ancestors() {
if is_dot_git(ancestor, self.fs.as_ref()).await {
let path_in_git_dir = abs_path
.as_path()
.strip_prefix(ancestor)
.expect("stripping off the ancestor");
dot_git_paths = Some((ancestor.to_owned(), path_in_git_dir.to_owned()));
break;
if self.track_git_repositories {
for ancestor in abs_path.as_path().ancestors() {
if is_dot_git(ancestor, self.fs.as_ref()).await {
let path_in_git_dir = abs_path
.as_path()
.strip_prefix(ancestor)
.expect("stripping off the ancestor");
dot_git_paths = Some((ancestor.to_owned(), path_in_git_dir.to_owned()));
break;
}
}
}
@@ -4384,9 +4395,10 @@ impl BackgroundScanner {
}
}
if abs_path
.as_path()
.ends_with(Path::new(DOT_GIT).join(REPO_EXCLUDE))
if self.track_git_repositories
&& abs_path
.as_path()
.ends_with(Path::new(DOT_GIT).join(REPO_EXCLUDE))
{
if let Some(repository) = snapshot.git_repositories.values().find(|repo| {
repo.common_dir_abs_path.join(REPO_EXCLUDE) == abs_path.as_path()
@@ -4437,7 +4449,9 @@ impl BackgroundScanner {
continue;
};
if abs_path.file_name() == Some(OsStr::new(GITIGNORE)) {
if self.track_git_repositories
&& abs_path.file_name() == Some(OsStr::new(GITIGNORE))
{
for (_, repo) in snapshot
.git_repositories
.iter()
@@ -4774,29 +4788,33 @@ impl BackgroundScanner {
continue;
};
if child_name == DOT_GIT {
let mut state = self.state.lock().await;
state
.insert_git_repository(
child_path.clone(),
self.fs.as_ref(),
self.watcher.as_ref(),
)
.await;
} else if child_name == GITIGNORE {
match build_gitignore(&child_abs_path, self.fs.as_ref()).await {
Ok(ignore) => {
let ignore = Arc::new(ignore);
ignore_stack = ignore_stack
.append(IgnoreKind::Gitignore(job.abs_path.clone()), ignore.clone());
new_ignore = Some(ignore);
}
Err(error) => {
log::error!(
"error loading .gitignore file {:?} - {:?}",
child_name,
error
);
if self.track_git_repositories {
if child_name == DOT_GIT {
let mut state = self.state.lock().await;
state
.insert_git_repository(
child_path.clone(),
self.fs.as_ref(),
self.watcher.as_ref(),
)
.await;
} else if child_name == GITIGNORE {
match build_gitignore(&child_abs_path, self.fs.as_ref()).await {
Ok(ignore) => {
let ignore = Arc::new(ignore);
ignore_stack = ignore_stack.append(
IgnoreKind::Gitignore(job.abs_path.clone()),
ignore.clone(),
);
new_ignore = Some(ignore);
}
Err(error) => {
log::error!(
"error loading .gitignore file {:?} - {:?}",
child_name,
error
);
}
}
}
}
@@ -5004,11 +5022,12 @@ impl BackgroundScanner {
)
.await;
let mut new_ancestor_repo = if relative_paths.iter().any(|path| path.is_empty()) {
Some(discover_ancestor_git_repo(self.fs.clone(), &root_abs_path).await)
} else {
None
};
let mut new_ancestor_repo =
if self.track_git_repositories && relative_paths.iter().any(|path| path.is_empty()) {
Some(discover_ancestor_git_repo(self.fs.clone(), &root_abs_path).await)
} else {
None
};
let mut state = self.state.lock().await;
let doing_recursive_update = scan_queue_tx.is_some();
@@ -5054,7 +5073,8 @@ impl BackgroundScanner {
if let (Some(scan_queue_tx), true) = (&scan_queue_tx, is_dir) {
if state.should_scan_directory(&fs_entry)
|| (fs_entry.path.is_empty()
|| (self.track_git_repositories
&& fs_entry.path.is_empty()
&& abs_path.file_name() == Some(OsStr::new(DOT_GIT)))
{
state
+44
View File
@@ -3283,6 +3283,50 @@ async fn test_root_repo_common_dir(executor: BackgroundExecutor, cx: &mut TestAp
);
}
#[gpui::test]
async fn test_invisible_worktree_does_not_track_ancestor_git_repository(
executor: BackgroundExecutor,
cx: &mut TestAppContext,
) {
init_test(cx);
let fs = FakeFs::new(executor);
fs.insert_tree(
path!("/repo"),
json!({
".git": {},
"project": {
"file.txt": "content",
},
}),
)
.await;
let worktree = Worktree::local(
path!("/repo/project").as_ref(),
false,
fs.clone(),
Arc::default(),
true,
WorktreeId::from_proto(0),
&mut cx.to_async(),
)
.await
.unwrap();
worktree
.update(cx, |worktree, _| {
worktree.as_local().unwrap().scan_complete()
})
.await;
cx.run_until_parked();
worktree.read_with(cx, |worktree, _| {
let local_worktree = worktree.as_local().unwrap();
assert!(local_worktree.repositories().is_empty());
assert_eq!(local_worktree.root_repo_common_dir(), None);
});
}
#[gpui::test]
async fn test_linked_worktree_git_file_event_does_not_panic(
executor: BackgroundExecutor,