@@ -3,7 +3,7 @@ use gix_object::FindExt;
3
3
4
4
use crate :: { ext:: ObjectIdExt , revision, Repository } ;
5
5
6
- /// The error returned by [`Platform::all()`].
6
+ /// The error returned by [`Platform::all()`] and [`Platform::selected()`] .
7
7
#[ derive( Debug , thiserror:: Error ) ]
8
8
#[ allow( missing_docs) ]
9
9
pub enum Error {
@@ -15,6 +15,65 @@ pub enum Error {
15
15
ConfigBoolean ( #[ from] crate :: config:: boolean:: Error ) ,
16
16
}
17
17
18
+ /// Specify how to sort commits during a [revision::Walk] traversal.
19
+ ///
20
+ /// ### Sample History
21
+ ///
22
+ /// The following history will be referred to for explaining how the sort order works, with the number denoting the commit timestamp
23
+ /// (*their X-alignment doesn't matter*).
24
+ ///
25
+ /// ```text
26
+ /// ---1----2----4----7 <- second parent of 8
27
+ /// \ \
28
+ /// 3----5----6----8---
29
+ /// ```
30
+ #[ derive( Default , Debug , Copy , Clone ) ]
31
+ pub enum Sorting {
32
+ /// Commits are sorted as they are mentioned in the commit graph.
33
+ ///
34
+ /// In the *sample history* the order would be `8, 6, 7, 5, 4, 3, 2, 1`
35
+ ///
36
+ /// ### Note
37
+ ///
38
+ /// This is not to be confused with `git log/rev-list --topo-order`, which is notably different from
39
+ /// as it avoids overlapping branches.
40
+ #[ default]
41
+ BreadthFirst ,
42
+ /// Commits are sorted by their commit time in descending order, that is newest first.
43
+ ///
44
+ /// The sorting applies to all currently queued commit ids and thus is full.
45
+ ///
46
+ /// In the *sample history* the order would be `8, 7, 6, 4, 5, 2, 3, 1`
47
+ ///
48
+ /// # Performance
49
+ ///
50
+ /// This mode benefits greatly from having an [object cache](crate::Repository::object_cache_size) configured
51
+ /// to avoid having to look up each commit twice.
52
+ ByCommitTimeNewestFirst ,
53
+ /// This sorting is similar to `ByCommitTimeNewestFirst`, but adds a cutoff to not return commits older than
54
+ /// a given time, stopping the iteration once no younger commits is queued to be traversed.
55
+ ///
56
+ /// As the query is usually repeated with different cutoff dates, this search mode benefits greatly from an object cache.
57
+ ///
58
+ /// In the *sample history* and a cut-off date of 4, the returned list of commits would be `8, 7, 6, 4`
59
+ ByCommitTimeNewestFirstCutoffOlderThan {
60
+ /// The amount of seconds since unix epoch to use as cut-off time.
61
+ seconds : gix_date:: SecondsSinceUnixEpoch ,
62
+ } ,
63
+ }
64
+
65
+ impl Sorting {
66
+ fn into_simple ( self ) -> Option < gix_traverse:: commit:: simple:: Sorting > {
67
+ Some ( match self {
68
+ Sorting :: BreadthFirst => gix_traverse:: commit:: simple:: Sorting :: BreadthFirst ,
69
+ Sorting :: ByCommitTimeNewestFirst => gix_traverse:: commit:: simple:: Sorting :: ByCommitTimeNewestFirst ,
70
+ Sorting :: ByCommitTimeNewestFirstCutoffOlderThan { seconds } => {
71
+ gix_traverse:: commit:: simple:: Sorting :: ByCommitTimeNewestFirstCutoffOlderThan { seconds }
72
+ }
73
+ } )
74
+ }
75
+ }
76
+
18
77
/// Information about a commit that we obtained naturally as part of the iteration.
19
78
#[ derive( Debug , Clone ) ]
20
79
pub struct Info < ' repo > {
@@ -91,7 +150,8 @@ impl<'repo> Info<'repo> {
91
150
pub struct Platform < ' repo > {
92
151
pub ( crate ) repo : & ' repo Repository ,
93
152
pub ( crate ) tips : Vec < ObjectId > ,
94
- pub ( crate ) sorting : gix_traverse:: commit:: simple:: Sorting ,
153
+ pub ( crate ) prune : Vec < ObjectId > ,
154
+ pub ( crate ) sorting : Sorting ,
95
155
pub ( crate ) parents : gix_traverse:: commit:: Parents ,
96
156
pub ( crate ) use_commit_graph : Option < bool > ,
97
157
pub ( crate ) commit_graph : Option < gix_commitgraph:: Graph > ,
@@ -106,14 +166,15 @@ impl<'repo> Platform<'repo> {
106
166
parents : Default :: default ( ) ,
107
167
use_commit_graph : None ,
108
168
commit_graph : None ,
169
+ prune : Vec :: new ( ) ,
109
170
}
110
171
}
111
172
}
112
173
113
174
/// Create-time builder methods
114
175
impl < ' repo > Platform < ' repo > {
115
176
/// Set the sort mode for commits to the given value. The default is to order topologically breadth-first.
116
- pub fn sorting ( mut self , sorting : gix_traverse :: commit :: simple :: Sorting ) -> Self {
177
+ pub fn sorting ( mut self , sorting : Sorting ) -> Self {
117
178
self . sorting = sorting;
118
179
self
119
180
}
@@ -143,6 +204,37 @@ impl<'repo> Platform<'repo> {
143
204
self . commit_graph = graph;
144
205
self
145
206
}
207
+
208
+ /// Prune the commit with the given `ids` such that they won't be returned, and such that none of their ancestors is returned either.
209
+ ///
210
+ /// Note that this forces the [sorting](Self::sorting) to
211
+ /// [`ByCommitTimeNewestFirstCutoffOlderThan`](Sorting::ByCommitTimeNewestFirstCutoffOlderThan) configured with
212
+ /// the oldest available commit time, ensuring that no commits older than the oldest of `ids` will be returned either.
213
+ ///
214
+ /// Also note that commits that can't be accessed or are missing are simply ignored for the purpose of obtaining the cutoff date.
215
+ #[ doc( alias = "hide" , alias = "git2" ) ]
216
+ pub fn with_pruned ( mut self , ids : impl IntoIterator < Item = impl Into < ObjectId > > ) -> Self {
217
+ let mut cutoff = match self . sorting {
218
+ Sorting :: ByCommitTimeNewestFirstCutoffOlderThan { seconds } => Some ( seconds) ,
219
+ Sorting :: BreadthFirst | Sorting :: ByCommitTimeNewestFirst => None ,
220
+ } ;
221
+ for id in ids. into_iter ( ) {
222
+ let id = id. into ( ) ;
223
+ if !self . prune . contains ( & id) {
224
+ if let Some ( time) = self . repo . find_commit ( id) . ok ( ) . and_then ( |c| c. time ( ) . ok ( ) ) {
225
+ if cutoff. is_none ( ) || cutoff > Some ( time. seconds ) {
226
+ cutoff = time. seconds . into ( ) ;
227
+ }
228
+ }
229
+ self . prune . push ( id) ;
230
+ }
231
+ }
232
+
233
+ if let Some ( cutoff) = cutoff {
234
+ self . sorting = Sorting :: ByCommitTimeNewestFirstCutoffOlderThan { seconds : cutoff }
235
+ }
236
+ self
237
+ }
146
238
}
147
239
148
240
/// Produce the iterator
@@ -162,7 +254,9 @@ impl<'repo> Platform<'repo> {
162
254
parents,
163
255
use_commit_graph,
164
256
commit_graph,
257
+ mut prune,
165
258
} = self ;
259
+ prune. sort ( ) ;
166
260
Ok ( revision:: Walk {
167
261
repo,
168
262
inner : Box :: new (
@@ -176,9 +270,12 @@ impl<'repo> Platform<'repo> {
176
270
if !filter ( id) {
177
271
return false ;
178
272
}
273
+ let id = id. to_owned ( ) ;
274
+ if prune. binary_search ( & id) . is_ok ( ) {
275
+ return false ;
276
+ }
179
277
match shallow_commits. as_ref ( ) {
180
278
Some ( commits) => {
181
- let id = id. to_owned ( ) ;
182
279
if let Ok ( idx) = grafted_parents_to_skip. binary_search ( & id) {
183
280
grafted_parents_to_skip. remove ( idx) ;
184
281
return false ;
@@ -195,38 +292,50 @@ impl<'repo> Platform<'repo> {
195
292
}
196
293
}
197
294
} )
198
- . sorting ( sorting) ?
295
+ . sorting ( sorting. into_simple ( ) . expect ( "for now there is nothing else" ) ) ?
199
296
. parents ( parents)
200
297
. commit_graph (
201
298
commit_graph. or ( use_commit_graph
202
299
. map_or_else ( || self . repo . config . may_use_commit_graph ( ) , Ok ) ?
203
300
. then ( || self . repo . commit_graph ( ) . ok ( ) )
204
301
. flatten ( ) ) ,
205
- ) ,
302
+ )
303
+ . map ( |res| res. map_err ( iter:: Error :: from) ) ,
206
304
) ,
207
305
} )
208
306
}
209
307
/// Return an iterator to traverse all commits reachable as configured by the [Platform].
210
308
///
211
309
/// # Performance
212
310
///
213
- /// It's highly recommended to set an [`object cache`][ Repository::object_cache_size()] on the parent repo
311
+ /// It's highly recommended to set an [`object cache`]( Repository::object_cache_size()) on the parent repo
214
312
/// to greatly speed up performance if the returned id is supposed to be looked up right after.
215
313
pub fn all ( self ) -> Result < revision:: Walk < ' repo > , Error > {
216
314
self . selected ( |_| true )
217
315
}
218
316
}
219
317
220
- pub ( crate ) mod iter {
318
+ ///
319
+ #[ allow( clippy:: empty_docs) ]
320
+ pub mod iter {
321
+ /// The error returned by the [Walk](crate::revision::Walk) iterator.
322
+ #[ derive( Debug , thiserror:: Error ) ]
323
+ #[ allow( missing_docs) ]
324
+ pub enum Error {
325
+ #[ error( transparent) ]
326
+ SimpleTraversal ( #[ from] gix_traverse:: commit:: simple:: Error ) ,
327
+ }
328
+ }
329
+
330
+ pub ( crate ) mod iter_impl {
221
331
/// The iterator returned by [`crate::revision::walk::Platform::all()`].
222
332
pub struct Walk < ' repo > {
223
333
pub ( crate ) repo : & ' repo crate :: Repository ,
224
- pub ( crate ) inner :
225
- Box < dyn Iterator < Item = Result < gix_traverse:: commit:: Info , gix_traverse:: commit:: simple:: Error > > + ' repo > ,
334
+ pub ( crate ) inner : Box < dyn Iterator < Item = Result < gix_traverse:: commit:: Info , super :: iter:: Error > > + ' repo > ,
226
335
}
227
336
228
337
impl < ' repo > Iterator for Walk < ' repo > {
229
- type Item = Result < super :: Info < ' repo > , gix_traverse :: commit :: simple :: Error > ;
338
+ type Item = Result < super :: Info < ' repo > , super :: iter :: Error > ;
230
339
231
340
fn next ( & mut self ) -> Option < Self :: Item > {
232
341
self . inner
0 commit comments