@@ -9,9 +9,20 @@ use std::sync::{LazyLock, RwLock};
99pub ( crate ) const CACHE_VERSION : u64 = 24 ;
1010
1111pub trait CacheBackend : Send + Sync {
12- fn read ( & self , filter : filter:: Filter , from : git2:: Oid ) -> JoshResult < Option < git2:: Oid > > ;
13-
14- fn write ( & self , filter : filter:: Filter , from : git2:: Oid , to : git2:: Oid ) -> JoshResult < ( ) > ;
12+ fn read (
13+ & self ,
14+ filter : filter:: Filter ,
15+ from : git2:: Oid ,
16+ sequence_number : u128 ,
17+ ) -> JoshResult < Option < git2:: Oid > > ;
18+
19+ fn write (
20+ & self ,
21+ filter : filter:: Filter ,
22+ from : git2:: Oid ,
23+ to : git2:: Oid ,
24+ sequence_number : u128 ,
25+ ) -> JoshResult < ( ) > ;
1526}
1627
1728pub trait FilterHook {
@@ -323,6 +334,11 @@ impl Transaction {
323334 }
324335
325336 pub fn insert ( & self , filter : filter:: Filter , from : git2:: Oid , to : git2:: Oid , store : bool ) {
337+ let sequence_number = if filter != filter:: sequence_number ( ) {
338+ compute_sequence_number ( self , from) . expect ( "compute_sequence_number failed" )
339+ } else {
340+ 0
341+ } ;
326342 let mut t2 = self . t2 . borrow_mut ( ) ;
327343 t2. commit_map
328344 . entry ( filter. id ( ) )
@@ -334,14 +350,13 @@ impl Transaction {
334350 // the history length by a very large factor.
335351 if store || from. as_bytes ( ) [ 0 ] == 0 {
336352 t2. cache
337- . write_all ( filter, from, to)
353+ . write_all ( filter, from, to, sequence_number )
338354 . expect ( "Failed to write cache" ) ;
339355 }
340356 }
341357
342358 pub fn get_missing ( & self ) -> Vec < ( filter:: Filter , git2:: Oid ) > {
343359 let mut missing = self . t2 . borrow ( ) . missing . clone ( ) ;
344- missing. sort_by_key ( |( f, i) | ( filter:: nesting ( * f) , * f, * i) ) ;
345360 missing. dedup ( ) ;
346361 missing. retain ( |( f, i) | !self . known ( * f, * i) ) ;
347362 self . t2 . borrow_mut ( ) . missing = missing. clone ( ) ;
@@ -358,7 +373,9 @@ impl Transaction {
358373 } else {
359374 let mut t2 = self . t2 . borrow_mut ( ) ;
360375 t2. misses += 1 ;
361- t2. missing . push ( ( filter, from) ) ;
376+ if !t2. missing . contains ( & ( filter, from) ) {
377+ t2. missing . insert ( 0 , ( filter, from) ) ;
378+ }
362379 None
363380 }
364381 }
@@ -367,6 +384,11 @@ impl Transaction {
367384 if filter == filter:: nop ( ) {
368385 return Some ( from) ;
369386 }
387+ let sequence_number = if filter != filter:: sequence_number ( ) {
388+ compute_sequence_number ( self , from) . expect ( "compute_sequence_number failed" )
389+ } else {
390+ 0
391+ } ;
370392 let t2 = self . t2 . borrow_mut ( ) ;
371393 if let Some ( m) = t2. commit_map . get ( & filter. id ( ) ) {
372394 if let Some ( oid) = m. get ( & from) . cloned ( ) {
@@ -376,7 +398,7 @@ impl Transaction {
376398
377399 let oid = t2
378400 . cache
379- . read_propagate ( filter, from)
401+ . read_propagate ( filter, from, sequence_number )
380402 . expect ( "Failed to read from cache backend" ) ;
381403
382404 let oid = if let Some ( oid) = oid { Some ( oid) } else { None } ;
@@ -385,6 +407,9 @@ impl Transaction {
385407 if oid == git2:: Oid :: zero ( ) {
386408 return Some ( oid) ;
387409 }
410+ if filter == filter:: sequence_number ( ) {
411+ return Some ( oid) ;
412+ }
388413
389414 if self . repo . odb ( ) . unwrap ( ) . exists ( oid) {
390415 // Only report an object as cached if it exists in the object database.
@@ -396,3 +421,70 @@ impl Transaction {
396421 None
397422 }
398423}
424+
425+ /// Encode a `u128` into a 20-byte git OID (SHA-1 sized).
426+ /// The high 4 bytes of the OID are zero; the low 16 bytes
427+ /// contain the big-endian integer.
428+ pub fn oid_from_u128 ( n : u128 ) -> git2:: Oid {
429+ let mut bytes = [ 0u8 ; 20 ] ;
430+ // place the 16 integer bytes at the end (big-endian)
431+ bytes[ 20 - 16 ..] . copy_from_slice ( & n. to_be_bytes ( ) ) ;
432+ // Safe: length is exactly 20
433+ git2:: Oid :: from_bytes ( & bytes) . expect ( "20-byte OID construction cannot fail" )
434+ }
435+
436+ /// Decode a `u128` previously encoded by `oid_from_u128`.
437+ pub fn u128_from_oid ( oid : git2:: Oid ) -> u128 {
438+ let b = oid. as_bytes ( ) ;
439+ let mut n = [ 0u8 ; 16 ] ;
440+ n. copy_from_slice ( & b[ 20 - 16 ..] ) ; // take the last 16 bytes
441+ u128:: from_be_bytes ( n)
442+ }
443+
444+ pub fn compute_sequence_number (
445+ transaction : & cache:: Transaction ,
446+ input : git2:: Oid ,
447+ ) -> JoshResult < u128 > {
448+ if let Some ( count) = transaction. get ( filter:: sequence_number ( ) , input) {
449+ return Ok ( u128_from_oid ( count) ) ;
450+ }
451+
452+ let commit = transaction. repo ( ) . find_commit ( input) ?;
453+ if let Some ( p) = commit. parent_ids ( ) . next ( ) {
454+ if let Some ( count) = transaction. get ( filter:: sequence_number ( ) , p) {
455+ let pc = u128_from_oid ( count) ;
456+ transaction. insert (
457+ filter:: sequence_number ( ) ,
458+ input,
459+ oid_from_u128 ( pc + 1 ) ,
460+ true ,
461+ ) ;
462+ return Ok ( pc + 1 ) ;
463+ }
464+ }
465+
466+ let mut walk = transaction. repo ( ) . revwalk ( ) ?;
467+ walk. set_sorting ( git2:: Sort :: REVERSE | git2:: Sort :: TOPOLOGICAL ) ?;
468+ walk. push ( input) ?;
469+
470+ for c in walk {
471+ let commit = transaction. repo ( ) . find_commit ( c?) ?;
472+ let pc = if let Some ( p) = commit. parent_ids ( ) . next ( ) {
473+ compute_sequence_number ( transaction, p) ?
474+ } else {
475+ 0
476+ } ;
477+
478+ transaction. insert (
479+ filter:: sequence_number ( ) ,
480+ commit. id ( ) ,
481+ oid_from_u128 ( pc + 1 ) ,
482+ true ,
483+ ) ;
484+ }
485+ if let Some ( count) = transaction. get ( filter:: sequence_number ( ) , input) {
486+ Ok ( u128_from_oid ( count) )
487+ } else {
488+ Err ( josh_error ( "missing sequence_number" ) )
489+ }
490+ }
0 commit comments