use std::path::PathBuf;
use git2::{build::RepoBuilder, FetchOptions, Repository};
use log::debug;
use crate::error::Result;
use crate::{convert_to_bare, get_default_branch_name, get_remote_callbacks};
pub struct CloneOptions {
pub on_transfer_progress: Box<dyn FnMut(usize, usize, usize)>,
}
impl Default for CloneOptions {
fn default() -> Self {
Self {
on_transfer_progress: Box::new(|_, _, _| {}),
}
}
}
pub fn clone(path: PathBuf, url: &str, options: CloneOptions) -> Result<Repository> {
let CloneOptions {
mut on_transfer_progress,
} = options;
debug!("path {}", path.display());
let path = if path.ends_with(".bare") {
debug!("ended with .bare!");
path
} else {
debug!("didn't end with .bare!");
path.join(".bare")
};
debug!("final path {}", path.display());
let mut callbacks = get_remote_callbacks(Some(url))?;
callbacks.transfer_progress(move |progress| {
on_transfer_progress(
progress.received_objects(),
progress.total_objects(),
progress.received_bytes(),
);
true
});
let mut fetch_options = FetchOptions::new();
fetch_options.remote_callbacks(callbacks);
let mut builder = RepoBuilder::new();
builder.bare(true);
builder.fetch_options(fetch_options);
builder.remote_create(|repo, name, url| {
debug!("Creating remote {} at {}", name, url);
let remote = repo.remote(name, url)?;
match get_default_branch_name(repo, Some(remote)) {
Ok(default_branch) => {
debug!("Default branch: {}", default_branch);
repo.remote_add_fetch(
name,
format!(
"+refs/heads/{default_branch}:refs/remotes/origin/{default_branch}",
default_branch = default_branch
)
.as_str(),
)?;
repo.find_remote(name)
}
Err(_) => {
debug!("No default branch found");
repo.remote(name, url)
}
}
});
debug!("Cloning {} into {}", url, path.display());
let repo = builder.clone(url, &path)?;
convert_to_bare(repo)
}