1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
use gix_hash::ObjectId;
use gix_odb::FindExt;
use crate::{revision, Repository};
/// The error returned by [`Platform::all()`].
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
AncestorIter(#[from] gix_traverse::commit::ancestors::Error),
#[error(transparent)]
ShallowCommits(#[from] crate::shallow::open::Error),
}
/// A platform to traverse the revision graph by adding starting points as well as points which shouldn't be crossed,
/// returned by [`Repository::rev_walk()`].
pub struct Platform<'repo> {
pub(crate) repo: &'repo Repository,
pub(crate) tips: Vec<ObjectId>,
pub(crate) sorting: gix_traverse::commit::Sorting,
pub(crate) parents: gix_traverse::commit::Parents,
}
impl<'repo> Platform<'repo> {
pub(crate) fn new(tips: impl IntoIterator<Item = impl Into<ObjectId>>, repo: &'repo Repository) -> Self {
revision::walk::Platform {
repo,
tips: tips.into_iter().map(Into::into).collect(),
sorting: Default::default(),
parents: Default::default(),
}
}
}
/// Create-time builder methods
impl<'repo> Platform<'repo> {
/// Set the sort mode for commits to the given value. The default is to order by topology.
pub fn sorting(mut self, sorting: gix_traverse::commit::Sorting) -> Self {
self.sorting = sorting;
self
}
/// Only traverse the first parent of the commit graph.
pub fn first_parent_only(mut self) -> Self {
self.parents = gix_traverse::commit::Parents::First;
self
}
}
/// Produce the iterator
impl<'repo> Platform<'repo> {
/// For each commit, let `filter` return `true` if it and its parents should be included in the traversal, or `false`
/// if the traversal should exclude it and its ancestry entirely.
///
/// If `filter` is None, no pruning of the graph will be performed which is the default.
pub fn selected(
self,
mut filter: impl FnMut(&gix_hash::oid) -> bool + 'repo,
) -> Result<revision::Walk<'repo>, Error> {
let Platform {
repo,
tips,
sorting,
parents,
} = self;
Ok(revision::Walk {
repo,
inner: Box::new(
gix_traverse::commit::Ancestors::filtered(
tips,
gix_traverse::commit::ancestors::State::default(),
move |oid, buf| repo.objects.find_commit_iter(oid, buf),
{
let shallow_commits = repo.shallow_commits()?;
let mut grafted_parents_to_skip = Vec::new();
let mut buf = Vec::new();
move |id| {
if !filter(id) {
return false;
}
match shallow_commits.as_ref() {
Some(commits) => {
let id = id.to_owned();
if let Ok(idx) = grafted_parents_to_skip.binary_search(&id) {
grafted_parents_to_skip.remove(idx);
return false;
};
if commits.binary_search(&id).is_ok() {
if let Ok(commit) = repo.objects.find_commit_iter(id, &mut buf) {
grafted_parents_to_skip.extend(commit.parent_ids());
grafted_parents_to_skip.sort();
}
};
true
}
None => true,
}
}
},
)
.sorting(sorting)?
.parents(parents),
),
})
}
/// Return an iterator to traverse all commits reachable as configured by the [Platform].
///
/// # Performance
///
/// It's highly recommended to set an [`object cache`][Repository::object_cache_size()] on the parent repo
/// to greatly speed up performance if the returned id is supposed to be looked up right after.
pub fn all(self) -> Result<revision::Walk<'repo>, Error> {
self.selected(|_| true)
}
}
pub(crate) mod iter {
use crate::{ext::ObjectIdExt, Id};
/// The iterator returned by [`crate::revision::walk::Platform::all()`].
pub struct Walk<'repo> {
pub(crate) repo: &'repo crate::Repository,
pub(crate) inner:
Box<dyn Iterator<Item = Result<gix_hash::ObjectId, gix_traverse::commit::ancestors::Error>> + 'repo>,
}
impl<'repo> Iterator for Walk<'repo> {
type Item = Result<Id<'repo>, gix_traverse::commit::ancestors::Error>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|res| res.map(|id| id.attach(self.repo)))
}
}
}
|