@@ -78,13 +78,13 @@ pub enum Error {
78
78
use Result as Either ;
79
79
80
80
type QueueKey < T > = Either < T , Reverse < T > > ;
81
- type CommitDateQueue = gix_revwalk:: PriorityQueue < QueueKey < SecondsSinceUnixEpoch > , ObjectId > ;
81
+ type CommitDateQueue = gix_revwalk:: PriorityQueue < QueueKey < SecondsSinceUnixEpoch > , ( ObjectId , CommitState ) > ;
82
82
type Candidates = VecDeque < crate :: commit:: Info > ;
83
83
84
84
/// The state used and potentially shared by multiple graph traversals.
85
85
#[ derive( Clone ) ]
86
86
pub ( super ) struct State {
87
- next : VecDeque < ObjectId > ,
87
+ next : VecDeque < ( ObjectId , CommitState ) > ,
88
88
queue : CommitDateQueue ,
89
89
buf : Vec < u8 > ,
90
90
seen : gix_revwalk:: graph:: IdMap < CommitState > ,
@@ -184,9 +184,10 @@ mod init {
184
184
}
185
185
Sorting :: ByCommitTime ( order) | Sorting :: ByCommitTimeCutoff { order, .. } => {
186
186
let state = & mut self . state ;
187
- for commit_id in state. next . drain ( ..) {
187
+ for ( commit_id, commit_state ) in state. next . drain ( ..) {
188
188
add_to_queue (
189
189
commit_id,
190
+ commit_state,
190
191
order,
191
192
sorting. cutoff_time ( ) ,
192
193
& mut state. queue ,
@@ -226,11 +227,12 @@ mod init {
226
227
// Assure we *start* traversing hidden variants of a commit first, give them a head-start.
227
228
match self . sorting {
228
229
Sorting :: BreadthFirst => {
229
- state. next . push_front ( id_to_ignore) ;
230
+ state. next . push_front ( ( id_to_ignore, CommitState :: Hidden ) ) ;
230
231
}
231
232
Sorting :: ByCommitTime ( order) | Sorting :: ByCommitTimeCutoff { order, .. } => {
232
233
add_to_queue (
233
234
id_to_ignore,
235
+ CommitState :: Hidden ,
234
236
order,
235
237
self . sorting . cutoff_time ( ) ,
236
238
& mut state. queue ,
@@ -273,6 +275,7 @@ mod init {
273
275
274
276
fn add_to_queue (
275
277
commit_id : ObjectId ,
278
+ commit_state : CommitState ,
276
279
order : CommitTimeOrder ,
277
280
cutoff_time : Option < SecondsSinceUnixEpoch > ,
278
281
queue : & mut CommitDateQueue ,
@@ -284,11 +287,11 @@ mod init {
284
287
let key = to_queue_key ( time, order) ;
285
288
match ( cutoff_time, order) {
286
289
( Some ( cutoff_time) , _) if time >= cutoff_time => {
287
- queue. insert ( key, commit_id) ;
290
+ queue. insert ( key, ( commit_id, commit_state ) ) ;
288
291
}
289
292
( Some ( _) , _) => { }
290
293
( None , _) => {
291
- queue. insert ( key, commit_id) ;
294
+ queue. insert ( key, ( commit_id, commit_state ) ) ;
292
295
}
293
296
}
294
297
Ok ( ( ) )
@@ -339,10 +342,11 @@ mod init {
339
342
state. clear ( ) ;
340
343
state. next . reserve ( tips. size_hint ( ) . 0 ) ;
341
344
for tip in tips. map ( Into :: into) {
342
- let seen = state. seen . insert ( tip, CommitState :: Interesting ) ;
345
+ let commit_state = CommitState :: Interesting ;
346
+ let seen = state. seen . insert ( tip, commit_state) ;
343
347
// We know there can only be duplicate interesting ones.
344
348
if seen. is_none ( ) && predicate ( & tip) {
345
- state. next . push_back ( tip) ;
349
+ state. next . push_back ( ( tip, commit_state ) ) ;
346
350
}
347
351
}
348
352
}
@@ -418,16 +422,23 @@ mod init {
418
422
cutoff : Option < SecondsSinceUnixEpoch > ,
419
423
) -> Option < Result < Info , Error > > {
420
424
let state = & mut self . state ;
425
+ let next = & mut state. queue ;
421
426
422
427
' skip_hidden: loop {
423
- let ( commit_time, oid) = match state . queue . pop ( ) ? {
428
+ let ( commit_time, ( oid, _queued_commit_state ) ) = match next . pop ( ) ? {
424
429
( Newest ( t) | Oldest ( Reverse ( t) ) , o) => ( t, o) ,
425
430
} ;
426
431
let mut parents: ParentIds = Default :: default ( ) ;
427
- // TODO(perf): can avoid this lookup by storing state on `queue` respectively.
428
- // ALSO: need to look ahead for early aborts, i.e. if there is only hidden left to traverse.
429
- // Maybe this can be counted?
432
+
433
+ // Always use the state that is actually stored, as we may change the type as we go.
430
434
let commit_state = * state. seen . get ( & oid) . expect ( "every commit we traverse has state added" ) ;
435
+ if can_deplete_candidates_early (
436
+ next. iter_unordered ( ) . map ( |t| t. 1 ) ,
437
+ commit_state,
438
+ state. candidates . as_ref ( ) ,
439
+ ) {
440
+ return None ;
441
+ }
431
442
match super :: super :: find ( self . cache . as_ref ( ) , & self . objects , & oid, & mut state. buf ) {
432
443
Ok ( Either :: CachedCommit ( commit) ) => {
433
444
if !collect_parents ( & mut state. parent_ids , self . cache . as_ref ( ) , commit. iter_parents ( ) ) {
@@ -443,7 +454,7 @@ mod init {
443
454
& mut state. candidates ,
444
455
commit_state,
445
456
& mut self . predicate ,
446
- & mut state . queue ,
457
+ next ,
447
458
order,
448
459
cutoff,
449
460
|| parent_commit_time,
@@ -462,7 +473,7 @@ mod init {
462
473
& mut state. candidates ,
463
474
commit_state,
464
475
& mut self . predicate ,
465
- & mut state . queue ,
476
+ next ,
466
477
order,
467
478
cutoff,
468
479
|| {
@@ -505,6 +516,28 @@ mod init {
505
516
}
506
517
}
507
518
519
+ /// Returns `true` if we have only hidden cursors queued for traversal, assuming that we don't see interesting ones ever again.
520
+ ///
521
+ /// `unqueued_commit_state` is the state of the commit that is currently being processed.
522
+ fn can_deplete_candidates_early (
523
+ mut queued_states : impl Iterator < Item = CommitState > ,
524
+ unqueued_commit_state : CommitState ,
525
+ candidates : Option < & Candidates > ,
526
+ ) -> bool {
527
+ if candidates. is_none ( ) {
528
+ return false ;
529
+ }
530
+ if unqueued_commit_state. is_interesting ( ) {
531
+ return false ;
532
+ }
533
+
534
+ let mut is_empty = true ;
535
+ queued_states. all ( |state| {
536
+ is_empty = false ;
537
+ state. is_hidden ( )
538
+ } ) && !is_empty
539
+ }
540
+
508
541
/// Utilities
509
542
impl < Find , Predicate > Simple < Find , Predicate >
510
543
where
@@ -513,14 +546,16 @@ mod init {
513
546
{
514
547
fn next_by_topology ( & mut self ) -> Option < Result < Info , Error > > {
515
548
let state = & mut self . state ;
549
+ let next = & mut state. next ;
516
550
' skip_hidden: loop {
517
- let oid = state . next . pop_front ( ) ?;
551
+ let ( oid, _queued_commit_state ) = next. pop_front ( ) ?;
518
552
let mut parents: ParentIds = Default :: default ( ) ;
519
- // TODO(perf): can avoid this lookup by storing state on `next` respectively.
520
- // ALSO: need to look ahead for early aborts, i.e. if there is only hidden left to traverse.
521
- // Maybe this can be counted?
522
- let commit_state = * state. seen . get ( & oid) . expect ( "every commit we traverse has state added" ) ;
523
553
554
+ // Always use the state that is actually stored, as we may change the type as we go.
555
+ let commit_state = * state. seen . get ( & oid) . expect ( "every commit we traverse has state added" ) ;
556
+ if can_deplete_candidates_early ( next. iter ( ) . map ( |t| t. 1 ) , commit_state, state. candidates . as_ref ( ) ) {
557
+ return None ;
558
+ }
524
559
match super :: super :: find ( self . cache . as_ref ( ) , & self . objects , & oid, & mut state. buf ) {
525
560
Ok ( Either :: CachedCommit ( commit) ) => {
526
561
if !collect_parents ( & mut state. parent_ids , self . cache . as_ref ( ) , commit. iter_parents ( ) ) {
@@ -537,7 +572,7 @@ mod init {
537
572
& mut state. candidates ,
538
573
commit_state,
539
574
& mut self . predicate ,
540
- & mut state . next ,
575
+ next,
541
576
) ;
542
577
if commit_state. is_interesting ( ) && matches ! ( self . parents, Parents :: First ) {
543
578
break ;
@@ -556,7 +591,7 @@ mod init {
556
591
& mut state. candidates ,
557
592
commit_state,
558
593
& mut self . predicate ,
559
- & mut state . next ,
594
+ next,
560
595
) ;
561
596
if commit_state. is_interesting ( ) && matches ! ( self . parents, Parents :: First ) {
562
597
break ;
@@ -608,7 +643,7 @@ mod init {
608
643
candidates : & mut Option < Candidates > ,
609
644
commit_state : CommitState ,
610
645
predicate : & mut impl FnMut ( & oid ) -> bool ,
611
- next : & mut VecDeque < ObjectId > ,
646
+ next : & mut VecDeque < ( ObjectId , CommitState ) > ,
612
647
) {
613
648
let enqueue = match seen. entry ( parent_id) {
614
649
Entry :: Occupied ( mut e) => {
@@ -627,7 +662,7 @@ mod init {
627
662
}
628
663
} ;
629
664
if enqueue {
630
- next. push_back ( parent_id) ;
665
+ next. push_back ( ( parent_id, commit_state ) ) ;
631
666
}
632
667
}
633
668
@@ -665,7 +700,7 @@ mod init {
665
700
let key = to_queue_key ( parent_commit_time, order) ;
666
701
match cutoff {
667
702
Some ( cutoff_older_than) if parent_commit_time < cutoff_older_than => { }
668
- Some ( _) | None => queue. insert ( key, parent_id) ,
703
+ Some ( _) | None => queue. insert ( key, ( parent_id, commit_state ) ) ,
669
704
}
670
705
}
671
706
}
0 commit comments