circus/crates/evaluator/src/git.rs
NotAShelf a127f3f62c
treewide: address all clippy lints
Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I5cf55cc4cb558c3f9f764c71224e87176a6a6964
2026-02-28 17:37:53 +03:00

68 lines
1.9 KiB
Rust

use std::path::{Path, PathBuf};
use fc_common::error::Result;
use git2::Repository;
/// Clone or fetch a repository. Returns (`repo_path`, `commit_hash`).
///
/// If `branch` is `Some`, resolve `refs/remotes/origin/<branch>` instead of
/// HEAD.
///
/// # Errors
///
/// Returns error if git operations fail.
#[tracing::instrument(skip(work_dir))]
pub fn clone_or_fetch(
url: &str,
work_dir: &Path,
project_name: &str,
branch: Option<&str>,
) -> Result<(PathBuf, String)> {
let repo_path = work_dir.join(project_name);
let is_fetch = repo_path.exists();
let repo = if is_fetch {
let repo = Repository::open(&repo_path)?;
// Fetch origin. Scope the borrow so `remote` is dropped before we move
// `repo`
{
let mut remote = repo.find_remote("origin")?;
remote.fetch(&["refs/heads/*:refs/remotes/origin/*"], None, None)?;
}
repo
} else {
Repository::clone(url, &repo_path)?
};
// Resolve commit from remote refs (which are always up-to-date after fetch).
// When no branch is specified, detect the default branch from local HEAD's
// tracking target.
let branch_name = if let Some(b) = branch {
b.to_string()
} else {
let head = repo.head()?;
head.shorthand().unwrap_or("master").to_string()
};
let remote_ref = format!("refs/remotes/origin/{branch_name}");
let reference = repo.find_reference(&remote_ref).map_err(|e| {
fc_common::error::CiError::NotFound(format!(
"Branch '{branch_name}' not found ({remote_ref}): {e}"
))
})?;
let commit = reference.peel_to_commit()?;
let hash = commit.id().to_string();
// After fetch, update the working tree so nix evaluation sees the latest
// files. Skip on fresh clone since the checkout is already current.
if is_fetch {
repo.checkout_tree(
commit.as_object(),
Some(git2::build::CheckoutBuilder::new().force()),
)?;
repo.set_head_detached(commit.id())?;
}
Ok((repo_path, hash))
}