154 lines
4.8 KiB
Rust
154 lines
4.8 KiB
Rust
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<ObjectId>,
|
|
pub hide_oids: Vec<ObjectId>,
|
|
pub limit: Option<usize>,
|
|
pub skip: usize,
|
|
pub first_parent: bool,
|
|
pub sort: CommitWalkSort,
|
|
pub branch: Option<String>,
|
|
}
|
|
|
|
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<Vec<CommitMeta>> {
|
|
let repo = self.gix_repo()?;
|
|
let tips: Vec<gix::hash::ObjectId> = 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::<Result<Vec<_>, _>>()?
|
|
};
|
|
let hide: Vec<gix::hash::ObjectId> = params
|
|
.hide_oids
|
|
.iter()
|
|
.map(|oid| oid.try_into())
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
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)
|
|
}
|
|
}
|