diff --git a/LibGit2Sharp.Tests/RemoteFixture.cs b/LibGit2Sharp.Tests/RemoteFixture.cs index 8140508ef..dda9fdb01 100644 --- a/LibGit2Sharp.Tests/RemoteFixture.cs +++ b/LibGit2Sharp.Tests/RemoteFixture.cs @@ -103,7 +103,7 @@ public void CanFetchIntoAnEmptyRepository(string url) } // Perform the actual fetch - remote.Fetch(new FetchProgress(), onUpdateTips: expectedFetchState.RemoteUpdateTipsHandler); + remote.Fetch(onUpdateTips: expectedFetchState.RemoteUpdateTipsHandler); // Verify the expected expectedFetchState.CheckUpdatedReferences(repo); @@ -141,7 +141,7 @@ public void CanFetchAllTagsIntoAnEmptyRepository(string url) } // Perform the actual fetch - remote.Fetch(new FetchProgress(), TagFetchMode.All, onUpdateTips: expectedFetchState.RemoteUpdateTipsHandler); + remote.Fetch(TagFetchMode.All, onUpdateTips: expectedFetchState.RemoteUpdateTipsHandler); // Verify the expected expectedFetchState.CheckUpdatedReferences(repo); diff --git a/LibGit2Sharp/Core/GitTransferProgress.cs b/LibGit2Sharp/Core/GitTransferProgress.cs new file mode 100644 index 000000000..b57dadf55 --- /dev/null +++ b/LibGit2Sharp/Core/GitTransferProgress.cs @@ -0,0 +1,17 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + /// + /// Managed structure corresponding to git_transfer_progress native structure. + /// + [StructLayout(LayoutKind.Sequential)] + internal struct GitTransferProgress + { + public uint total_objects; + public uint indexed_objects; + public uint received_objects; + public UIntPtr received_bytes; + } +} diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index 06892dc0a..b47f58d82 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -784,7 +784,7 @@ internal static extern int git_tag_delete( [DllImport(libgit2)] internal static extern void git_threads_shutdown(); - internal delegate void git_transfer_progress_callback(IntPtr stats, IntPtr payload); + internal delegate void git_transfer_progress_callback(ref GitTransferProgress stats, IntPtr payload); [DllImport(libgit2)] internal static extern int git_tree_create_fromindex(out GitOid treeOid, IndexSafeHandle index); diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index 823fca66d..d60ce2cb7 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -6,6 +6,7 @@ using System.Runtime.InteropServices; using System.Threading; using LibGit2Sharp.Core.Handles; +using LibGit2Sharp.Handlers; // ReSharper disable InconsistentNaming namespace LibGit2Sharp.Core @@ -1046,12 +1047,13 @@ public static void git_remote_disconnect(RemoteSafeHandle remote) } } - // TODO: callback & payload - public static void git_remote_download(RemoteSafeHandle remote, ref long bytes, ref GitIndexerStats indexerStats) + public static void git_remote_download(RemoteSafeHandle remote, TransferProgressHandler onTransferProgress) { using (ThreadAffinity()) { - int res = NativeMethods.git_remote_download(remote, null, IntPtr.Zero); + NativeMethods.git_transfer_progress_callback cb = TransferCallbacks.GenerateCallback(onTransferProgress); + + int res = NativeMethods.git_remote_download(remote, cb, IntPtr.Zero); Ensure.Success(res); } } diff --git a/LibGit2Sharp/FetchProgress.cs b/LibGit2Sharp/FetchProgress.cs deleted file mode 100644 index 0f99bbc4d..000000000 --- a/LibGit2Sharp/FetchProgress.cs +++ /dev/null @@ -1,52 +0,0 @@ -using LibGit2Sharp.Core; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; - -namespace LibGit2Sharp -{ - /// - /// Contains data regarding fetch progress. - /// - public class FetchProgress - { - /// - /// Fetch progress constructor. - /// - public FetchProgress() - { - IndexerStats = new IndexerStats(); - } - - /// - /// Bytes received. - /// - public long Bytes - { - get - { - // read the bytes atomically - return Interlocked.Read(ref bytes); - } - } - - /// - /// The IndexerStats - /// - public IndexerStats IndexerStats { get; private set; } - - internal void Reset() - { - IndexerStats.Reset(); - bytes = 0; - } - - #region Fields - - internal long bytes; - - #endregion - } -} diff --git a/LibGit2Sharp/Handlers.cs b/LibGit2Sharp/Handlers.cs index fca898048..bf88a4f73 100644 --- a/LibGit2Sharp/Handlers.cs +++ b/LibGit2Sharp/Handlers.cs @@ -1,12 +1,19 @@ using System; +using LibGit2Sharp.Core; namespace LibGit2Sharp.Handlers { /// - /// Delegate definition to handle Progress callback. + /// Delegate definition to handle Progress callback. + /// Returns the text as reported by the server. The text + /// in the serverProgressOutput parameter is not delivered + /// in any particular units (i.e. not necessarily delivered + /// as whole lines) and is likely to be chunked as partial lines. /// - /// Progress message. - public delegate void ProgressHandler(string message); + /// text reported by the server. + /// Text can be chunked at arbitrary increments (i.e. can be composed + /// of a partial line of text). + public delegate void ProgressHandler(string serverProgressOutput); /// /// Delegate definition to handle UpdateTips callback. @@ -23,4 +30,10 @@ namespace LibGit2Sharp.Handlers /// /// public delegate int CompletionHandler(RemoteCompletionType RemoteCompletionType); + + /// + /// Delegate definition for transfer progress callback. + /// + /// The object containing progress information. + public delegate void TransferProgressHandler(TransferProgress progress); } diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index 8de2023b4..f0d357f68 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -70,6 +70,7 @@ + @@ -79,7 +80,6 @@ - @@ -121,6 +121,8 @@ + + diff --git a/LibGit2Sharp/Remote.cs b/LibGit2Sharp/Remote.cs index e5a43c2dd..32ee2a879 100644 --- a/LibGit2Sharp/Remote.cs +++ b/LibGit2Sharp/Remote.cs @@ -51,20 +51,19 @@ internal static Remote BuildFromPtr(RemoteSafeHandle handle, Repository repo) /// /// Fetch from the . /// - /// The datastructure where the progress of the fetch is reported. /// Optional parameter indicating what tags to download. /// Progress callback. Corresponds to libgit2 progress callback. /// Completion callback. Corresponds to libgit2 completion callback. /// UpdateTips callback. Corresponds to libgit2 update_tips callback. - public virtual void Fetch(FetchProgress progress = null, + /// Callback method that transfer progress will be reported through. + /// Reports the client's state regarding the received and processed (bytes, objects) from the server. + public virtual void Fetch( TagFetchMode tagFetchMode = TagFetchMode.Auto, ProgressHandler onProgress = null, CompletionHandler onCompletion = null, - UpdateTipsHandler onUpdateTips = null) + UpdateTipsHandler onUpdateTips = null, + TransferProgressHandler onTransferProgress = null) { - progress = progress ?? new FetchProgress(); - progress.Reset(); - using (RemoteSafeHandle remoteHandle = Proxy.git_remote_load(repository.Handle, this.Name, true)) { var callbacks = new RemoteCallbacks(onProgress, onCompletion, onUpdateTips); @@ -85,8 +84,7 @@ public virtual void Fetch(FetchProgress progress = null, try { Proxy.git_remote_connect(remoteHandle, GitDirection.Fetch); - - Proxy.git_remote_download(remoteHandle, ref progress.bytes, ref progress.IndexerStats.gitIndexerStats); + Proxy.git_remote_download(remoteHandle, onTransferProgress); } finally { diff --git a/LibGit2Sharp/RepositoryExtensions.cs b/LibGit2Sharp/RepositoryExtensions.cs index d59fbefdc..32572ae1f 100644 --- a/LibGit2Sharp/RepositoryExtensions.cs +++ b/LibGit2Sharp/RepositoryExtensions.cs @@ -151,22 +151,24 @@ public static Commit Commit(this IRepository repository, string message, Signatu /// /// The being worked with. /// The name of the to fetch from. - /// The datastructure where the progress of the fetch is reported. /// Optional parameter indicating what tags to download. /// Progress callback. Corresponds to libgit2 progress callback. /// Completion callback. Corresponds to libgit2 completion callback. /// UpdateTips callback. Corresponds to libgit2 update_tips callback. - public static void Fetch(this IRepository repository, string remoteName, FetchProgress progress = null, + /// Callback method that transfer progress will be reported through. + /// Reports the client's state regarding the received and processed (bytes, objects) from the server. + public static void Fetch(this IRepository repository, string remoteName, TagFetchMode tagFetchMode = TagFetchMode.Auto, ProgressHandler onProgress = null, CompletionHandler onCompletion = null, - UpdateTipsHandler onUpdateTips = null) + UpdateTipsHandler onUpdateTips = null, + TransferProgressHandler onTransferProgress = null) { Ensure.ArgumentNotNull(repository, "repository"); Ensure.ArgumentNotNullOrEmptyString(remoteName, "remoteName"); Remote remote = repository.Remotes.RemoteForName(remoteName, true); - remote.Fetch(progress, tagFetchMode, onProgress, onCompletion, onUpdateTips); + remote.Fetch(tagFetchMode, onProgress, onCompletion, onUpdateTips, onTransferProgress); } private static Signature BuildSignatureFromGlobalConfiguration(IRepository repository, DateTimeOffset now) diff --git a/LibGit2Sharp/TransferCallbacks.cs b/LibGit2Sharp/TransferCallbacks.cs new file mode 100644 index 000000000..f5ccd8f52 --- /dev/null +++ b/LibGit2Sharp/TransferCallbacks.cs @@ -0,0 +1,54 @@ +using System; +using LibGit2Sharp.Core; +using LibGit2Sharp.Handlers; + +namespace LibGit2Sharp +{ + /// + /// Class to handle the mapping between libgit2 git_transfer_progress_callback function and + /// a corresponding . Generates a delegate that + /// wraps the delegate with a delegate that matches + /// the git_transfer_progress_callback signature. + /// + internal class TransferCallbacks + { + /// + /// Managed delegate to be called in response to a git_transfer_progress_callback callback from libgit2. + /// + private TransferProgressHandler onTransferProgress; + + /// + /// Constructor to set up the native callback given managed delegate. + /// + /// The delegate that the git_transfer_progress_callback will call. + private TransferCallbacks(TransferProgressHandler onTransferProgress) + { + this.onTransferProgress = onTransferProgress; + } + + /// + /// Generates a delegate that matches the native git_transfer_progress_callback function's signature and wraps the delegate. + /// + /// The delegate to call in responde to a the native git_transfer_progress_callback callback. + /// A delegate method with a signature that matches git_transfer_progress_callback. + internal static NativeMethods.git_transfer_progress_callback GenerateCallback(TransferProgressHandler onTransferProgress) + { + if (onTransferProgress == null) + { + return null; + } + + return new TransferCallbacks(onTransferProgress).OnGitTransferProgress; + } + + /// + /// The delegate with the signature that matches the native git_transfer_progress_callback function's signature. + /// + /// structure containing progress information. + /// Payload data. + private void OnGitTransferProgress(ref GitTransferProgress progress, IntPtr payload) + { + onTransferProgress(new TransferProgress(progress)); + } + } +} diff --git a/LibGit2Sharp/TransferProgress.cs b/LibGit2Sharp/TransferProgress.cs new file mode 100644 index 000000000..f3acfe679 --- /dev/null +++ b/LibGit2Sharp/TransferProgress.cs @@ -0,0 +1,71 @@ +using System; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// Expose progress values from a fetch operation. + /// + public class TransferProgress + { + private GitTransferProgress gitTransferProgress; + + /// + /// Empty constructor. + /// + protected TransferProgress() + { } + + /// + /// Constructor. + /// + internal TransferProgress(GitTransferProgress gitTransferProgress) + { + this.gitTransferProgress = gitTransferProgress; + } + + /// + /// Total number of objects. + /// + public virtual int TotalObjects + { + get + { + return (int) gitTransferProgress.total_objects; + } + } + + /// + /// Number of objects indexed. + /// + public virtual int IndexedObjects + { + get + { + return (int) gitTransferProgress.indexed_objects; + } + } + + /// + /// Number of objects received. + /// + public virtual int ReceivedObjects + { + get + { + return (int) gitTransferProgress.received_objects; + } + } + + /// + /// Number of bytes received. + /// + public virtual long ReceivedBytes + { + get + { + return (long) gitTransferProgress.received_bytes; + } + } + } +}