diff --git a/src/git.rs b/src/git.rs index 0c21751..4dedd50 100644 --- a/src/git.rs +++ b/src/git.rs @@ -145,6 +145,30 @@ impl version_control::System for Repo { .collect::>(); Ok(filtered_files) } + + fn get_all_files(&self, _under: Option<&AbsPath>) -> Result> { + let output = Command::new("git") + .arg("grep") + .arg("-Il") + .arg(".") + .current_dir(&self.root) + .output()?; + + ensure_output("git grep -Il", &output)?; + + let files = + std::str::from_utf8(&output.stdout).context("failed to parse paths_cmd output")?; + let files = files + .lines() + .map(|s| s.to_string()) + .collect::>(); + let mut files = files.into_iter().collect::>(); + files.sort(); + files + .into_iter() + .map(AbsPath::try_from) + .collect::>() + } } pub fn get_paths_from_cmd(paths_cmd: &str) -> Result> { diff --git a/src/lib.rs b/src/lib.rs index 1cb326f..199fe75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -171,6 +171,12 @@ pub fn do_lint( return Ok(0); } + let config_dir = if only_lint_under_config_dir { + Some(AbsPath::try_from(linters[0].get_config_dir())?) + } else { + None + }; + let mut files = match paths_opt { PathsOpt::Auto => { let relative_to = match revision_opt { @@ -186,20 +192,18 @@ pub fn do_lint( PathsOpt::PathsCmd(paths_cmd) => get_paths_from_cmd(&paths_cmd)?, PathsOpt::Paths(paths) => get_paths_from_input(paths)?, PathsOpt::PathsFile(file) => get_paths_from_file(file)?, - PathsOpt::AllFiles => get_paths_from_cmd("git grep -Il .")?, + PathsOpt::AllFiles => repo.get_all_files(config_dir.as_ref())?, }; // Sort and unique the files so we pass a consistent ordering to linters - files.sort(); - files.dedup(); - - if only_lint_under_config_dir { - let config_dir = linters[0].get_config_dir(); + if let Some(config_dir) = config_dir { files = files .into_iter() - .filter(|path| path.starts_with(config_dir)) + .filter(|path| path.starts_with(&config_dir)) .collect(); } + files.sort(); + files.dedup(); let files = Arc::new(files); diff --git a/src/sapling.rs b/src/sapling.rs index a8991d5..0e2eae4 100644 --- a/src/sapling.rs +++ b/src/sapling.rs @@ -1,4 +1,8 @@ -use crate::{log_utils, path, version_control}; +use crate::{ + log_utils, + path::{self, AbsPath}, + version_control, +}; use anyhow; @@ -41,6 +45,44 @@ impl version_control::System for Repo { Ok(merge_base.to_string()) } + fn get_all_files(&self, under: Option<&AbsPath>) -> anyhow::Result> { + // Output of sl status looks like: + // D src/lib.rs + // M foo/bar.baz + let re = regex::Regex::new(r"^[A-Z?]\s+")?; + + let mut cmd = std::process::Command::new("sl"); + cmd.arg("status").arg("--all"); + if let Some(under) = under { + cmd.arg(under.as_os_str()); + } + cmd.current_dir(&self.root); + let output = cmd.output()?; + log_utils::ensure_output(&format!("{:?}", cmd), &output)?; + let commit_files_str = std::str::from_utf8(&output.stdout)?; + let commit_files: std::collections::HashSet = commit_files_str + .split('\n') + .map(|x| x.to_string()) + .map(|line| re.replace(&line, "").to_string()) + .filter(|line| !line.is_empty()) + .filter(|line| !line.starts_with('I')) + .collect(); + + let filtered_commit_files = commit_files + .into_iter() + .map(|f| format!("{}", self.root.join(f).display())) + .filter_map(|f| match path::AbsPath::try_from(&f) { + Ok(abs_path) => Some(abs_path), + Err(_) => { + eprintln!("Failed to find file while gathering files to lint: {}", f); + None + } + }) + .collect::>(); + + Ok(filtered_commit_files) + } + fn get_changed_files(&self, relative_to: Option<&str>) -> anyhow::Result> { // Output of sl status looks like: // D src/lib.rs @@ -92,7 +134,7 @@ mod tests { use std::{fs::OpenOptions, io::Write, sync::Mutex}; // 1.4.0 static SL_GLOBAL_MUTEX: Lazy> = Lazy::new(Mutex::default); - use crate::testing; + use crate::{testing, version_control::System}; use super::*; use anyhow::Result; @@ -175,7 +217,6 @@ mod tests { fn changed_files(&self, relative_to: Option<&str>) -> Result> { let _shared = SL_GLOBAL_MUTEX.lock().unwrap(); std::env::set_current_dir(&self.root)?; - use version_control::System; let repo = Repo::new()?; let files = repo.get_changed_files(relative_to)?; let files = files @@ -188,10 +229,16 @@ mod tests { fn merge_base_with(&self, merge_base_with: &str) -> Result { let _shared = SL_GLOBAL_MUTEX.lock().unwrap(); std::env::set_current_dir(&self.root)?; - use version_control::System; let repo = Repo::new()?; repo.get_merge_base_with(merge_base_with) } + + fn get_all_files(&self) -> Result> { + let _shared = SL_GLOBAL_MUTEX.lock().unwrap(); + std::env::set_current_dir(&self.root)?; + let repo = Repo::new()?; + repo.get_all_files(None) + } } // Should properly detect changes in the commit (and not check other files) @@ -374,6 +421,34 @@ mod tests { Ok(()) } + #[test] + #[cfg_attr(target_os = "windows", ignore)] // remove when sapling installation is better + #[cfg_attr(target_os = "linux", ignore)] // remove when sapling installation is better + fn get_all_files() -> Result<()> { + let git = testing::GitCheckout::new()?; + git.write_file("test_1.txt", "Initial commit")?; + git.write_file("test_2.txt", "Initial commit")?; + git.write_file("test_3.txt", "Initial commit")?; + git.write_file("test_4.txt", "Initial commit")?; + + git.add(".")?; + git.commit("I am main")?; + let sl = SaplingClone::new(&git)?; + let mut all_files = sl.get_all_files()?; + all_files.sort(); + assert_eq!( + all_files, + vec!( + AbsPath::try_from("README")?, + AbsPath::try_from("test_1.txt")?, + AbsPath::try_from("test_2.txt")?, + AbsPath::try_from("test_3.txt")?, + AbsPath::try_from("test_4.txt")? + ) + ); + Ok(()) + } + #[test] #[cfg_attr(target_os = "windows", ignore)] // remove when sapling installation is better #[cfg_attr(target_os = "linux", ignore)] // remove when sapling installation is better diff --git a/src/version_control.rs b/src/version_control.rs index d9c7f72..00486e8 100644 --- a/src/version_control.rs +++ b/src/version_control.rs @@ -1,4 +1,8 @@ -use crate::{git, path, sapling}; +use crate::{ + git, + path::{self, AbsPath}, + sapling, +}; use anyhow; @@ -27,6 +31,9 @@ pub trait System { // Gets the files that have changed relative to the given commit. fn get_changed_files(&self, relative_to: Option<&str>) -> anyhow::Result>; + + // Get all files in the repo. + fn get_all_files(&self, under: Option<&AbsPath>) -> anyhow::Result>; } impl Repo { @@ -57,4 +64,8 @@ impl Repo { RepoImpl::Sapling(sapling) => Box::new(sapling as &dyn System), } } + + pub fn get_all_files(&self, under: Option<&AbsPath>) -> anyhow::Result> { + self.get_system().get_all_files(under) + } }