Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for comparing two commits #428

Merged
merged 11 commits into from
Mar 23, 2014
14 changes: 14 additions & 0 deletions Octokit.Reactive/Clients/IObservableRepositoriesClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,20 @@ public interface IObservableRepositoriesClient
/// </remarks>
IObservableRepoCollaboratorsClient RepoCollaborators { get; }

/// <summary>
/// Client for GitHub's Repository Commits API
/// </summary>
/// <remarks>
/// See the <a href="http://developer.github.com/v3/repos/commits/">Commits API documentation</a> for more details
///</remarks>
IObservableRepositoryCommitsClient Commits { get; }

/// <summary>
/// Client for managing pull requests.
/// </summary>
/// <remarks>
/// See the <a href="http://developer.github.com/v3/pulls/">Pull Requests API documentation</a> for more details
/// </remarks>
IObservablePullRequestsClient PullRequest { get; }
}
}
19 changes: 19 additions & 0 deletions Octokit.Reactive/Clients/IObservableRepositoryCommitsClients.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Diagnostics.CodeAnalysis;

namespace Octokit.Reactive
{
public interface IObservableRepositoryCommitsClient
{
/// <summary>
/// Compare two references in a repository
/// </summary>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="base">The reference to use as the base commit</param>
/// <param name="head">The reference to use as the head commit</param>
/// <returns></returns>
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "base")]
IObservable<CompareResult> Compare(string owner, string name, string @base, string @head);
}
}
22 changes: 22 additions & 0 deletions Octokit.Reactive/Clients/ObservableRepositoriesClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public ObservableRepositoriesClient(IGitHubClient client)
Statistics = new ObservableStatisticsClient(client);
PullRequest = new ObservablePullRequestsClient(client);
RepositoryComments = new ObservableRepositoryCommentsClient(client);
Commits = new ObservableRepositoryCommitsClient(client);
}

/// <summary>
Expand Down Expand Up @@ -328,6 +329,19 @@ public IObservable<Repository> Edit(string owner, string name, RepositoryUpdate
return _client.Edit(owner, name, update).ToObservable();
}

/// <summary>
/// Compare two references in a repository
/// </summary>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="base">The reference to use as the base commit</param>
/// <param name="head">The reference to use as the head commit</param>
/// <returns></returns>
public IObservable<CompareResult> Compare(string owner, string name, string @base, string head)
{
return _client.Commits.Compare(owner, name, @base, head).ToObservable();
}

