fluidattacks-core-git 0.34.0

Fluid Attacks Core Git
Documentation
use std::path::Path;

use crate::cli;

/// Runs `git reset --hard`, removes symlinks, and returns whether it succeeded.
pub async fn reset_repo(repo_path: &Path) -> bool {
    if let Err(error) = cli::set_safe_directory().await {
        tracing::error!(repo_path = %repo_path.display(), error = %error, "failed to add the safe directory");
    }
    if !repo_path.join(".git").exists() {
        tracing::error!(repo_path = %repo_path.display(), "not the root of a repository");
        return false;
    }
    if let Err(error) = cli::reset_hard(repo_path).await {
        tracing::error!(repo_path = %repo_path.display(), error = %error, "failed to reset the repository");
        return false;
    }
    let root = repo_path.to_path_buf();
    if let Err(error) = tokio::task::spawn_blocking(move || super::fs::remove_symlinks(&root)).await
    {
        tracing::error!(repo_path = %repo_path.display(), error = %error, "failed to remove the symlinks");
        return false;
    }
    true
}

#[cfg(test)]
mod tests {
    use super::reset_repo;
    use std::path::Path;

    fn git(repo: &Path, args: &[&str]) {
        let status = std::process::Command::new("git")
            .arg("-C")
            .arg(repo)
            .args(args)
            .status()
            .unwrap();
        assert!(status.success());
    }

    #[tokio::test]
    async fn only_the_root_of_the_repository_gets_reset() {
        let dir = tempfile::tempdir().unwrap();
        std::env::set_var("GIT_CONFIG_GLOBAL", dir.path().join("gitconfig"));

        let repo = dir.path().join("repo");
        std::fs::create_dir_all(repo.join("sub/deep")).unwrap();
        git(&repo, &["init", "-q"]);
        std::fs::write(repo.join("f.txt"), b"committed").unwrap();
        git(&repo, &["add", "-A"]);
        git(
            &repo,
            &[
                "-c",
                "user.email=dev@fluidattacks.com",
                "-c",
                "user.name=dev",
                "commit",
                "-q",
                "-m",
                "files",
            ],
        );
        std::fs::write(repo.join("f.txt"), b"dirty").unwrap();

        let deep = repo.join("sub/deep");
        assert!(!reset_repo(&deep).await);
        assert_eq!(
            std::fs::read_to_string(repo.join("f.txt")).unwrap(),
            "dirty"
        );

        assert!(reset_repo(&repo).await);
        assert_eq!(
            std::fs::read_to_string(repo.join("f.txt")).unwrap(),
            "committed"
        );
    }
}