@@ -15,6 +15,27 @@ namespace Microsoft.Git.CredentialManager
15
15
/// </summary>
16
16
public interface ISettings : IDisposable
17
17
{
18
+ /// <summary>
19
+ /// Try and get the value of a specified setting as specified in the environment and Git configuration,
20
+ /// with the environment taking precedence over Git.
21
+ /// </summary>
22
+ /// <param name="envarName">Optional environment variable name.</param>
23
+ /// <param name="section">Optional Git configuration section name.</param>
24
+ /// <param name="property">Git configuration property name. Required if <paramref name="section"/> is set, optional otherwise.</param>
25
+ /// <param name="value">Value of the requested setting.</param>
26
+ /// <returns>True if a setting value was found, false otherwise.</returns>
27
+ bool TryGetSetting ( string envarName , string section , string property , out string value ) ;
28
+
29
+ /// <summary>
30
+ /// Try and get the all values of a specified setting as specified in the environment and Git configuration,
31
+ /// in the correct order or precedence.
32
+ /// </summary>
33
+ /// <param name="envarName">Optional environment variable name.</param>
34
+ /// <param name="section">Optional Git configuration section name.</param>
35
+ /// <param name="property">Git configuration property name. Required if <paramref name="section"/> is set, optional otherwise.</param>
36
+ /// <returns>All values for the specified setting, in order of precedence, or an empty collection if no such values are set.</returns>
37
+ IEnumerable < string > GetSettingValues ( string envarName , string section , string property ) ;
38
+
18
39
/// <summary>
19
40
/// Git repository that local configuration lookup is scoped to, or null if this instance is not scoped to a repository.
20
41
/// </summary>
@@ -112,6 +133,124 @@ public Settings(IEnvironment environment, IGit git, string repositoryPath = null
112
133
RepositoryPath = repositoryPath ;
113
134
}
114
135
136
+ public bool TryGetSetting ( string envarName , string section , string property , out string value )
137
+ {
138
+ IEnumerable < string > allValues = GetSettingValues ( envarName , section , property ) ;
139
+
140
+ value = allValues . FirstOrDefault ( ) ;
141
+
142
+ return value != null ;
143
+ }
144
+
145
+ public IEnumerable < string > GetSettingValues ( string envarName , string section , string property )
146
+ {
147
+ string value ;
148
+
149
+ if ( envarName != null )
150
+ {
151
+ if ( _environment . Variables . TryGetValue ( envarName , out value ) )
152
+ {
153
+ yield return value ;
154
+ }
155
+ }
156
+
157
+ if ( section != null && property != null )
158
+ {
159
+ IGitConfiguration config = GetGitConfiguration ( ) ;
160
+
161
+ if ( RemoteUri != null )
162
+ {
163
+ /*
164
+ * Look for URL scoped "section" configuration entries, starting from the most specific
165
+ * down to the least specific (stopping before the TLD).
166
+ *
167
+ * In a divergence from standard Git configuration rules, we also consider matching URL scopes
168
+ * without a scheme ("protocol://").
169
+ *
170
+ * For each level of scope, we look for an entry with the scheme included (the default), and then
171
+ * also one without it specified. This allows you to have one configuration scope for both "http" and
172
+ * "https" without needing to repeat yourself, for example.
173
+ *
174
+ * For example, starting with "https://foo.example.com/bar/buzz" we have:
175
+ *
176
+ * 1a. [section "https://foo.example.com/bar/buzz"]
177
+ * property = value
178
+ *
179
+ * 1b. [section "foo.example.com/bar/buzz"]
180
+ * property = value
181
+ *
182
+ * 2a. [section "https://foo.example.com/bar"]
183
+ * property = value
184
+ *
185
+ * 2b. [section "foo.example.com/bar"]
186
+ * property = value
187
+ *
188
+ * 3a. [section "https://foo.example.com"]
189
+ * property = value
190
+ *
191
+ * 3b. [section "foo.example.com"]
192
+ * property = value
193
+ *
194
+ * 4a. [section "https://example.com"]
195
+ * property = value
196
+ *
197
+ * 4b. [section "example.com"]
198
+ * property = value
199
+ *
200
+ */
201
+
202
+ // Enumerate all configuration entries with the correct section and property name
203
+ // and make a local copy of them here to avoid needing to call `TryGetValue` on the
204
+ // IGitConfiguration object multiple times in a loop below.
205
+ var configEntries = new Dictionary < string , string > ( ) ;
206
+ config . Enumerate ( ( entryName , entryValue ) =>
207
+ {
208
+ string entrySection = entryName . TruncateFromIndexOf ( '.' ) ;
209
+ string entryProperty = entryName . TrimUntilLastIndexOf ( '.' ) ;
210
+
211
+ if ( StringComparer . OrdinalIgnoreCase . Equals ( entrySection , section ) &&
212
+ StringComparer . OrdinalIgnoreCase . Equals ( entryProperty , property ) )
213
+ {
214
+ configEntries [ entryName ] = entryValue ;
215
+ }
216
+
217
+ // Continue the enumeration
218
+ return true ;
219
+ } ) ;
220
+
221
+ foreach ( string scope in RemoteUri . GetGitConfigurationScopes ( ) )
222
+ {
223
+ string queryName = $ "{ section } .{ scope } .{ property } ";
224
+ // Look for a scoped entry that includes the scheme "protocol://example.com" first as this is more specific
225
+ if ( configEntries . TryGetValue ( queryName , out value ) )
226
+ {
227
+ yield return value ;
228
+ }
229
+
230
+ // Now look for a scoped entry that omits the scheme "example.com" second as this is less specific
231
+ string scopeWithoutScheme = scope . TrimUntilIndexOf ( Uri . SchemeDelimiter ) ;
232
+ string queryWithSchemeName = $ "{ section } .{ scopeWithoutScheme } .{ property } ";
233
+ if ( configEntries . TryGetValue ( queryWithSchemeName , out value ) )
234
+ {
235
+ yield return value ;
236
+ }
237
+ }
238
+ }
239
+
240
+ /*
241
+ * Try to look for an un-scoped "section" property setting:
242
+ *
243
+ * [section]
244
+ * property = value
245
+ *
246
+ */
247
+ if ( config . TryGetValue ( $ "{ section } .{ property } ", out value ) )
248
+ {
249
+ yield return value ;
250
+ }
251
+ }
252
+ }
253
+
115
254
public string RepositoryPath { get ; }
116
255
117
256
public Uri RemoteUri { get ; set ; }
@@ -279,141 +418,6 @@ bool TryGetUriSetting(string envarName, string section, string property, out Uri
279
418
280
419
public string ParentWindowId => _environment . Variables . TryGetValue ( Constants . EnvironmentVariables . GcmParentWindow , out string parentWindowId ) ? parentWindowId : null ;
281
420
282
- /// <summary>
283
- /// Try and get the value of a specified setting as specified in the environment and Git configuration,
284
- /// with the environment taking precedence over Git.
285
- /// </summary>
286
- /// <param name="envarName">Optional environment variable name.</param>
287
- /// <param name="section">Optional Git configuration section name.</param>
288
- /// <param name="property">Git configuration property name. Required if <paramref name="section"/> is set, optional otherwise.</param>
289
- /// <param name="value">Value of the requested setting.</param>
290
- /// <returns>True if a setting value was found, false otherwise.</returns>
291
- public bool TryGetSetting ( string envarName , string section , string property , out string value )
292
- {
293
- IEnumerable < string > allValues = GetSettingValues ( envarName , section , property ) ;
294
-
295
- value = allValues . FirstOrDefault ( ) ;
296
-
297
- return value != null ;
298
- }
299
-
300
- /// <summary>
301
- /// Try and get the all values of a specified setting as specified in the environment and Git configuration,
302
- /// in the correct order or precedence.
303
- /// </summary>
304
- /// <param name="envarName">Optional environment variable name.</param>
305
- /// <param name="section">Optional Git configuration section name.</param>
306
- /// <param name="property">Git configuration property name. Required if <paramref name="section"/> is set, optional otherwise.</param>
307
- /// <returns>All values for the specified setting, in order of precedence, or an empty collection if no such values are set.</returns>
308
- public IEnumerable < string > GetSettingValues ( string envarName , string section , string property )
309
- {
310
- string value ;
311
-
312
- if ( envarName != null )
313
- {
314
- if ( _environment . Variables . TryGetValue ( envarName , out value ) )
315
- {
316
- yield return value ;
317
- }
318
- }
319
-
320
- if ( section != null && property != null )
321
- {
322
- IGitConfiguration config = GetGitConfiguration ( ) ;
323
-
324
- if ( RemoteUri != null )
325
- {
326
- /*
327
- * Look for URL scoped "section" configuration entries, starting from the most specific
328
- * down to the least specific (stopping before the TLD).
329
- *
330
- * In a divergence from standard Git configuration rules, we also consider matching URL scopes
331
- * without a scheme ("protocol://").
332
- *
333
- * For each level of scope, we look for an entry with the scheme included (the default), and then
334
- * also one without it specified. This allows you to have one configuration scope for both "http" and
335
- * "https" without needing to repeat yourself, for example.
336
- *
337
- * For example, starting with "https://foo.example.com/bar/buzz" we have:
338
- *
339
- * 1a. [section "https://foo.example.com/bar/buzz"]
340
- * property = value
341
- *
342
- * 1b. [section "foo.example.com/bar/buzz"]
343
- * property = value
344
- *
345
- * 2a. [section "https://foo.example.com/bar"]
346
- * property = value
347
- *
348
- * 2b. [section "foo.example.com/bar"]
349
- * property = value
350
- *
351
- * 3a. [section "https://foo.example.com"]
352
- * property = value
353
- *
354
- * 3b. [section "foo.example.com"]
355
- * property = value
356
- *
357
- * 4a. [section "https://example.com"]
358
- * property = value
359
- *
360
- * 4b. [section "example.com"]
361
- * property = value
362
- *
363
- */
364
-
365
- // Enumerate all configuration entries with the correct section and property name
366
- // and make a local copy of them here to avoid needing to call `TryGetValue` on the
367
- // IGitConfiguration object multiple times in a loop below.
368
- var configEntries = new Dictionary < string , string > ( ) ;
369
- config . Enumerate ( ( entryName , entryValue ) =>
370
- {
371
- string entrySection = entryName . TruncateFromIndexOf ( '.' ) ;
372
- string entryProperty = entryName . TrimUntilLastIndexOf ( '.' ) ;
373
-
374
- if ( StringComparer . OrdinalIgnoreCase . Equals ( entrySection , section ) &&
375
- StringComparer . OrdinalIgnoreCase . Equals ( entryProperty , property ) )
376
- {
377
- configEntries [ entryName ] = entryValue ;
378
- }
379
-
380
- // Continue the enumeration
381
- return true ;
382
- } ) ;
383
-
384
- foreach ( string scope in RemoteUri . GetGitConfigurationScopes ( ) )
385
- {
386
- string queryName = $ "{ section } .{ scope } .{ property } ";
387
- // Look for a scoped entry that includes the scheme "protocol://example.com" first as this is more specific
388
- if ( configEntries . TryGetValue ( queryName , out value ) )
389
- {
390
- yield return value ;
391
- }
392
-
393
- // Now look for a scoped entry that omits the scheme "example.com" second as this is less specific
394
- string scopeWithoutScheme = scope . TrimUntilIndexOf ( Uri . SchemeDelimiter ) ;
395
- string queryWithSchemeName = $ "{ section } .{ scopeWithoutScheme } .{ property } ";
396
- if ( configEntries . TryGetValue ( queryWithSchemeName , out value ) )
397
- {
398
- yield return value ;
399
- }
400
- }
401
- }
402
-
403
- /*
404
- * Try to look for an un-scoped "section" property setting:
405
- *
406
- * [section]
407
- * property = value
408
- *
409
- */
410
- if ( config . TryGetValue ( $ "{ section } .{ property } ", out value ) )
411
- {
412
- yield return value ;
413
- }
414
- }
415
- }
416
-
417
421
private IGitConfiguration GetGitConfiguration ( ) => _gitConfig ?? ( _gitConfig = _git . GetConfiguration ( RepositoryPath ) ) ;
418
422
419
423
#region IDisposable
0 commit comments