Skip to content
Clone
52850d3
137 lines 4.6 KB Rust
//! log usecase: implements LogOps for GitRepository.
use crate::GitRepository;
use crate::commit::types::Commit;
use crate::log::impls::LogOps;
use crate::log::types::{CommitGraph, GraphNode, LogList, LogOptions};
fn decode_commit(repo: &gix::Repository, id: gix::hash::ObjectId) -> Result<Commit, crate::Error> {
let c = repo
.find_commit(id)
.map_err(|e| crate::Error::Find(e.to_string()))?;
let decoded = c
.decode()
.map_err(|e| crate::Error::Find(e.to_string()))?
.into_owned()
.map_err(|e| crate::Error::Find(e.to_string()))?;
Ok(Commit::from_gix(id, &decoded))
}
impl LogOps for GitRepository {
fn log_list(&self, opts: &LogOptions) -> Result<LogList, crate::Error> {
let tip = self.tip_id()?;
let limit = opts.limit.unwrap_or(50);
let skip = opts.skip.unwrap_or(0);
let key = format!("{limit}.{skip}");
if let Some(hit) = self.cache_get_rev::<LogList>("log", "list", &key, tip) {
return Ok(hit);
}
let repo = self.repo()?;
let mut walk = repo
.rev_walk([tip])
.all()
.map_err(|e| crate::Error::RevParse(e.to_string()))?;
let mut commits = Vec::new();
let mut skipped = 0u64;
let mut last_id = None;
for info in walk.by_ref() {
let info = info.map_err(|e| crate::Error::Find(e.to_string()))?;
if skipped < skip {
skipped += 1;
continue;
}
if commits.len() as u64 >= limit {
last_id = Some(info.id().to_string());
break;
}
let id = info.id().detach();
last_id = Some(id.to_string());
commits.push(decode_commit(&repo, id)?);
}
let v = LogList {
commits,
cursor: last_id,
has_more: false,
};
self.cache_put_rev("log", "list", &key, tip, &v);
Ok(v)
}
fn log_commits_between(
&self,
after: &str,
before: &str,
limit: u64,
) -> Result<LogList, crate::Error> {
let tip = self.tip_id()?;
let key = format!("{after}.{before}.{limit}");
if let Some(hit) = self.cache_get_rev::<LogList>("log", "between", &key, tip) {
return Ok(hit);
}
let repo = self.repo()?;
let before_id = repo
.rev_parse_single(before.as_bytes())
.map_err(|e| crate::Error::RevParse(e.to_string()))?
.detach();
let after_id = repo
.rev_parse_single(after.as_bytes())
.map_err(|e| crate::Error::RevParse(e.to_string()))?
.detach();
let mut walk = repo
.rev_walk([before_id])
.with_hidden([after_id])
.all()
.map_err(|e| crate::Error::RevParse(e.to_string()))?;
let mut commits = Vec::new();
let mut last_id = None;
for info in walk.by_ref() {
if commits.len() as u64 >= limit {
break;
}
let info = info.map_err(|e| crate::Error::Find(e.to_string()))?;
let id = info.id().detach();
last_id = Some(id.to_string());
commits.push(decode_commit(&repo, id)?);
}
let v = LogList {
commits,
cursor: last_id,
has_more: false,
};
self.cache_put_rev("log", "between", &key, tip, &v);
Ok(v)
}
fn log_graph(&self, opts: &LogOptions) -> Result<CommitGraph, crate::Error> {
let list = self.log_list(opts)?;
let nodes = list
.commits
.into_iter()
.map(|c| GraphNode {
commit: c,
lanes: Vec::new(),
})
.collect();
Ok(CommitGraph { nodes })
}
fn log_last_commit(&self, branch_name: String) -> Result<Commit, crate::Error> {
crate::validate::validate_short_name("branch", &branch_name)?;
let full = format!("refs/heads/{branch_name}");
let repo = self.repo()?;
let id = repo
.find_reference(full.as_str())
.map_err(|_| crate::Error::BranchNotExist(branch_name.clone()))?
.target()
.try_id()
.ok_or_else(|| crate::Error::BranchNotExist(branch_name.clone()))?
.to_owned();
let tip = self.tip_id()?;
if let Some(hit) = self.cache_get_rev::<Commit>("log", "last", &branch_name, tip) {
return Ok(hit);
}
let v = decode_commit(&repo, id)?;
self.cache_put_rev("log", "last", &branch_name, tip, &v);
Ok(v)
}
}