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 { 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 { 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::("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 { let tip = self.tip_id()?; let key = format!("{after}.{before}.{limit}"); if let Some(hit) = self.cache_get_rev::("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 { 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 { 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::("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) } }