use anyhow::{Context, Result};
use std::process::Command;
pub struct GitOperations;
impl GitOperations {
pub fn get_staged_diff() -> Result<String> {
let output = Command::new("git")
.args(["diff", "--staged"])
.output()
.context("Failed to execute git diff")?;
if !output.status.success() {
let error = String::from_utf8_lossy(&output.stderr);
return Err(anyhow::anyhow!("git diff failed: {}", error));
}
Ok(String::from_utf8_lossy(&output.stdout).to_string())
}
pub fn has_staged_changes() -> Result<bool> {
let output = Command::new("git")
.args(["diff", "--staged", "--quiet"])
.output()
.context("Failed to execute git diff")?;
Ok(!output.status.success())
}
pub fn create_commit(message: &str) -> Result<()> {
let output = Command::new("git")
.args(["commit", "-m", message])
.output()
.context("Failed to execute git commit")?;
if !output.status.success() {
let error = String::from_utf8_lossy(&output.stderr);
return Err(anyhow::anyhow!("git commit failed: {}", error));
}
Ok(())
}
pub fn get_current_branch() -> Result<String> {
let output = Command::new("git")
.args(["rev-parse", "--abbrev-ref", "HEAD"])
.output()
.context("Failed to get current branch")?;
if !output.status.success() {
let error = String::from_utf8_lossy(&output.stderr);
return Err(anyhow::anyhow!("Failed to get current branch: {}", error));
}
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
use std::fs;
use tempfile::TempDir;
fn setup_test_repo() -> Result<TempDir> {
let temp_dir = TempDir::new()?;
Command::new("git")
.arg("init")
.current_dir(temp_dir.path())
.output()?;
Command::new("git")
.args(&["config", "user.email", "test@example.com"])
.current_dir(temp_dir.path())
.output()?;
Command::new("git")
.args(&["config", "user.name", "Test User"])
.current_dir(temp_dir.path())
.output()?;
Ok(temp_dir)
}
#[test]
#[serial]
fn test_has_staged_changes_with_changes() -> Result<()> {
let temp_dir = setup_test_repo()?;
let original_dir = std::env::current_dir()?;
fs::write(temp_dir.path().join("test.txt"), "Hello, world!")?;
Command::new("git")
.args(&["add", "test.txt"])
.current_dir(temp_dir.path())
.output()?;
std::env::set_current_dir(temp_dir.path())?;
let result = GitOperations::has_staged_changes();
std::env::set_current_dir(original_dir)?;
assert!(result?);
Ok(())
}
#[test]
#[serial]
fn test_has_staged_changes_without_changes() -> Result<()> {
let temp_dir = setup_test_repo()?;
let original_dir = std::env::current_dir()?;
std::env::set_current_dir(temp_dir.path())?;
let result = GitOperations::has_staged_changes();
std::env::set_current_dir(original_dir)?;
assert!(!result?);
Ok(())
}
#[test]
#[serial]
fn test_get_staged_diff() -> Result<()> {
let temp_dir = setup_test_repo()?;
let original_dir = std::env::current_dir()?;
fs::write(temp_dir.path().join("test.txt"), "Hello, world!")?;
Command::new("git")
.args(&["add", "test.txt"])
.current_dir(temp_dir.path())
.output()?;
std::env::set_current_dir(temp_dir.path())?;
let diff = GitOperations::get_staged_diff();
std::env::set_current_dir(original_dir)?;
let diff = diff?;
assert!(diff.contains("Hello, world!"));
assert!(diff.contains("test.txt"));
Ok(())
}
#[test]
#[serial]
fn test_create_commit() -> Result<()> {
let temp_dir = setup_test_repo()?;
let original_dir = std::env::current_dir()?;
fs::write(temp_dir.path().join("test.txt"), "Hello, world!")?;
Command::new("git")
.args(&["add", "test.txt"])
.current_dir(temp_dir.path())
.output()?;
std::env::set_current_dir(temp_dir.path())?;
GitOperations::create_commit("test: Add test file")?;
let output = Command::new("git")
.args(&["log", "--oneline"])
.current_dir(temp_dir.path())
.output()?;
let log = String::from_utf8(output.stdout)?;
std::env::set_current_dir(original_dir)?;
assert!(log.contains("test: Add test file"));
Ok(())
}
#[test]
#[serial]
fn test_get_current_branch() -> Result<()> {
let temp_dir = setup_test_repo()?;
let original_dir = std::env::current_dir()?;
fs::write(temp_dir.path().join("README.md"), "Initial commit")?;
Command::new("git")
.args(&["add", "README.md"])
.current_dir(temp_dir.path())
.output()?;
Command::new("git")
.args(&["commit", "-m", "Initial commit"])
.current_dir(temp_dir.path())
.output()?;
std::env::set_current_dir(temp_dir.path())?;
let branch = GitOperations::get_current_branch()?;
assert!(branch == "main" || branch == "master");
Command::new("git")
.args(&["checkout", "-b", "feature-branch"])
.current_dir(temp_dir.path())
.output()?;
let branch = GitOperations::get_current_branch()?;
assert_eq!(branch, "feature-branch");
std::env::set_current_dir(original_dir)?;
Ok(())
}
}