дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 03:05:40 +02:00
fix(search): stop outer .gitignore rules at nested repositories in the remaining walkers (#1304)
#1309 made the Find in Files, file scope, index and walk_files walkers require git inside a repository, so an outer repository's .gitignore stops at a nested repository. External TypeScript project discovery (ide-lsp) and Lombok detection (ide-sdk) still walked with require_git(false) and could miss a nested repository's sources the same way; they now use the same check. Adds tests for walk_files (nested repository with its own .gitignore and info/exclude, a plain folder inside the outer repository, the outer repository, a folder outside any repository) and for scope_files. Co-authored-by: jaw52 <[email protected]>
Цей коміт міститься в:
4 files changed
+71
-2
No files matched your search
@@ -59,7 +59,7 @@ fn source_files(root: &Path, exclude_root: &Path, excludes: Vec<String>, limit:
|
||||
}
|
||||
let Ok(ignores) = ignores.build() else { return Vec::new() };
|
||||
let mut walk = ignore::WalkBuilder::new(root);
|
||||
walk.require_git(false).follow_links(false).filter_entry(move |entry| {
|
||||
walk.require_git(ide_core::ignored::inside_git_repo(root)).follow_links(false).filter_entry(move |entry| {
|
||||
if entry.depth() == 0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ fn sources_import_lombok(root: &Path) -> bool {
|
||||
// Imports precede the first type, after at most a license header.
|
||||
const HEAD: u64 = 8 * 1024;
|
||||
let mut walker = ignore::WalkBuilder::new(root);
|
||||
walker.require_git(false).follow_links(false).filter_entry(|e| {
|
||||
walker.require_git(ide_core::ignored::inside_git_repo(root)).follow_links(false).filter_entry(|e| {
|
||||
e.depth() == 0
|
||||
|| !e.file_type().is_some_and(|t| t.is_dir())
|
||||
|| !OUTPUT_DIRS.contains(&e.file_name().to_string_lossy().as_ref())
|
||||
|
||||
@@ -207,6 +207,34 @@ fn honours_ignore_files_hidden_and_git() {
|
||||
assert_eq!(paths(&views), ["src/a.rs", "sub/kept.txt"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_repository_ignores_outer_repository_rules() {
|
||||
// #1254: the opened project is a Git repository inside a folder its parent repository ignores.
|
||||
let p = project(&[
|
||||
(".git/HEAD", b"ref: refs/heads/main\n"),
|
||||
("projects/.gitignore", b"*\n!.gitignore\n"),
|
||||
("projects/nested/.git/HEAD", b"ref: refs/heads/main\n"),
|
||||
("projects/nested/.gitignore", b"*.log\n"),
|
||||
("projects/nested/search-test.txt", b"somesss\n"),
|
||||
("projects/nested/debug.log", b"somesss\n"),
|
||||
]);
|
||||
let nested = p.path().join("projects/nested");
|
||||
let (views, _) = run(&nested, &query("somesss"));
|
||||
assert_eq!(paths(&views), ["search-test.txt"]);
|
||||
|
||||
let files = ide_search::scope_files(
|
||||
&nested,
|
||||
&SearchScope::Project,
|
||||
&ide_search::FileMask::new(&[], &[]).unwrap(),
|
||||
None,
|
||||
&CancellationToken::new(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut rels: Vec<_> = files.iter().map(|f| f.rel_path.as_str()).collect();
|
||||
rels.sort();
|
||||
assert_eq!(rels, ["search-test.txt"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skips_binary_files() {
|
||||
let mut late_nul = b"needle\n".to_vec();
|
||||
|
||||
@@ -259,6 +259,47 @@ fn walk_honours_ignores() {
|
||||
assert_eq!(found, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn walk_scopes_gitignore_to_each_repository() {
|
||||
let (_t, r) = root();
|
||||
// An outer repository ignores everything under `projects/`, nested repository included (#1254).
|
||||
write(&r.join("outer/.git/HEAD"), "ref: refs/heads/main\n");
|
||||
write(&r.join("outer/top.txt"), "");
|
||||
write(&r.join("outer/projects/.gitignore"), "*\n!.gitignore\n");
|
||||
write(&r.join("outer/projects/nested/.git/HEAD"), "ref: refs/heads/main\n");
|
||||
write(&r.join("outer/projects/nested/.git/info/exclude"), "local.txt\n");
|
||||
write(&r.join("outer/projects/nested/.gitignore"), "*.log\n");
|
||||
write(&r.join("outer/projects/nested/search-test.txt"), "");
|
||||
write(&r.join("outer/projects/nested/src/a.rs"), "");
|
||||
write(&r.join("outer/projects/nested/debug.log"), "");
|
||||
write(&r.join("outer/projects/nested/local.txt"), "");
|
||||
write(&r.join("outer/projects/plain/file.txt"), "");
|
||||
write(&r.join("loose/.gitignore"), "*.log\n");
|
||||
write(&r.join("loose/a.txt"), "");
|
||||
write(&r.join("loose/b.log"), "");
|
||||
|
||||
let walk = |dir: &str| {
|
||||
let base = r.join(dir);
|
||||
let found = Mutex::new(BTreeSet::new());
|
||||
walk_files(&base, &[], |p| {
|
||||
found.lock().insert(p.strip_prefix(&base).unwrap().to_string_lossy().replace('\\', "/"));
|
||||
});
|
||||
found.into_inner().into_iter().collect::<Vec<_>>()
|
||||
};
|
||||
let cases: [(&str, &[&str]); 4] = [
|
||||
// The nested repository's own rules apply; the outer repository's do not.
|
||||
("outer/projects/nested", &[".gitignore", "search-test.txt", "src/a.rs"]),
|
||||
// A plain folder inside the outer repository is still governed by it, as in Git.
|
||||
("outer/projects/plain", &[]),
|
||||
("outer", &["projects/.gitignore", "top.txt"]),
|
||||
// Outside any repository `.gitignore` still applies.
|
||||
("loose", &[".gitignore", "a.txt"]),
|
||||
];
|
||||
for (dir, expected) in cases {
|
||||
assert_eq!(walk(dir), expected, "{dir}");
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_for(events: &Mutex<Vec<FsBatch>>, pred: impl Fn(&[FsBatch]) -> bool) -> bool {
|
||||
let deadline = Instant::now() + Duration::from_secs(10);
|
||||
while Instant::now() < deadline {
|
||||
|
||||
Reference in new issue
Заблокувати користувача