use crate::GitRepository;
use git2::{CertificateCheckStatus, Error, RemoteCallbacks, build::CheckoutBuilder};
#[derive(Clone)]
pub struct CheckoutConfig {
pub(crate) spec: String,
pub(crate) flags: CheckoutFlagsInternal,
}
impl CheckoutConfig {
pub fn new(spec: String) -> Self {
CheckoutConfig {
spec: spec,
flags: CheckoutFlagsInternal::default(),
}
}
pub fn add_flags(&mut self, flag: CheckoutFlags) {
match flag {}
}
}
#[derive(Default, Clone)]
pub(crate) struct CheckoutFlagsInternal {}
pub enum CheckoutFlags {}
impl GitRepository {
pub fn git_checkout(&self, config: CheckoutConfig) -> Result<(), Error> {
if let Some(repository) = &self.repository {
if self.skip_owner_validation {
unsafe {
git2::opts::set_verify_owner_validation(false)?;
};
}
let mut checkout_builder = CheckoutBuilder::new();
match repository.find_branch(&config.spec, git2::BranchType::Local) {
Ok(local_branch) => {
let reference = local_branch.get();
let name = match reference.name() {
Some(name) => name,
None => &config.spec,
};
repository.set_head(name)?;
checkout_builder.safe();
repository.checkout_head(Some(&mut checkout_builder))?;
return Ok(());
}
Err(_) => {}
};
let tag = format!("refs/tags/{}", &config.spec);
match repository.find_reference(&tag) {
Ok(tag) => {
let name = match tag.name() {
Some(name) => name,
None => &config.spec,
};
repository.set_head(name)?;
checkout_builder.safe();
repository.checkout_head(Some(&mut checkout_builder))?;
return Ok(());
}
Err(_) => {}
};
let remotes = repository.remotes()?;
for remote in &remotes {
if let Some(remote) = remote {
let mut remote = repository.find_remote(remote)?;
let mut callback = RemoteCallbacks::new();
if self.bypass_certificate_check {
callback
.certificate_check(|_, _| Ok(CertificateCheckStatus::CertificateOk));
}
callback.credentials(move |_a: &str, _b, _c| self.cred.get_cred());
remote.connect_auth(git2::Direction::Fetch, Some(callback), None)?;
if let Ok(remote_heads) = remote.list() {
let branch_full = format!("refs/heads/{}", &config.spec);
for remote_head in remote_heads {
if branch_full == remote_head.name() {
let target_commit = remote_head.oid();
let target_commit = repository.find_commit(target_commit)?;
let mut remote = remote.clone();
let refspec = format!(
"{}:refs/remotes/{}/{}",
branch_full,
remote.name().unwrap(),
&config.spec
);
remote.fetch(&[refspec], None, None)?;
let mut local_branch =
repository.branch(&config.spec, &target_commit, false)?;
let upstream =
format!("{}/{}", remote.name().unwrap(), &config.spec);
local_branch.set_upstream(Some(&upstream))?;
repository.set_head(&branch_full)?;
checkout_builder.safe();
repository.checkout_head(Some(&mut checkout_builder))?;
return Ok(());
}
let tag_full = format!("refs/tags/{}", &config.spec);
if tag_full == remote_head.name() {
let tag_ref = format!("{}:{}", tag_full, tag_full);
let mut remote = remote.clone();
remote.fetch(&[tag_ref], None, None)?;
let reference = repository.find_reference(&tag_full)?;
let name = match reference.name() {
Some(name) => name,
None => &config.spec,
};
repository.set_head(name)?;
checkout_builder.safe();
repository.checkout_head(Some(&mut checkout_builder))?;
return Ok(());
}
}
}
remote.disconnect();
}
}
match repository.revparse_single(&config.spec) {
Ok(obj) => {
repository.set_head_detached(obj.id())?;
repository.checkout_tree(&obj, Some(&mut checkout_builder))?;
return Ok(());
}
Err(_) => {}
}
let err_msg = format!("Failed to resolve spec: {}", &config.spec);
return Err(Error::from_str(&err_msg));
}
Err(Error::from_str(
"Repository not found or created, try opening a valid repository or cloning one",
))
}
}
#[cfg(test)]
mod checkout_test {
use std::{env, path::Path, process::Command};
use crate::{GitRepository, configs::checkout_config::CheckoutConfig};
#[test]
fn checkout() {
let dir_name = "./temp_test/checkout/";
Command::new("mkdir")
.args(["-p", dir_name])
.output()
.unwrap();
let _ = Command::new("git")
.args([
"-C",
dir_name,
"clone",
"https://github.com/rust-lang/git2-rs.git",
])
.output()
.expect("git cli needs to be installed for comparing test results");
let path = env::current_dir().unwrap();
let dir_name = dir_name.to_owned() + "./git2-rs";
let path = path.join(&dir_name);
let repo = GitRepository::open(Path::new(&path)).unwrap();
let checkout_config = CheckoutConfig::new("curl".to_string());
repo.git_checkout(checkout_config).unwrap();
let out_1 = Command::new("git")
.args(["-C", &dir_name, "branch"])
.output()
.expect("git cli needs to be installed for comparing test results");
let checkout_config = CheckoutConfig::new("libgit2-sys-0.14.2".to_string());
repo.git_checkout(checkout_config).unwrap();
let out_2 = Command::new("git")
.args(["-C", &dir_name, "branch"])
.output()
.expect("git cli needs to be installed for comparing test results");
let checkout_config = CheckoutConfig::new("d1b40aa".to_string());
repo.git_checkout(checkout_config).unwrap();
let out_3 = Command::new("git")
.args(["-C", &dir_name, "branch"])
.output()
.expect("git cli needs to be installed for comparing test results");
let checkout_config = CheckoutConfig::new("nonExistant".to_string());
let out_4 = repo.git_checkout(checkout_config);
Command::new("rm")
.args(["-rf", &dir_name])
.output()
.unwrap();
assert_eq!(
String::from_utf8_lossy(&out_1.stdout),
"* curl
master\n"
);
assert_eq!(
String::from_utf8_lossy(&out_2.stdout),
"* (HEAD detached at libgit2-sys-0.14.2)
curl
master\n"
);
assert_eq!(
String::from_utf8_lossy(&out_3.stdout),
"* (HEAD detached at d1b40aa)
curl
master\n"
);
assert!(out_4.is_err());
}
}