diff --git a/Octokit.Tests.Integration/Clients/PullRequestsClientTests.cs b/Octokit.Tests.Integration/Clients/PullRequestsClientTests.cs index 8e0c4384..a5d17a88 100644 --- a/Octokit.Tests.Integration/Clients/PullRequestsClientTests.cs +++ b/Octokit.Tests.Integration/Clients/PullRequestsClientTests.cs @@ -227,7 +227,7 @@ public class PullRequestsClientTests : IDisposable } [IntegrationTest] - public async Task CannotBeMerged() + public async Task CannotBeMergedDueMismatchConflict() { await CreateTheWorld(); var fakeSha = new string('f', 40); @@ -236,9 +236,27 @@ public class PullRequestsClientTests : IDisposable var pullRequest = await _fixture.Create(Helper.UserName, _context.RepositoryName, newPullRequest); var merge = new MergePullRequest { Sha = fakeSha }; - var ex = await Assert.ThrowsAsync(() => _fixture.Merge(Helper.UserName, _context.RepositoryName, pullRequest.Number, merge)); + var ex = await Assert.ThrowsAsync(() => _fixture.Merge(Helper.UserName, _context.RepositoryName, pullRequest.Number, merge)); - Assert.True(ex.ApiError.Message.StartsWith("Head branch was modified")); + Assert.True(ex.Message.StartsWith("Head branch was modified")); + } + + [IntegrationTest] + public async Task CannotBeMergedDueNotInMergeableState() + { + await CreateTheWorld(); + + var master = await _github.GitDatabase.Reference.Get(Helper.UserName, _context.RepositoryName, "heads/master"); + var newMasterTree = await CreateTree(new Dictionary { { "README.md", "Hello World, we meet again!" } }); + await CreateCommit("baseline for pull request", newMasterTree.Sha, master.Object.Sha); + + var newPullRequest = new NewPullRequest("a pull request", branchName, "master"); + var pullRequest = await _fixture.Create(Helper.UserName, _context.RepositoryName, newPullRequest); + + var merge = new MergePullRequest { Sha = pullRequest.Head.Sha }; + var ex = await Assert.ThrowsAsync(() => _fixture.Merge(Helper.UserName, _context.RepositoryName, pullRequest.Number, merge)); + + Assert.True(ex.Message.Equals("Pull Request is not mergeable")); } [IntegrationTest] diff --git a/Octokit/Clients/PullRequestsClient.cs b/Octokit/Clients/PullRequestsClient.cs index e1f2bc99..20f5d6f8 100644 --- a/Octokit/Clients/PullRequestsClient.cs +++ b/Octokit/Clients/PullRequestsClient.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Net; using System.Threading.Tasks; namespace Octokit @@ -115,7 +116,24 @@ namespace Octokit Ensure.ArgumentNotNullOrEmptyString(name, "name"); Ensure.ArgumentNotNull(mergePullRequest, "mergePullRequest"); - return ApiConnection.Put(ApiUrls.MergePullRequest(owner, name, number), mergePullRequest); + try + { + return ApiConnection.Put(ApiUrls.MergePullRequest(owner, name, number), mergePullRequest); + } + catch (ApiException ex) + { + if (ex.StatusCode == HttpStatusCode.MethodNotAllowed) + { + throw new PullRequestNotMergeableException(ex.HttpResponse); + } + + if (ex.StatusCode == HttpStatusCode.Conflict) + { + throw new PullRequestMismatchException(ex.HttpResponse); + } + + throw; + } } /// diff --git a/Octokit/Exceptions/PullRequestMismatchException.cs b/Octokit/Exceptions/PullRequestMismatchException.cs new file mode 100644 index 00000000..f16504b5 --- /dev/null +++ b/Octokit/Exceptions/PullRequestMismatchException.cs @@ -0,0 +1,64 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Net; +using System.Runtime.Serialization; + +namespace Octokit +{ + /// + /// Represents an error that occurs when the specified SHA + /// doesn't match the current pull request's HEAD + /// +#if !NETFX_CORE + [Serializable] +#endif + [SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", + Justification = "These exceptions are specific to the GitHub API and not general purpose exceptions")] + public class PullRequestMismatchException : ApiException + { + /// + /// Constructs an instace of . + /// + /// + public PullRequestMismatchException(IResponse response) : this(response, null) + { + } + + /// + /// Constructs an instance of . + /// + /// The HTTP payload from the server + /// The inner exception + public PullRequestMismatchException(IResponse response, Exception innerException) + : base(response, innerException) + { + Debug.Assert(response != null && response.StatusCode == HttpStatusCode.Conflict, + "PullRequestMismatchException created with the wrong HTTP status code"); + } + + public override string Message + { + //https://developer.github.com/v3/pulls/#response-if-sha-was-provided-and-pull-request-head-did-not-match + get { return ApiErrorMessageSafe ?? "Head branch was modified. Review and try the merge again."; } + } + +#if !NETFX_CORE + /// + /// Constructs an instance of . + /// + /// + /// The that holds the + /// serialized object data about the exception being thrown. + /// + /// + /// The that contains + /// contextual information about the source or destination. + /// + protected PullRequestMismatchException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +} diff --git a/Octokit/Exceptions/PullRequestNotMergeableException.cs b/Octokit/Exceptions/PullRequestNotMergeableException.cs new file mode 100644 index 00000000..ca4143dc --- /dev/null +++ b/Octokit/Exceptions/PullRequestNotMergeableException.cs @@ -0,0 +1,64 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Net; +using System.Runtime.Serialization; + +namespace Octokit +{ + /// + /// Represents an error that occurs when the pull request is in an + /// unmergeable state + /// +#if !NETFX_CORE + [Serializable] +#endif + [SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", + Justification = "These exceptions are specific to the GitHub API and not general purpose exceptions")] + public class PullRequestNotMergeableException : ApiException + { + /// + /// Constructs an instance of the class. + /// + /// The HTTP payload from the server + public PullRequestNotMergeableException(IResponse response) : this(response, null) + { + } + + /// + /// Constructs an instance of the class. + /// + /// The HTTP payload from the server + /// The inner exception + public PullRequestNotMergeableException(IResponse response, Exception innerException) + : base(response, innerException) + { + Debug.Assert(response != null && response.StatusCode == HttpStatusCode.MethodNotAllowed, + "PullRequestNotMergeableException created with the wrong HTTP status code"); + } + + public override string Message + { + //https://developer.github.com/v3/pulls/#response-if-merge-cannot-be-performed + get { return ApiErrorMessageSafe ?? "Pull Request is not mergeable"; } + } + +#if !NETFX_CORE + /// + /// Constructs an instance of . + /// + /// + /// The that holds the + /// serialized object data about the exception being thrown. + /// + /// + /// The that contains + /// contextual information about the source or destination. + /// + protected PullRequestNotMergeableException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +} diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index fd3d9f2c..a1ecc399 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -409,6 +409,8 @@ + + \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index 7a813d2c..a3491a78 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -416,6 +416,8 @@ + + diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 5eb79ac2..26f3ba4e 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -412,6 +412,8 @@ + + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index 6757e1a6..133952d8 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -406,6 +406,8 @@ + + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index d6a2fd84..2f6e1698 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -413,6 +413,8 @@ + + @@ -427,4 +429,4 @@ --> - \ No newline at end of file + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index 2c15e753..aebce259 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -74,6 +74,8 @@ + +