use std::fs;
use std::path::Path;
use std::process::Command;
pub fn init_bare_repo(path: &Path) {
fs::create_dir_all(path).unwrap();
let status = Command::new("git")
.args(["init", "--bare", "-b", "main"])
.current_dir(path)
.output()
.expect("failed to init bare repo");
assert!(
status.status.success(),
"git init --bare failed: {}",
String::from_utf8_lossy(&status.stderr)
);
}
pub fn init_repo(path: &Path) {
fs::create_dir_all(path).unwrap();
git(path, &["init", "-b", "main"]);
git(path, &["config", "user.email", "test@example.com"]);
git(path, &["config", "user.name", "Test User"]);
}
pub fn commit_file(repo_path: &Path, filename: &str, content: &str, message: &str) -> String {
fs::write(repo_path.join(filename), content).unwrap();
git(repo_path, &["add", filename]);
git(repo_path, &["commit", "-m", message]);
get_head_sha(repo_path)
}
pub fn create_branch(repo_path: &Path, branch_name: &str) {
git(repo_path, &["checkout", "-b", branch_name]);
}
pub fn checkout(repo_path: &Path, branch_name: &str) {
git(repo_path, &["checkout", branch_name]);
}
pub fn push_branch(repo_path: &Path, remote: &str, branch: &str) {
git(repo_path, &["push", remote, branch]);
}
pub fn fetch(repo_path: &Path, remote: &str, branch: Option<&str>) {
let mut args = vec!["fetch", remote];
if let Some(branch) = branch {
args.push(branch);
}
git(repo_path, &args);
}
pub fn push_upstream(repo_path: &Path, remote: &str, branch: &str) {
git(repo_path, &["push", "-u", remote, branch]);
}
pub fn add_remote(repo_path: &Path, name: &str, url: &str) {
git(repo_path, &["remote", "add", name, url]);
}
pub fn remove_remote(repo_path: &Path, name: &str) {
git(repo_path, &["remote", "remove", name]);
}
pub fn current_branch(repo_path: &Path) -> String {
git_output(repo_path, &["rev-parse", "--abbrev-ref", "HEAD"])
}
pub fn branch_upstream(repo_path: &Path, branch_name: &str) -> Option<String> {
let output = Command::new("git")
.current_dir(repo_path)
.args([
"rev-parse",
"--abbrev-ref",
&format!("{}@{{upstream}}", branch_name),
])
.output()
.unwrap_or_else(|e| panic!("failed to run git rev-parse for upstream: {}", e));
if !output.status.success() {
return None;
}
Some(String::from_utf8_lossy(&output.stdout).trim().to_string())
}
pub fn log_contains(repo_path: &Path, message: &str) -> bool {
git_output(repo_path, &["log", "--oneline", "-n", "10"]).contains(message)
}
pub fn get_head_sha(repo_path: &Path) -> String {
git_output(repo_path, &["rev-parse", "HEAD"])
}
pub fn branch_exists(repo_path: &Path, branch_name: &str) -> bool {
Command::new("git")
.args([
"rev-parse",
"--verify",
&format!("refs/heads/{}", branch_name),
])
.current_dir(repo_path)
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
pub fn clone_repo(url: &str, dest: &Path) {
let status = Command::new("git")
.args(["clone", url, dest.to_str().unwrap()])
.output()
.expect("failed to clone repo");
assert!(
status.status.success(),
"git clone failed: {}",
String::from_utf8_lossy(&status.stderr)
);
git(dest, &["config", "user.email", "test@example.com"]);
git(dest, &["config", "user.name", "Test User"]);
}
fn git(dir: &Path, args: &[&str]) {
let output = Command::new("git")
.current_dir(dir)
.args(args)
.output()
.unwrap_or_else(|e| panic!("failed to run git {:?}: {}", args, e));
assert!(
output.status.success(),
"git {:?} failed in {}: {}",
args,
dir.display(),
String::from_utf8_lossy(&output.stderr)
);
}
fn git_output(dir: &Path, args: &[&str]) -> String {
let output = Command::new("git")
.current_dir(dir)
.args(args)
.output()
.unwrap_or_else(|e| panic!("failed to run git {:?}: {}", args, e));
assert!(
output.status.success(),
"git {:?} failed: {}",
args,
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8_lossy(&output.stdout).trim().to_string()
}