use std::fs;
use std::path::Path;
use std::process::Command;
use std::sync::atomic::{AtomicU64, Ordering};
use rag_rat_core::language::Language;
use rag_rat_core::{Config, IndexDatabase};
static TEMP: AtomicU64 = AtomicU64::new(0);
fn git(dir: &Path, args: &[&str]) {
let out = Command::new("git").arg("-C").arg(dir).args(args).output().unwrap();
assert!(out.status.success(), "git {args:?}: {}", String::from_utf8_lossy(&out.stderr));
}
#[test]
fn worktree_overlay_ignores_inherited_git_dir_env() {
let base = std::env::temp_dir().join(format!(
"rag-rat-wtenv-{}-{}",
std::process::id(),
TEMP.fetch_add(1, Ordering::Relaxed)
));
let _ = fs::remove_dir_all(&base);
let main = base.join("main");
fs::create_dir_all(main.join("src")).unwrap();
fs::write(main.join("src/keep.rs"), "pub fn keep_fn() {}\n").unwrap();
fs::write(main.join("src/reinf.rs"), "pub fn classify_seg() {}\n").unwrap();
fs::write(main.join(".gitignore"), "/wt/\n").unwrap();
fs::write(
main.join("rag-rat.toml"),
"[index]\nroot = \".\"\ndatabase = \".rag-rat/index.sqlite\"\n\n[target_bindings]\nrust = \
[\"src\"]\n",
)
.unwrap();
git(&main, &["init", "-q"]);
git(&main, &["config", "user.email", "t@e.com"]);
git(&main, &["config", "user.name", "t"]);
git(&main, &["add", "."]);
git(&main, &["commit", "-q", "-m", "C1 has reinf"]);
let wt = main.join("wt");
git(&main, &["worktree", "add", "-q", "-b", "feat", wt.to_str().unwrap()]);
fs::remove_file(main.join("src/reinf.rs")).unwrap();
git(&main, &["add", "."]);
git(&main, &["commit", "-q", "-m", "C2 removed reinf"]);
let config_path = main.join("rag-rat.toml");
let config = Config::load(&config_path).unwrap();
IndexDatabase::rebuild(&config).unwrap();
let git_dir = main.join(".git/worktrees/feat");
let binary = env!("CARGO_BIN_EXE_rag-rat");
let out = Command::new(binary)
.arg("--config")
.arg(&config_path)
.args(["index", "--worktree"])
.arg(&wt)
.env("RAG_RAT_NO_WATCH", "1")
.env("GIT_DIR", &git_dir)
.env("GIT_WORK_TREE", &wt)
.output()
.unwrap();
assert!(
out.status.success(),
"index --worktree failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let mut db = IndexDatabase::open(&config.database).unwrap();
db.use_worktree_scope(&main, Some(&wt)).unwrap();
let hits = db.symbols("classify_seg", Some(Language::Rust), 10).unwrap();
assert!(
!hits.is_empty(),
"an inherited worktree GIT_DIR hijacked resolution and pruned the overlay (reinf.rs \
missing)"
);
let _ = fs::remove_dir_all(&base);
}