gix_traverse/commit/topo/
init.rs1use gix_hash::{oid, ObjectId};
2use gix_revwalk::{graph::IdMap, PriorityQueue};
3
4use crate::commit::{
5 find,
6 topo::{iter::gen_and_commit_time, Error, Sorting, WalkFlags},
7 Info, Parents, Topo,
8};
9
10pub struct Builder<Find, Predicate> {
12 commit_graph: Option<gix_commitgraph::Graph>,
13 find: Find,
14 predicate: Predicate,
15 sorting: Sorting,
16 parents: Parents,
17 tips: Vec<ObjectId>,
18 ends: Vec<ObjectId>,
19}
20
21impl<Find> Builder<Find, fn(&oid) -> bool>
22where
23 Find: gix_object::Find,
24{
25 pub fn from_iters(
29 find: Find,
30 tips: impl IntoIterator<Item = impl Into<ObjectId>>,
31 ends: Option<impl IntoIterator<Item = impl Into<ObjectId>>>,
32 ) -> Self {
33 Self::new(find).with_tips(tips).with_ends(ends.into_iter().flatten())
34 }
35
36 pub fn new(find: Find) -> Self {
39 Self {
40 commit_graph: Default::default(),
41 find,
42 sorting: Default::default(),
43 parents: Default::default(),
44 tips: Default::default(),
45 ends: Default::default(),
46 predicate: |_| true,
47 }
48 }
49
50 pub fn with_predicate<Predicate>(self, predicate: Predicate) -> Builder<Find, Predicate>
55 where
56 Predicate: FnMut(&oid) -> bool,
57 {
58 Builder {
59 commit_graph: self.commit_graph,
60 find: self.find,
61 sorting: self.sorting,
62 parents: self.parents,
63 tips: self.tips,
64 ends: self.ends,
65 predicate,
66 }
67 }
68}
69
70impl<Find, Predicate> Builder<Find, Predicate>
71where
72 Find: gix_object::Find,
73 Predicate: FnMut(&oid) -> bool,
74{
75 pub fn with_tips(mut self, tips: impl IntoIterator<Item = impl Into<ObjectId>>) -> Self {
79 self.tips.extend(tips.into_iter().map(Into::into));
80 self
81 }
82
83 pub fn with_ends(mut self, ends: impl IntoIterator<Item = impl Into<ObjectId>>) -> Self {
88 self.ends.extend(ends.into_iter().map(Into::into));
89 self
90 }
91
92 pub fn sorting(mut self, sorting: Sorting) -> Self {
94 self.sorting = sorting;
95 self
96 }
97
98 pub fn parents(mut self, parents: Parents) -> Self {
100 self.parents = parents;
101 self
102 }
103
104 pub fn with_commit_graph(mut self, commit_graph: Option<gix_commitgraph::Graph>) -> Self {
106 self.commit_graph = commit_graph;
107 self
108 }
109
110 pub fn build(self) -> Result<Topo<Find, Predicate>, Error> {
114 let mut w = Topo {
115 commit_graph: self.commit_graph,
116 find: self.find,
117 predicate: self.predicate,
118 indegrees: IdMap::default(),
119 states: IdMap::default(),
120 explore_queue: PriorityQueue::new(),
121 indegree_queue: PriorityQueue::new(),
122 topo_queue: super::iter::Queue::new(self.sorting),
123 parents: self.parents,
124 min_gen: gix_commitgraph::GENERATION_NUMBER_INFINITY,
125 buf: vec![],
126 };
127
128 let tip_flags = WalkFlags::Seen | WalkFlags::Explored | WalkFlags::InDegree;
132 let end_flags = tip_flags | WalkFlags::Uninteresting | WalkFlags::Bottom;
133
134 for (id, flags) in self
135 .tips
136 .iter()
137 .map(|id| (id, tip_flags))
138 .chain(self.ends.iter().map(|id| (id, end_flags)))
139 {
140 *w.indegrees.entry(*id).or_default() = 1;
141 let commit = find(w.commit_graph.as_ref(), &w.find, id, &mut w.buf)?;
142 let (gen, time) = gen_and_commit_time(commit)?;
143
144 if gen < w.min_gen {
145 w.min_gen = gen;
146 }
147
148 w.states.insert(*id, flags);
149 w.explore_queue.insert((gen, time), *id);
150 w.indegree_queue.insert((gen, time), *id);
151 }
152
153 for id in &self.ends {
156 let parents = w.collect_all_parents(id)?;
157 for (id, _) in parents {
158 w.states
159 .entry(id)
160 .and_modify(|s| *s |= WalkFlags::Uninteresting)
161 .or_insert(WalkFlags::Uninteresting | WalkFlags::Seen);
162 }
163 }
164
165 w.compute_indegrees_to_depth(w.min_gen)?;
166
167 for id in self.tips.iter() {
171 let i = w.indegrees.get(id).ok_or(Error::MissingIndegreeUnexpected)?;
172
173 if *i != 1 {
174 continue;
175 }
176
177 let commit = find(w.commit_graph.as_ref(), &w.find, id, &mut w.buf)?;
178 let (_, time) = gen_and_commit_time(commit)?;
179 let parent_ids = w.collect_all_parents(id)?.into_iter().map(|e| e.0).collect();
180
181 w.topo_queue.push(
182 time,
183 Info {
184 id: *id,
185 parent_ids,
186 commit_time: Some(time),
187 },
188 );
189 }
190
191 w.topo_queue.initial_sort();
192 Ok(w)
193 }
194}