@@ -20,15 +20,36 @@ pub enum Parents {
20
20
}
21
21
22
22
/// Specify how to sort commits during traversal.
23
+ ///
24
+ /// ### Sample History
25
+ ///
26
+ /// The following history will be referred to for explaining how the sort order works, with the number denoting the commit timestamp
27
+ /// (*their X-alignment doesn't matter*).
28
+ ///
29
+ /// ```text
30
+ /// ---1----2----4----7 <- second parent of 8
31
+ /// \ \
32
+ /// 3----5----6----8---
33
+ /// ```
34
+
23
35
#[ derive( Default , Debug , Copy , Clone ) ]
24
36
pub enum Sorting {
25
37
/// Commits are sorted as they are mentioned in the commit graph.
38
+ ///
39
+ /// In the *sample history* the order would be `8, 6, 7, 5, 4, 3, 2, 1`
40
+ ///
41
+ /// ### Note
42
+ ///
43
+ /// This is not to be confused with `git log/rev-list --topo-order`, which is notably different from
44
+ /// as it avoids overlapping branches.
26
45
#[ default]
27
- Topological ,
46
+ BreadthFirst ,
28
47
/// Commits are sorted by their commit time in descending order, that is newest first.
29
48
///
30
49
/// The sorting applies to all currently queued commit ids and thus is full.
31
50
///
51
+ /// In the *sample history* the order would be `8, 7, 6, 5, 4, 3, 2, 1`
52
+ ///
32
53
/// # Performance
33
54
///
34
55
/// This mode benefits greatly from having an object_cache in `find()`
@@ -38,6 +59,8 @@ pub enum Sorting {
38
59
/// a given time, stopping the iteration once no younger commits is queued to be traversed.
39
60
///
40
61
/// As the query is usually repeated with different cutoff dates, this search mode benefits greatly from an object cache.
62
+ ///
63
+ /// In the *sample history* and a cut-off date of 4, the returned list of commits would be `8, 7, 6, 4`
41
64
ByCommitTimeNewestFirstCutoffOlderThan {
42
65
/// The amount of seconds since unix epoch, the same value obtained by any `gix_date::Time` structure and the way git counts time.
43
66
time_in_seconds_since_epoch : u32 ,
@@ -53,7 +76,7 @@ pub struct Info {
53
76
pub id : gix_hash:: ObjectId ,
54
77
/// All parent ids we have encountered. Note that these will be at most one if [`Parents::First`] is enabled.
55
78
pub parent_ids : ParentIds ,
56
- /// The time at which the commit was created. It's only `Some(_)` if sorting is not [`Sorting::Topological `], as the walk
79
+ /// The time at which the commit was created. It's only `Some(_)` if sorting is not [`Sorting::BreadthFirst `], as the walk
57
80
/// needs to require the commit-date.
58
81
pub commit_time : Option < u64 > ,
59
82
}
@@ -63,7 +86,6 @@ pub mod ancestors {
63
86
use std:: {
64
87
borrow:: { Borrow , BorrowMut } ,
65
88
collections:: VecDeque ,
66
- iter:: FromIterator ,
67
89
} ;
68
90
69
91
use gix_hash:: { oid, ObjectId } ;
@@ -88,31 +110,36 @@ pub mod ancestors {
88
110
type TimeInSeconds = u32 ;
89
111
90
112
/// The state used and potentially shared by multiple graph traversals.
91
- #[ derive( Default , Clone ) ]
113
+ #[ derive( Clone ) ]
92
114
pub struct State {
93
- next : VecDeque < ( ObjectId , TimeInSeconds ) > ,
115
+ next : VecDeque < ObjectId > ,
116
+ queue : gix_revwalk:: PriorityQueue < TimeInSeconds , ObjectId > ,
94
117
buf : Vec < u8 > ,
95
118
seen : HashSet < ObjectId > ,
96
119
parents_buf : Vec < u8 > ,
97
120
}
98
121
122
+ impl Default for State {
123
+ fn default ( ) -> Self {
124
+ State {
125
+ next : Default :: default ( ) ,
126
+ queue : gix_revwalk:: PriorityQueue :: new ( ) ,
127
+ buf : vec ! [ ] ,
128
+ seen : Default :: default ( ) ,
129
+ parents_buf : vec ! [ ] ,
130
+ }
131
+ }
132
+ }
133
+
99
134
impl State {
100
135
fn clear ( & mut self ) {
101
136
self . next . clear ( ) ;
137
+ self . queue . clear ( ) ;
102
138
self . buf . clear ( ) ;
103
139
self . seen . clear ( ) ;
104
140
}
105
141
}
106
142
107
- /// Builder
108
- impl < Find , Predicate , StateMut > Ancestors < Find , Predicate , StateMut > {
109
- /// Change our commit parent handling mode to the given one.
110
- pub fn parents ( mut self , mode : Parents ) -> Self {
111
- self . parents = mode;
112
- self
113
- }
114
- }
115
-
116
143
/// Builder
117
144
impl < Find , Predicate , StateMut , E > Ancestors < Find , Predicate , StateMut >
118
145
where
@@ -123,32 +150,52 @@ pub mod ancestors {
123
150
/// Set the sorting method, either topological or by author date
124
151
pub fn sorting ( mut self , sorting : Sorting ) -> Result < Self , Error > {
125
152
self . sorting = sorting;
126
- if !matches ! ( self . sorting, Sorting :: Topological ) {
127
- let mut cutoff_time_storage = self . sorting . cutoff_time ( ) . map ( |cot| ( cot, Vec :: new ( ) ) ) ;
128
- let state = self . state . borrow_mut ( ) ;
129
- for ( commit_id, commit_time) in & mut state. next {
130
- let commit_iter = ( self . find ) ( commit_id, & mut state. buf ) . map_err ( |err| Error :: FindExisting {
131
- oid : * commit_id,
132
- source : err. into ( ) ,
133
- } ) ?;
134
- let time = commit_iter. committer ( ) ?. time . seconds_since_unix_epoch ;
135
- match & mut cutoff_time_storage {
136
- Some ( ( cutoff_time, storage) ) if time >= * cutoff_time => {
137
- storage. push ( ( * commit_id, time) ) ;
153
+ match self . sorting {
154
+ Sorting :: BreadthFirst => {
155
+ self . queue_to_vecdeque ( ) ;
156
+ }
157
+ Sorting :: ByCommitTimeNewestFirst | Sorting :: ByCommitTimeNewestFirstCutoffOlderThan { .. } => {
158
+ let cutoff_time = self . sorting . cutoff_time ( ) ;
159
+ let state = self . state . borrow_mut ( ) ;
160
+ for commit_id in state. next . drain ( ..) {
161
+ let commit_iter =
162
+ ( self . find ) ( & commit_id, & mut state. buf ) . map_err ( |err| Error :: FindExisting {
163
+ oid : commit_id,
164
+ source : err. into ( ) ,
165
+ } ) ?;
166
+ let time = commit_iter. committer ( ) ?. time . seconds_since_unix_epoch ;
167
+ match cutoff_time {
168
+ Some ( cutoff_time) if time >= cutoff_time => {
169
+ state. queue . insert ( time, commit_id) ;
170
+ }
171
+ Some ( _) => { }
172
+ None => {
173
+ state. queue . insert ( time, commit_id) ;
174
+ }
138
175
}
139
- Some ( _) => { }
140
- None => * commit_time = time,
141
176
}
142
177
}
143
- let mut v = match cutoff_time_storage {
144
- Some ( ( _, storage) ) => storage,
145
- None => Vec :: from_iter ( std:: mem:: take ( & mut state. next ) . into_iter ( ) ) ,
146
- } ;
147
- v. sort_by ( |a, b| a. 1 . cmp ( & b. 1 ) . reverse ( ) ) ;
148
- state. next = v. into ( ) ;
149
178
}
150
179
Ok ( self )
151
180
}
181
+
182
+ /// Change our commit parent handling mode to the given one.
183
+ pub fn parents ( mut self , mode : Parents ) -> Self {
184
+ self . parents = mode;
185
+ if matches ! ( self . parents, Parents :: First ) {
186
+ self . queue_to_vecdeque ( ) ;
187
+ }
188
+ self
189
+ }
190
+
191
+ fn queue_to_vecdeque ( & mut self ) {
192
+ let state = self . state . borrow_mut ( ) ;
193
+ state. next . extend (
194
+ std:: mem:: replace ( & mut state. queue , gix_revwalk:: PriorityQueue :: new ( ) )
195
+ . into_iter_unordered ( )
196
+ . map ( |( _time, id) | id) ,
197
+ ) ;
198
+ }
152
199
}
153
200
154
201
/// Initialization
@@ -207,7 +254,7 @@ pub mod ancestors {
207
254
for tip in tips. map ( Into :: into) {
208
255
let was_inserted = state. seen . insert ( tip) ;
209
256
if was_inserted && predicate ( & tip) {
210
- state. next . push_back ( ( tip, 0 ) ) ;
257
+ state. next . push_back ( tip) ;
211
258
}
212
259
}
213
260
}
@@ -245,7 +292,7 @@ pub mod ancestors {
245
292
self . next_by_topology ( )
246
293
} else {
247
294
match self . sorting {
248
- Sorting :: Topological => self . next_by_topology ( ) ,
295
+ Sorting :: BreadthFirst => self . next_by_topology ( ) ,
249
296
Sorting :: ByCommitTimeNewestFirst => self . next_by_commit_date ( None ) ,
250
297
Sorting :: ByCommitTimeNewestFirstCutoffOlderThan {
251
298
time_in_seconds_since_epoch,
@@ -278,7 +325,7 @@ pub mod ancestors {
278
325
fn next_by_commit_date ( & mut self , cutoff_older_than : Option < TimeInSeconds > ) -> Option < Result < Info , Error > > {
279
326
let state = self . state . borrow_mut ( ) ;
280
327
281
- let ( oid , commit_time ) = state. next . pop_front ( ) ?;
328
+ let ( commit_time , oid ) = state. queue . pop ( ) ?;
282
329
let mut parents: ParentIds = Default :: default ( ) ;
283
330
match ( self . find ) ( & oid, & mut state. buf ) {
284
331
Ok ( commit_iter) => {
@@ -309,17 +356,9 @@ pub mod ancestors {
309
356
} )
310
357
. unwrap_or_default ( ) ;
311
358
312
- let pos = match state. next . binary_search_by ( |c| c. 1 . cmp ( & parent_commit_time) . reverse ( ) )
313
- {
314
- Ok ( _) => None ,
315
- Err ( pos) => Some ( pos) ,
316
- } ;
317
359
match cutoff_older_than {
318
360
Some ( cutoff_older_than) if parent_commit_time < cutoff_older_than => continue ,
319
- Some ( _) | None => match pos {
320
- Some ( pos) => state. next . insert ( pos, ( id, parent_commit_time) ) ,
321
- None => state. next . push_back ( ( id, parent_commit_time) ) ,
322
- } ,
361
+ Some ( _) | None => state. queue . insert ( parent_commit_time, id) ,
323
362
}
324
363
325
364
if is_first && matches ! ( self . parents, Parents :: First ) {
@@ -356,7 +395,7 @@ pub mod ancestors {
356
395
{
357
396
fn next_by_topology ( & mut self ) -> Option < Result < Info , Error > > {
358
397
let state = self . state . borrow_mut ( ) ;
359
- let ( oid, _commit_time ) = state. next . pop_front ( ) ?;
398
+ let oid = state. next . pop_front ( ) ?;
360
399
let mut parents: ParentIds = Default :: default ( ) ;
361
400
match ( self . find ) ( & oid, & mut state. buf ) {
362
401
Ok ( commit_iter) => {
@@ -367,7 +406,7 @@ pub mod ancestors {
367
406
parents. push ( id) ;
368
407
let was_inserted = state. seen . insert ( id) ;
369
408
if was_inserted && ( self . predicate ) ( & id) {
370
- state. next . push_back ( ( id , 0 ) ) ;
409
+ state. next . push_back ( id ) ;
371
410
}
372
411
if matches ! ( self . parents, Parents :: First ) {
373
412
break ;
0 commit comments