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