use gix_hash::ObjectId;
use gix_object::FindExt;
use crate::{ext::ObjectIdExt, revision, Repository};
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
SimpleTraversal(#[from] gix_traverse::commit::simple::Error),
#[error(transparent)]
ShallowCommits(#[from] crate::shallow::open::Error),
#[error(transparent)]
ConfigBoolean(#[from] crate::config::boolean::Error),
}
#[derive(Debug, Clone)]
pub struct Info<'repo> {
pub id: gix_hash::ObjectId,
pub parent_ids: gix_traverse::commit::ParentIds,
pub commit_time: Option<gix_date::SecondsSinceUnixEpoch>,
repo: &'repo Repository,
}
impl<'repo> Info<'repo> {
pub fn id(&self) -> crate::Id<'repo> {
self.id.attach(self.repo)
}
pub fn object(&self) -> Result<crate::Commit<'repo>, crate::object::find::existing::Error> {
Ok(self.id().object()?.into_commit())
}
pub fn parent_ids(&self) -> impl Iterator<Item = crate::Id<'repo>> + '_ {
self.parent_ids.iter().map(|id| id.attach(self.repo))
}
pub fn commit_time(&self) -> gix_date::SecondsSinceUnixEpoch {
self.commit_time.expect("traversal involving date caused it to be set")
}
}
impl<'repo> Info<'repo> {
pub fn new(info: gix_traverse::commit::Info, repo: &'repo Repository) -> Self {
Info {
id: info.id,
parent_ids: info.parent_ids,
commit_time: info.commit_time,
repo,
}
}
pub fn detach(self) -> gix_traverse::commit::Info {
gix_traverse::commit::Info {
id: self.id,
parent_ids: self.parent_ids,
commit_time: self.commit_time,
}
}
}
pub struct Platform<'repo> {
pub(crate) repo: &'repo Repository,
pub(crate) tips: Vec<ObjectId>,
pub(crate) sorting: gix_traverse::commit::simple::Sorting,
pub(crate) parents: gix_traverse::commit::Parents,
pub(crate) use_commit_graph: Option<bool>,
pub(crate) commit_graph: Option<gix_commitgraph::Graph>,
}
impl<'repo> Platform<'repo> {
pub(crate) fn new(tips: impl IntoIterator<Item = impl Into<ObjectId>>, repo: &'repo Repository) -> Self {
revision::walk::Platform {
repo,
tips: tips.into_iter().map(Into::into).collect(),
sorting: Default::default(),
parents: Default::default(),
use_commit_graph: None,
commit_graph: None,
}
}
}
impl<'repo> Platform<'repo> {
pub fn sorting(mut self, sorting: gix_traverse::commit::simple::Sorting) -> Self {
self.sorting = sorting;
self
}
pub fn first_parent_only(mut self) -> Self {
self.parents = gix_traverse::commit::Parents::First;
self
}
pub fn use_commit_graph(mut self, toggle: impl Into<Option<bool>>) -> Self {
self.use_commit_graph = toggle.into();
self
}
pub fn with_commit_graph(mut self, graph: Option<gix_commitgraph::Graph>) -> Self {
self.commit_graph = graph;
self
}
}
impl<'repo> Platform<'repo> {
pub fn selected(
self,
mut filter: impl FnMut(&gix_hash::oid) -> bool + 'repo,
) -> Result<revision::Walk<'repo>, Error> {
let Platform {
repo,
tips,
sorting,
parents,
use_commit_graph,
commit_graph,
} = self;
Ok(revision::Walk {
repo,
inner: Box::new(
gix_traverse::commit::Simple::filtered(tips, &repo.objects, {
let shallow_commits = repo.shallow_commits()?;
let mut grafted_parents_to_skip = Vec::new();
let mut buf = Vec::new();
move |id| {
if !filter(id) {
return false;
}
match shallow_commits.as_ref() {
Some(commits) => {
let id = id.to_owned();
if let Ok(idx) = grafted_parents_to_skip.binary_search(&id) {
grafted_parents_to_skip.remove(idx);
return false;
};
if commits.binary_search(&id).is_ok() {
if let Ok(commit) = repo.objects.find_commit_iter(&id, &mut buf) {
grafted_parents_to_skip.extend(commit.parent_ids());
grafted_parents_to_skip.sort();
}
};
true
}
None => true,
}
}
})
.sorting(sorting)?
.parents(parents)
.commit_graph(
commit_graph.or(use_commit_graph
.map_or_else(|| self.repo.config.may_use_commit_graph(), Ok)?
.then(|| self.repo.commit_graph().ok())
.flatten()),
),
),
})
}
pub fn all(self) -> Result<revision::Walk<'repo>, Error> {
self.selected(|_| true)
}
}
pub(crate) mod iter {
pub struct Walk<'repo> {
pub(crate) repo: &'repo crate::Repository,
pub(crate) inner:
Box<dyn Iterator<Item = Result<gix_traverse::commit::Info, gix_traverse::commit::simple::Error>> + 'repo>,
}
impl<'repo> Iterator for Walk<'repo> {
type Item = Result<super::Info<'repo>, gix_traverse::commit::simple::Error>;
fn next(&mut self) -> Option<Self::Item> {
self.inner
.next()
.map(|res| res.map(|info| super::Info::new(info, self.repo)))
}
}
}