gix_traverse/commit/topo/
iter.rs1use gix_hash::{ObjectId, oid};
2use gix_revwalk::PriorityQueue;
3use smallvec::SmallVec;
4
5use crate::commit::{
6 Either, Info, Parents, Topo, find,
7 topo::{Error, Sorting, WalkFlags},
8};
9
10pub(in crate::commit) type GenAndCommitTime = (u32, i64);
11
12#[derive(Debug)]
17pub(in crate::commit) enum Queue {
18 Date(PriorityQueue<i64, Info>),
19 Topo(Vec<(i64, Info)>),
20}
21
22impl Queue {
23 pub(super) fn new(s: Sorting) -> Self {
24 match s {
25 Sorting::DateOrder => Self::Date(PriorityQueue::new()),
26 Sorting::TopoOrder => Self::Topo(vec![]),
27 }
28 }
29
30 pub(super) fn push(&mut self, commit_time: i64, info: Info) {
31 match self {
32 Self::Date(q) => q.insert(commit_time, info),
33 Self::Topo(q) => q.push((commit_time, info)),
34 }
35 }
36
37 fn pop(&mut self) -> Option<Info> {
38 match self {
39 Self::Date(q) => q.pop().map(|(_, info)| info),
40 Self::Topo(q) => q.pop().map(|(_, info)| info),
41 }
42 }
43
44 pub(super) fn initial_sort(&mut self) {
45 if let Self::Topo(inner_vec) = self {
46 inner_vec.sort_by_key(|a| a.0);
47 }
48 }
49}
50
51impl<Find, Predicate> Topo<Find, Predicate>
52where
53 Find: gix_object::Find,
54{
55 pub(super) fn compute_indegrees_to_depth(&mut self, gen_cutoff: u32) -> Result<(), Error> {
56 while let Some(((generation, _), _)) = self.indegree_queue.peek() {
57 if *generation >= gen_cutoff {
58 self.indegree_walk_step()?;
59 } else {
60 break;
61 }
62 }
63
64 Ok(())
65 }
66
67 fn indegree_walk_step(&mut self) -> Result<(), Error> {
68 if let Some(((generation, _), id)) = self.indegree_queue.pop() {
69 self.explore_to_depth(generation)?;
70
71 let parents = self.collect_parents(&id)?;
72 for (id, gen_time) in parents {
73 self.indegrees.entry(id).and_modify(|e| *e += 1).or_insert(2);
74
75 let state = self.states.get_mut(&id).ok_or(Error::MissingStateUnexpected)?;
76 if !state.contains(WalkFlags::InDegree) {
77 *state |= WalkFlags::InDegree;
78 self.indegree_queue.insert(gen_time, id);
79 }
80 }
81 }
82 Ok(())
83 }
84
85 fn explore_to_depth(&mut self, gen_cutoff: u32) -> Result<(), Error> {
86 while let Some(((generation, _), _)) = self.explore_queue.peek() {
87 if *generation >= gen_cutoff {
88 self.explore_walk_step()?;
89 } else {
90 break;
91 }
92 }
93 Ok(())
94 }
95
96 fn explore_walk_step(&mut self) -> Result<(), Error> {
97 if let Some((_, id)) = self.explore_queue.pop() {
98 let parents = self.collect_parents(&id)?;
99 self.process_parents(&id, &parents)?;
100
101 for (id, gen_time) in parents {
102 let state = self.states.get_mut(&id).ok_or(Error::MissingStateUnexpected)?;
103
104 if !state.contains(WalkFlags::Explored) {
105 *state |= WalkFlags::Explored;
106 self.explore_queue.insert(gen_time, id);
107 }
108 }
109 }
110 Ok(())
111 }
112
113 fn expand_topo_walk(&mut self, id: &oid) -> Result<(), Error> {
114 let parents = self.collect_parents(id)?;
115 self.process_parents(id, &parents)?;
116
117 for (pid, (parent_gen, parent_commit_time)) in parents {
118 let parent_state = self.states.get(&pid).ok_or(Error::MissingStateUnexpected)?;
119 if parent_state.contains(WalkFlags::Uninteresting) {
120 continue;
121 }
122
123 if parent_gen < self.min_gen {
124 self.min_gen = parent_gen;
125 self.compute_indegrees_to_depth(self.min_gen)?;
126 }
127
128 let i = self.indegrees.get_mut(&pid).ok_or(Error::MissingIndegreeUnexpected)?;
129 *i -= 1;
130 if *i != 1 {
131 continue;
132 }
133
134 let parent_ids = self.collect_all_parents(&pid)?.into_iter().map(|e| e.0).collect();
135 self.topo_queue.push(
136 parent_commit_time,
137 Info {
138 id: pid,
139 parent_ids,
140 commit_time: Some(parent_commit_time),
141 },
142 );
143 }
144
145 Ok(())
146 }
147
148 fn process_parents(&mut self, id: &oid, parents: &[(ObjectId, GenAndCommitTime)]) -> Result<(), Error> {
149 let state = self.states.get_mut(id).ok_or(Error::MissingStateUnexpected)?;
150 if state.contains(WalkFlags::Added) {
151 return Ok(());
152 }
153
154 *state |= WalkFlags::Added;
155
156 let (pass, insert) = if state.contains(WalkFlags::Uninteresting) {
159 let flags = WalkFlags::Uninteresting;
160 for (id, _) in parents {
161 let grand_parents = self.collect_all_parents(id)?;
162
163 for (id, _) in &grand_parents {
164 self.states
165 .entry(*id)
166 .and_modify(|s| *s |= WalkFlags::Uninteresting)
167 .or_insert(WalkFlags::Uninteresting | WalkFlags::Seen);
168 }
169 }
170 (flags, flags)
171 } else {
172 let flags = WalkFlags::empty();
175 (flags, WalkFlags::Seen)
176 };
177
178 for (id, _) in parents {
179 self.states.entry(*id).and_modify(|s| *s |= pass).or_insert(insert);
180 }
181 Ok(())
182 }
183
184 fn collect_parents(&mut self, id: &oid) -> Result<SmallVec<[(ObjectId, GenAndCommitTime); 1]>, Error> {
185 collect_parents(
186 &mut self.commit_graph,
187 &self.find,
188 id,
189 matches!(self.parents, Parents::First),
190 &mut self.buf,
191 )
192 }
193
194 pub(super) fn collect_all_parents(
196 &mut self,
197 id: &oid,
198 ) -> Result<SmallVec<[(ObjectId, GenAndCommitTime); 1]>, Error> {
199 collect_parents(&mut self.commit_graph, &self.find, id, false, &mut self.buf)
200 }
201
202 fn pop_commit(&mut self) -> Option<Result<Info, Error>> {
203 let commit = self.topo_queue.pop()?;
204 let i = match self.indegrees.get_mut(&commit.id) {
205 Some(i) => i,
206 None => {
207 return Some(Err(Error::MissingIndegreeUnexpected));
208 }
209 };
210
211 *i = 0;
212 if let Err(e) = self.expand_topo_walk(&commit.id) {
213 return Some(Err(e));
214 }
215
216 Some(Ok(commit))
217 }
218}
219
220impl<Find, Predicate> Iterator for Topo<Find, Predicate>
221where
222 Find: gix_object::Find,
223 Predicate: FnMut(&oid) -> bool,
224{
225 type Item = Result<Info, Error>;
226
227 fn next(&mut self) -> Option<Self::Item> {
228 loop {
229 match self.pop_commit()? {
230 Ok(id) => {
231 if (self.predicate)(&id.id) {
232 return Some(Ok(id));
233 }
234 }
235 Err(e) => return Some(Err(e)),
236 }
237 }
238 }
239}
240
241fn collect_parents<Find>(
242 cache: &mut Option<gix_commitgraph::Graph>,
243 f: Find,
244 id: &oid,
245 first_only: bool,
246 buf: &mut Vec<u8>,
247) -> Result<SmallVec<[(ObjectId, GenAndCommitTime); 1]>, Error>
248where
249 Find: gix_object::Find,
250{
251 let mut parents = SmallVec::<[(ObjectId, GenAndCommitTime); 1]>::new();
252 match find(cache.as_ref(), &f, id, buf)? {
253 Either::CommitRefIter(c) => {
254 for token in c {
255 use gix_object::commit::ref_iter::Token as T;
256 match token {
257 Ok(T::Tree { .. }) => continue,
258 Ok(T::Parent { id }) => {
259 parents.push((id, (0, 0))); if first_only {
261 break;
262 }
263 }
264 Ok(_past_parents) => break,
265 Err(err) => return Err(err.into()),
266 }
267 }
268 for (id, gen_time) in parents.iter_mut() {
271 let commit = find(cache.as_ref(), &f, id, buf)?;
272 *gen_time = gen_and_commit_time(commit)?;
273 }
274 }
275 Either::CachedCommit(c) => {
276 for pos in c.iter_parents() {
277 let Ok(pos) = pos else {
278 *cache = None;
280 return collect_parents(cache, f, id, first_only, buf);
281 };
282 let parent_commit = cache
283 .as_ref()
284 .expect("cache exists if CachedCommit was returned")
285 .commit_at(pos);
286 parents.push((
287 parent_commit.id().into(),
288 (parent_commit.generation(), parent_commit.committer_timestamp() as i64),
289 ));
290 if first_only {
291 break;
292 }
293 }
294 }
295 }
296 Ok(parents)
297}
298
299pub(super) fn gen_and_commit_time(c: Either<'_, '_>) -> Result<GenAndCommitTime, Error> {
300 match c {
301 Either::CommitRefIter(c) => {
302 let mut commit_time = 0;
303 for token in c {
304 use gix_object::commit::ref_iter::Token as T;
305 match token {
306 Ok(T::Tree { .. }) => continue,
307 Ok(T::Parent { .. }) => continue,
308 Ok(T::Author { .. }) => continue,
309 Ok(T::Committer { signature }) => {
310 commit_time = signature.seconds();
311 break;
312 }
313 Ok(_unused_token) => break,
314 Err(err) => return Err(err.into()),
315 }
316 }
317 Ok((gix_commitgraph::GENERATION_NUMBER_INFINITY, commit_time))
318 }
319 Either::CachedCommit(c) => Ok((c.generation(), c.committer_timestamp() as i64)),
320 }
321}