use std::process::Command;
use anyhow::{Context, Result, bail};
pub fn repo_cwd_is_git() -> bool {
Command::new("git")
.args(["rev-parse", "--show-toplevel"])
.output()
.is_ok_and(|o| o.status.success())
}
pub fn is_working_tree_clean() -> Result<bool> {
let output = Command::new("git")
.args(["status", "--porcelain"])
.output()
.context("failed to spawn `git status --porcelain`")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
bail!("git status failed: {stderr}");
}
Ok(output.stdout.is_empty())
}
#[allow(dead_code)] pub fn current_branch() -> Result<String> {
let output = Command::new("git")
.args(["rev-parse", "--abbrev-ref", "HEAD"])
.output()
.context("failed to spawn `git rev-parse --abbrev-ref HEAD`")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
bail!("git rev-parse failed: {stderr}");
}
let branch = String::from_utf8_lossy(&output.stdout).trim().to_owned();
Ok(branch)
}
pub fn checkout_branch(branch: &str) -> Result<()> {
let output = Command::new("git")
.args(["checkout", branch])
.output()
.context(format!("failed to spawn `git checkout {branch}`"))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
bail!("git checkout failed: {}", stderr.trim());
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn repo_cwd_is_git_does_not_panic() {
let _ = repo_cwd_is_git();
}
#[test]
#[ignore = "requires a git repository in cwd; run manually with `cargo test -- --ignored`"]
fn current_branch_returns_non_empty() {
match current_branch() {
Ok(branch) => assert!(!branch.is_empty(), "branch name should not be empty"),
Err(e) => panic!("current_branch failed: {e}"),
}
}
#[test]
#[ignore = "requires a git repository in cwd; run manually with `cargo test -- --ignored`"]
fn is_working_tree_clean_returns_bool() {
match is_working_tree_clean() {
Ok(_clean) => {} Err(e) => panic!("is_working_tree_clean failed: {e}"),
}
}
#[test]
#[ignore = "requires a git repository in cwd; run manually with `cargo test -- --ignored`"]
fn checkout_nonexistent_branch_returns_err() {
let result = checkout_branch("this-branch-definitely-does-not-exist-octopeek-test");
assert!(result.is_err(), "expected Err for non-existent branch");
}
}