@@ -23,11 +23,16 @@ impl File<'static> {
23
23
/// times. It's recommended use is as part of a multi-step bootstrapping which needs fine-grained control,
24
24
/// and unless that's given one should prefer one of the other ways of initialization that resolve includes
25
25
/// at the right time.
26
+ ///
27
+ /// # Deviation
28
+ ///
26
29
/// - included values are added after the _section_ that included them, not directly after the value. This is
27
30
/// a deviation from how git does it, as it technically adds new value right after the include path itself,
28
31
/// technically 'splitting' the section. This can only make a difference if the `include` section also has values
29
32
/// which later overwrite portions of the included file, which seems unusual as these would be related to `includes`.
30
33
/// 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.
31
36
pub fn resolve_includes ( & mut self , options : init:: Options < ' _ > ) -> Result < ( ) , Error > {
32
37
if options. includes . max_depth == 0 {
33
38
return Ok ( ( ) ) ;
@@ -38,10 +43,11 @@ impl File<'static> {
38
43
}
39
44
40
45
pub ( 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)
42
47
}
43
48
44
49
fn resolve_includes_recursive (
50
+ search_config : Option < & File < ' static > > ,
45
51
target_config : & mut File < ' static > ,
46
52
depth : u8 ,
47
53
buf : & mut Vec < u8 > ,
@@ -57,30 +63,34 @@ fn resolve_includes_recursive(
57
63
} ;
58
64
}
59
65
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] ;
66
68
let header = & section. header ;
67
69
let header_name = header. name . as_ref ( ) ;
70
+ let mut paths = None ;
68
71
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) ) ;
70
73
} else if header_name == "includeIf" {
71
74
if let Some ( condition) = & header. subsection_name {
72
75
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) ) ;
75
83
}
76
84
}
77
85
}
86
+ if let Some ( paths) = paths {
87
+ insert_includes_recursively ( paths, target_config, depth, options, buf) ?;
88
+ }
78
89
}
79
-
80
- append_followed_includes_recursively ( section_ids_and_include_paths, target_config, depth, options, buf)
90
+ Ok ( ( ) )
81
91
}
82
92
83
- fn append_followed_includes_recursively (
93
+ fn insert_includes_recursively (
84
94
section_ids_and_include_paths : Vec < ( SectionId , crate :: Path < ' _ > ) > ,
85
95
target_config : & mut File < ' static > ,
86
96
depth : u8 ,
@@ -124,30 +134,26 @@ fn append_followed_includes_recursively(
124
134
init:: Error :: Interpolate ( err) => Error :: Interpolate ( err) ,
125
135
init:: Error :: Includes ( _) => unreachable ! ( "BUG: {:?} not possible due to no-follow options" , err) ,
126
136
} ) ?;
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) ?;
128
138
129
139
target_config. append_or_insert ( include_config, Some ( section_id) ) ;
130
140
}
131
141
Ok ( ( ) )
132
142
}
133
143
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 ( )
146
151
}
147
152
148
153
fn include_condition_match (
149
154
condition : & BStr ,
150
155
target_config_path : Option < & Path > ,
156
+ search_config : & File < ' static > ,
151
157
options : Options < ' _ > ,
152
158
) -> Result < bool , Error > {
153
159
let mut tokens = condition. splitn ( 2 , |b| * b == b':' ) ;
@@ -170,6 +176,32 @@ fn include_condition_match(
170
176
gix_glob:: wildmatch:: Mode :: IGNORE_CASE ,
171
177
) ,
172
178
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
+ }
173
205
_ => Ok ( false ) ,
174
206
}
175
207
}
0 commit comments