Skip to content

Commit 555c275

Browse files
author
Ryan Roden-Corrent
committed
Support redirecting the config search path.
Allow getting/setting the global/system/XDG config search path using Configuration.GetSearchPath Configuration.SetSearchPath. This is useful for libgit2sharp consumers who are writing unit tests that want to 'fake' a global config. While the config search path can be changed using the RepositoryOptions, this requires that every function you test would need to accept a config path argument. Changing the search path at the library level ensures that all config access is redirected. This is how the libgit2 config tests work; see the initialization of tests/config/global.c for an example.
1 parent 180ffa4 commit 555c275

File tree

4 files changed

+191
-0
lines changed

4 files changed

+191
-0
lines changed

LibGit2Sharp.Tests/ConfigurationFixture.cs

+89
Original file line numberDiff line numberDiff line change
@@ -409,5 +409,94 @@ public void PassingANonExistingLocalConfigurationFileToBuildFromthrowss()
409409
Assert.Throws<FileNotFoundException>(() => Configuration.BuildFrom(
410410
Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())));
411411
}
412+
413+
[Fact]
414+
public void CanSetAndGetSearchPath()
415+
{
416+
string globalPath = Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName());
417+
string systemPath = Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName());
418+
string xdgPath = Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName());
419+
420+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.Global, globalPath);
421+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.System, systemPath);
422+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.Xdg, xdgPath);
423+
424+
Assert.Equal(globalPath, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global).Single());
425+
Assert.Equal(systemPath, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.System).Single());
426+
Assert.Equal(xdgPath, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Xdg).Single());
427+
428+
// reset the search paths to their defaults
429+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.Global, null);
430+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.System, null);
431+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.Xdg, null);
432+
}
433+
434+
[Fact]
435+
public void CanSetAndGetMultipleSearchPaths()
436+
{
437+
string[] paths =
438+
{
439+
Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()),
440+
Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()),
441+
Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()),
442+
};
443+
444+
GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, paths);
445+
446+
Assert.Equal(paths, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global));
447+
448+
// set back to the defaults
449+
GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, null);
450+
}
451+
452+
[Fact]
453+
public void CanResetSearchPaths()
454+
{
455+
// set the global search path to its default value
456+
GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, null);
457+
var oldPaths = GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global);
458+
Assert.NotNull(oldPaths);
459+
460+
// change to something other than the default
461+
var newPaths = new string[] { Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()) };
462+
GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, newPaths);
463+
Assert.Equal(newPaths, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global));
464+
465+
// set it back to the default
466+
GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, null);
467+
Assert.Equal(oldPaths, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global));
468+
}
469+
470+
[Fact]
471+
public void CanRedirectConfigAccess()
472+
{
473+
var scd1 = BuildSelfCleaningDirectory();
474+
var scd2 = BuildSelfCleaningDirectory();
475+
476+
Touch(scd1.RootedDirectoryPath, ".gitconfig");
477+
Touch(scd2.RootedDirectoryPath, ".gitconfig");
478+
479+
// redirect global access to the first path
480+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.Global, scd1.RootedDirectoryPath);
481+
482+
// set a value in the first config
483+
using (var config = Configuration.BuildFrom(null))
484+
{
485+
config.Set("luggage.code", 9876, ConfigurationLevel.Global);
486+
Assert.Equal(9876, config.Get<int>("luggage.code", ConfigurationLevel.Global).Value);
487+
}
488+
489+
// redirect global config access to path2
490+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.Global, scd2.RootedDirectoryPath);
491+
492+
// if the redirect succeeds, the value set in the prior config should not be visible
493+
using (var config = Configuration.BuildFrom(null))
494+
{
495+
Assert.Equal(-1, config.GetValueOrDefault<int>("luggage.code", ConfigurationLevel.Global, -1));
496+
}
497+
498+
// reset the search path to the default
499+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.Global, null);
500+
}
412501
}
413502
}

LibGit2Sharp/Core/NativeMethods.cs

+4
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,10 @@ internal static extern int git_filter_unregister(
581581
[DllImport(libgit2)]
582582
internal static extern int git_libgit2_features();
583583

584+
// use __arglist to bind varargs in C#. The C signature is git_libgit2_opts(int option, ...)
585+
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
586+
internal static extern int git_libgit2_opts(int option, __arglist);
587+
584588
[DllImport(libgit2)]
585589
internal static extern int git_graph_ahead_behind(out UIntPtr ahead, out UIntPtr behind, RepositorySafeHandle repo, ref GitOid one, ref GitOid two);
586590

LibGit2Sharp/Core/Proxy.cs

+54
Original file line numberDiff line numberDiff line change
@@ -3148,6 +3148,60 @@ public static BuiltInFeatures git_libgit2_features()
31483148
return (BuiltInFeatures)NativeMethods.git_libgit2_features();
31493149
}
31503150

