use std::{
path::{Path, PathBuf},
process::Command,
};
pub struct GitRepo {
pub dir: PathBuf,
}
impl GitRepo {
fn run_command(&self, args: &[&str]) -> Result<String, String> {
let mut command = Command::new("git");
command.current_dir(&self.dir).args(args);
let output = command.output().map_err(|e| e.to_string())?;
if !output.status.success() {
let err = String::from_utf8_lossy(&output.stderr);
Err(format!("{} from args {:?}", err.trim(), command.get_args()))
} else {
let stdout = String::from_utf8_lossy(&output.stdout);
Ok(stdout.trim().to_string())
}
}
pub fn checkout(&self, version: &str) -> Result<(), String> {
self.run_command(&["checkout", version]).map(|_a| ())
}
pub fn clone_to(dir: &Path, url: &str, version: &str, shallow: bool) -> Result<(), String> {
let container = GitRepo { dir: dir.to_path_buf() };
if shallow {
container.run_command(&["clone", "--branch", version, "--depth", "1", url])?;
} else {
container.run_command(&["clone", "--branch", version, url])?;
}
Ok(())
}
pub fn current_head(&self) -> Result<GitHead, String> {
let branch = self.run_command(&["branch", "--show-current"])?;
if branch.is_empty() {
Ok(GitHead::Tag(self.describe_tag()?))
} else {
Ok(GitHead::Branch(branch))
}
}
pub fn timestamp(&self, sha: &str) -> Result<u32, String> {
let timestamp = self.run_command(&["show", "-s", "--format=%ct", sha])?;
let v = timestamp.trim().parse::<u32>().map_err(|e| e.to_string())?;
Ok(v)
}
pub fn latest_tag(&self) -> Result<String, String> {
let rev_output = self.run_command(&["rev-list", "--tags", "--max-count=1"])?;
let tag = self.run_command(&["describe", "--tags", rev_output.trim()])?;
Ok(tag.trim().to_string())
}
#[allow(dead_code)]
pub fn rev_parse(&self, ref_name: &str) -> Result<String, String> {
let sha = self.run_command(&["rev-parse", ref_name])?;
Ok(sha.trim().to_string())
}
pub fn check_branch_or_tag(&self, version: &str, folder: &str) -> Result<bool, String> {
match self.run_command(&["show-ref", "--verify", &format!("refs/tags/{version}")]) {
Ok(_) => Ok(true),
Err(_) => {
let ret = self.run_command(&["show-ref", "--verify", &format!("refs/heads/{version}")]);
match ret {
Ok(_) => Ok(true),
Err(_) => Err(format!("failed to check branch or tag `{version}` in `{folder}`")),
}
}
}
}
pub fn fetch(&self) -> Result<(), String> {
self.run_command(&["fetch", "origin", "--tags"])?;
Ok(())
}
pub fn describe_tag(&self) -> Result<String, String> {
let tag = self.run_command(&["describe", "--tags"])?.trim().to_string();
Ok(tag)
}
pub fn pull(&self, branch: &str) -> Result<(), String> {
self.run_command(&["pull", "origin", branch])?;
Ok(())
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum GitHead {
Branch(String),
Tag(String),
}
impl GitHead {
pub fn get_name(&self) -> String {
match self {
GitHead::Branch(s) => s.to_string(),
GitHead::Tag(s) => s.to_string(),
}
}
}