summaryrefslogtreecommitdiffstats
path: root/src/tools/build_helper/src/git.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/build_helper/src/git.rs')
-rw-r--r--src/tools/build_helper/src/git.rs72
1 files changed, 65 insertions, 7 deletions
diff --git a/src/tools/build_helper/src/git.rs b/src/tools/build_helper/src/git.rs
index dc62051cb..168633c8f 100644
--- a/src/tools/build_helper/src/git.rs
+++ b/src/tools/build_helper/src/git.rs
@@ -1,5 +1,24 @@
+use std::process::Stdio;
use std::{path::Path, process::Command};
+/// Runs a command and returns the output
+fn output_result(cmd: &mut Command) -> Result<String, String> {
+ let output = match cmd.stderr(Stdio::inherit()).output() {
+ Ok(status) => status,
+ Err(e) => return Err(format!("failed to run command: {:?}: {}", cmd, e)),
+ };
+ if !output.status.success() {
+ return Err(format!(
+ "command did not execute successfully: {:?}\n\
+ expected success, got: {}\n{}",
+ cmd,
+ output.status,
+ String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))?
+ ));
+ }
+ Ok(String::from_utf8(output.stdout).map_err(|err| format!("{err:?}"))?)
+}
+
/// Finds the remote for rust-lang/rust.
/// For example for these remotes it will return `upstream`.
/// ```text
@@ -14,13 +33,7 @@ pub fn get_rust_lang_rust_remote(git_dir: Option<&Path>) -> Result<String, Strin
git.current_dir(git_dir);
}
git.args(["config", "--local", "--get-regex", "remote\\..*\\.url"]);
-
- let output = git.output().map_err(|err| format!("{err:?}"))?;
- if !output.status.success() {
- return Err("failed to execute git config command".to_owned());
- }
-
- let stdout = String::from_utf8(output.stdout).map_err(|err| format!("{err:?}"))?;
+ let stdout = output_result(&mut git)?;
let rust_lang_remote = stdout
.lines()
@@ -73,3 +86,48 @@ pub fn updated_master_branch(git_dir: Option<&Path>) -> Result<String, String> {
// We could implement smarter logic here in the future.
Ok("origin/master".into())
}
+
+/// Returns the files that have been modified in the current branch compared to the master branch.
+/// The `extensions` parameter can be used to filter the files by their extension.
+/// If `extensions` is empty, all files will be returned.
+pub fn get_git_modified_files(
+ git_dir: Option<&Path>,
+ extensions: &Vec<&str>,
+) -> Result<Option<Vec<String>>, String> {
+ let Ok(updated_master) = updated_master_branch(git_dir) else { return Ok(None); };
+
+ let git = || {
+ let mut git = Command::new("git");
+ if let Some(git_dir) = git_dir {
+ git.current_dir(git_dir);
+ }
+ git
+ };
+
+ let merge_base = output_result(git().arg("merge-base").arg(&updated_master).arg("HEAD"))?;
+ let files = output_result(git().arg("diff-index").arg("--name-only").arg(merge_base.trim()))?
+ .lines()
+ .map(|s| s.trim().to_owned())
+ .filter(|f| {
+ Path::new(f).extension().map_or(false, |ext| {
+ extensions.is_empty() || extensions.contains(&ext.to_str().unwrap())
+ })
+ })
+ .collect();
+ Ok(Some(files))
+}
+
+/// Returns the files that haven't been added to git yet.
+pub fn get_git_untracked_files(git_dir: Option<&Path>) -> Result<Option<Vec<String>>, String> {
+ let Ok(_updated_master) = updated_master_branch(git_dir) else { return Ok(None); };
+ let mut git = Command::new("git");
+ if let Some(git_dir) = git_dir {
+ git.current_dir(git_dir);
+ }
+
+ let files = output_result(git.arg("ls-files").arg("--others").arg("--exclude-standard"))?
+ .lines()
+ .map(|s| s.trim().to_owned())
+ .collect();
+ Ok(Some(files))
+}