@@ -23,11 +23,16 @@ impl File<'static> {
2323///   times. It's recommended use is as part of a multi-step bootstrapping which needs fine-grained control, 
2424///   and unless that's given one should prefer one of the other ways of initialization that resolve includes 
2525///   at the right time. 
26+ /// 
27+ /// # Deviation 
28+ /// 
2629/// - included values are added after the _section_ that included them, not directly after the value. This is 
2730///   a deviation from how git does it, as it technically adds new value right after the include path itself, 
2831///   technically 'splitting' the section. This can only make a difference if the `include` section also has values 
2932///   which later overwrite portions of the included file, which seems unusual as these would be related to `includes`. 
3033///   We can fix this by 'splitting' the include section if needed so the included sections are put into the right place. 
34+ /// - `hasconfig:remote.*.url` will not prevent itself to include files with `[remote "name"]\nurl = x` values, but it also 
35+ ///    won't match them, i.e. one cannot include something that will cause the condition to match or to always be true. 
3136pub  fn  resolve_includes ( & mut  self ,  options :  init:: Options < ' _ > )  -> Result < ( ) ,  Error >  { 
3237        if  options. includes . max_depth  == 0  { 
3338            return  Ok ( ( ) ) ; 
@@ -38,10 +43,11 @@ impl File<'static> {
3843} 
3944
4045pub ( crate )  fn  resolve ( config :  & mut  File < ' static > ,  buf :  & mut  Vec < u8 > ,  options :  init:: Options < ' _ > )  -> Result < ( ) ,  Error >  { 
41-     resolve_includes_recursive ( config,  0 ,  buf,  options) 
46+     resolve_includes_recursive ( None ,   config,  0 ,  buf,  options) 
4247} 
4348
4449fn  resolve_includes_recursive ( 
50+     search_config :  Option < & File < ' static > > , 
4551    target_config :  & mut  File < ' static > , 
4652    depth :  u8 , 
4753    buf :  & mut  Vec < u8 > , 
@@ -57,30 +63,34 @@ fn resolve_includes_recursive(
5763        } ; 
5864    } 
5965
60-     let  mut  section_ids_and_include_paths = Vec :: new ( ) ; 
61-     for  ( id,  section)  in  target_config
62-         . section_order 
63-         . iter ( ) 
64-         . map ( |id| ( * id,  & target_config. sections [ id] ) ) 
65-     { 
66+     for  id in  target_config. section_order . clone ( ) . into_iter ( )  { 
67+         let  section = & target_config. sections [ & id] ; 
6668        let  header = & section. header ; 
6769        let  header_name = header. name . as_ref ( ) ; 
70+         let  mut  paths = None ; 
6871        if  header_name == "include"  && header. subsection_name . is_none ( )  { 
69-             detach_include_paths ( & mut  section_ids_and_include_paths ,   section,  id) ; 
72+             paths =  Some ( gather_paths ( section,  id) ) ; 
7073        }  else  if  header_name == "includeIf"  { 
7174            if  let  Some ( condition)  = & header. subsection_name  { 
7275                let  target_config_path = section. meta . path . as_deref ( ) ; 
73-                 if  include_condition_match ( condition. as_ref ( ) ,  target_config_path,  options. includes ) ? { 
74-                     detach_include_paths ( & mut  section_ids_and_include_paths,  section,  id) ; 
76+                 if  include_condition_match ( 
77+                     condition. as_ref ( ) , 
78+                     target_config_path, 
79+                     search_config. unwrap_or ( target_config) , 
80+                     options. includes , 
81+                 ) ? { 
82+                     paths = Some ( gather_paths ( section,  id) ) ; 
7583                } 
7684            } 
7785        } 
86+         if  let  Some ( paths)  = paths { 
87+             insert_includes_recursively ( paths,  target_config,  depth,  options,  buf) ?; 
88+         } 
7889    } 
79- 
80-     append_followed_includes_recursively ( section_ids_and_include_paths,  target_config,  depth,  options,  buf) 
90+     Ok ( ( ) ) 
8191} 
8292
83- fn  append_followed_includes_recursively ( 
93+ fn  insert_includes_recursively ( 
8494    section_ids_and_include_paths :  Vec < ( SectionId ,  crate :: Path < ' _ > ) > , 
8595    target_config :  & mut  File < ' static > , 
8696    depth :  u8 , 
@@ -124,30 +134,26 @@ fn append_followed_includes_recursively(
124134                init:: Error :: Interpolate ( err)  => Error :: Interpolate ( err) , 
125135                init:: Error :: Includes ( _)  => unreachable ! ( "BUG: {:?} not possible due to no-follow options" ,  err) , 
126136            } ) ?; 
127-         resolve_includes_recursive ( & mut  include_config,  depth + 1 ,  buf,  options) ?; 
137+         resolve_includes_recursive ( Some ( target_config ) ,   & mut  include_config,  depth + 1 ,  buf,  options) ?; 
128138
129139        target_config. append_or_insert ( include_config,  Some ( section_id) ) ; 
130140    } 
131141    Ok ( ( ) ) 
132142} 
133143
134- fn  detach_include_paths ( 
135-     include_paths :  & mut  Vec < ( SectionId ,  crate :: Path < ' static > ) > , 
136-     section :  & file:: Section < ' _ > , 
137-     id :  SectionId , 
138- )  { 
139-     include_paths. extend ( 
140-         section
141-             . body 
142-             . values ( "path" ) 
143-             . into_iter ( ) 
144-             . map ( |path| ( id,  crate :: Path :: from ( Cow :: Owned ( path. into_owned ( ) ) ) ) ) , 
145-     ) ; 
144+ fn  gather_paths ( section :  & file:: Section < ' _ > ,  id :  SectionId )  -> Vec < ( SectionId ,  crate :: Path < ' static > ) >  { 
145+     section
146+         . body 
147+         . values ( "path" ) 
148+         . into_iter ( ) 
149+         . map ( |path| ( id,  crate :: Path :: from ( Cow :: Owned ( path. into_owned ( ) ) ) ) ) 
150+         . collect ( ) 
146151} 
147152
148153fn  include_condition_match ( 
149154    condition :  & BStr , 
150155    target_config_path :  Option < & Path > , 
156+     search_config :  & File < ' static > , 
151157    options :  Options < ' _ > , 
152158)  -> Result < bool ,  Error >  { 
153159    let  mut  tokens = condition. splitn ( 2 ,  |b| * b == b':' ) ; 
@@ -170,6 +176,32 @@ fn include_condition_match(
170176            gix_glob:: wildmatch:: Mode :: IGNORE_CASE , 
171177        ) , 
172178        b"onbranch"  => Ok ( onbranch_matches ( condition,  options. conditional ) . is_some ( ) ) , 
179+         b"hasconfig"  => { 
180+             let  mut  tokens = condition. splitn ( 2 ,  |b| * b == b':' ) ; 
181+             let  ( key_glob,  value_glob)  = match  ( tokens. next ( ) ,  tokens. next ( ) )  { 
182+                 ( Some ( a) ,  Some ( b) )  => ( a,  b) , 
183+                 _ => return  Ok ( false ) , 
184+             } ; 
185+             if  key_glob. as_bstr ( )  != "remote.*.url"  { 
186+                 return  Ok ( false ) ; 
187+             } 
188+             let  Some ( sections)  = search_config. sections_by_name ( "remote" )  else  { 
189+                 return  Ok ( false ) ; 
190+             } ; 
191+             for  remote in  sections { 
192+                 for  url in  remote. values ( "url" )  { 
193+                     let  glob_matches = gix_glob:: wildmatch ( 
194+                         value_glob. as_bstr ( ) , 
195+                         url. as_ref ( ) , 
196+                         gix_glob:: wildmatch:: Mode :: NO_MATCH_SLASH_LITERAL , 
197+                     ) ; 
198+                     if  glob_matches { 
199+                         return  Ok ( true ) ; 
200+                     } 
201+                 } 
202+             } 
203+             Ok ( false ) 
204+         } 
173205        _ => Ok ( false ) , 
174206    } 
175207} 
0 commit comments