/// <summary>
/// A client for GitHub's Repo Collaborators.
/// </summary>
Expand All @@ -336,6 +350,14 @@ public IObservable<Repository> Edit(string owner, string name, RepositoryUpdate
/// </remarks>
public IObservableRepoCollaboratorsClient RepoCollaborators { get; private set; }

/// <summary>
/// Client for GitHub's Repository Commits API
/// </summary>
/// <remarks>
/// See the <a href="http://developer.github.com/v3/repos/commits/">Commits API documentation</a> for more details
///</remarks>
public IObservableRepositoryCommitsClient Commits { get; private set; }

/// <summary>
/// Client for managing pull requests.
/// </summary>
Expand Down
28 changes: 28 additions & 0 deletions Octokit.Reactive/Clients/ObservableRepositoryCommitsClients.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Reactive.Threading.Tasks;

namespace Octokit.Reactive
{
public class ObservableRepositoryCommitsClient : IObservableRepositoryCommitsClient
{
readonly IGitHubClient _client;

public ObservableRepositoryCommitsClient(IGitHubClient client)
{
_client = client;
}

/// <summary>
/// Compare two references in a repository
/// </summary>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="base">The reference to use as the base commit</param>
/// <param name="head">The reference to use as the head commit</param>
/// <returns></returns>
public IObservable<CompareResult> Compare(string owner, string name, string @base, string head)
{
return _client.Repository.Commits.Compare(owner, name, @base, head).ToObservable();
}
}
}
2 changes: 2 additions & 0 deletions Octokit.Reactive/Octokit.Reactive-Mono.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@
<Compile Include="Clients\ObservableIssuesLabelsClient.cs" />
<Compile Include="Clients\IObservableRepositoryCommentsClient.cs" />
<Compile Include="Clients\ObservableRepositoryCommentsClient.cs" />
<Compile Include="Clients\IObservableRepositoryCommitsClients.cs" />
<Compile Include="Clients\ObservableRepositoryCommitsClients.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@
<Compile Include="Clients\ObservableIssuesLabelsClient.cs" />
<Compile Include="Clients\IObservableRepositoryCommentsClient.cs" />
<Compile Include="Clients\ObservableRepositoryCommentsClient.cs" />
<Compile Include="Clients\IObservableRepositoryCommitsClients.cs" />
<Compile Include="Clients\ObservableRepositoryCommitsClients.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Novell\Novell.MonoDroid.CSharp.targets" />
<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions Octokit.Reactive/Octokit.Reactive-Monotouch.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@
<Compile Include="Clients\ObservableIssuesLabelsClient.cs" />
<Compile Include="Clients\IObservableRepositoryCommentsClient.cs" />
<Compile Include="Clients\ObservableRepositoryCommentsClient.cs" />
<Compile Include="Clients\IObservableRepositoryCommitsClients.cs" />
<Compile Include="Clients\ObservableRepositoryCommitsClients.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions Octokit.Reactive/Octokit.Reactive.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
<Compile Include="..\SolutionInfo.cs">
<Link>Properties\SolutionInfo.cs</Link>
</Compile>
<Compile Include="Clients\IObservableRepositoryCommitsClients.cs" />
<Compile Include="Clients\ObservableRepositoryCommentsClient.cs" />
<Compile Include="Clients\IObservableRepositoryCommentsClient.cs" />
<Compile Include="Clients\IObservableDeploymentsClient.cs" />
Expand All @@ -87,6 +88,7 @@
<Compile Include="Clients\IObservableStatisticsClient.cs" />
<Compile Include="Clients\ObservableFeedsClient.cs" />
<Compile Include="Clients\ObservableIssuesLabelsClient.cs" />
<Compile Include="Clients\ObservableRepositoryCommitsClients.cs" />
<Compile Include="Clients\ObservableSearchClient.cs" />
<Compile Include="Clients\IObservableBlobsClient.cs" />
<Compile Include="Clients\IObservableGistCommentsClient.cs" />
Expand Down
139 changes: 139 additions & 0 deletions Octokit.Tests.Integration/Clients/RepositoryCommitsClientTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Reactive.Threading.Tasks;
using System.Threading.Tasks;
using Octokit;
using Octokit.Tests.Integration;
using Xunit;

public class RepositoryCommitsClientTests : IDisposable
{
readonly IGitHubClient _client;
IRepositoryCommitsClient _fixture;
Repository _repository;

public RepositoryCommitsClientTests()
{
_client = new GitHubClient(new ProductHeaderValue("OctokitTests"))
{
Credentials = Helper.Credentials
};

_fixture = _client.Repository.Commits;

var repoName = Helper.MakeNameWithTimestamp("source-repo");

_repository = _client.Repository.Create(new NewRepository { Name = repoName, AutoInit = true }).Result;
}

[IntegrationTest]
public async Task CanCompareReferences()
{
await CreateTheWorld();

var result = await _fixture.Compare(Helper.UserName, _repository.Name, "master", "my-branch");

Assert.Equal(1, result.TotalCommits);
Assert.Equal(1, result.Commits.Count);
Assert.Equal(1, result.AheadBy);
Assert.Equal(0, result.BehindBy);
}

[IntegrationTest]
public async Task CanCompareReferencesOtherWayRound()
{
await CreateTheWorld();

var result = await _fixture.Compare(Helper.UserName, _repository.Name, "my-branch", "master");

Assert.Equal(0, result.TotalCommits);
Assert.Equal(0, result.Commits.Count);
Assert.Equal(0, result.AheadBy);
Assert.Equal(1, result.BehindBy);
}

[IntegrationTest]
public async Task ReturnsUrlsToResources()
{
await CreateTheWorld();

var result = await _fixture.Compare(Helper.UserName, _repository.Name, "my-branch", "master");

Assert.NotNull(result.DiffUrl);
Assert.NotNull(result.HtmlUrl);
Assert.NotNull(result.PatchUrl);
Assert.NotNull(result.PermalinkUrl);
}

[IntegrationTest]
public async Task CanCompareUsingSha()
{
await CreateTheWorld();

var master = await _client.GitDatabase.Reference.Get(Helper.UserName, _repository.Name, "heads/master");
var branch = await _client.GitDatabase.Reference.Get(Helper.UserName, _repository.Name, "heads/my-branch");

var result = await _fixture.Compare(Helper.UserName, _repository.Name, master.Object.Sha, branch.Object.Sha);

Assert.Equal(1, result.Commits.Count);
Assert.Equal(1, result.AheadBy);
Assert.Equal(0, result.BehindBy);
}

async Task CreateTheWorld()
{
var master = await _client.GitDatabase.Reference.Get(Helper.UserName, _repository.Name, "heads/master");

// create new commit for master branch
var newMasterTree = await CreateTree(new Dictionary<string, string> { { "README.md", "Hello World!" } });
var newMaster = await CreateCommit("baseline for pull request", newMasterTree.Sha, master.Object.Sha);

// update master
await _client.GitDatabase.Reference.Update(Helper.UserName, _repository.Name, "heads/master", new ReferenceUpdate(newMaster.Sha));

// create new commit for feature branch
var featureBranchTree = await CreateTree(new Dictionary<string, string> { { "README.md", "I am overwriting this blob with something new" } });
var newFeature = await CreateCommit("this is the commit to merge into the pull request", featureBranchTree.Sha, newMaster.Sha);

// create branch
await _client.GitDatabase.Reference.Create(Helper.UserName, _repository.Name, new NewReference("refs/heads/my-branch", newFeature.Sha));
}

async Task<TreeResponse> CreateTree(IDictionary<string, string> treeContents)
{
var collection = new List<NewTreeItem>();

foreach (var c in treeContents)
{
var baselineBlob = new NewBlob
{
Content = c.Value,
Encoding = EncodingType.Utf8
};
var baselineBlobResult = await _client.GitDatabase.Blob.Create(Helper.UserName, _repository.Name, baselineBlob);

collection.Add(new NewTreeItem
{
Type = TreeType.Blob,
Mode = FileMode.File,
Path = c.Key,
Sha = baselineBlobResult.Sha
});
}

var newTree = new NewTree { Tree = collection };

return await _client.GitDatabase.Tree.Create(Helper.UserName, _repository.Name, newTree);
}

async Task<Commit> CreateCommit(string message, string sha, string parent)
{
var newCommit = new NewCommit(message, sha, parent);
return await _client.GitDatabase.Commit.Create(Helper.UserName, _repository.Name, newCommit);
}

public void Dispose()
{
_client.Repository.Delete(_repository.Owner.Login, _repository.Name);
}
}
1 change: 1 addition & 0 deletions Octokit.Tests.Integration/Octokit.Tests.Integration.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
<Compile Include="Clients\MilestonesClientTests.cs" />
<Compile Include="Clients\PullRequestsClientTests.cs" />
<Compile Include="Clients\ReferencesClientTests.cs" />
<Compile Include="Clients\RepositoryCommitsClientTests.cs" />
<Compile Include="Clients\SearchClientTests.cs" />
<Compile Include="Clients\StatisticsClientTests.cs" />
<Compile Include="Clients\TreeClientTests.cs" />
Expand Down
53 changes: 50 additions & 3 deletions Octokit.Tests/Clients/RepositoriesClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public async Task EnsuresNonNullArguments()
await AssertEx.Throws<ArgumentNullException>(async () => await client.Create(null));
await AssertEx.Throws<ArgumentException>(async () => await client.Create(new NewRepository { Name = null }));
}

