diff --git a/LibGit2Sharp.Tests/PushFixture.cs b/LibGit2Sharp.Tests/PushFixture.cs index af26b7577..2b836e457 100644 --- a/LibGit2Sharp.Tests/PushFixture.cs +++ b/LibGit2Sharp.Tests/PushFixture.cs @@ -60,9 +60,20 @@ private void AssertPush(Action push) [Fact] public void CanPushABranchTrackingAnUpstreamBranch() { + bool packBuilderCalled = false; + Handlers.PackBuilderProgressHandler packBuilderCb = (x, y, z) => { packBuilderCalled = true; return false; }; + AssertPush(repo => repo.Network.Push(repo.Head)); AssertPush(repo => repo.Network.Push(repo.Branches["master"])); - AssertPush(repo => repo.Network.Push(repo.Network.Remotes["origin"], "HEAD", @"refs/heads/master", OnPushStatusError)); + + PushOptions options = new PushOptions() + { + OnPushStatusError = OnPushStatusError, + OnPackBuilderProgress = packBuilderCb, + }; + + AssertPush(repo => repo.Network.Push(repo.Network.Remotes["origin"], "HEAD", @"refs/heads/master", options)); + Assert.True(packBuilderCalled); } [Fact] diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index c84c99110..57ef92df8 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -626,6 +626,18 @@ private static bool IsRunningOnLinux() [DllImport(libgit2)] internal static extern int git_push_new(out PushSafeHandle push, RemoteSafeHandle remote); + /* Push network progress notification function */ + internal delegate int git_push_transfer_progress(uint current, uint total, UIntPtr bytes, IntPtr payload); + internal delegate int git_packbuilder_progress(int stage, uint current, uint total, IntPtr payload); + + [DllImport(libgit2)] + internal static extern int git_push_set_callbacks( + PushSafeHandle push, + git_packbuilder_progress pack_progress_cb, + IntPtr pack_progress_cb_payload, + git_push_transfer_progress transfer_progress_cb, + IntPtr transfer_progress_cb_payload); + [DllImport(libgit2)] internal static extern int git_push_set_options(PushSafeHandle push, GitPushOptions options); diff --git a/LibGit2Sharp/Core/PackbuilderCallbacks.cs b/LibGit2Sharp/Core/PackbuilderCallbacks.cs new file mode 100644 index 000000000..880748c31 --- /dev/null +++ b/LibGit2Sharp/Core/PackbuilderCallbacks.cs @@ -0,0 +1,39 @@ +using System; +using LibGit2Sharp.Handlers; + +namespace LibGit2Sharp.Core +{ + // + internal class PackbuilderCallbacks + { + private readonly PackBuilderProgressHandler onPackBuilderProgress; + + /// S + /// Constructor to set up the native callback given managed delegate. + /// + /// The delegate that the git_packbuilder_progress will call. + internal PackbuilderCallbacks(PackBuilderProgressHandler onPackBuilderProgress) + { + this.onPackBuilderProgress = onPackBuilderProgress; + } + + /// + /// Generates a delegate that matches the native git_packbuilder_progress function's signature and wraps the delegate. + /// + /// A delegate method with a signature that matches git_transfer_progress_callback. + internal NativeMethods.git_packbuilder_progress GenerateCallback() + { + if (onPackBuilderProgress == null) + { + return null; + } + + return new PackbuilderCallbacks(onPackBuilderProgress).OnGitPackBuilderProgress; + } + + private int OnGitPackBuilderProgress(int stage, uint current, uint total, IntPtr payload) + { + return onPackBuilderProgress((PackBuilderStage) stage, (int)current, (int)total) ? -1 : 0; + } + } +} diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index 5fdd63906..3cd16b8bd 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -1134,6 +1134,17 @@ public static PushSafeHandle git_push_new(RemoteSafeHandle remote) return handle; } } + public static void git_push_set_callbacks( + PushSafeHandle push, + NativeMethods.git_push_transfer_progress pushTransferProgress, + NativeMethods.git_packbuilder_progress packBuilderProgress) + { + using (ThreadAffinity()) + { + int res = NativeMethods.git_push_set_callbacks(push, packBuilderProgress, IntPtr.Zero, pushTransferProgress, IntPtr.Zero); + Ensure.ZeroResult(res); + } + } public static void git_push_set_options(PushSafeHandle push, GitPushOptions options) { diff --git a/LibGit2Sharp/Core/PushTransferProgressCallbacks.cs b/LibGit2Sharp/Core/PushTransferProgressCallbacks.cs new file mode 100644 index 000000000..b5cee8420 --- /dev/null +++ b/LibGit2Sharp/Core/PushTransferProgressCallbacks.cs @@ -0,0 +1,38 @@ +using System; +using LibGit2Sharp.Handlers; + +namespace LibGit2Sharp.Core +{ + internal class PushTransferCallbacks + { + private readonly PushTransferProgressHandler onPushTransferProgress; + + /// + /// Constructor to set up the native callback given managed delegate. + /// + /// The delegate that the git_transfer_progress_callback will call. + internal PushTransferCallbacks(PushTransferProgressHandler onPushTransferProgress) + { + this.onPushTransferProgress = onPushTransferProgress; + } + + /// + /// Generates a delegate that matches the native git_transfer_progress_callback function's signature and wraps the delegate. + /// + /// A delegate method with a signature that matches git_transfer_progress_callback. + internal NativeMethods.git_push_transfer_progress GenerateCallback() + { + if (onPushTransferProgress == null) + { + return null; + } + + return new PushTransferCallbacks(onPushTransferProgress).OnGitTransferProgress; + } + + private int OnGitTransferProgress(uint current, uint total, UIntPtr bytes, IntPtr payload) + { + return onPushTransferProgress((int)current, (int)total, (long)bytes) ? -1 : 0; + } + } +} diff --git a/LibGit2Sharp/Handlers.cs b/LibGit2Sharp/Handlers.cs index 4d8ceefe4..85e0dd9e5 100644 --- a/LibGit2Sharp/Handlers.cs +++ b/LibGit2Sharp/Handlers.cs @@ -33,6 +33,24 @@ /// Return negative integer to cancel. public delegate int TransferProgressHandler(TransferProgress progress); + /// + /// Delegate definition for callback reporting push network progress. + /// + /// The current number of objects sent to server. + /// The total number of objects to send to the server. + /// The number of bytes sent to the server. + /// True to cancel. + public delegate bool PushTransferProgressHandler(int current, int total, long bytes); + + /// + /// Delegate definition for callback reporting pack builder progress. + /// + /// The current stage progress is being reported for. + /// The current number of objects processed in this this stage. + /// The total number of objects to process for the current stage. + /// True to cancel. + public delegate bool PackBuilderProgressHandler(PackBuilderStage stage, int current, int total); + /// /// Delegate definition to handle reporting errors when updating references on the remote. /// @@ -63,4 +81,20 @@ /// /// The unmatched path. public delegate void UnmatchedPathHandler(string unmatchedPath); + + /// + /// The stages of pack building. + /// + public enum PackBuilderStage + { + /// + /// Counting stage. + /// + Counting, + + /// + /// Deltafying stage. + /// + Deltafying + } } diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index 620847f10..2e9b9d340 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -72,6 +72,8 @@ + + diff --git a/LibGit2Sharp/Network.cs b/LibGit2Sharp/Network.cs index b77cb8792..7b69a37b6 100644 --- a/LibGit2Sharp/Network.cs +++ b/LibGit2Sharp/Network.cs @@ -150,13 +150,11 @@ public virtual IEnumerable ListReferences(Remote remote) /// The to push to. /// The source objectish to push. /// The reference to update on the remote. - /// Handler for reporting failed push updates. /// controlling push behavior public virtual void Push( Remote remote, string objectish, string destinationSpec, - PushStatusErrorHandler onPushStatusError, PushOptions pushOptions = null) { Ensure.ArgumentNotNull(remote, "remote"); @@ -164,7 +162,7 @@ public virtual IEnumerable ListReferences(Remote remote) Ensure.ArgumentNotNullOrEmptyString(destinationSpec, destinationSpec); Push(remote, string.Format(CultureInfo.InvariantCulture, - "{0}:{1}", objectish, destinationSpec), onPushStatusError, pushOptions); + "{0}:{1}", objectish, destinationSpec), pushOptions); } /// @@ -172,18 +170,16 @@ public virtual IEnumerable ListReferences(Remote remote) /// /// The to push to. /// The pushRefSpec to push. - /// Handler for reporting failed push updates. /// controlling push behavior public virtual void Push( Remote remote, string pushRefSpec, - PushStatusErrorHandler onPushStatusError, PushOptions pushOptions = null) { Ensure.ArgumentNotNull(remote, "remote"); Ensure.ArgumentNotNullOrEmptyString(pushRefSpec, "pushRefSpec"); - Push(remote, new string[] { pushRefSpec }, onPushStatusError, pushOptions); + Push(remote, new string[] { pushRefSpec }, pushOptions); } /// @@ -191,12 +187,10 @@ public virtual IEnumerable ListReferences(Remote remote) /// /// The to push to. /// The pushRefSpecs to push. - /// Handler for reporting failed push updates. /// controlling push behavior public virtual void Push( Remote remote, IEnumerable pushRefSpecs, - PushStatusErrorHandler onPushStatusError, PushOptions pushOptions = null) { Ensure.ArgumentNotNull(remote, "remote"); @@ -213,7 +207,7 @@ public virtual IEnumerable ListReferences(Remote remote) pushOptions = new PushOptions(); } - PushCallbacks pushStatusUpdates = new PushCallbacks(onPushStatusError); + PushCallbacks pushStatusUpdates = new PushCallbacks(pushOptions.OnPushStatusError); // Load the remote. using (RemoteSafeHandle remoteHandle = Proxy.git_remote_load(repository.Handle, remote.Name, true)) @@ -229,6 +223,14 @@ public virtual IEnumerable ListReferences(Remote remote) // Perform the actual push. using (PushSafeHandle pushHandle = Proxy.git_push_new(remoteHandle)) { + PushTransferCallbacks pushTransferCallbacks = new PushTransferCallbacks(pushOptions.OnPushTransferProgress); + PackbuilderCallbacks packBuilderCallbacks = new PackbuilderCallbacks(pushOptions.OnPackBuilderProgress); + + NativeMethods.git_push_transfer_progress pushProgress = pushTransferCallbacks.GenerateCallback(); + NativeMethods.git_packbuilder_progress packBuilderProgress = packBuilderCallbacks.GenerateCallback(); + + Proxy.git_push_set_callbacks(pushHandle, pushProgress, packBuilderProgress); + // Set push options. Proxy.git_push_set_options(pushHandle, new GitPushOptions() diff --git a/LibGit2Sharp/NetworkExtensions.cs b/LibGit2Sharp/NetworkExtensions.cs index 59493b827..943ecac8e 100644 --- a/LibGit2Sharp/NetworkExtensions.cs +++ b/LibGit2Sharp/NetworkExtensions.cs @@ -25,7 +25,7 @@ public static class NetworkExtensions PushStatusErrorHandler onPushStatusError = null, PushOptions pushOptions = null) { - network.Push(new[] { branch }, onPushStatusError, pushOptions); + network.Push(new[] { branch }, pushOptions); } /// @@ -33,13 +33,11 @@ public static class NetworkExtensions /// /// The being worked with. /// The branches to push. - /// Handler for reporting failed push updates. /// controlling push behavior /// Throws if either the Remote or the UpstreamBranchCanonicalName is not set. public static void Push( this Network network, IEnumerable branches, - PushStatusErrorHandler onPushStatusError = null, PushOptions pushOptions = null) { var enumeratedBranches = branches as IList ?? branches.ToList(); @@ -55,80 +53,8 @@ public static class NetworkExtensions foreach (var branch in enumeratedBranches) { - network.Push(branch.Remote, string.Format("{0}:{1}", branch.CanonicalName, branch.UpstreamBranchCanonicalName), onPushStatusError, pushOptions); + network.Push(branch.Remote, string.Format("{0}:{1}", branch.CanonicalName, branch.UpstreamBranchCanonicalName), pushOptions); } } - - /// - /// Push the objectish to the destination reference on the . - /// - /// The being worked with. - /// The to push to. - /// The source objectish to push. - /// The reference to update on the remote. - /// controlling push behavior - /// Results of the push operation. - public static PushResult Push( - this Network network, - Remote remote, - string objectish, - string destinationSpec, - PushOptions pushOptions = null) - { - Ensure.ArgumentNotNull(remote, "remote"); - Ensure.ArgumentNotNull(objectish, "objectish"); - Ensure.ArgumentNotNullOrEmptyString(destinationSpec, "destinationSpec"); - - return network.Push(remote, string.Format(CultureInfo.InvariantCulture, - "{0}:{1}", objectish, destinationSpec), pushOptions); - } - - /// - /// Push specified reference to the . - /// - /// The being worked with. - /// The to push to. - /// The pushRefSpec to push. - /// controlling push behavior - /// Results of the push operation. - public static PushResult Push( - this Network network, - Remote remote, - string pushRefSpec, - PushOptions pushOptions = null) - { - Ensure.ArgumentNotNull(remote, "remote"); - Ensure.ArgumentNotNullOrEmptyString(pushRefSpec, "pushRefSpec"); - - return network.Push(remote, new string[] { pushRefSpec }, pushOptions); - } - - /// - /// Push specified references to the . - /// - /// The being worked with. - /// The to push to. - /// The pushRefSpecs to push. - /// controlling push behavior - /// Results of the push operation. - public static PushResult Push( - this Network network, - Remote remote, - IEnumerable pushRefSpecs, - PushOptions pushOptions = null) - { - Ensure.ArgumentNotNull(remote, "remote"); - Ensure.ArgumentNotNull(pushRefSpecs, "pushRefSpecs"); - - var failedRemoteUpdates = new List(); - - network.Push( - remote, - pushRefSpecs, - failedRemoteUpdates.Add, - pushOptions); - - return new PushResult(failedRemoteUpdates); - } } } diff --git a/LibGit2Sharp/PushOptions.cs b/LibGit2Sharp/PushOptions.cs index 0799877bf..5306f8517 100644 --- a/LibGit2Sharp/PushOptions.cs +++ b/LibGit2Sharp/PushOptions.cs @@ -20,5 +20,20 @@ public sealed class PushOptions /// the number of threads to create. /// public int PackbuilderDegreeOfParallelism { get; set; } + + /// + /// Delegate to report errors when updating references on the remote. + /// + public PushStatusErrorHandler OnPushStatusError { get; set; } + + /// + /// Delegate to report push network transfer progress. + /// + public PushTransferProgressHandler OnPushTransferProgress { get; set; } + + /// + /// Delagate to report pack builder progress. + /// + public PackBuilderProgressHandler OnPackBuilderProgress { get; set; } } }