1use anyhow::{Context, Result};
2use regex::Regex;
3use std::path::Path;
4use std::process::{Command, Stdio};
5
6use crate::log_debug;
7
8pub fn is_inside_work_tree() -> Result<bool> {
14 let status = Command::new("git")
15 .args(["rev-parse", "--is-inside-work-tree"])
16 .stdout(Stdio::null())
17 .stderr(Stdio::null())
18 .status();
19
20 match status {
21 Ok(exit) => Ok(exit.success()),
22 Err(_) => Ok(false),
23 }
24}
25
26pub fn is_binary_diff(diff: &str) -> bool {
28 diff.contains("Binary files")
29 || diff.contains("GIT binary patch")
30 || diff.contains("[Binary file changed]")
31}
32
33pub fn run_git_command(args: &[&str]) -> Result<String> {
43 let output = Command::new("git")
44 .args(args)
45 .output()
46 .context("Failed to execute git command")?;
47
48 if !output.status.success() {
49 return Err(anyhow::anyhow!(
50 "Git command failed: {}",
51 String::from_utf8_lossy(&output.stderr)
52 ));
53 }
54
55 let stdout =
56 String::from_utf8(output.stdout).context("Invalid UTF-8 output from git command")?;
57
58 Ok(stdout.trim().to_string())
59}
60
61pub fn should_exclude_file(path: &str) -> bool {
66 log_debug!("Checking if file should be excluded: {}", path);
67 let exclude_patterns = vec![
68 (String::from(r"(^|/)\.git(/|$)"), false), (String::from(r"(^|/)\.svn(/|$)"), false),
70 (String::from(r"(^|/)\.hg(/|$)"), false),
71 (String::from(r"(^|/)\.DS_Store$"), false),
72 (String::from(r"(^|/)node_modules(/|$)"), false),
73 (String::from(r"(^|/)target(/|$)"), false),
74 (String::from(r"(^|/)build(/|$)"), false),
75 (String::from(r"(^|/)dist(/|$)"), false),
76 (String::from(r"(^|/)\.vscode(/|$)"), false),
77 (String::from(r"(^|/)\.idea(/|$)"), false),
78 (String::from(r"(^|/)\.vs(/|$)"), false),
79 (String::from(r"package-lock\.json$"), true),
80 (String::from(r"\.lock$"), true),
81 (String::from(r"\.log$"), true),
82 (String::from(r"\.tmp$"), true),
83 (String::from(r"\.temp$"), true),
84 (String::from(r"\.swp$"), true),
85 (String::from(r"\.min\.js$"), true),
86 ];
87
88 let path = Path::new(path);
89
90 for (pattern, is_extension) in exclude_patterns {
91 let re = match Regex::new(&pattern) {
92 Ok(re) => re,
93 Err(e) => {
94 log_debug!("Failed to compile regex '{}': {}", pattern, e);
95 continue;
96 }
97 };
98
99 if is_extension {
100 if let Some(file_name) = path.file_name()
101 && let Some(file_name_str) = file_name.to_str()
102 && re.is_match(file_name_str)
103 {
104 log_debug!("File excluded: {}", path.display());
105 return true;
106 }
107 } else if let Some(path_str) = path.to_str()
108 && re.is_match(path_str)
109 {
110 log_debug!("File excluded: {}", path.display());
111 return true;
112 }
113 }
114 log_debug!("File not excluded: {}", path.display());
115 false
116}