Skip to content

Commit c5f83a3

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 e734df5 commit c5f83a3

File tree

4 files changed

+213
-0
lines changed

4 files changed

+213
-0
lines changed

LibGit2Sharp.Tests/ConfigurationFixture.cs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,5 +431,94 @@ public void CannotBuildAProperSignatureFromConfigWhenFullIdentityCannotBeFoundIn
431431
Assert.Null(signature);
432432
}
433433
}
434+
435+
[Fact]
436+
public void CanSetAndGetSearchPath()
437+
{
438+
string globalPath = Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName());
439+
string systemPath = Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName());
440+
string xdgPath = Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName());
441+
442+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.Global, globalPath);
443+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.System, systemPath);
444+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.Xdg, xdgPath);
445+
446+
Assert.Equal(globalPath, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global).Single());
447+
Assert.Equal(systemPath, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.System).Single());
448+
Assert.Equal(xdgPath, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Xdg).Single());
449+
450+
// reset the search paths to their defaults
451+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.Global, null);
452+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.System, null);
453+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.Xdg, null);
454+
}
455+
456+
[Fact]
457+
public void CanSetAndGetMultipleSearchPaths()
458+
{
459+
string[] paths =
460+
{
461+
Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()),
462+
Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()),
463+
Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()),
464+
};
465+
466+
GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, paths);
467+
468+
Assert.Equal(paths, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global));
469+
470+
// set back to the defaults
471+
GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, null);
472+
}
473+
474+
[Fact]
475+
public void CanResetSearchPaths()
476+
{
477+
// set the global search path to its default value
478+
GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, null);
479+
var oldPaths = GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global);
480+
Assert.NotNull(oldPaths);
481+
482+
// change to something other than the default
483+
var newPaths = new string[] { Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()) };
484+
GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, newPaths);
485+
Assert.Equal(newPaths, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global));
486+
487+
// set it back to the default
488+
GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, null);
489+
Assert.Equal(oldPaths, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global));
490+
}
491+
492+
[Fact]
493+
public void CanRedirectConfigAccess()
494+
{
495+
var scd1 = BuildSelfCleaningDirectory();
496+
var scd2 = BuildSelfCleaningDirectory();
497+
498+
Touch(scd1.RootedDirectoryPath, ".gitconfig");
499+
Touch(scd2.RootedDirectoryPath, ".gitconfig");
500+
501+
// redirect global access to the first path
502+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.Global, scd1.RootedDirectoryPath);
503+
504+
// set a value in the first config
505+
using (var config = Configuration.BuildFrom(null))
506+
{
507+
config.Set("luggage.code", 9876, ConfigurationLevel.Global);
508+
Assert.Equal(9876, config.Get<int>("luggage.code", ConfigurationLevel.Global).Value);
509+
}
510+
511+
// redirect global config access to path2
512+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.Global, scd2.RootedDirectoryPath);
513+
514+
// if the redirect succeeds, the value set in the prior config should not be visible
515+
using (var config = Configuration.BuildFrom(null))
516+
{
517+
Assert.Equal(-1, config.GetValueOrDefault<int>("luggage.code", ConfigurationLevel.Global, -1));
518+
}
519+
520+
// reset the search path to the default
521+
GlobalSettings.SetConfigSearchPath(ConfigurationLevel.Global, null);
522+
}
434523
}
435524
}

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,32 @@ internal static extern int git_filter_unregister(
581581
[DllImport(libgit2)]
582582
internal static extern int git_libgit2_features();
583583

584+
// Bindings for git_libgit2_opts(int option, ...):
585+
// Varargs can be bound using __arglist in .NET, but I had trouble getting that working with Mono.
586+
// Instead of using __arglist, I enumerate the possible signatures here:
587+
588+
// git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, int level, git_buf *buf)
589+
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
590+
internal static extern int git_libgit2_opts(int option, uint level, GitBuf buf);
591+
592+
// git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, int level, git_buf *buf)
593+
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
594+
internal static extern int git_libgit2_opts(int option, uint level,
595+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))]string name);
596+
597+
// Not yet implemented libgit2_opts signatures:
598+
// git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, size_t*)
599+
// git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, size_t)
600+
// git_libgit2_opts(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, size_t*)
601+
// git_libgit2_opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, size_t)
602+
// git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, git_otype type, size_t size)
603+
// git_libgit2_opts(GIT_OPT_SET_CACHE_MAX_SIZE, ssize_t max_storage_bytes)
604+
// git_libgit2_opts(GIT_OPT_ENABLE_CACHING, int enabled)
605+
// git_libgit2_opts(GIT_OPT_GET_CACHED_MEMORY, ssize_t* current, ssize_t* allowed)
606+
// git_libgit2_opts(GIT_OPT_GET_TEMPLATE_PATH, git_buf*out)
607+
// git_libgit2_opts(GIT_OPT_SET_TEMPLATE_PATH, const char* path)
608+
// git_libgit2_opts(GIT_OPT_SET_SSL_CERT_LOCATIONS, const char* file, const char* path)
609+
584610
[DllImport(libgit2)]
585611
internal static extern int git_graph_ahead_behind(out UIntPtr ahead, out UIntPtr behind, RepositorySafeHandle repo, ref GitOid one, ref GitOid two);
586612

LibGit2Sharp/Core/Proxy.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3193,6 +3193,60 @@ public static BuiltInFeatures git_libgit2_features()
31933193
return (BuiltInFeatures)NativeMethods.git_libgit2_features();
31943194
}
31953195

