From df12e68f934f85cd6c1d0e2a5e9aaeb2dd49da7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 20 Mar 2015 04:17:21 +0100 Subject: [PATCH 1/2] Indexer: add bindings for the pack indexer This allows the user to index a packfile they have in a file or are retrieving through a Stream. --- LibGit2Sharp.Tests/IndexerFixture.cs | 48 +++++++++ LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj | 1 + LibGit2Sharp/Advanced/Indexer.cs | 101 ++++++++++++++++++ .../Core/Handles/IndexerSafeHandle.cs | 13 +++ LibGit2Sharp/Core/NativeMethods.cs | 21 ++++ LibGit2Sharp/Core/Proxy.cs | 46 ++++++++ LibGit2Sharp/LibGit2Sharp.csproj | 2 + LibGit2Sharp/ObjectDatabase.cs | 5 + libgit2 | 2 +- 9 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 LibGit2Sharp.Tests/IndexerFixture.cs create mode 100644 LibGit2Sharp/Advanced/Indexer.cs create mode 100644 LibGit2Sharp/Core/Handles/IndexerSafeHandle.cs diff --git a/LibGit2Sharp.Tests/IndexerFixture.cs b/LibGit2Sharp.Tests/IndexerFixture.cs new file mode 100644 index 000000000..fb478b952 --- /dev/null +++ b/LibGit2Sharp.Tests/IndexerFixture.cs @@ -0,0 +1,48 @@ +using System; +using System.IO; +using LibGit2Sharp.Advanced; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; + +namespace LibGit2Sharp.Tests +{ + public class IndexerFixture : BaseFixture + { + [Fact] + public void CanIndexFromAFilePath() + { + TransferProgress copiedProgress = default(TransferProgress); + using (var repo = new Repository(SandboxStandardTestRepoGitDir())) + { + var path = Path.Combine(repo.Info.Path, "objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack"); + using (var indexer = new Indexer(repo.Info.WorkingDirectory, 438 /* 0666 */, + onProgress: (p) => { + copiedProgress = p; + return true; + })) + { + indexer.Index(path); + Assert.Equal(1628, indexer.Progress.TotalObjects); + Assert.Equal(indexer.Progress.TotalObjects, copiedProgress.TotalObjects); + } + } + } + + [Fact] + public void CanIndexFromAStream() + { + using (var repo = new Repository(SandboxStandardTestRepoGitDir())) + { + var path = Path.Combine(repo.Info.Path, "objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack"); + using (var indexer = new Indexer(repo.Info.WorkingDirectory, 438 /* 0666 */)) + using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read)) + { + indexer.Index(stream); + Assert.Equal(1628, indexer.Progress.TotalObjects); + } + } + } + + } +} + diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index f93a24f30..6aafebda9 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -122,6 +122,7 @@ + diff --git a/LibGit2Sharp/Advanced/Indexer.cs b/LibGit2Sharp/Advanced/Indexer.cs new file mode 100644 index 000000000..54fef8556 --- /dev/null +++ b/LibGit2Sharp/Advanced/Indexer.cs @@ -0,0 +1,101 @@ +using System; +using System.IO; +using LibGit2Sharp.Core; +using LibGit2Sharp.Handlers; +using LibGit2Sharp.Core.Handles; + +namespace LibGit2Sharp.Advanced +{ + public class Indexer : IDisposable + { + + readonly IndexerSafeHandle handle; + readonly TransferProgressHandler callback; + + GitTransferProgress progress; + byte[] buffer; + + /// + /// The indexing progress + /// + /// The progres information for the current operation. + public TransferProgress Progress + { + get + { + return new TransferProgress(progress); + } + } + + public Indexer(string prefix, uint mode, ObjectDatabase odb = null, TransferProgressHandler onProgress = null) + { + /* The runtime won't let us pass null as a SafeHandle, wo create a "dummy" one to represent NULL */ + ObjectDatabaseSafeHandle odbHandle = odb != null ? odb.Handle : new ObjectDatabaseSafeHandle(); + callback = onProgress; + handle = Proxy.git_indexer_new(prefix, odbHandle, mode, GitDownloadTransferProgressHandler); + progress = new GitTransferProgress(); + } + + /// + /// Index the packfile at the specified path. This function runs synchronously and should usually be run + /// in a background thread. + /// + /// The packfile's path + public ObjectId Index(string path) + { + using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read)) + { + return Index(fs); + } + } + + /// + /// Index the packfile from the specified stream. This function runs synchronously and should usually be run + /// in a background thread. + /// + /// The stream from which to read the packfile data + public ObjectId Index(Stream stream) + { + buffer = new byte[65536]; + int read; + + do + { + read = stream.Read(buffer, 0, buffer.Length); + Proxy.git_indexer_append(handle, buffer, (UIntPtr)read, ref progress); + } while (read > 0); + + Proxy.git_indexer_commit(handle, ref progress); + + return Proxy.git_indexer_hash(handle); + } + + // This comes from RemoteCallbacks + /// + /// The delegate with the signature that matches the native git_transfer_progress_callback function's signature. + /// + /// structure containing progress information. + /// Payload data. + /// the result of the wrapped + int GitDownloadTransferProgressHandler(ref GitTransferProgress progress, IntPtr payload) + { + bool shouldContinue = true; + + if (callback != null) + { + shouldContinue = callback(new TransferProgress(progress)); + } + + return Proxy.ConvertResultToCancelFlag(shouldContinue); + } + + #region IDisposable implementation + + public void Dispose() + { + handle.SafeDispose(); + } + + #endregion + } +} diff --git a/LibGit2Sharp/Core/Handles/IndexerSafeHandle.cs b/LibGit2Sharp/Core/Handles/IndexerSafeHandle.cs new file mode 100644 index 000000000..1e60f6491 --- /dev/null +++ b/LibGit2Sharp/Core/Handles/IndexerSafeHandle.cs @@ -0,0 +1,13 @@ +using System; +namespace LibGit2Sharp.Core.Handles +{ + internal class IndexerSafeHandle : SafeHandleBase + { + protected override bool ReleaseHandleImpl() + { + Proxy.git_indexer_free(handle); + return true; + } + } +} + diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index a8fb1eacf..0e84d56f7 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -1578,6 +1578,27 @@ internal static extern int git_treebuilder_insert( [DllImport(libgit2)] internal static extern int git_cherrypick(RepositorySafeHandle repo, GitObjectSafeHandle commit, GitCherryPickOptions options); + + [DllImport(libgit2)] + public static extern int git_indexer_new( + out IndexerSafeHandle handle, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath prefix, + uint mode, + ObjectDatabaseSafeHandle odbHandle, + git_transfer_progress_callback progress_callback, + IntPtr payload); + + [DllImport(libgit2)] + public static extern int git_indexer_append(IndexerSafeHandle idx, byte[] data, UIntPtr size, ref GitTransferProgress progress); + + [DllImport(libgit2)] + public static extern int git_indexer_commit(IndexerSafeHandle idx, ref GitTransferProgress progress); + + [DllImport(libgit2)] + public static extern OidSafeHandle git_indexer_hash(IndexerSafeHandle idx); + + [DllImport(libgit2)] + public static extern void git_indexer_free(IntPtr idx); } } // ReSharper restore InconsistentNaming diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index ab2f7eadc..04bc86fd4 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -1086,6 +1086,52 @@ public static void git_index_clear(Index index) #endregion + #region git_indexer_ + + public static IndexerSafeHandle git_indexer_new(FilePath prefix, ObjectDatabaseSafeHandle odbHandle, uint mode, + NativeMethods.git_transfer_progress_callback progressCallback) + { + using (ThreadAffinity()) + { + IndexerSafeHandle handle; + + int res = NativeMethods.git_indexer_new(out handle, prefix, mode, odbHandle, progressCallback, IntPtr.Zero); + Ensure.ZeroResult(res); + + return handle; + } + } + + public static void git_indexer_append(IndexerSafeHandle handle, byte[] data, UIntPtr length, ref GitTransferProgress progress) + { + using (ThreadAffinity()) + { + int res = NativeMethods.git_indexer_append(handle, data, length, ref progress); + Ensure.ZeroResult(res); + } + } + + public static void git_indexer_commit(IndexerSafeHandle handle, ref GitTransferProgress progress) + { + using (ThreadAffinity()) + { + int res = NativeMethods.git_indexer_commit(handle, ref progress); + Ensure.ZeroResult(res); + } + } + + public static ObjectId git_indexer_hash(IndexerSafeHandle handle) + { + return NativeMethods.git_indexer_hash(handle).MarshalAsObjectId(); + } + + public static void git_indexer_free(IntPtr handle) + { + NativeMethods.git_indexer_free(handle); + } + + #endregion + #region git_merge_ public static IndexSafeHandle git_merge_trees(RepositorySafeHandle repo, GitObjectSafeHandle ancestorTree, GitObjectSafeHandle ourTree, GitObjectSafeHandle theirTree) diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index 3ca4a3892..2aa011cf6 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -340,6 +340,8 @@ + + diff --git a/LibGit2Sharp/ObjectDatabase.cs b/LibGit2Sharp/ObjectDatabase.cs index b4b1da006..a0b77cd77 100644 --- a/LibGit2Sharp/ObjectDatabase.cs +++ b/LibGit2Sharp/ObjectDatabase.cs @@ -60,6 +60,11 @@ IEnumerator IEnumerable.GetEnumerator() #endregion + internal ObjectDatabaseSafeHandle Handle + { + get { return handle; } + } + /// /// Determines if the given object can be found in the object database. /// diff --git a/libgit2 b/libgit2 index 9bbc8f350..8acf058ff 160000 --- a/libgit2 +++ b/libgit2 @@ -1 +1 @@ -Subproject commit 9bbc8f350b80a5a6e94651ec667cf9e5d545b317 +Subproject commit 8acf058ff708582e9bbfe04e297d3b17a4d5c2e8 From 34035087b6db27a043c1c8384f15667bb9220bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 20 Mar 2015 09:10:39 +0100 Subject: [PATCH 2/2] Indexer: use static methods Since you cannot re-use the same Indexer object to index two different files, it doesn't make much sense to provide a class which would allow you to do this. Instead expose two static methods which internally create an Indexer instance and feed it from the stream or file. --- LibGit2Sharp.Tests/IndexerFixture.cs | 23 ++++---- LibGit2Sharp/Advanced/Indexer.cs | 80 +++++++++++++++------------- 2 files changed, 57 insertions(+), 46 deletions(-) diff --git a/LibGit2Sharp.Tests/IndexerFixture.cs b/LibGit2Sharp.Tests/IndexerFixture.cs index fb478b952..73623faae 100644 --- a/LibGit2Sharp.Tests/IndexerFixture.cs +++ b/LibGit2Sharp.Tests/IndexerFixture.cs @@ -14,17 +14,18 @@ public void CanIndexFromAFilePath() TransferProgress copiedProgress = default(TransferProgress); using (var repo = new Repository(SandboxStandardTestRepoGitDir())) { + var expectedId = new ObjectId("a81e489679b7d3418f9ab594bda8ceb37dd4c695"); var path = Path.Combine(repo.Info.Path, "objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack"); - using (var indexer = new Indexer(repo.Info.WorkingDirectory, 438 /* 0666 */, + TransferProgress progress; + var packId = Indexer.Index(out progress, path, repo.Info.WorkingDirectory, 438 /* 0666 */, onProgress: (p) => { copiedProgress = p; return true; - })) - { - indexer.Index(path); - Assert.Equal(1628, indexer.Progress.TotalObjects); - Assert.Equal(indexer.Progress.TotalObjects, copiedProgress.TotalObjects); - } + }); + + Assert.Equal(expectedId, packId); + Assert.Equal(1628, progress.TotalObjects); + Assert.Equal(1628, copiedProgress.TotalObjects); } } @@ -33,12 +34,14 @@ public void CanIndexFromAStream() { using (var repo = new Repository(SandboxStandardTestRepoGitDir())) { + var expectedId = new ObjectId("a81e489679b7d3418f9ab594bda8ceb37dd4c695"); var path = Path.Combine(repo.Info.Path, "objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack"); - using (var indexer = new Indexer(repo.Info.WorkingDirectory, 438 /* 0666 */)) + TransferProgress progress; using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read)) { - indexer.Index(stream); - Assert.Equal(1628, indexer.Progress.TotalObjects); + var packId = Indexer.Index(out progress, stream, repo.Info.WorkingDirectory, 438 /* 0666 */); + Assert.Equal(expectedId, packId); + Assert.Equal(1628, progress.TotalObjects); } } } diff --git a/LibGit2Sharp/Advanced/Indexer.cs b/LibGit2Sharp/Advanced/Indexer.cs index 54fef8556..efdfd32c0 100644 --- a/LibGit2Sharp/Advanced/Indexer.cs +++ b/LibGit2Sharp/Advanced/Indexer.cs @@ -6,68 +6,76 @@ namespace LibGit2Sharp.Advanced { + /// + /// The Indexer is our implementation of the git-index-pack command. It is used to process the packfile + /// which comes from the remote side on a fetch in order to create the corresponding .idx file. + /// public class Indexer : IDisposable { readonly IndexerSafeHandle handle; readonly TransferProgressHandler callback; - GitTransferProgress progress; - byte[] buffer; - - /// - /// The indexing progress - /// - /// The progres information for the current operation. - public TransferProgress Progress - { - get - { - return new TransferProgress(progress); - } - } - - public Indexer(string prefix, uint mode, ObjectDatabase odb = null, TransferProgressHandler onProgress = null) + Indexer(string prefix, uint mode, ObjectDatabase odb = null, TransferProgressHandler onProgress = null) { /* The runtime won't let us pass null as a SafeHandle, wo create a "dummy" one to represent NULL */ ObjectDatabaseSafeHandle odbHandle = odb != null ? odb.Handle : new ObjectDatabaseSafeHandle(); callback = onProgress; handle = Proxy.git_indexer_new(prefix, odbHandle, mode, GitDownloadTransferProgressHandler); - progress = new GitTransferProgress(); } /// - /// Index the packfile at the specified path. This function runs synchronously and should usually be run + /// Index the specified stream. This function runs synchronously; you may want to run it /// in a background thread. /// - /// The packfile's path - public ObjectId Index(string path) + /// The amount of objects processed etc will be written to this structure on exit + /// Stream to run the indexing process on + /// Path in which to store the pack and index files + /// Filemode to use for creating the pack and index files + /// Optional object db to use if the pack contains thin deltas + /// Function to call to report progress. It returns a boolean indicating whether + /// to continue working on the stream + public static ObjectId Index(out TransferProgress progress, Stream stream, string prefix, uint mode, ObjectDatabase odb = null, TransferProgressHandler onProgress = null) { - using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read)) + var buffer = new byte[65536]; + int read; + var indexProgress = default(GitTransferProgress); + + using (var idx = new Indexer(prefix, mode, odb, onProgress)) { - return Index(fs); + var handle = idx.handle; + + do + { + read = stream.Read(buffer, 0, buffer.Length); + Proxy.git_indexer_append(handle, buffer, (UIntPtr)read, ref indexProgress); + } while (read > 0); + + Proxy.git_indexer_commit(handle, ref indexProgress); + + progress = new TransferProgress(indexProgress); + return Proxy.git_indexer_hash(handle); } } /// - /// Index the packfile from the specified stream. This function runs synchronously and should usually be run + /// Index the packfile at the specified path. This function runs synchronously; you may want to run it /// in a background thread. + /// The amount of objects processed etc will be written to this structure on exit + /// Path to the file to index + /// Path in which to store the pack and index files + /// Filemode to use for creating the pack and index files + /// Optional object db to use if the pack contains thin deltas + /// Function to call to report progress. It returns a boolean indicating whether + /// to continue working on the stream + /// - /// The stream from which to read the packfile data - public ObjectId Index(Stream stream) + public static ObjectId Index(out TransferProgress progress, string path, string prefix, uint mode, ObjectDatabase odb = null, TransferProgressHandler onProgress = null) { - buffer = new byte[65536]; - int read; - - do + using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read)) { - read = stream.Read(buffer, 0, buffer.Length); - Proxy.git_indexer_append(handle, buffer, (UIntPtr)read, ref progress); - } while (read > 0); - - Proxy.git_indexer_commit(handle, ref progress); - - return Proxy.git_indexer_hash(handle); + return Index(out progress, fs, prefix, mode, odb, onProgress); + } } // This comes from RemoteCallbacks