use anyhow::{Context, Result, bail};
use std::path::{Path, PathBuf};
use std::process::Command;
pub struct TestGit {
repo_path: PathBuf,
}
impl TestGit {
fn run_git_command(&self, args: &[&str], action: &str) -> Result<std::process::Output> {
let output = Command::new("git")
.args(args)
.current_dir(&self.repo_path)
.output()
.with_context(|| action.to_string())?;
if !output.status.success() {
bail!("{} failed: {}", action, String::from_utf8_lossy(&output.stderr));
}
Ok(output)
}
pub fn new(repo_path: impl Into<PathBuf>) -> Self {
Self {
repo_path: repo_path.into(),
}
}
pub fn init(&self) -> Result<()> {
self.run_git_command(&["init"], "Failed to initialize git repository")?;
Ok(())
}
pub fn config_user(&self) -> Result<()> {
self.run_git_command(
&["config", "user.email", "test@agpm.example"],
"Failed to configure git user email",
)?;
self.run_git_command(
&["config", "user.name", "Test User"],
"Failed to configure git user name",
)?;
Ok(())
}
pub fn add_all(&self) -> Result<()> {
self.run_git_command(&["add", "."], "Failed to add files to git")?;
Ok(())
}
pub fn commit(&self, message: &str) -> Result<()> {
self.run_git_command(&["commit", "-m", message], "Failed to create git commit")?;
Ok(())
}
pub fn tag(&self, tag_name: &str) -> Result<()> {
self.run_git_command(&["tag", tag_name], &format!("Failed to create tag: {}", tag_name))?;
Ok(())
}
pub fn ensure_branch(&self, branch_name: &str) -> Result<()> {
if self.checkout(branch_name).is_ok() {
return Ok(());
}
self.create_branch(branch_name)?;
Ok(())
}
pub fn repo_path(&self) -> &Path {
&self.repo_path
}
pub fn init_bare(&self) -> Result<()> {
self.run_git_command(&["init", "--bare"], "Failed to initialize bare git repository")?;
Ok(())
}
pub fn remote_add(&self, name: &str, url: &str) -> Result<()> {
self.run_git_command(
&["remote", "add", name, url],
&format!("Failed to add remote: {}", name),
)?;
Ok(())
}
pub fn fetch(&self) -> Result<()> {
self.run_git_command(&["fetch"], "Failed to fetch from remotes")?;
Ok(())
}
pub fn rev_parse_head(&self) -> Result<String> {
let output =
self.run_git_command(&["rev-parse", "HEAD"], "Failed to get current commit SHA")?;
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
}
pub fn checkout(&self, ref_name: &str) -> Result<()> {
self.run_git_command(
&["checkout", ref_name],
&format!("Failed to checkout: {}", ref_name),
)?;
Ok(())
}
pub fn create_branch(&self, branch_name: &str) -> Result<()> {
self.run_git_command(
&["checkout", "-b", branch_name],
&format!("Failed to create branch: {}", branch_name),
)?;
Ok(())
}
pub fn get_commit_hash(&self) -> Result<String> {
let output = self.run_git_command(&["rev-parse", "HEAD"], "Failed to get commit hash")?;
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
}
pub fn get_head_sha(&self) -> Result<String> {
self.get_commit_hash()
}
pub fn get_current_branch(&self) -> Result<String> {
let output = self
.run_git_command(&["branch", "--show-current"], "Failed to get current branch name")?;
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
}
pub fn get_default_branch(&self) -> Result<String> {
if let Ok(branch) = self.get_current_branch() {
if !branch.is_empty() {
return Ok(branch);
}
}
for branch in ["main", "master"] {
if self.checkout(branch).is_ok() {
return Ok(branch.to_string());
}
}
let output = self.run_git_command(
&["symbolic-ref", "--short", "HEAD"],
"Failed to get default branch name from symbolic-ref",
)?;
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
}
pub fn status_porcelain(&self) -> Result<String> {
let output =
self.run_git_command(&["status", "--porcelain"], "Failed to get git status")?;
Ok(String::from_utf8_lossy(&output.stdout).to_string())
}
pub fn set_head(&self, branch_name: &str) -> Result<()> {
self.run_git_command(
&["symbolic-ref", "HEAD", &format!("refs/heads/{}", branch_name)],
&format!("Failed to set HEAD to branch: {}", branch_name),
)?;
Ok(())
}
}