@@ -2,8 +2,19 @@ use gix_date::SecondsSinceUnixEpoch;
2
2
use gix_hash:: ObjectId ;
3
3
use gix_hashtable:: HashSet ;
4
4
use smallvec:: SmallVec ;
5
+ use std:: cmp:: Reverse ;
5
6
use std:: collections:: VecDeque ;
6
7
8
+ #[ derive( Default , Debug , Copy , Clone ) ]
9
+ /// The order with which to prioritize the search
10
+ pub enum CommitTimeOrder {
11
+ #[ default]
12
+ /// sort commits by newest first
13
+ NewestFirst ,
14
+ /// sort commits by oldest first
15
+ OldestFirst ,
16
+ }
17
+
7
18
/// Specify how to sort commits during a [simple](super::Simple) traversal.
8
19
///
9
20
/// ### Sample History
@@ -28,24 +39,27 @@ pub enum Sorting {
28
39
/// as it avoids overlapping branches.
29
40
#[ default]
30
41
BreadthFirst ,
31
- /// Commits are sorted by their commit time in descending order, that is newest first.
42
+ /// Commits are sorted by their commit time in the order specified, either newest or oldest first.
32
43
///
33
44
/// The sorting applies to all currently queued commit ids and thus is full.
34
45
///
35
- /// In the *sample history* the order would be `8, 7, 6, 5, 4, 3, 2, 1`
46
+ /// In the *sample history* the order would be `8, 7, 6, 5, 4, 3, 2, 1` for NewestFirst
47
+ /// Or `1, 2, 3, 4, 5, 6, 7, 8` for OldestFirst
36
48
///
37
49
/// # Performance
38
50
///
39
51
/// This mode benefits greatly from having an object_cache in `find()`
40
52
/// to avoid having to lookup each commit twice.
41
- ByCommitTimeNewestFirst ,
42
- /// This sorting is similar to `ByCommitTimeNewestFirst `, but adds a cutoff to not return commits older than
53
+ ByCommitTime ( CommitTimeOrder ) ,
54
+ /// This sorting is similar to `ByCommitTime `, but adds a cutoff to not return commits older than
43
55
/// a given time, stopping the iteration once no younger commits is queued to be traversed.
44
56
///
45
57
/// As the query is usually repeated with different cutoff dates, this search mode benefits greatly from an object cache.
46
58
///
47
59
/// In the *sample history* and a cut-off date of 4, the returned list of commits would be `8, 7, 6, 4`
48
- ByCommitTimeNewestFirstCutoffOlderThan {
60
+ ByCommitTimeCutoff {
61
+ /// The order in wich to prioritize lookups
62
+ order : CommitTimeOrder ,
49
63
/// The amount of seconds since unix epoch, the same value obtained by any `gix_date::Time` structure and the way git counts time.
50
64
seconds : gix_date:: SecondsSinceUnixEpoch ,
51
65
} ,
@@ -61,27 +75,32 @@ pub enum Error {
61
75
ObjectDecode ( #[ from] gix_object:: decode:: Error ) ,
62
76
}
63
77
78
+ use Result as Either ;
79
+ type QueueKey < T > = Either < T , Reverse < T > > ;
80
+
64
81
/// The state used and potentially shared by multiple graph traversals.
65
82
#[ derive( Clone ) ]
66
83
pub ( super ) struct State {
67
84
next : VecDeque < ObjectId > ,
68
- queue : gix_revwalk:: PriorityQueue < SecondsSinceUnixEpoch , ObjectId > ,
85
+ queue : gix_revwalk:: PriorityQueue < QueueKey < SecondsSinceUnixEpoch > , ObjectId > ,
69
86
buf : Vec < u8 > ,
70
87
seen : HashSet < ObjectId > ,
71
88
parents_buf : Vec < u8 > ,
72
89
parent_ids : SmallVec < [ ( ObjectId , SecondsSinceUnixEpoch ) ; 2 ] > ,
73
90
}
74
91
75
92
///
76
- #[ allow( clippy:: empty_docs) ]
77
93
mod init {
78
94
use gix_date:: SecondsSinceUnixEpoch ;
79
95
use gix_hash:: { oid, ObjectId } ;
80
96
use gix_object:: { CommitRefIter , FindExt } ;
97
+ use std:: cmp:: Reverse ;
98
+ use Err as Oldest ;
99
+ use Ok as Newest ;
81
100
82
101
use super :: {
83
102
super :: { simple:: Sorting , Either , Info , ParentIds , Parents , Simple } ,
84
- collect_parents, Error , State ,
103
+ collect_parents, CommitTimeOrder , Error , State ,
85
104
} ;
86
105
87
106
impl Default for State {
@@ -106,6 +125,14 @@ mod init {
106
125
}
107
126
}
108
127
128
+ fn order_time ( i : i64 , order : CommitTimeOrder ) -> super :: QueueKey < i64 > {
129
+ if let CommitTimeOrder :: NewestFirst = order {
130
+ Newest ( i)
131
+ } else {
132
+ Oldest ( Reverse ( i) )
133
+ }
134
+ }
135
+
109
136
/// Builder
110
137
impl < Find , Predicate > Simple < Find , Predicate >
111
138
where
@@ -118,19 +145,23 @@ mod init {
118
145
Sorting :: BreadthFirst => {
119
146
self . queue_to_vecdeque ( ) ;
120
147
}
121
- Sorting :: ByCommitTimeNewestFirst | Sorting :: ByCommitTimeNewestFirstCutoffOlderThan { .. } => {
148
+ Sorting :: ByCommitTime ( order ) | Sorting :: ByCommitTimeCutoff { order , .. } => {
122
149
let cutoff_time = self . sorting . cutoff_time ( ) ;
123
150
let state = & mut self . state ;
124
151
for commit_id in state. next . drain ( ..) {
125
152
let commit_iter = self . objects . find_commit_iter ( & commit_id, & mut state. buf ) ?;
126
153
let time = commit_iter. committer ( ) ?. time . seconds ;
127
- match cutoff_time {
128
- Some ( cutoff_time) if time >= cutoff_time => {
129
- state. queue . insert ( time, commit_id) ;
154
+ let ordered_time = order_time ( time, order) ;
155
+ match ( cutoff_time, order) {
156
+ ( Some ( cutoff_time) , CommitTimeOrder :: NewestFirst ) if time >= cutoff_time => {
157
+ state. queue . insert ( ordered_time, commit_id) ;
158
+ }
159
+ ( Some ( cutoff_time) , CommitTimeOrder :: OldestFirst ) if time <= cutoff_time => {
160
+ state. queue . insert ( ordered_time, commit_id) ;
130
161
}
131
- Some ( _) => { }
132
- None => {
133
- state. queue . insert ( time , commit_id) ;
162
+ ( Some ( _ ) , _) => { }
163
+ ( None , _ ) => {
164
+ state. queue . insert ( ordered_time , commit_id) ;
134
165
}
135
166
}
136
167
}
@@ -255,10 +286,8 @@ mod init {
255
286
} else {
256
287
match self . sorting {
257
288
Sorting :: BreadthFirst => self . next_by_topology ( ) ,
258
- Sorting :: ByCommitTimeNewestFirst => self . next_by_commit_date ( None ) ,
259
- Sorting :: ByCommitTimeNewestFirstCutoffOlderThan { seconds } => {
260
- self . next_by_commit_date ( seconds. into ( ) )
261
- }
289
+ Sorting :: ByCommitTime ( order) => self . next_by_commit_date ( order, None ) ,
290
+ Sorting :: ByCommitTimeCutoff { seconds, order } => self . next_by_commit_date ( order, seconds. into ( ) ) ,
262
291
}
263
292
}
264
293
}
@@ -268,7 +297,7 @@ mod init {
268
297
/// If not topo sort, provide the cutoff date if present.
269
298
fn cutoff_time ( & self ) -> Option < SecondsSinceUnixEpoch > {
270
299
match self {
271
- Sorting :: ByCommitTimeNewestFirstCutoffOlderThan { seconds } => Some ( * seconds) ,
300
+ Sorting :: ByCommitTimeCutoff { seconds, .. } => Some ( * seconds) ,
272
301
_ => None ,
273
302
}
274
303
}
@@ -282,18 +311,21 @@ mod init {
282
311
{
283
312
fn next_by_commit_date (
284
313
& mut self ,
285
- cutoff_older_than : Option < SecondsSinceUnixEpoch > ,
314
+ order : CommitTimeOrder ,
315
+ cutoff : Option < SecondsSinceUnixEpoch > ,
286
316
) -> Option < Result < Info , Error > > {
287
317
let state = & mut self . state ;
288
318
289
- let ( commit_time, oid) = state. queue . pop ( ) ?;
319
+ let ( commit_time, oid) = match state. queue . pop ( ) ? {
320
+ ( Newest ( t) | Oldest ( Reverse ( t) ) , o) => ( t, o) ,
321
+ } ;
290
322
let mut parents: ParentIds = Default :: default ( ) ;
291
323
match super :: super :: find ( self . cache . as_ref ( ) , & self . objects , & oid, & mut state. buf ) {
292
324
Ok ( Either :: CachedCommit ( commit) ) => {
293
325
if !collect_parents ( & mut state. parent_ids , self . cache . as_ref ( ) , commit. iter_parents ( ) ) {
294
326
// drop corrupt caches and try again with ODB
295
327
self . cache = None ;
296
- return self . next_by_commit_date ( cutoff_older_than ) ;
328
+ return self . next_by_commit_date ( order , cutoff ) ;
297
329
}
298
330
for ( id, parent_commit_time) in state. parent_ids . drain ( ..) {
299
331
parents. push ( id) ;
@@ -302,9 +334,19 @@ mod init {
302
334
continue ;
303
335
}
304
336
305
- match cutoff_older_than {
306
- Some ( cutoff_older_than) if parent_commit_time < cutoff_older_than => continue ,
307
- Some ( _) | None => state. queue . insert ( parent_commit_time, id) ,
337
+ let time = order_time ( parent_commit_time, order) ;
338
+ match ( cutoff, order) {
339
+ ( Some ( cutoff_older_than) , CommitTimeOrder :: NewestFirst )
340
+ if parent_commit_time < cutoff_older_than =>
341
+ {
342
+ continue
343
+ }
344
+ ( Some ( cutoff_newer_than) , CommitTimeOrder :: OldestFirst )
345
+ if parent_commit_time > cutoff_newer_than =>
346
+ {
347
+ continue
348
+ }
349
+ ( Some ( _) | None , _) => state. queue . insert ( time, id) ,
308
350
}
309
351
}
310
352
}
@@ -324,9 +366,19 @@ mod init {
324
366
. and_then ( |parent| parent. committer ( ) . ok ( ) . map ( |committer| committer. time . seconds ) )
325
367
. unwrap_or_default ( ) ;
326
368
327
- match cutoff_older_than {
328
- Some ( cutoff_older_than) if parent_commit_time < cutoff_older_than => continue ,
329
- Some ( _) | None => state. queue . insert ( parent_commit_time, id) ,
369
+ let time = order_time ( parent_commit_time, order) ;
370
+ match ( cutoff, order) {
371
+ ( Some ( cutoff_older_than) , CommitTimeOrder :: NewestFirst )
372
+ if parent_commit_time < cutoff_older_than =>
373
+ {
374
+ continue
375
+ }
376
+ ( Some ( cutoff_newer_than) , CommitTimeOrder :: OldestFirst )
377
+ if parent_commit_time > cutoff_newer_than =>
378
+ {
379
+ continue
380
+ }
381
+ ( Some ( _) | None , _) => state. queue . insert ( time, id) ,
330
382
}
331
383
}
332
384
Ok ( _unused_token) => break ,
0 commit comments