gix_revwalk/graph/
commit.rs1use gix_date::SecondsSinceUnixEpoch;
2use smallvec::SmallVec;
3
4use super::LazyCommit;
5use crate::graph::{Commit, Either, Generation};
6
7impl<'graph, 'cache> LazyCommit<'graph, 'cache> {
8 pub fn iter_parents(&self) -> Parents<'graph, 'cache> {
10 let backing = match &self.backing {
11 Either::Left(buf) => Either::Left(gix_object::CommitRefIter::from_bytes(buf)),
12 Either::Right((cache, pos)) => Either::Right((*cache, cache.commit_at(*pos).iter_parents())),
13 };
14 Parents { backing }
15 }
16
17 pub fn committer_timestamp(&self) -> Result<SecondsSinceUnixEpoch, gix_object::decode::Error> {
22 Ok(match &self.backing {
23 Either::Left(buf) => gix_object::CommitRefIter::from_bytes(buf).committer()?.time.seconds,
24 Either::Right((cache, pos)) => cache.commit_at(*pos).committer_timestamp() as SecondsSinceUnixEpoch, })
26 }
27
28 pub fn generation(&self) -> Option<Generation> {
30 match &self.backing {
31 Either::Left(_) => None,
32 Either::Right((cache, pos)) => cache.commit_at(*pos).generation().into(),
33 }
34 }
35
36 pub fn generation_and_timestamp(
38 &self,
39 ) -> Result<(Option<Generation>, SecondsSinceUnixEpoch), gix_object::decode::Error> {
40 Ok(match &self.backing {
41 Either::Left(buf) => (
42 None,
43 gix_object::CommitRefIter::from_bytes(buf).committer()?.time.seconds,
44 ),
45 Either::Right((cache, pos)) => {
46 let commit = cache.commit_at(*pos);
47 (
48 commit.generation().into(),
49 cache.commit_at(*pos).committer_timestamp() as SecondsSinceUnixEpoch,
51 )
52 }
53 })
54 }
55
56 pub fn to_owned<T>(&self, new_data: impl FnOnce() -> T) -> Result<Commit<T>, to_owned::Error> {
59 let data = new_data();
60 Ok(match &self.backing {
61 Either::Left(buf) => {
62 use gix_object::commit::ref_iter::Token;
63 let iter = gix_object::CommitRefIter::from_bytes(buf);
64 let mut parents = SmallVec::default();
65 let mut timestamp = None;
66 for token in iter {
67 match token? {
68 Token::Tree { .. } => {}
69 Token::Parent { id } => parents.push(id),
70 Token::Author { .. } => {}
71 Token::Committer { signature } => {
72 timestamp = Some(signature.time.seconds);
73 break;
74 }
75 _ => {
76 unreachable!(
77 "we break naturally after seeing the committer which is always at the same spot"
78 )
79 }
80 }
81 }
82 Commit {
83 parents,
84 commit_time: timestamp.unwrap_or_default(),
85 generation: None,
86 data,
87 }
88 }
89 Either::Right((cache, pos)) => {
90 let mut parents = SmallVec::default();
91 let commit = cache.commit_at(*pos);
92 for pos in commit.iter_parents() {
93 let pos = pos?;
94 parents.push(cache.commit_at(pos).id().to_owned());
95 }
96 Commit {
97 parents,
98 commit_time: commit.committer_timestamp().try_into().map_err(|_| {
99 to_owned::Error::CommitGraphTime {
100 actual: commit.committer_timestamp(),
101 }
102 })?,
103 generation: Some(commit.generation()),
104 data,
105 }
106 }
107 })
108 }
109}
110
111pub struct Parents<'graph, 'cache> {
113 backing: Either<
114 gix_object::CommitRefIter<'graph>,
115 (
116 &'cache gix_commitgraph::Graph,
117 gix_commitgraph::file::commit::Parents<'cache>,
118 ),
119 >,
120}
121
122impl Iterator for Parents<'_, '_> {
123 type Item = Result<gix_hash::ObjectId, iter_parents::Error>;
124
125 fn next(&mut self) -> Option<Self::Item> {
126 match &mut self.backing {
127 Either::Left(it) => {
128 for token in it {
129 match token {
130 Ok(gix_object::commit::ref_iter::Token::Tree { .. }) => continue,
131 Ok(gix_object::commit::ref_iter::Token::Parent { id }) => return Some(Ok(id)),
132 Ok(_unused_token) => break,
133 Err(err) => return Some(Err(err.into())),
134 }
135 }
136 None
137 }
138 Either::Right((cache, it)) => it
139 .next()
140 .map(|r| r.map(|pos| cache.id_at(pos).to_owned()).map_err(Into::into)),
141 }
142 }
143}
144
145pub mod iter_parents {
147 #[derive(Debug, thiserror::Error)]
149 #[allow(missing_docs)]
150 pub enum Error {
151 #[error("An error occurred when parsing commit parents")]
152 DecodeCommit(#[from] gix_object::decode::Error),
153 #[error("An error occurred when parsing parents from the commit graph")]
154 DecodeCommitGraph(#[from] gix_commitgraph::file::commit::Error),
155 }
156}
157
158pub mod to_owned {
160 #[derive(Debug, thiserror::Error)]
162 #[allow(missing_docs)]
163 pub enum Error {
164 #[error("A commit could not be decoded during traversal")]
165 Decode(#[from] gix_object::decode::Error),
166 #[error("Could not find commit position in graph when traversing parents")]
167 CommitGraphParent(#[from] gix_commitgraph::file::commit::Error),
168 #[error("Commit-graph time could not be presented as signed integer: {actual}")]
169 CommitGraphTime { actual: u64 },
170 }
171}