Respect .git/info/exclude in secondary worktrees (#51536) (cherry-pick to preview) (#55685)

Cherry-pick of #51536 to preview

----
Closes #50880 

When a git worktree linked via a `.git` file (e.g. `gitdir:
/repo/.git/worktrees/my-worktree`) was opened in Zed, entries in
`.git/info/exclude` were not respected. This is now fixed.

Before you mark this PR as ready for review, make sure that you have:
- [x] Added a solid test coverage and/or screenshots from doing manual
testing
- [x] Done a self-review taking into account security and performance
aspects
- [x] Aligned any UI changes with the [UI

checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)

Release Notes:

- Fixed `.git/info/exclude` not being respected when opening a secondary
git worktree



https://github.com/user-attachments/assets/f38df5dc-96eb-40a8-a77c-0932a2c8575b

---------

Co-authored-by: Lukas Wirth <lukas@zed.dev>
Co-authored-by: Lukas Wirth <me@lukaswirth.dev>

Co-authored-by: Fanteria <jiri.alexandrovic@gmail.com>
Co-authored-by: Lukas Wirth <lukas@zed.dev>
Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
This commit is contained in:
zed-zippy[bot] 2026-05-04 17:33:41 +00:00 committed by GitHub
parent 8af115d0aa
commit 3653b995b0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 73 additions and 1 deletions

View file

@ -5568,7 +5568,9 @@ async fn discover_ancestor_git_repo(
};
}
let repo_exclude_abs_path = ancestor_dot_git.join(REPO_EXCLUDE);
let dot_git_path: Arc<Path> = ancestor_dot_git.into();
let (_, common_dir_abs_path) = discover_git_paths(&dot_git_path, fs.as_ref()).await;
let repo_exclude_abs_path = common_dir_abs_path.join(REPO_EXCLUDE);
if let Ok(repo_exclude) = build_gitignore(&repo_exclude_abs_path, fs.as_ref()).await {
exclude = Some(Arc::new(repo_exclude));
}

View file

@ -2844,6 +2844,76 @@ async fn test_global_gitignore(executor: BackgroundExecutor, cx: &mut TestAppCon
});
}
#[gpui::test]
async fn test_repo_exclude_in_worktree(executor: BackgroundExecutor, cx: &mut TestAppContext) {
init_test(cx);
let fs = FakeFs::new(executor);
fs.insert_tree(
path!("/repo"),
json!({
".git": {
"info": {
"exclude": ".env.*"
},
"worktrees": {
"my-worktree": {
"commondir": "../.."
}
}
}
}),
)
.await;
fs.insert_tree(
path!("/worktree"),
json!({
// .git is pointing to the repo
".git": "gitdir: /repo/.git/worktrees/my-worktree",
".env.local": "secret=1234",
"not-ignored.txt": "",
}),
)
.await;
let worktree = Worktree::local(
path!("/worktree").as_ref(),
true,
fs.clone(),
Default::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();
// .env.local should be ignored via info/exclude from the repo's exclude
worktree.update(cx, |worktree, _cx| {
let expected_excluded_paths = [];
let expected_ignored_paths = [".env.local"];
let expected_tracked_paths = ["not-ignored.txt"];
let expected_included_paths = [];
check_worktree_entries(
worktree,
&expected_excluded_paths,
&expected_ignored_paths,
&expected_tracked_paths,
&expected_included_paths,
);
});
}
#[gpui::test]
async fn test_repo_exclude(executor: BackgroundExecutor, cx: &mut TestAppContext) {
init_test(cx);