ai_agent/utils/git/
gitignore.rs1use std::io::Write;
3use std::path::Path;
4use std::process::Command;
5
6pub fn is_path_gitignored<P: AsRef<std::path::Path>, C: AsRef<std::path::Path>>(file_path: P, cwd: C) -> bool {
8 let output = Command::new("git")
9 .args(["check-ignore", file_path.as_ref().to_str().unwrap_or("")])
10 .current_dir(cwd.as_ref())
11 .output();
12
13 match output {
14 Ok(o) => o.status.code() == Some(0),
15 Err(_) => false,
16 }
17}
18
19pub fn get_global_gitignore_path() -> String {
21 if let Some(home) = std::env::var("HOME").ok() {
22 format!("{}/.config/git/ignore", home)
23 } else {
24 ".config/git/ignore".to_string()
25 }
26}
27
28pub fn dir_is_in_git_repo(dir: &str) -> bool {
30 Command::new("git")
31 .args(["rev-parse", "--git-dir"])
32 .current_dir(dir)
33 .output()
34 .map(|o| o.status.success())
35 .unwrap_or(false)
36}
37
38pub async fn add_file_glob_rule_to_gitignore(file_name: &str, cwd: &str) {
40 if let Err(e) = add_file_glob_rule_inner(file_name, cwd).await {
41 eprintln!("Failed to add gitignore rule: {}", e);
42 }
43}
44
45async fn add_file_glob_rule_inner(file_name: &str, cwd: &str) -> std::io::Result<()> {
46 if !dir_is_in_git_repo(cwd) {
47 return Ok(());
48 }
49
50 let gitignore_entry = format!("**/{}", file_name);
51 let test_path = if file_name.ends_with('/') {
52 format!("{}sample-file.txt", file_name)
53 } else {
54 file_name.to_string()
55 };
56
57 if is_path_gitignored(&test_path, cwd) {
58 return Ok(());
59 }
60
61 let global_gitignore_path = get_global_gitignore_path();
62
63 if let Some(parent) = Path::new(&global_gitignore_path).parent() {
64 std::fs::create_dir_all(parent)?;
65 }
66
67 if let Ok(content) = std::fs::read_to_string(&global_gitignore_path) {
68 if content.contains(&gitignore_entry) {
69 return Ok(());
70 }
71 std::fs::OpenOptions::new()
72 .append(true)
73 .open(&global_gitignore_path)?
74 .write_all(format!("\n{}\n", gitignore_entry).as_bytes())?;
75 } else {
76 std::fs::write(&global_gitignore_path, format!("{}\n", gitignore_entry))?;
77 }
78
79 Ok(())
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn test_is_path_gitignored_not_in_repo() {
88 assert!(!is_path_gitignored("some_file.txt", "/tmp"));
89 }
90
91 #[test]
92 fn test_get_global_gitignore_path() {
93 let path = get_global_gitignore_path();
94 assert!(path.contains(".config/git/ignore"));
95 }
96}