1use std::path::Path;
5use std::process::Command;
6
7pub fn changed_since(repo: &Path, since_commit: &str, paths: &[String]) -> Option<bool> {
11 if paths.is_empty() {
12 return Some(false);
13 }
14 let mut args: Vec<String> = vec![
15 "rev-list".into(),
16 format!("{since_commit}..HEAD"),
17 "--".into(),
18 ];
19 args.extend(paths.iter().cloned());
20 let out = Command::new("git")
21 .args(&args)
22 .current_dir(repo)
23 .output()
24 .ok()?;
25 if !out.status.success() {
26 return None; }
28 Some(!out.stdout.is_empty())
29}
30
31#[cfg(test)]
32mod tests {
33 use super::*;
34 use std::process::Command;
35
36 fn two_commit_repo() -> (std::path::PathBuf, String, String) {
39 use std::sync::atomic::{AtomicU64, Ordering};
40 static N: AtomicU64 = AtomicU64::new(0);
41 let p = std::env::temp_dir().join(format!(
42 "ev-liveness-{}-{}",
43 std::process::id(),
44 N.fetch_add(1, Ordering::Relaxed)
45 ));
46 let _ = std::fs::remove_dir_all(&p);
47 std::fs::create_dir_all(&p).unwrap();
48 let git = |args: &[&str]| {
49 Command::new("git")
50 .args(args)
51 .current_dir(&p)
52 .output()
53 .unwrap();
54 };
55 git(&["init"]);
56 git(&["config", "user.email", "t@e.st"]);
57 git(&["config", "user.name", "Tester"]);
58 std::fs::write(p.join("readme.md"), "a").unwrap();
59 git(&["add", "."]);
60 git(&["commit", "-m", "first"]);
61 let first = String::from_utf8(
62 Command::new("git")
63 .args(["rev-parse", "HEAD"])
64 .current_dir(&p)
65 .output()
66 .unwrap()
67 .stdout,
68 )
69 .unwrap()
70 .trim()
71 .to_string();
72 std::fs::write(p.join("pyproject.toml"), "deps=[]").unwrap();
73 git(&["add", "."]);
74 git(&["commit", "-m", "second touches pyproject"]);
75 let second = String::from_utf8(
76 Command::new("git")
77 .args(["rev-parse", "HEAD"])
78 .current_dir(&p)
79 .output()
80 .unwrap()
81 .stdout,
82 )
83 .unwrap()
84 .trim()
85 .to_string();
86 (p, first, second)
87 }
88
89 #[test]
90 fn changed_since_should_be_true_when_a_triggered_path_changed_after_the_commit() {
91 let (repo, first, _second) = two_commit_repo();
93
94 let r = changed_since(&repo, &first, &["pyproject.toml".into()]);
96
97 assert_eq!(r, Some(true));
99 }
100
101 #[test]
102 fn changed_since_should_be_false_when_no_triggered_path_changed_after_the_commit() {
103 let (repo, _first, second) = two_commit_repo();
105
106 let r = changed_since(&repo, &second, &["pyproject.toml".into()]);
108
109 assert_eq!(r, Some(false));
111 }
112
113 #[test]
114 fn changed_since_should_be_none_when_the_commit_is_unknown() {
115 let (repo, _first, _second) = two_commit_repo();
117
118 let r = changed_since(
120 &repo,
121 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
122 &["pyproject.toml".into()],
123 );
124
125 assert_eq!(r, None);
127 }
128}