use std::path::PathBuf;
use git2::build::{CheckoutBuilder, RepoBuilder};
use git2::{FetchOptions, RemoteCallbacks, Repository};
use crate::config::Config;
use crate::error::*;
use crate::repo_config::RepoConfig;
use crate::repo_info::RepoInfo;
use crate::utils::with_authentication;
pub type ProgressCallback<'a> = FnMut(usize, usize) + 'a;
#[derive(Debug, Clone, PartialEq)]
pub struct Repo {
base_config: Config,
path: PathBuf,
repo_info: RepoInfo,
}
impl Repo {
pub fn new(base_config: Config, path: PathBuf, repo_info: RepoInfo) -> Repo {
Repo {
base_config,
path,
repo_info,
}
}
pub fn repository(&self) -> Result<Repository> {
Ok(Repository::discover(&self.path)?)
}
pub fn init<'a>(&self, cb: Option<Box<ProgressCallback<'a>>>) -> Result<Repository> {
if let Ok(repository) = self.repository() {
self.fetch(&repository, cb)?;
Ok(repository)
} else {
self.clone_repo(cb)
}
}
pub fn clone_repo<'a>(&self, mut cb: Option<Box<ProgressCallback<'a>>>) -> Result<Repository> {
let git_config = git2::Config::open_default()?;
let url = &self.repo_info.clone_url;
let into = &self.path;
Ok(with_authentication(url, &git_config, |f| {
let mut rcb = RemoteCallbacks::new();
rcb.credentials(f);
if let Some(ref mut cb) = cb {
rcb.transfer_progress(move |progress| {
cb(progress.received_objects(), progress.total_objects());
true
});
}
let mut fo = FetchOptions::new();
fo.remote_callbacks(rcb);
let co = CheckoutBuilder::new();
Ok(RepoBuilder::new()
.fetch_options(fo)
.with_checkout(co)
.clone(url, &into)?)
})?)
}
pub fn fetch<'a>(
&self,
repository: &Repository,
mut cb: Option<Box<ProgressCallback<'a>>>,
) -> Result<()> {
let remote = "origin";
let mut remote = repository.find_remote(remote)?;
let git_config = git2::Config::open_default()?;
let url = remote.url().unwrap().to_owned();
Ok(with_authentication(&url, &git_config, move |f| {
let mut rcb = RemoteCallbacks::new();
rcb.credentials(f);
if let Some(ref mut cb) = cb {
rcb.transfer_progress(move |progress| {
cb(progress.received_objects(), progress.total_objects());
true
});
}
let mut fo = FetchOptions::new();
fo.remote_callbacks(rcb);
Ok(remote.fetch(
&self.repo_info.refs.iter().map(|s| &**s).collect::<Vec<_>>()[..],
Some(&mut fo),
None,
)?)
})?)
}
pub fn repo_info(&self) -> &RepoInfo {
&self.repo_info
}
pub fn config(&self) -> RepoConfig {
RepoConfig {
repo: self.repo_info().clone(),
config: self.base_config.clone(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_clone() -> Result<()> {
let config = Config::default();
let tmp_dir = tempdir::TempDir::new("example")?;
let repo_info = RepoInfo {
name: "simple".to_owned(),
clone_url: "./fixtures/repos/simple".to_owned(),
..Default::default()
};
let repo = Repo::new(config, tmp_dir.path().join("simple.git"), repo_info);
repo.clone_repo(None)?;
Ok(())
}
}