use crate::types::GenericRepository;
use crate::types::Provider;
use anyhow::{Context, Result};
use git2::build::CheckoutBuilder;
use git2::{Repository as Git, Repository};
use std::path::{Path, PathBuf};
use std::time::Duration;
pub fn sync_repositories(
backup_path: &Path,
provider: &Provider,
repositories: &[impl GenericRepository],
) {
println!("Found {:#?} repositories", &repositories.len());
for r in repositories {
if let Err(e) = sync_repository(backup_path, provider, r) {
eprintln!(
"Unable to sync repository {} with error {e:?}",
&r.full_name()
);
}
std::thread::sleep(Duration::from_millis(1000));
}
}
fn sync_repository(
backup_path: &Path,
provider: &Provider,
repository: &impl GenericRepository,
) -> Result<()> {
let repository_directory = format!(
"{}-{}",
provider.host,
&repository.full_name().replace('/', "-")
);
let path = backup_path.join(repository_directory);
if let Ok(git) = Git::open(&path) {
pull(&git, &path, provider, repository)?;
} else {
let git_url = format!(
"https://{}:{}@{}/{}.git",
&provider.username,
&provider.token,
&provider.host,
&repository.full_name()
);
clone(&git_url, &path, provider, repository)?;
};
Ok(())
}
fn pull(
git: &Repository,
path: &Path,
provider: &Provider,
repository: &impl GenericRepository,
) -> Result<()> {
println!(
"Syncing {}/{} to {}",
&provider.host,
&repository.full_name(),
&path.display()
);
if git.is_empty()? {
return Ok(());
}
git.find_remote("origin")?
.fetch(&[&repository.default_branch()], None, None)?;
let fetch_head = git.find_reference("FETCH_HEAD")?;
let fetch_commit = git.reference_to_annotated_commit(&fetch_head)?;
let analysis = git.merge_analysis(&[&fetch_commit])?;
if analysis.0.is_fast_forward() {
let refname = format!("refs/heads/{}", &repository.default_branch());
let mut reference = git.find_reference(&refname)?;
reference.set_target(fetch_commit.id(), "Fast-Forward")?;
git.set_head(&refname)?;
git.checkout_head(Some(CheckoutBuilder::default().force()))?;
};
Ok(())
}
fn clone(
url: &str,
path: &PathBuf,
provider: &Provider,
repository: &impl GenericRepository,
) -> Result<()> {
println!(
"Cloning {}/{} to {}",
&provider.host,
&repository.full_name(),
&path.display()
);
Git::clone(url, path).with_context(|| {
format!(
"Unable to clone repository {} with url {}",
&repository.full_name(),
&url
)
})?;
Ok(())
}