use crate::{command, os};
use anyhow::Error;
use std::io;
use std::path::{Path, PathBuf};
pub struct GitSystem {
command: command::Command<'static>,
}
impl GitSystem {
pub fn new() -> Self {
Self {
command: command::Command::new(os::command("git")),
}
}
}
impl super::GitSystem for GitSystem {
fn test(&self) -> Result<bool, Error> {
match self.command.run(&["--version"]) {
Ok(output) => Ok(output.status.success()),
Err(e) => match e.kind() {
io::ErrorKind::NotFound => Ok(false),
_ => Err(e.into()),
},
}
}
fn clone(&self, url: &str, path: &Path) -> Result<Box<dyn super::Git>, Error> {
use std::ffi::OsStr;
let args = &[OsStr::new("clone"), OsStr::new(url), path.as_os_str()];
self.command.run_checked(args)?;
Ok(Box::new(External {
path: path.to_owned(),
command: self.command.clone(),
}))
}
fn open(&self, path: &Path) -> Result<Box<dyn super::Git>, Error> {
Ok(Box::new(External {
path: path.to_owned(),
command: self.command.clone(),
}))
}
}
#[derive(Debug)]
struct External {
pub path: PathBuf,
command: command::Command<'static>,
}
impl External {
fn rev_parse(&self, git_ref: &str) -> Result<String, Error> {
Ok(self
.command
.clone()
.working_directory(self.path.as_path())
.run_stdout(&["rev-parse", git_ref])?
.trim()
.to_string())
}
fn merge_base(&self, a: &str, b: &str) -> Result<String, Error> {
Ok(self
.command
.clone()
.working_directory(self.path.as_path())
.run_stdout(&["merge-base", a, b])?
.trim()
.to_string())
}
}
impl super::Git for External {
fn path(&self) -> &Path {
&self.path
}
fn needs_update(&self) -> Result<bool, Error> {
self.command
.clone()
.working_directory(self.path())
.run_checked(&["fetch", "origin", "master"])?;
let remote_head = self.rev_parse("FETCH_HEAD")?;
let head = self.rev_parse("HEAD")?;
if remote_head != head {
return Ok(self.merge_base(&remote_head, &head)? != remote_head);
}
Ok(false)
}
fn is_fresh(&self) -> Result<bool, Error> {
let output = self
.command
.clone()
.working_directory(self.path.as_path())
.run(&["diff-index", "--quiet", "HEAD"])?;
Ok(output.status.success())
}
fn force_update(&self) -> Result<(), Error> {
self.command
.clone()
.working_directory(self.path.as_path())
.run_checked(&["reset", "--hard", "FETCH_HEAD"])
}
fn update(&self) -> Result<(), Error> {
self.command
.clone()
.working_directory(self.path.as_path())
.run_checked(&["merge", "--ff-only", "FETCH_HEAD"])
}
}