[Fact]
public void UsesTheUserReposUrl()
{
Expand Down Expand Up @@ -345,9 +345,9 @@ public async Task ReturnsReadme()
var readme = await reposEndpoint.GetReadme("fake", "repo");

Assert.Equal("README.md", readme.Name);
connection.Received().Get<ReadmeResponse>(Arg.Is<Uri>(u => u.ToString() == "repos/fake/repo/readme"),
connection.Received().Get<ReadmeResponse>(Arg.Is<Uri>(u => u.ToString() == "repos/fake/repo/readme"),
null);
connection.DidNotReceive().GetHtml(Arg.Is<Uri>(u => u.ToString() == "https://github.example.com/readme"),
connection.DidNotReceive().GetHtml(Arg.Is<Uri>(u => u.ToString() == "https://github.example.com/readme"),
null);
var htmlReadme = await readme.GetHtmlContent();
Assert.Equal("<html>README</html>", htmlReadme);
Expand Down Expand Up @@ -557,5 +557,52 @@ public void EnsuresNonNullArguments()
Assert.Throws<ArgumentException>(() => client.Edit("owner", "", update));
}
}

public class TheCompareMethod
{
[Fact]
public void EnsureNonNullArguments()
{
var client = new RepositoryCommitsClient(Substitute.For<IApiConnection>());

Assert.Throws<ArgumentNullException>(() => client.Compare(null, "repo", "base", "head"));
Assert.Throws<ArgumentException>(() => client.Compare("", "repo", "base", "head"));

Assert.Throws<ArgumentNullException>(() => client.Compare("owner", null, "base", "head"));
Assert.Throws<ArgumentException>(() => client.Compare("owner", "", "base", "head"));

Assert.Throws<ArgumentNullException>(() => client.Compare("owner", "repo", null, "head"));
Assert.Throws<ArgumentException>(() => client.Compare("owner", "repo", "", "head"));

Assert.Throws<ArgumentNullException>(() => client.Compare("owner", "repo", "base", null));
Assert.Throws<ArgumentException>(() => client.Compare("owner", "repo", "base", ""));
}

[Fact]
public void GetsCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();

var client = new RepositoryCommitsClient(connection);

client.Compare("owner", "repo", "base", "head");

connection.Received()
.Get<CompareResult>(Arg.Is<Uri>(u => u.ToString() == "repos/owner/repo/compare/base...head"), null);
}

[Fact]
public void EncodesUrl()
{
var connection = Substitute.For<IApiConnection>();

var client = new RepositoryCommitsClient(connection);

client.Compare("owner", "repo", "base", "shiftkey/my-cool-branch");

connection.Received()
.Get<CompareResult>(Arg.Is<Uri>(u => u.ToString() == "repos/owner/repo/compare/base...shiftkey%2Fmy-cool-branch"), null);
}
}
}
}
8 changes: 8 additions & 0 deletions Octokit/Clients/IRepositoriesClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ public interface IRepositoriesClient
///</remarks>
IStatisticsClient Statistics { get; }

/// <summary>
/// Client for GitHub's Repository Commits API
/// </summary>
/// <remarks>
/// See the <a href="http://developer.github.com/v3/repos/commits/">Commits API documentation</a> for more details
///</remarks>
IRepositoryCommitsClient Commits { get; }

/// <summary>
/// Gets all the branches for the specified repository.
/// </summary>
Expand Down
Loading