Skip to content

Commit ebf6117

Browse files
committed
refactor
- adjust module structure - remove unused method with `todo!()` - no recursion in `next()` - baseline comparisons
1 parent 3bcee37 commit ebf6117

File tree

11 files changed

+1366
-1395
lines changed

11 files changed

+1366
-1395
lines changed

gix-traverse/src/commit/topo.rs

Lines changed: 0 additions & 611 deletions
This file was deleted.

gix-traverse/src/commit/topo/init.rs

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
use crate::commit::topo::iter::gen_and_commit_time;
2+
use crate::commit::topo::{Error, Sorting, Walk, WalkFlags};
3+
use crate::commit::{find, Info, Parents};
4+
use gix_hash::{oid, ObjectId};
5+
use gix_revwalk::graph::IdMap;
6+
use gix_revwalk::PriorityQueue;
7+
8+
/// Builder for [`Walk`].
9+
pub 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+
19+
impl<Find> Builder<Find, fn(&oid) -> bool>
20+
where
21+
Find: gix_object::Find,
22+
{
23+
/// Create a new `Builder` for a [`Walk`] that reads commits from a repository with `find`.
24+
/// starting at the `tips` and ending at the `ends`. Like `git rev-list
25+
/// --topo-order ^ends... tips...`.
26+
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+
let tips = tips.into_iter().map(Into::into).collect::<Vec<_>>();
32+
let ends = ends
33+
.map(|e| e.into_iter().map(Into::into).collect::<Vec<_>>())
34+
.unwrap_or_default();
35+
36+
Self {
37+
commit_graph: Default::default(),
38+
find,
39+
sorting: Default::default(),
40+
parents: Default::default(),
41+
tips,
42+
ends,
43+
predicate: |_| true,
44+
}
45+
}
46+
47+
/// Set a `predicate` to filter out revisions from the walk. Can be used to
48+
/// implement e.g. filtering on paths or time. This does *not* exclude the
49+
/// parent(s) of a revision that is excluded. Specify a revision as an 'end'
50+
/// if you want that behavior.
51+
pub fn with_predicate<Predicate>(self, predicate: Predicate) -> Builder<Find, Predicate>
52+
where
53+
Predicate: FnMut(&oid) -> bool,
54+
{
55+
Builder {
56+
commit_graph: self.commit_graph,
57+
find: self.find,
58+
sorting: self.sorting,
59+
parents: self.parents,
60+
tips: self.tips,
61+
ends: self.ends,
62+
predicate,
63+
}
64+
}
65+
}
66+
67+
impl<Find, Predicate> Builder<Find, Predicate>
68+
where
69+
Find: gix_object::Find,
70+
Predicate: FnMut(&oid) -> bool,
71+
{
72+
/// Set the `sorting` to use for the topological walk.
73+
pub fn sorting(mut self, sorting: Sorting) -> Self {
74+
self.sorting = sorting;
75+
self
76+
}
77+
78+
/// Specify how to handle commit `parents` during traversal.
79+
pub fn parents(mut self, parents: Parents) -> Self {
80+
self.parents = parents;
81+
self
82+
}
83+
84+
/// Set or unset the `commit_graph` to use for the iteration.
85+
pub fn with_commit_graph(mut self, commit_graph: Option<gix_commitgraph::Graph>) -> Self {
86+
self.commit_graph = commit_graph;
87+
self
88+
}
89+
90+
/// Build a new [`Walk`] instance.
91+
///
92+
/// Note that merely building an instance is currently expensive.
93+
pub fn build(self) -> Result<Walk<Find, Predicate>, Error> {
94+
let mut w = Walk {
95+
commit_graph: self.commit_graph,
96+
find: self.find,
97+
predicate: self.predicate,
98+
indegrees: IdMap::default(),
99+
states: IdMap::default(),
100+
explore_queue: PriorityQueue::new(),
101+
indegree_queue: PriorityQueue::new(),
102+
topo_queue: super::iter::Queue::new(self.sorting),
103+
parents: self.parents,
104+
min_gen: gix_commitgraph::GENERATION_NUMBER_INFINITY,
105+
buf: vec![],
106+
};
107+
108+
// Initial flags for the states of the tips and ends. All of them are
109+
// seen and added to the explore and indegree queues. The ends are by
110+
// definition (?) uninteresting and bottom.
111+
let tip_flags = WalkFlags::Seen | WalkFlags::Explored | WalkFlags::InDegree;
112+
let end_flags = tip_flags | WalkFlags::Uninteresting | WalkFlags::Bottom;
113+
114+
for (id, flags) in self
115+
.tips
116+
.iter()
117+
.map(|id| (id, tip_flags))
118+
.chain(self.ends.iter().map(|id| (id, end_flags)))
119+
{
120+
*w.indegrees.entry(*id).or_default() = 1;
121+
let commit = find(w.commit_graph.as_ref(), &w.find, id, &mut w.buf)?;
122+
let (gen, time) = gen_and_commit_time(commit)?;
123+
124+
if gen < w.min_gen {
125+
w.min_gen = gen;
126+
}
127+
128+
w.states.insert(*id, flags);
129+
w.explore_queue.insert((gen, time), *id);
130+
w.indegree_queue.insert((gen, time), *id);
131+
}
132+
133+
// NOTE: Parents of the ends must also be marked uninteresting for some
134+
// reason. See handle_commit()
135+
for id in &self.ends {
136+
let parents = w.collect_all_parents(id)?;
137+
for (id, _) in parents {
138+
w.states
139+
.entry(id)
140+
.and_modify(|s| *s |= WalkFlags::Uninteresting)
141+
.or_insert(WalkFlags::Uninteresting | WalkFlags::Seen);
142+
}
143+
}
144+
145+
w.compute_indegrees_to_depth(w.min_gen)?;
146+
147+
// NOTE: in Git the ends are also added to the topo_queue in addition to
148+
// the tips, but then in simplify_commit() Git is told to ignore it. For
149+
// now the tests pass.
150+
for id in self.tips.iter() {
151+
let i = w.indegrees.get(id).ok_or(Error::MissingIndegreeUnexpected)?;
152+
153+
if *i != 1 {
154+
continue;
155+
}
156+
157+
let commit = find(w.commit_graph.as_ref(), &w.find, id, &mut w.buf)?;
158+
let (_, time) = gen_and_commit_time(commit)?;
159+
let parent_ids = w.collect_all_parents(id)?.into_iter().map(|e| e.0).collect();
160+
161+
w.topo_queue.push(
162+
time,
163+
Info {
164+
id: *id,
165+
parent_ids,
166+
commit_time: Some(time),
167+
},
168+
);
169+
}
170+
171+
w.topo_queue.initial_sort();
172+
Ok(w)
173+
}
174+
}

0 commit comments

Comments
 (0)