use serde::{Deserialize, Serialize}; use crate::{ bare::GitBare, cmd::{commit::CommitMeta, oid::ObjectId}, errors::{GitError, GitResult}, }; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum CommitWalkSort { None, Topological, Time, Reverse, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CommitWalkParams { pub start_oids: Vec, pub hide_oids: Vec, pub limit: Option, pub skip: usize, pub first_parent: bool, pub sort: CommitWalkSort, pub branch: Option, } impl Default for CommitWalkParams { fn default() -> Self { Self { start_oids: Vec::new(), hide_oids: Vec::new(), limit: None, skip: 0, first_parent: false, sort: CommitWalkSort::Time, branch: None, } } } impl GitBare { pub fn commit_walk( &self, params: CommitWalkParams, ) -> GitResult> { let repo = self.gix_repo()?; let tips: Vec = if let Some(ref branch_name) = params.branch { if branch_name.is_empty() { vec![repo.head_id()?.detach()] } else { let ref_name = format!("refs/heads/{branch_name}"); match repo.try_find_reference(&ref_name)? { Some(reference) => { let target = reference.target(); let target_id = target.try_id().ok_or_else(|| { GitError::Internal(format!( "branch '{branch_name}' has no direct target" )) })?; vec![target_id.to_owned()] } None => { let remote_ref = format!("refs/remotes/{branch_name}"); match repo.try_find_reference(&remote_ref)? { Some(reference) => { let target = reference.target(); let target_id = target.try_id().ok_or_else(|| { GitError::Internal(format!("remote branch '{branch_name}' has no direct target")) })?; vec![target_id.to_owned()] } None => { return Err(GitError::RefNotFound( branch_name.clone(), )); } } } } } } else if params.start_oids.is_empty() { vec![repo.head_id()?.detach()] } else { params .start_oids .iter() .map(|oid| oid.try_into()) .collect::, _>>()? }; let hide: Vec = params .hide_oids .iter() .map(|oid| oid.try_into()) .collect::, _>>()?; let mut platform = repo.rev_walk(tips); match params.sort { CommitWalkSort::None => {} CommitWalkSort::Topological => { platform = platform .sorting(gix::revision::walk::Sorting::BreadthFirst); } CommitWalkSort::Time => { platform = platform.sorting(gix::revision::walk::Sorting::ByCommitTime( gix::traverse::commit::simple::CommitTimeOrder::NewestFirst, )); } CommitWalkSort::Reverse => { platform = platform.sorting(gix::revision::walk::Sorting::ByCommitTime( gix::traverse::commit::simple::CommitTimeOrder::OldestFirst, )); } } if params.first_parent { platform = platform.first_parent_only(); } if !hide.is_empty() { platform = platform.with_boundary(hide); } let walk = platform.all()?; let mut commits = Vec::new(); let mut count = 0; let skip = params.skip; for info in walk { if count < skip { count += 1; continue; } let info = info?; let oid = ObjectId::new(info.id().detach().to_hex().to_string()); let commit = self.commit_info(oid)?; commits.push(commit); if let Some(limit) = params.limit { if commits.len() >= limit { break; } } count += 1; } if matches!(params.sort, CommitWalkSort::Reverse) { commits.reverse(); } Ok(commits) } }