@@ -184,3 +184,102 @@ mod multiple {
184184 ) ;
185185 }
186186}
187+
188+ mod complex_globs {
189+ use gix_refspec:: { parse:: Operation , MatchGroup } ;
190+ use gix_hash:: ObjectId ;
191+ use bstr:: BString ;
192+ use std:: borrow:: Cow ;
193+
194+ #[ test]
195+ fn one_sided_complex_glob_patterns_can_be_parsed ( ) {
196+ // The key change is that complex glob patterns with multiple asterisks
197+ // can now be parsed for one-sided refspecs
198+ let spec1 = gix_refspec:: parse ( "refs/*/foo/*" . into ( ) , Operation :: Fetch ) ;
199+ assert ! ( spec1. is_ok( ) , "Should parse complex glob pattern for one-sided refspec" ) ;
200+
201+ let spec2 = gix_refspec:: parse ( "refs/*/*/bar" . into ( ) , Operation :: Fetch ) ;
202+ assert ! ( spec2. is_ok( ) , "Should parse complex glob pattern with multiple asterisks" ) ;
203+
204+ let spec3 = gix_refspec:: parse ( "refs/heads/*/release/*" . into ( ) , Operation :: Fetch ) ;
205+ assert ! ( spec3. is_ok( ) , "Should parse complex glob pattern" ) ;
206+
207+ // Two-sided refspecs with multiple asterisks should still fail
208+ let spec4 = gix_refspec:: parse ( "refs/*/foo/*:refs/remotes/*" . into ( ) , Operation :: Fetch ) ;
209+ assert ! ( spec4. is_err( ) , "Two-sided refspecs with multiple asterisks should fail" ) ;
210+ }
211+
212+ #[ test]
213+ fn one_sided_simple_glob_patterns_match ( ) {
214+ // Test that simple glob patterns (one asterisk) work correctly with matching
215+ let refs = vec ! [
216+ create_ref( "refs/heads/feature/foo" , "1111111111111111111111111111111111111111" ) ,
217+ create_ref( "refs/heads/bugfix/bar" , "2222222222222222222222222222222222222222" ) ,
218+ create_ref( "refs/tags/v1.0" , "3333333333333333333333333333333333333333" ) ,
219+ create_ref( "refs/pull/123" , "4444444444444444444444444444444444444444" ) ,
220+ ] ;
221+ let items: Vec < _ > = refs. iter ( ) . map ( |r| r. to_item ( ) ) . collect ( ) ;
222+
223+ // Test: refs/heads/* should match all refs under refs/heads/
224+ let spec = gix_refspec:: parse ( "refs/heads/*" . into ( ) , Operation :: Fetch ) . unwrap ( ) ;
225+ let group = MatchGroup :: from_fetch_specs ( [ spec] ) ;
226+ let outcome = group. match_lhs ( items. iter ( ) . copied ( ) ) ;
227+ let mappings = outcome. mappings ;
228+
229+ assert_eq ! ( mappings. len( ) , 2 , "Should match two refs under refs/heads/" ) ;
230+
231+ // Test: refs/tags/* should match all refs under refs/tags/
232+ let items2: Vec < _ > = refs. iter ( ) . map ( |r| r. to_item ( ) ) . collect ( ) ;
233+ let spec2 = gix_refspec:: parse ( "refs/tags/*" . into ( ) , Operation :: Fetch ) . unwrap ( ) ;
234+ let group2 = MatchGroup :: from_fetch_specs ( [ spec2] ) ;
235+ let outcome2 = group2. match_lhs ( items2. iter ( ) . copied ( ) ) ;
236+ let mappings2 = outcome2. mappings ;
237+
238+ assert_eq ! ( mappings2. len( ) , 1 , "Should match one ref under refs/tags/" ) ;
239+ }
240+
241+ #[ test]
242+ fn one_sided_glob_with_suffix_matches ( ) {
243+ // Test that glob patterns with suffix work correctly
244+ let refs = vec ! [
245+ create_ref( "refs/heads/feature" , "1111111111111111111111111111111111111111" ) ,
246+ create_ref( "refs/heads/feat" , "2222222222222222222222222222222222222222" ) ,
247+ create_ref( "refs/heads/main" , "3333333333333333333333333333333333333333" ) ,
248+ ] ;
249+ let items: Vec < _ > = refs. iter ( ) . map ( |r| r. to_item ( ) ) . collect ( ) ;
250+
251+ // Test: refs/heads/feat* should match refs/heads/feature and refs/heads/feat
252+ let spec = gix_refspec:: parse ( "refs/heads/feat*" . into ( ) , Operation :: Fetch ) . unwrap ( ) ;
253+ let group = MatchGroup :: from_fetch_specs ( [ spec] ) ;
254+ let outcome = group. match_lhs ( items. iter ( ) . copied ( ) ) ;
255+ let mappings = outcome. mappings ;
256+
257+ assert_eq ! ( mappings. len( ) , 2 , "Should match two refs starting with feat" ) ;
258+ }
259+
260+ // Helper function to create a ref
261+ fn create_ref ( name : & str , id_hex : & str ) -> Ref {
262+ Ref {
263+ name : name. into ( ) ,
264+ target : ObjectId :: from_hex ( id_hex. as_bytes ( ) ) . unwrap ( ) ,
265+ object : None ,
266+ }
267+ }
268+
269+ #[ derive( Debug , Clone ) ]
270+ struct Ref {
271+ name : BString ,
272+ target : ObjectId ,
273+ object : Option < ObjectId > ,
274+ }
275+
276+ impl Ref {
277+ fn to_item ( & self ) -> gix_refspec:: match_group:: Item < ' _ > {
278+ gix_refspec:: match_group:: Item {
279+ full_ref_name : self . name . as_ref ( ) ,
280+ target : & self . target ,
281+ object : self . object . as_deref ( ) ,
282+ }
283+ }
284+ }
285+ }
0 commit comments