Add handling of git's core.excludesFile (#33592)

Taking over from #28314.

Part of https://github.com/zed-industries/zed/issues/4824

Co-authored-by: Paul Nameless <reacsdas@gmail.com>

Release Notes:

- Zed now respects git's `core.excludesFile` (~/.config/git/ignore) in
addition to .gitignore.

---------

Co-authored-by: Paul Nameless <reacsdas@gmail.com>
This commit is contained in:
Cole Miller
2025-09-11 21:00:03 -04:00
committed by GitHub
co-authored by Paul Nameless
parent b60e705782
commit 45ee1327a4
15 changed files with 454 additions and 136 deletions
+111 -1
View File
@@ -2120,7 +2120,6 @@ async fn test_repository_above_root(executor: BackgroundExecutor, cx: &mut TestA
});
pretty_assertions::assert_eq!(repos, [Path::new(path!("/root")).into()]);
eprintln!(">>>>>>>>>> touch");
fs.touch_path(path!("/root/subproject")).await;
worktree
.update(cx, |worktree, _| {
@@ -2141,6 +2140,117 @@ async fn test_repository_above_root(executor: BackgroundExecutor, cx: &mut TestA
pretty_assertions::assert_eq!(repos, [Path::new(path!("/root")).into()]);
}
#[gpui::test]
async fn test_global_gitignore(executor: BackgroundExecutor, cx: &mut TestAppContext) {
init_test(cx);
let home = paths::home_dir();
let fs = FakeFs::new(executor);
fs.insert_tree(
home,
json!({
".config": {
"git": {
"ignore": "foo\n/bar\nbaz\n"
}
},
"project": {
".git": {},
".gitignore": "!baz",
"foo": "",
"bar": "",
"sub": {
"bar": "",
},
"subrepo": {
".git": {},
"bar": ""
},
"baz": ""
}
}),
)
.await;
let worktree = Worktree::local(
home.join("project"),
true,
fs.clone(),
Arc::default(),
&mut cx.to_async(),
)
.await
.unwrap();
worktree
.update(cx, |worktree, _| {
worktree.as_local().unwrap().scan_complete()
})
.await;
cx.run_until_parked();
// .gitignore overrides excludesFile, and anchored paths in excludesFile are resolved
// relative to the nearest containing repository
worktree.update(cx, |worktree, _cx| {
check_worktree_entries(
worktree,
&[],
&["foo", "bar", "subrepo/bar"],
&["sub/bar", "baz"],
&[],
);
});
// Ignore statuses are updated when excludesFile changes
fs.write(
&home.join(".config").join("git").join("ignore"),
"/bar\nbaz\n".as_bytes(),
)
.await
.unwrap();
worktree
.update(cx, |worktree, _| {
worktree.as_local().unwrap().scan_complete()
})
.await;
cx.run_until_parked();
worktree.update(cx, |worktree, _cx| {
check_worktree_entries(
worktree,
&[],
&["bar", "subrepo/bar"],
&["foo", "sub/bar", "baz"],
&[],
);
});
// Statuses are updated when .git added/removed
fs.remove_dir(
&home.join("project").join("subrepo").join(".git"),
RemoveOptions {
recursive: true,
..Default::default()
},
)
.await
.unwrap();
worktree
.update(cx, |worktree, _| {
worktree.as_local().unwrap().scan_complete()
})
.await;
cx.run_until_parked();
worktree.update(cx, |worktree, _cx| {
check_worktree_entries(
worktree,
&[],
&["bar"],
&["foo", "sub/bar", "baz", "subrepo/bar"],
&[],
);
});
}
#[track_caller]
fn check_worktree_entries(
tree: &Worktree,