3151+
// C# equivalent of libgit2's git_libgit2_opt_t
3152+
private enum LibGitOption
3153+
{
3154+
GetMWindowSize, // GIT_OPT_GET_MWINDOW_SIZE
3155+
SetMWindowSize, // GIT_OPT_SET_MWINDOW_SIZE
3156+
GetMWindowMappedLimit, // GIT_OPT_GET_MWINDOW_MAPPED_LIMIT
3157+
SetMWindowMappedLimit, // GIT_OPT_SET_MWINDOW_MAPPED_LIMIT
3158+
GetSearchPath, // GIT_OPT_GET_SEARCH_PATH
3159+
SetSearchPath, // GIT_OPT_SET_SEARCH_PATH
3160+
SetCacheObjectLimit, // GIT_OPT_SET_CACHE_OBJECT_LIMIT
3161+
SetCacheMaxSize, // GIT_OPT_SET_CACHE_MAX_SIZE
3162+
EnableCaching, // GIT_OPT_ENABLE_CACHING
3163+
GetCachedMemory, // GIT_OPT_GET_CACHED_MEMORY
3164+
GetTemplatePath, // GIT_OPT_GET_TEMPLATE_PATH
3165+
SetTemplatePath, // GIT_OPT_SET_TEMPLATE_PATH
3166+
SetSslCertLocations, // GIT_OPT_SET_SSL_CERT_LOCATIONS
3167+
}
3168+
3169+
/// <summary>
3170+
/// Get the paths under which libgit2 searches for the configuration file of a given level.
3171+
/// </summary>
3172+
/// <param name="level">The level (global/system/XDG) of the config.</param>
3173+
/// <returns>
3174+
/// The paths delimited by 'GIT_PATH_LIST_SEPARATOR' (<see cref="GlobalSettings.PathListSeparator"/>).
3175+
/// </returns>
3176+
public static string git_libgit2_opts_get_search_path(ConfigurationLevel level)
3177+
{
3178+
string path;
3179+
3180+
using (var buf = new GitBuf())
3181+
{
3182+
var res = NativeMethods.git_libgit2_opts((int)LibGitOption.GetSearchPath, __arglist((uint)level, buf));
3183+
Ensure.ZeroResult(res);
3184+
3185+
path = LaxUtf8Marshaler.FromNative(buf.ptr) ?? string.Empty;
3186+
}
3187+
3188+
return path;
3189+
}
3190+
3191+
/// <summary>
3192+
/// Set the path(s) under which libgit2 searches for the configuration file of a given level.
3193+
/// </summary>
3194+
/// <param name="level">The level (global/system/XDG) of the config.</param>
3195+
/// <param name="path">
3196+
/// A string of paths delimited by 'GIT_PATH_LIST_SEPARATOR' (<see cref="GlobalSettings.PathListSeparator"/>).
3197+
/// Pass null to reset the search path to the default.
3198+
/// </param>
3199+
public static void git_libgit2_opts_set_search_path(ConfigurationLevel level, string path)
3200+
{
3201+
var res = NativeMethods.git_libgit2_opts((int)LibGitOption.SetSearchPath, __arglist((uint)level, path));
3202+
Ensure.ZeroResult(res);
3203+
}
3204+
31513205
#endregion
31523206

31533207
private static ICollection<TResult> git_foreach<T, TResult>(

LibGit2Sharp/GlobalSettings.cs

+44
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.IO;
33
using System.Reflection;
4+
using System.Collections.Generic;
45
using LibGit2Sharp.Core;
56

67
namespace LibGit2Sharp
@@ -203,5 +204,48 @@ public static void DeregisterFilter(FilterRegistration registration)
203204
Proxy.git_filter_unregister(registration.Name);
204205
registration.Free();
205206
}
207+
208+
/// <summary>
209+
/// The separator used in path list strings (like in the PATH environment variable).
210+
/// A semi-colon ";" is used on Windows, and a colon ":" for all other systems.
211+
/// </summary>
212+
public static char PathListSeparator
213+
{
214+
get
215+
{
216+
return (Platform.OperatingSystem == OperatingSystemType.Windows) ? ';' : ':';
217+
}
218+
}
219+
220+
/// <summary>
221+
/// Get the paths under which libgit2 searches for the configuration file of a given level.
222+
/// </summary>
223+
/// <param name="level">The level (global/system/XDG) of the config.</param>
224+
/// <returns>The paths that are searched</returns>
225+
public static IEnumerable<string> GetConfigSearchPaths(ConfigurationLevel level)
226+
{
227+
return Proxy.git_libgit2_opts_get_search_path(level).Split(PathListSeparator);
228+
}
229+
230+
/// <summary>
231+
/// Set the path under which libgit2 searches for the configuration file of a given level.
232+
/// </summary>
233+
/// <param name="level">The level (global/system/XDG) of the config.</param>
234+
/// <param name="path">The new search path, or null to reset to default.</param>
235+
public static void SetConfigSearchPath(ConfigurationLevel level, string path)
236+
{
237+
Proxy.git_libgit2_opts_set_search_path(level, path);
238+
}
239+
240+
/// <summary>
241+
/// Set the paths under which libgit2 searches for the configuration file of a given level.
242+
/// </summary>
243+
/// <param name="level">The level (global/system/XDG) of the config.</param>
244+
/// <param name="paths">The new search paths, or null to reset to default.</param>
245+
public static void SetConfigSearchPaths(ConfigurationLevel level, IEnumerable<string> paths)
246+
{
247+
var pathString = (paths == null) ? null : string.Join(PathListSeparator.ToString(), paths);
248+
Proxy.git_libgit2_opts_set_search_path(level, pathString);
249+
}
206250
}
207251
}

0 commit comments

Comments
 (0)