diff --git a/LibGit2Sharp.Tests/ConfigurationFixture.cs b/LibGit2Sharp.Tests/ConfigurationFixture.cs index 39c37769..9917d92a 100644 --- a/LibGit2Sharp.Tests/ConfigurationFixture.cs +++ b/LibGit2Sharp.Tests/ConfigurationFixture.cs @@ -226,6 +226,35 @@ namespace LibGit2Sharp.Tests } } + [Fact] + public void CanFindInLocalConfig() + { + using (var repo = new Repository(StandardTestRepoPath)) + { + var matches = repo.Config.Find("unit"); + + Assert.NotNull(matches); + Assert.Equal(new[] { "unittests.intsetting", "unittests.longsetting" }, + matches.Select(m => m.Key).ToArray()); + } + } + + [Fact] + public void CanFindInGlobalConfig() + { + string configPath = CreateConfigurationWithDummyUser(Constants.Signature); + var options = new RepositoryOptions { GlobalConfigurationLocation = configPath }; + + using (var repo = new Repository(StandardTestRepoPath, options)) + { + var matches = repo.Config.Find(@"\.name", ConfigurationLevel.Global); + + Assert.NotNull(matches); + Assert.Equal(new[] { "user.name" }, + matches.Select(m => m.Key).ToArray()); + } + } + [Fact] public void CanSetBooleanValue() { diff --git a/LibGit2Sharp/Configuration.cs b/LibGit2Sharp/Configuration.cs index 3734554d..8a2aa8b2 100644 --- a/LibGit2Sharp/Configuration.cs +++ b/LibGit2Sharp/Configuration.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using System.Runtime.InteropServices; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; @@ -235,6 +236,23 @@ namespace LibGit2Sharp } } + /// + /// Find configuration entries matching . + /// + /// A regular expression. + /// The configuration file into which the key should be searched for. + /// Matching entries. + public virtual IEnumerable> Find(string regexp, + ConfigurationLevel level = ConfigurationLevel.Local) + { + Ensure.ArgumentNotNullOrEmptyString(regexp, "regexp"); + + using (ConfigurationSafeHandle h = RetrieveConfigurationHandle(level, true)) + { + return Proxy.git_config_iterator_glob(h, regexp, BuildConfigEntry).ToList(); + } + } + private ConfigurationSafeHandle RetrieveConfigurationHandle(ConfigurationLevel level, bool throwIfStoreHasNotBeenFound) { ConfigurationSafeHandle handle = null; @@ -278,14 +296,16 @@ namespace LibGit2Sharp private IEnumerable> BuildConfigEntries() { - return Proxy.git_config_foreach(configHandle, entryPtr => - { - var entry = (GitConfigEntry)Marshal.PtrToStructure(entryPtr, typeof(GitConfigEntry)); + return Proxy.git_config_foreach(configHandle, BuildConfigEntry); + } - return new ConfigurationEntry(LaxUtf8Marshaler.FromNative(entry.namePtr), - LaxUtf8Marshaler.FromNative(entry.valuePtr), - (ConfigurationLevel)entry.level); - }); + private static ConfigurationEntry BuildConfigEntry(IntPtr entryPtr) + { + var entry = (GitConfigEntry)Marshal.PtrToStructure(entryPtr, typeof(GitConfigEntry)); + + return new ConfigurationEntry(LaxUtf8Marshaler.FromNative(entry.namePtr), + LaxUtf8Marshaler.FromNative(entry.valuePtr), + (ConfigurationLevel)entry.level); } internal Signature BuildSignatureFromGlobalConfiguration(DateTimeOffset now, bool shouldThrowIfNotFound) diff --git a/LibGit2Sharp/Core/Handles/ConfigurationIteratorSafeHandle.cs b/LibGit2Sharp/Core/Handles/ConfigurationIteratorSafeHandle.cs new file mode 100644 index 00000000..0d2cb6ab --- /dev/null +++ b/LibGit2Sharp/Core/Handles/ConfigurationIteratorSafeHandle.cs @@ -0,0 +1,11 @@ +namespace LibGit2Sharp.Core.Handles +{ + internal class ConfigurationIteratorSafeHandle : SafeHandleBase + { + protected override bool ReleaseHandleImpl() + { + Proxy.git_config_iterator_free(handle); + return true; + } + } +} diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index b43e6a99..c250a08d 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -343,6 +343,20 @@ namespace LibGit2Sharp.Core config_foreach_callback callback, IntPtr payload); + [DllImport(libgit2)] + internal static extern int git_config_iterator_glob_new( + out ConfigurationIteratorSafeHandle iter, + ConfigurationSafeHandle cfg, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string regexp); + + [DllImport(libgit2)] + internal static extern int git_config_next( + out IntPtr entry, + ConfigurationIteratorSafeHandle iter); + + [DllImport(libgit2)] + internal static extern void git_config_iterator_free(IntPtr iter); + // Ordinarily we would decorate the `url` parameter with the StrictUtf8Marshaler like we do everywhere // else, but apparently doing a native->managed callback with the 64-bit version of CLR 2.0 can // sometimes vomit when using a custom IMarshaler. So yeah, don't do that. If you need the url, diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index d824a408..82119b2d 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -530,6 +530,31 @@ namespace LibGit2Sharp.Core return git_foreach(resultSelector, c => NativeMethods.git_config_foreach(config, (e, p) => c(e, p), IntPtr.Zero)); } + public static IEnumerable> git_config_iterator_glob( + ConfigurationSafeHandle config, + string regexp, + Func> resultSelector) + { + return git_iterator( + (out ConfigurationIteratorSafeHandle iter) => + NativeMethods.git_config_iterator_glob_new(out iter, config, regexp), + (ConfigurationIteratorSafeHandle iter, out SafeHandleBase handle, out int res) => + { + handle = null; + + IntPtr entry; + res = NativeMethods.git_config_next(out entry, iter); + return new { EntryPtr = entry }; + }, + (handle, payload) => resultSelector(payload.EntryPtr) + ); + } + + public static void git_config_iterator_free(IntPtr iter) + { + NativeMethods.git_config_iterator_free(iter); + } + #endregion #region git_diff_ diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index 513db6a5..d9cd8c73 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -75,6 +75,7 @@ +