Skip to main content

fluidattacks_core_git/
ops.rs

1use std::path::Path;
2
3use crate::cli;
4
5/// Runs `git reset --hard`, removes symlinks, and returns whether it succeeded.
6pub async fn reset_repo(repo_path: &Path) -> bool {
7    if let Err(error) = cli::set_safe_directory().await {
8        tracing::error!(repo_path = %repo_path.display(), error = %error, "failed to add the safe directory");
9    }
10    if !repo_path.join(".git").exists() {
11        tracing::error!(repo_path = %repo_path.display(), "not the root of a repository");
12        return false;
13    }
14    if let Err(error) = cli::reset_hard(repo_path).await {
15        tracing::error!(repo_path = %repo_path.display(), error = %error, "failed to reset the repository");
16        return false;
17    }
18    let root = repo_path.to_path_buf();
19    match tokio::task::spawn_blocking(move || super::fs::remove_symlinks(&root)).await {
20        Ok(Ok(())) => true,
21        Ok(Err(error)) => {
22            tracing::error!(repo_path = %repo_path.display(), error = %error, "failed to remove the symlinks");
23            false
24        }
25        Err(error) => {
26            tracing::error!(repo_path = %repo_path.display(), error = %error, "failed to remove the symlinks");
27            false
28        }
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use super::reset_repo;
35    use std::path::Path;
36
37    fn git(repo: &Path, args: &[&str]) {
38        let status = std::process::Command::new("git")
39            .arg("-C")
40            .arg(repo)
41            .args(args)
42            .status()
43            .unwrap();
44        assert!(status.success());
45    }
46
47    #[tokio::test]
48    async fn only_the_root_of_the_repository_gets_reset() {
49        let dir = tempfile::tempdir().unwrap();
50        std::env::set_var("GIT_CONFIG_GLOBAL", dir.path().join("gitconfig"));
51
52        let repo = dir.path().join("repo");
53        std::fs::create_dir_all(repo.join("sub/deep")).unwrap();
54        git(&repo, &["init", "-q"]);
55        std::fs::write(repo.join("f.txt"), b"committed").unwrap();
56        git(&repo, &["add", "-A"]);
57        git(
58            &repo,
59            &[
60                "-c",
61                "user.email=dev@fluidattacks.com",
62                "-c",
63                "user.name=dev",
64                "commit",
65                "-q",
66                "-m",
67                "files",
68            ],
69        );
70        std::fs::write(repo.join("f.txt"), b"dirty").unwrap();
71
72        let deep = repo.join("sub/deep");
73        assert!(!reset_repo(&deep).await);
74        assert_eq!(
75            std::fs::read_to_string(repo.join("f.txt")).unwrap(),
76            "dirty"
77        );
78
79        assert!(reset_repo(&repo).await);
80        assert_eq!(
81            std::fs::read_to_string(repo.join("f.txt")).unwrap(),
82            "committed"
83        );
84    }
85}