11use gix_object:: WriteTo ;
22use gix_object:: bstr:: BString ;
33use std:: collections:: HashMap ;
4+ use std:: sync:: LazyLock ;
45
5- use crate :: filter:: { Filter , LazyRef , Op , to_filter , to_op } ;
6+ use crate :: filter:: { Filter , LazyRef , Op , sequence_number } ;
67use crate :: { JoshResult , josh_error} ;
78
9+ static FILTERS : LazyLock < std:: sync:: Mutex < std:: collections:: HashMap < Filter , Op > > > =
10+ LazyLock :: new ( || Default :: default ( ) ) ;
11+
12+ pub ( crate ) fn to_op ( filter : Filter ) -> Op {
13+ if filter == sequence_number ( ) {
14+ return Op :: Nop ;
15+ }
16+ FILTERS
17+ . lock ( )
18+ . unwrap ( )
19+ . get ( & filter)
20+ . expect ( "unknown filter" )
21+ . clone ( )
22+ }
23+
824fn push_blob_entries (
925 entries : & mut Vec < gix_object:: tree:: Entry > ,
1026 items : impl IntoIterator < Item = ( impl AsRef < str > , gix_hash:: ObjectId ) > ,
@@ -79,7 +95,7 @@ impl InMemoryBuilder {
7995 fn build_filter_params ( & mut self , params : & [ Filter ] ) -> JoshResult < gix_hash:: ObjectId > {
8096 let mut entries = Vec :: new ( ) ;
8197 for ( i, filter) in params. iter ( ) . enumerate ( ) {
82- let child = self . build_filter ( * filter) ? ;
98+ let child = gix_hash :: ObjectId :: from_bytes_or_panic ( filter. id ( ) . as_bytes ( ) ) ;
8399 entries. push ( gix_object:: tree:: Entry {
84100 mode : gix_object:: tree:: EntryKind :: Tree . into ( ) ,
85101 filename : BString :: from ( i. to_string ( ) ) ,
@@ -94,7 +110,7 @@ impl InMemoryBuilder {
94110 let mut outer_entries = Vec :: new ( ) ;
95111 for ( i, ( key, filter) ) in params. iter ( ) . enumerate ( ) {
96112 let key_blob = self . write_blob ( key. as_bytes ( ) ) ;
97- let filter_tree = self . build_filter ( * filter) ? ;
113+ let filter_tree = gix_hash :: ObjectId :: from_bytes_or_panic ( filter. id ( ) . as_bytes ( ) ) ;
98114
99115 let inner_entries = vec ! [
100116 gix_object:: tree:: Entry {
@@ -156,11 +172,6 @@ impl InMemoryBuilder {
156172 self . write_tree ( outer_tree)
157173 }
158174
159- fn build_filter ( & mut self , filter : Filter ) -> JoshResult < gix_hash:: ObjectId > {
160- let op = to_op ( filter) ;
161- self . build_op ( & op)
162- }
163-
164175 fn build_op ( & mut self , op : & Op ) -> JoshResult < gix_hash:: ObjectId > {
165176 let mut entries = Vec :: new ( ) ;
166177
@@ -332,32 +343,55 @@ impl InMemoryBuilder {
332343 }
333344}
334345
346+ pub ( crate ) fn to_filter ( op : Op ) -> Filter {
347+ let mut builder = InMemoryBuilder :: new ( ) ;
348+ let tree_id = builder. build_op ( & op) . unwrap ( ) ;
349+ let oid = git2:: Oid :: from_bytes ( tree_id. as_bytes ( ) ) . unwrap ( ) ;
350+
351+ let f = Filter ( oid) ;
352+ FILTERS . lock ( ) . unwrap ( ) . insert ( f, op) ;
353+ f
354+ }
355+
335356pub fn as_tree ( repo : & git2:: Repository , filter : crate :: filter:: Filter ) -> JoshResult < git2:: Oid > {
357+ let odb = repo. odb ( ) ?;
358+
359+ // If the tree exists in the ODB it means all children must already exist as
360+ // well so we can just return it.
361+ if odb. exists ( filter. id ( ) ) {
362+ return Ok ( filter. id ( ) ) ;
363+ }
364+
365+ // We don't try to figure out what to write exactly, just write all
366+ // filters we know about to the ODB
367+ let filters = FILTERS . lock ( ) . unwrap ( ) . clone ( ) ;
336368 let mut builder = InMemoryBuilder :: new ( ) ;
337- let tree_id = builder. build_filter ( filter) ?;
369+ for ( f, op) in filters. into_iter ( ) {
370+ if !odb. exists ( f. id ( ) ) {
371+ builder. build_op ( & op) ?;
372+ }
373+ }
338374
339375 // Write all pending objects to the git2 repository
340- let odb = repo. odb ( ) ?;
341376 for ( oid, ( kind, data) ) in builder. pending_writes {
342- let git2_type = match kind {
343- gix_object:: Kind :: Tree => git2:: ObjectType :: Tree ,
344- gix_object:: Kind :: Blob => git2:: ObjectType :: Blob ,
345- gix_object:: Kind :: Commit => git2:: ObjectType :: Commit ,
346- gix_object:: Kind :: Tag => git2:: ObjectType :: Tag ,
347- } ;
348-
349377 let oid = git2:: Oid :: from_bytes ( oid. as_bytes ( ) ) ?;
350378
351379 // On some platforms, .exists() is cheaper in terms of i/o
352380 // than .write(), because .write() updates file access time
353381 // in loose object backend
354382 if !odb. exists ( oid) {
383+ let git2_type = match kind {
384+ gix_object:: Kind :: Tree => git2:: ObjectType :: Tree ,
385+ gix_object:: Kind :: Blob => git2:: ObjectType :: Blob ,
386+ gix_object:: Kind :: Commit => git2:: ObjectType :: Commit ,
387+ gix_object:: Kind :: Tag => git2:: ObjectType :: Tag ,
388+ } ;
355389 odb. write ( git2_type, & data) ?;
356390 }
357391 }
358392
359- // Return the root tree OID
360- Ok ( git2 :: Oid :: from_bytes ( tree_id . as_bytes ( ) ) ? )
393+ // Now the tree should really be in the ODB
394+ Ok ( filter . id ( ) )
361395}
362396
363397pub fn from_tree ( repo : & git2:: Repository , tree_oid : git2:: Oid ) -> JoshResult < Filter > {
0 commit comments