@@ -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.
3136 pub 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