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