From 94066ec0e2c5325c5806ac6a5dfeeb789ebd28dc Mon Sep 17 00:00:00 2001 From: Keith Dahlby Date: Tue, 19 Nov 2013 13:25:38 -0600 Subject: [PATCH 1/4] Refactor git_branch_iterator --- LibGit2Sharp/Core/NativeMethods.cs | 2 +- LibGit2Sharp/Core/Proxy.cs | 16 +++++----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index df136d2f9..b43e6a991 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -182,7 +182,7 @@ internal static extern int git_branch_move( [DllImport(libgit2)] internal static extern int git_branch_next( out ReferenceSafeHandle ref_out, - out IntPtr type_out, + out GitBranchType type_out, BranchIteratorSafeHandle iter); [DllImport(libgit2)] diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index 6f438156a..4f0553717 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -147,32 +147,28 @@ public static IEnumerable git_branch_iterator(Repository repo, GitBranch { BranchIteratorSafeHandle iter_out = null; - var branches = new List(); - try { Ensure.ZeroResult(NativeMethods.git_branch_iterator_new(out iter_out, repo.Handle, branchType)); - int res = 0; - - while (res == (int)GitErrorCode.Ok) + while(true) { ReferenceSafeHandle ref_out = null; try { - IntPtr type_out; - res = NativeMethods.git_branch_next(out ref_out, out type_out, iter_out); + GitBranchType type_out; + var res = NativeMethods.git_branch_next(out ref_out, out type_out, iter_out); if (res == (int) GitErrorCode.IterOver) { - break; + yield break; } Ensure.ZeroResult(res); var reference = Reference.BuildFromPtr(ref_out, repo); - branches.Add(new Branch(repo, reference, reference.CanonicalName)); + yield return new Branch(repo, reference, reference.CanonicalName); } finally { @@ -184,8 +180,6 @@ public static IEnumerable git_branch_iterator(Repository repo, GitBranch { iter_out.SafeDispose(); } - - return branches; } } From 123bd64e59082735e6ed6f0f16562e3cc1f934da Mon Sep 17 00:00:00 2001 From: Keith Dahlby Date: Tue, 19 Nov 2013 14:38:30 -0600 Subject: [PATCH 2/4] Introduce Proxy.git_iterator --- LibGit2Sharp/BranchCollection.cs | 2 +- LibGit2Sharp/Core/Proxy.cs | 111 +++++++++++++++++++++---------- 2 files changed, 76 insertions(+), 37 deletions(-) diff --git a/LibGit2Sharp/BranchCollection.cs b/LibGit2Sharp/BranchCollection.cs index 645922e7e..3c02ee1d4 100644 --- a/LibGit2Sharp/BranchCollection.cs +++ b/LibGit2Sharp/BranchCollection.cs @@ -92,7 +92,7 @@ private Branch BuildFromReferenceName(string canonicalName) public virtual IEnumerator GetEnumerator() { return Proxy.git_branch_iterator(repo, GitBranchType.GIT_BRANCH_LOCAL | GitBranchType.GIT_BRANCH_REMOTE) - .GetEnumerator(); + .ToList().GetEnumerator(); } /// diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index 4f0553717..d824a4087 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -143,44 +143,21 @@ public static void git_branch_delete(ReferenceSafeHandle reference) public static IEnumerable git_branch_iterator(Repository repo, GitBranchType branchType) { - using (ThreadAffinity()) - { - BranchIteratorSafeHandle iter_out = null; - - try - { - Ensure.ZeroResult(NativeMethods.git_branch_iterator_new(out iter_out, repo.Handle, branchType)); - - while(true) + return git_iterator( + (out BranchIteratorSafeHandle iter_out) => + NativeMethods.git_branch_iterator_new(out iter_out, repo.Handle, branchType), + (BranchIteratorSafeHandle iter, out ReferenceSafeHandle ref_out, out int res) => { - ReferenceSafeHandle ref_out = null; - try - { - GitBranchType type_out; - var res = NativeMethods.git_branch_next(out ref_out, out type_out, iter_out); - - if (res == (int) GitErrorCode.IterOver) - { - yield break; - } - - Ensure.ZeroResult(res); - - var reference = Reference.BuildFromPtr(ref_out, repo); - - yield return new Branch(repo, reference, reference.CanonicalName); - } - finally - { - ref_out.SafeDispose(); - } + GitBranchType type_out; + res = NativeMethods.git_branch_next(out ref_out, out type_out, iter); + return new { BranchType = type_out }; + }, + (handle, payload) => + { + var reference = Reference.BuildFromPtr(handle, repo); + return new Branch(repo, reference, reference.CanonicalName); } - } - finally - { - iter_out.SafeDispose(); - } - } + ); } public static void git_branch_iterator_free(IntPtr iter) @@ -2522,6 +2499,68 @@ private static ICollection git_foreach( } } + private delegate int IteratorNew(out THandle iter); + + private delegate TPayload IteratorNext(TIterator iter, out THandle next, out int res); + + private static THandle git_iterator_new(IteratorNew newFunc) + where THandle : SafeHandleBase + { + THandle iter; + Ensure.ZeroResult(newFunc(out iter)); + return iter; + } + + private static IEnumerable git_iterator_next( + TIterator iter, + IteratorNext nextFunc, + Func resultSelector) + where THandle : SafeHandleBase + { + while (true) + { + var next = default(THandle); + try + { + int res; + var payload = nextFunc(iter, out next, out res); + + if (res == (int)GitErrorCode.IterOver) + { + yield break; + } + + Ensure.ZeroResult(res); + yield return resultSelector(next, payload); + } + finally + { + if (next != null) + next.SafeDispose(); + } + } + } + + private static IEnumerable git_iterator( + IteratorNew newFunc, + IteratorNext nextFunc, + Func resultSelector + ) + where TIterator : SafeHandleBase + where THandle : SafeHandleBase + { + using (ThreadAffinity()) + { + using (var iter = git_iterator_new(newFunc)) + { + foreach (var next in git_iterator_next(iter, nextFunc, resultSelector)) + { + yield return next; + } + } + } + } + private static unsafe class Libgit2UnsafeHelper { public static IList BuildListOf(UnSafeNativeMethods.git_strarray strArray) From 3cf04dd7244c2de237f7e997505d3edbdf26ff1c Mon Sep 17 00:00:00 2001 From: Keith Dahlby Date: Tue, 19 Nov 2013 16:19:41 -0600 Subject: [PATCH 3/4] Add Configuration.Find(regexp) --- LibGit2Sharp.Tests/ConfigurationFixture.cs | 29 ++++++++++++++++ LibGit2Sharp/Configuration.cs | 34 +++++++++++++++---- .../ConfigurationIteratorSafeHandle.cs | 11 ++++++ LibGit2Sharp/Core/NativeMethods.cs | 14 ++++++++ LibGit2Sharp/Core/Proxy.cs | 25 ++++++++++++++ LibGit2Sharp/LibGit2Sharp.csproj | 1 + 6 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 LibGit2Sharp/Core/Handles/ConfigurationIteratorSafeHandle.cs diff --git a/LibGit2Sharp.Tests/ConfigurationFixture.cs b/LibGit2Sharp.Tests/ConfigurationFixture.cs index 39c377691..9917d92a0 100644 --- a/LibGit2Sharp.Tests/ConfigurationFixture.cs +++ b/LibGit2Sharp.Tests/ConfigurationFixture.cs @@ -226,6 +226,35 @@ public void CanEnumerateLocalConfigContainingAKeyWithNoValue() } } + [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 3734554dd..8a2aa8b26 100644 --- a/LibGit2Sharp/Configuration.cs +++ b/LibGit2Sharp/Configuration.cs @@ -2,6 +2,7 @@ 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 @@ public virtual void Set(string key, T value, ConfigurationLevel level = Confi } } + /// + /// 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 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 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); + } + + 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); - }); + 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 000000000..0d2cb6ab6 --- /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 b43e6a991..c250a08d2 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -343,6 +343,20 @@ internal static extern int git_config_foreach( 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 d824a4087..82119b2db 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -530,6 +530,31 @@ public static ICollection git_config_foreach( 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 513db6a54..d9cd8c73a 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -75,6 +75,7 @@ + From af3bc8960f3e7556d03ae20ad739e8c2385f57f0 Mon Sep 17 00:00:00 2001 From: Keith Dahlby Date: Wed, 20 Nov 2013 17:54:04 -0600 Subject: [PATCH 4/4] Remove unnecessary global config check --- LibGit2Sharp.Tests/ConfigurationFixture.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/LibGit2Sharp.Tests/ConfigurationFixture.cs b/LibGit2Sharp.Tests/ConfigurationFixture.cs index 9917d92a0..0e7774617 100644 --- a/LibGit2Sharp.Tests/ConfigurationFixture.cs +++ b/LibGit2Sharp.Tests/ConfigurationFixture.cs @@ -194,9 +194,6 @@ public void CanEnumerateGlobalConfig() using (var repo = new Repository(StandardTestRepoPath, options)) { - InconclusiveIf(() => !repo.Config.HasConfig(ConfigurationLevel.Global), - "No Git global configuration available"); - var entry = repo.Config.FirstOrDefault>(e => e.Key == "user.name"); Assert.NotNull(entry); Assert.Equal(Constants.Signature.Name, entry.Value);