guardy 0.2.4

Fast, secure git hooks in Rust with secret scanning and protected file synchronization
Documentation
pub mod operations;
pub mod remote;
// TODO: Add hooks module for hook installation/management
// TODO: Add commit module for commit operations

use std::path::PathBuf;

use anyhow::{Context, Result};
use gix::bstr::ByteSlice;

pub struct GitRepo {
    pub path: PathBuf,
    repo: gix::ThreadSafeRepository, // Use thread-safe version for static caching
}

impl GitRepo {
    pub fn discover() -> Result<Self> {
        let repo = gix::discover(".")
            .context("Failed to discover git repository")?
            .into_sync();

        let path = repo
            .work_dir()
            .ok_or_else(|| anyhow::anyhow!("Repository has no working directory"))?
            .to_path_buf();

        Ok(GitRepo { path, repo })
    }

    pub fn open(path: &std::path::Path) -> Result<Self> {
        let repo = gix::open(path)
            .context("Failed to open git repository")?
            .into_sync();

        let path = repo
            .work_dir()
            .ok_or_else(|| anyhow::anyhow!("Repository has no working directory"))?
            .to_path_buf();

        Ok(GitRepo { path, repo })
    }

    pub fn current_branch(&self) -> Result<String> {
        let repo = self.repo.to_thread_local();
        let head = repo.head().context("Failed to get HEAD reference")?;

        if let Some(name) = head.referent_name() {
            // Extract branch name from refs/heads/branch_name
            let name_str = name
                .as_bstr()
                .to_str()
                .context("Invalid UTF-8 in branch name")?;

            if let Some(branch) = name_str.strip_prefix("refs/heads/") {
                return Ok(branch.to_string());
            }
        }

        Ok("HEAD".to_string())
    }

    pub fn git_dir(&self) -> PathBuf {
        self.path.join(".git")
    }

    // Internal access to gix repo for operations - returns thread-local handle
    pub(crate) fn gix_repo(&self) -> gix::Repository {
        self.repo.to_thread_local()
    }
}