diff --git a/LibGit2Sharp.Tests/IndexerFixture.cs b/LibGit2Sharp.Tests/IndexerFixture.cs
new file mode 100644
index 000000000..73623faae
--- /dev/null
+++ b/LibGit2Sharp.Tests/IndexerFixture.cs
@@ -0,0 +1,51 @@
+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 expectedId = new ObjectId("a81e489679b7d3418f9ab594bda8ceb37dd4c695");
+ var path = Path.Combine(repo.Info.Path, "objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack");
+ TransferProgress progress;
+ var packId = Indexer.Index(out progress, path, repo.Info.WorkingDirectory, 438 /* 0666 */,
+ onProgress: (p) => {
+ copiedProgress = p;
+ return true;
+ });
+
+ Assert.Equal(expectedId, packId);
+ Assert.Equal(1628, progress.TotalObjects);
+ Assert.Equal(1628, copiedProgress.TotalObjects);
+ }
+ }
+
+ [Fact]
+ 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");
+ TransferProgress progress;
+ using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
+ {
+ 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.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..efdfd32c0
--- /dev/null
+++ b/LibGit2Sharp/Advanced/Indexer.cs
@@ -0,0 +1,109 @@
+using System;
+using System.IO;
+using LibGit2Sharp.Core;
+using LibGit2Sharp.Handlers;
+using LibGit2Sharp.Core.Handles;
+
+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;
+
+ 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);
+ }
+
+ ///
+ /// Index the specified stream. 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
+ /// 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)
+ {
+ var buffer = new byte[65536];
+ int read;
+ var indexProgress = default(GitTransferProgress);
+
+ using (var idx = new Indexer(prefix, mode, odb, onProgress))
+ {
+ 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 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
+
+ ///
+ public static ObjectId Index(out TransferProgress progress, string path, string prefix, uint mode, ObjectDatabase odb = null, TransferProgressHandler onProgress = null)
+ {
+ using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
+ {
+ return Index(out progress, fs, prefix, mode, odb, onProgress);
+ }
+ }
+
+ // 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