@@ -2,8 +2,20 @@ use gix_date::SecondsSinceUnixEpoch;
22use  gix_hash:: ObjectId ; 
33use  gix_hashtable:: HashSet ; 
44use  smallvec:: SmallVec ; 
5+ use  std:: cmp:: Reverse ; 
56use  std:: collections:: VecDeque ; 
67
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+      #[ doc( alias = "Sort::REVERSE" ,  alias = "git2" ) ]  
16+     OldestFirst , 
17+ } 
18+ 
719/// Specify how to sort commits during a [simple](super::Simple) traversal. 
820/// 
921/// ### Sample History 
@@ -20,32 +32,35 @@ use std::collections::VecDeque;
2032pub  enum  Sorting  { 
2133    /// Commits are sorted as they are mentioned in the commit graph. 
2234     /// 
23-      /// In the *sample history* the order would be `8, 6, 7, 5, 4, 3, 2, 1` 
35+      /// In the *sample history* the order would be `8, 6, 7, 5, 4, 3, 2, 1`.  
2436     /// 
2537     /// ### Note 
2638     /// 
2739     /// This is not to be confused with `git log/rev-list --topo-order`, which is notably different from 
2840     /// as it avoids overlapping branches. 
2941     #[ default]  
3042    BreadthFirst , 
31-     /// Commits are sorted by their commit time in descending  order, that is  newest first. 
43+     /// Commits are sorted by their commit time in the  order specified, either  newest or oldest  first. 
3244     /// 
3345     /// The sorting applies to all currently queued commit ids and thus is full. 
3446     /// 
35-      /// In the *sample history* the order would be `8, 7, 6, 5, 4, 3, 2, 1` 
47+      /// In the *sample history* the order would be `8, 7, 6, 5, 4, 3, 2, 1` for [`NewestFirst`](CommitTimeOrder::NewestFirst), 
48+      /// or `1, 2, 3, 4, 5, 6, 7, 8` for [`OldestFirst`](CommitTimeOrder::OldestFirst). 
3649     /// 
3750     /// # Performance 
3851     /// 
3952     /// This mode benefits greatly from having an object_cache in `find()` 
4053     /// 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 
54+      ByCommitTime ( CommitTimeOrder ) , 
55+     /// This sorting is similar to [`ByCommitTime`](Sorting::ByCommitTime) , but adds a cutoff to not return commits older than 
4356     /// a given time, stopping the iteration once no younger commits is queued to be traversed. 
4457     /// 
4558     /// As the query is usually repeated with different cutoff dates, this search mode benefits greatly from an object cache. 
4659     /// 
47-      /// 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+      /// In the *sample history* and a cut-off date of 4, the returned list of commits would be `8, 7, 6, 4`. 
61+      ByCommitTimeCutoff  { 
62+         /// The order in which to prioritize lookups. 
63+          order :  CommitTimeOrder , 
4964        /// The amount of seconds since unix epoch, the same value obtained by any `gix_date::Time` structure and the way git counts time. 
5065         seconds :  gix_date:: SecondsSinceUnixEpoch , 
5166    } , 
@@ -61,11 +76,14 @@ pub enum Error {
6176    ObjectDecode ( #[ from]   gix_object:: decode:: Error ) , 
6277} 
6378
79+ use  Result  as  Either ; 
80+ type  QueueKey < T >  = Either < T ,  Reverse < T > > ; 
81+ 
6482/// The state used and potentially shared by multiple graph traversals. 
6583#[ derive( Clone ) ]  
6684pub ( super )  struct  State  { 
6785    next :  VecDeque < ObjectId > , 
68-     queue :  gix_revwalk:: PriorityQueue < SecondsSinceUnixEpoch ,  ObjectId > , 
86+     queue :  gix_revwalk:: PriorityQueue < QueueKey < SecondsSinceUnixEpoch > ,  ObjectId > , 
6987    buf :  Vec < u8 > , 
7088    seen :  HashSet < ObjectId > , 
7189    parents_buf :  Vec < u8 > , 
@@ -77,10 +95,13 @@ mod init {
7795    use  gix_date:: SecondsSinceUnixEpoch ; 
7896    use  gix_hash:: { oid,  ObjectId } ; 
7997    use  gix_object:: { CommitRefIter ,  FindExt } ; 
98+     use  std:: cmp:: Reverse ; 
99+     use  Err  as  Oldest ; 
100+     use  Ok  as  Newest ; 
80101
81102    use  super :: { 
82103        super :: { simple:: Sorting ,  Either ,  Info ,  ParentIds ,  Parents ,  Simple } , 
83-         collect_parents,  Error ,  State , 
104+         collect_parents,  CommitTimeOrder ,   Error ,  State , 
84105    } ; 
85106
86107    impl  Default  for  State  { 
@@ -105,6 +126,13 @@ mod init {
105126        } 
106127    } 
107128
129+     fn  to_queue_key ( i :  i64 ,  order :  CommitTimeOrder )  -> super :: QueueKey < i64 >  { 
130+         match  order { 
131+             CommitTimeOrder :: NewestFirst  => Newest ( i) , 
132+             CommitTimeOrder :: OldestFirst  => Oldest ( Reverse ( i) ) , 
133+         } 
134+     } 
135+ 
108136    /// Builder 
109137     impl < Find ,  Predicate >  Simple < Find ,  Predicate > 
110138    where 
@@ -117,19 +145,20 @@ mod init {
117145                Sorting :: BreadthFirst  => { 
118146                    self . queue_to_vecdeque ( ) ; 
119147                } 
120-                 Sorting :: ByCommitTimeNewestFirst  | Sorting :: ByCommitTimeNewestFirstCutoffOlderThan   {  .. }  => { 
148+                 Sorting :: ByCommitTime ( order )  | Sorting :: ByCommitTimeCutoff   {  order ,  .. }  => { 
121149                    let  cutoff_time = self . sorting . cutoff_time ( ) ; 
122150                    let  state = & mut  self . state ; 
123151                    for  commit_id in  state. next . drain ( ..)  { 
124152                        let  commit_iter = self . objects . find_commit_iter ( & commit_id,  & mut  state. buf ) ?; 
125153                        let  time = commit_iter. committer ( ) ?. time . seconds ; 
126-                         match  cutoff_time { 
127-                             Some ( cutoff_time)  if  time >= cutoff_time => { 
128-                                 state. queue . insert ( time,  commit_id) ; 
154+                         let  key = to_queue_key ( time,  order) ; 
155+                         match  ( cutoff_time,  order)  { 
156+                             ( Some ( cutoff_time) ,  _)  if  time >= cutoff_time => { 
157+                                 state. queue . insert ( key,  commit_id) ; 
129158                            } 
130-                             Some ( _)  => { } 
131-                             None  => { 
132-                                 state. queue . insert ( time ,  commit_id) ; 
159+                             ( Some ( _ ) ,   _)  => { } 
160+                             ( None ,  _ )  => { 
161+                                 state. queue . insert ( key ,  commit_id) ; 
133162                            } 
134163                        } 
135164                    } 
@@ -254,10 +283,8 @@ mod init {
254283            }  else  { 
255284                match  self . sorting  { 
256285                    Sorting :: BreadthFirst  => self . next_by_topology ( ) , 
257-                     Sorting :: ByCommitTimeNewestFirst  => self . next_by_commit_date ( None ) , 
258-                     Sorting :: ByCommitTimeNewestFirstCutoffOlderThan  {  seconds }  => { 
259-                         self . next_by_commit_date ( seconds. into ( ) ) 
260-                     } 
286+                     Sorting :: ByCommitTime ( order)  => self . next_by_commit_date ( order,  None ) , 
287+                     Sorting :: ByCommitTimeCutoff  {  seconds,  order }  => self . next_by_commit_date ( order,  seconds. into ( ) ) , 
261288                } 
262289            } 
263290        } 
@@ -267,7 +294,7 @@ mod init {
267294        /// If not topo sort, provide the cutoff date if present. 
268295         fn  cutoff_time ( & self )  -> Option < SecondsSinceUnixEpoch >  { 
269296            match  self  { 
270-                 Sorting :: ByCommitTimeNewestFirstCutoffOlderThan  {  seconds }  => Some ( * seconds) , 
297+                 Sorting :: ByCommitTimeCutoff  {  seconds,  ..  }  => Some ( * seconds) , 
271298                _ => None , 
272299            } 
273300        } 
@@ -281,18 +308,21 @@ mod init {
281308    { 
282309        fn  next_by_commit_date ( 
283310            & mut  self , 
284-             cutoff_older_than :  Option < SecondsSinceUnixEpoch > , 
311+             order :  CommitTimeOrder , 
312+             cutoff :  Option < SecondsSinceUnixEpoch > , 
285313        )  -> Option < Result < Info ,  Error > >  { 
286314            let  state = & mut  self . state ; 
287315
288-             let  ( commit_time,  oid)  = state. queue . pop ( ) ?; 
316+             let  ( commit_time,  oid)  = match  state. queue . pop ( ) ? { 
317+                 ( Newest ( t)  | Oldest ( Reverse ( t) ) ,  o)  => ( t,  o) , 
318+             } ; 
289319            let  mut  parents:  ParentIds  = Default :: default ( ) ; 
290320            match  super :: super :: find ( self . cache . as_ref ( ) ,  & self . objects ,  & oid,  & mut  state. buf )  { 
291321                Ok ( Either :: CachedCommit ( commit) )  => { 
292322                    if  !collect_parents ( & mut  state. parent_ids ,  self . cache . as_ref ( ) ,  commit. iter_parents ( ) )  { 
293323                        // drop corrupt caches and try again with ODB 
294324                        self . cache  = None ; 
295-                         return  self . next_by_commit_date ( cutoff_older_than ) ; 
325+                         return  self . next_by_commit_date ( order ,  cutoff ) ; 
296326                    } 
297327                    for  ( id,  parent_commit_time)  in  state. parent_ids . drain ( ..)  { 
298328                        parents. push ( id) ; 
@@ -301,9 +331,10 @@ mod init {
301331                            continue ; 
302332                        } 
303333
304-                         match  cutoff_older_than { 
334+                         let  key = to_queue_key ( parent_commit_time,  order) ; 
335+                         match  cutoff { 
305336                            Some ( cutoff_older_than)  if  parent_commit_time < cutoff_older_than => continue , 
306-                             Some ( _)  | None  => state. queue . insert ( parent_commit_time ,  id) , 
337+                             Some ( _)  | None  => state. queue . insert ( key ,  id) , 
307338                        } 
308339                    } 
309340                } 
@@ -323,9 +354,10 @@ mod init {
323354                                    . and_then ( |parent| parent. committer ( ) . ok ( ) . map ( |committer| committer. time . seconds ) ) 
324355                                    . unwrap_or_default ( ) ; 
325356
326-                                 match  cutoff_older_than { 
357+                                 let  time = to_queue_key ( parent_commit_time,  order) ; 
358+                                 match  cutoff { 
327359                                    Some ( cutoff_older_than)  if  parent_commit_time < cutoff_older_than => continue , 
328-                                     Some ( _)  | None  => state. queue . insert ( parent_commit_time ,  id) , 
360+                                     Some ( _)  | None  => state. queue . insert ( time ,  id) , 
329361                                } 
330362                            } 
331363                            Ok ( _unused_token)  => break , 
0 commit comments