use crate::error::Result;
use crate::git_utils;
use crate::session::Session;
#[must_use]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PullOutput {
pub remote_name: String,
pub new_commits: usize,
pub indexed_keys: usize,
pub materialized: bool,
}
pub fn run(session: &Session, remote: Option<&str>, now: i64) -> Result<PullOutput> {
let repo = &session.repo;
let ns = session.namespace();
let remote_name = git_utils::resolve_meta_remote(repo, remote)?;
let remote_refspec = format!("refs/{ns}/main");
let tracking_ref = format!("refs/{ns}/remotes/main");
let fetch_refspec = format!("{remote_refspec}:{tracking_ref}");
let old_tip = repo
.find_reference(&tracking_ref)
.ok()
.and_then(|r| r.into_fully_peeled_id().ok());
git_utils::run_git(repo, &["fetch", &remote_name, &fetch_refspec])?;
let new_tip = repo
.find_reference(&tracking_ref)
.ok()
.and_then(|r| r.into_fully_peeled_id().ok());
let needs_materialize = session.store.get_last_materialized()?.is_none()
|| repo.find_reference(&session.local_ref()).is_err();
let new_commits = match (old_tip.as_ref(), new_tip.as_ref()) {
(Some(old), Some(new)) if old == new => {
if !needs_materialize {
return Ok(PullOutput {
remote_name,
new_commits: 0,
indexed_keys: 0,
materialized: false,
});
}
0
}
(Some(old), Some(new)) => count_commits_between(repo, old.detach(), new.detach()),
(None, Some(_)) => 1, _ => 0,
};
let short_ref = format!("{ns}/remotes/main");
git_utils::hydrate_tip_blobs(repo, &remote_name, &short_ref)?;
let _ = crate::serialize::run(session, now, false)?;
let _ = crate::materialize::run(session, None, now)?;
let indexed_keys = if let Some(new) = new_tip {
let walk_from = if needs_materialize {
None
} else {
old_tip.map(gix::Id::detach)
};
session.index_history(new.detach(), walk_from)?
} else {
0
};
Ok(PullOutput {
remote_name,
new_commits,
indexed_keys,
materialized: true,
})
}
fn count_commits_between(repo: &gix::Repository, old: gix::ObjectId, new: gix::ObjectId) -> usize {
let walk = repo.rev_walk(Some(new)).with_boundary(Some(old));
match walk.all() {
Ok(iter) => iter
.filter(std::result::Result::is_ok)
.count()
.saturating_sub(1),
Err(_) => 0,
}
}