3196+
// C# equivalent of libgit2's git_libgit2_opt_t
3197+
private enum LibGitOption
3198+
{
3199+
GetMWindowSize, // GIT_OPT_GET_MWINDOW_SIZE
3200+
SetMWindowSize, // GIT_OPT_SET_MWINDOW_SIZE
3201+
GetMWindowMappedLimit, // GIT_OPT_GET_MWINDOW_MAPPED_LIMIT
3202+
SetMWindowMappedLimit, // GIT_OPT_SET_MWINDOW_MAPPED_LIMIT
3203+
GetSearchPath, // GIT_OPT_GET_SEARCH_PATH
3204+
SetSearchPath, // GIT_OPT_SET_SEARCH_PATH
3205+
SetCacheObjectLimit, // GIT_OPT_SET_CACHE_OBJECT_LIMIT
3206+
SetCacheMaxSize, // GIT_OPT_SET_CACHE_MAX_SIZE
3207+
EnableCaching, // GIT_OPT_ENABLE_CACHING
3208+
GetCachedMemory, // GIT_OPT_GET_CACHED_MEMORY
3209+
GetTemplatePath, // GIT_OPT_GET_TEMPLATE_PATH
3210+
SetTemplatePath, // GIT_OPT_SET_TEMPLATE_PATH
3211+
SetSslCertLocations, // GIT_OPT_SET_SSL_CERT_LOCATIONS
3212+
}
3213+
3214+
/// <summary>
3215+
/// Get the paths under which libgit2 searches for the configuration file of a given level.
3216+
/// </summary>
3217+
/// <param name="level">The level (global/system/XDG) of the config.</param>
3218+
/// <returns>
3219+
/// The paths delimited by 'GIT_PATH_LIST_SEPARATOR' (<see cref="GlobalSettings.PathListSeparator"/>).
3220+
/// </returns>
3221+
public static string git_libgit2_opts_get_search_path(ConfigurationLevel level)
3222+
{
3223+
string path;
3224+
3225+
using (var buf = new GitBuf())
3226+
{
3227+
var res = NativeMethods.git_libgit2_opts((int)LibGitOption.GetSearchPath, (uint)level, buf);
3228+
Ensure.ZeroResult(res);
3229+
3230+
path = LaxUtf8Marshaler.FromNative(buf.ptr) ?? string.Empty;
3231+
}
3232+
3233+
return path;
3234+
}
3235+
3236+
/// <summary>
3237+
/// Set the path(s) under which libgit2 searches for the configuration file of a given level.
3238+
/// </summary>
3239+
/// <param name="level">The level (global/system/XDG) of the config.</param>
3240+
/// <param name="path">
3241+
/// A string of paths delimited by 'GIT_PATH_LIST_SEPARATOR' (<see cref="GlobalSettings.PathListSeparator"/>).
3242+
/// Pass null to reset the search path to the default.
3243+
/// </param>
3244+
public static void git_libgit2_opts_set_search_path(ConfigurationLevel level, string path)
3245+
{
3246+
var res = NativeMethods.git_libgit2_opts((int)LibGitOption.SetSearchPath, (uint)level, path);
3247+
Ensure.ZeroResult(res);
3248+
}
3249+
31963250
#endregion
31973251

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

LibGit2Sharp/GlobalSettings.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Reflection;
5+
using System.Collections.Generic;
56
using LibGit2Sharp.Core;
67

78
namespace LibGit2Sharp
@@ -272,5 +273,48 @@ internal static void DeregisterFilter(Filter filter)
272273
DeregisterFilter(registration);
273274
}
274275
}
276+
277+
/// <summary>
278+
/// The separator used in path list strings (like in the PATH environment variable).
279+
/// A semi-colon ";" is used on Windows, and a colon ":" for all other systems.
280+
/// </summary>
281+
public static char PathListSeparator
282+
{
283+
get
284+
{
285+
return (Platform.OperatingSystem == OperatingSystemType.Windows) ? ';' : ':';
286+
}
287+
}
288+
289+
/// <summary>
290+
/// Get the paths under which libgit2 searches for the configuration file of a given level.
291+
/// </summary>
292+
/// <param name="level">The level (global/system/XDG) of the config.</param>
293+
/// <returns>The paths that are searched</returns>
294+
public static IEnumerable<string> GetConfigSearchPaths(ConfigurationLevel level)
295+
{
296+
return Proxy.git_libgit2_opts_get_search_path(level).Split(PathListSeparator);
297+
}
298+
299+
/// <summary>
300+
/// Set the path under which libgit2 searches for the configuration file of a given level.
301+
/// </summary>
302+
/// <param name="level">The level (global/system/XDG) of the config.</param>
303+
/// <param name="path">The new search path, or null to reset to default.</param>
304+
public static void SetConfigSearchPath(ConfigurationLevel level, string path)
305+
{
306+
Proxy.git_libgit2_opts_set_search_path(level, path);
307+
}
308+
309+
/// <summary>
310+
/// Set the paths under which libgit2 searches for the configuration file of a given level.
311+
/// </summary>
312+
/// <param name="level">The level (global/system/XDG) of the config.</param>
313+
/// <param name="paths">The new search paths, or null to reset to default.</param>
314+
public static void SetConfigSearchPaths(ConfigurationLevel level, IEnumerable<string> paths)
315+
{
316+
var pathString = (paths == null) ? null : string.Join(PathListSeparator.ToString(), paths);
317+
Proxy.git_libgit2_opts_set_search_path(level, pathString);
318+
}
275319
}
276320
}

0 commit comments

Comments
 (0)