Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 6e57be4

Browse files
committed
Avoid naming clashes in local PR branch names.
1 parent 55b731b commit 6e57be4

File tree

4 files changed

+74
-6
lines changed

4 files changed

+74
-6
lines changed

src/GitHub.App/Services/PullRequestService.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,19 @@ public IObservable<Unit> FetchAndCheckout(ILocalRepositoryModel repository, int
8888
return DoFetchAndCheckout(repository, pullRequestNumber, localBranchName).ToObservable();
8989
}
9090

91-
public string GetDefaultLocalBranchName(int pullRequestNumber, string pullRequestTitle)
91+
public string GetDefaultLocalBranchName(ILocalRepositoryModel repository, int pullRequestNumber, string pullRequestTitle)
9292
{
93-
return "pr/" + pullRequestNumber + "-" + GetSafeBranchName(pullRequestTitle);
93+
var initial = "pr/" + pullRequestNumber + "-" + GetSafeBranchName(pullRequestTitle);
94+
var current = initial;
95+
var repo = gitService.GetRepository(repository.LocalPath);
96+
var index = 2;
97+
98+
while (repo.Branches[current] != null)
99+
{
100+
current = initial + '-' + index++;
101+
}
102+
103+
return current;
94104
}
95105

96106
public IObservable<HistoryDivergence> CalculateHistoryDivergence(ILocalRepositoryModel repository, int pullRequestNumber)

src/GitHub.App/ViewModels/PullRequestDetailViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ IObservable<Unit> SwitchToBranch()
465465

466466
IObservable<Unit> FetchAndCheckout()
467467
{
468-
var branchName = pullRequestsService.GetDefaultLocalBranchName(Number, Title);
468+
var branchName = pullRequestsService.GetDefaultLocalBranchName(repository, Number, Title);
469469

470470
return pullRequestsService.FetchAndCheckout(repository, Number, branchName)
471471
.Catch<Unit, Exception>(ex =>

src/GitHub.Exports.Reactive/Services/IPullRequestService.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ IObservable<IPullRequestModel> CreatePullRequest(IRepositoryHost host,
2222
IObservable<Unit> FetchAndCheckout(ILocalRepositoryModel repository, int pullRequestNumber, string localBranchName);
2323

2424
/// <summary>
25-
/// Gets the name of the local branch that a call to <see cref="Checkout(ILocalRepositoryModel, IPullRequestModel)"/>
26-
/// will check out to.
25+
/// Calculates the name of a local branch for a pull request avoiding clashes with existing branches.
2726
/// </summary>
27+
/// <param name="repository">The repository.</param>
2828
/// <param name="pullRequestNumber">The pull request number.</param>
2929
/// <param name="pullRequestTitle">The pull request title.</param>
3030
/// <returns></returns>
31-
string GetDefaultLocalBranchName(int pullRequestNumber, string pullRequestTitle);
31+
string GetDefaultLocalBranchName(ILocalRepositoryModel repository, int pullRequestNumber, string pullRequestTitle);
3232

3333
/// <summary>
3434
/// Gets the local branches that exist for the specified pull request.

src/UnitTests/GitHub.App/Services/PullRequestServiceTests.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
using GitHub.Models;
77
using System;
88
using GitHub.Services;
9+
using Rothko;
10+
using LibGit2Sharp;
11+
using System.Collections.Generic;
912

1013
public class PullRequestServiceTests : TestBaseClass
1114
{
@@ -49,4 +52,59 @@ public void CreatePullRequestAllArgsMandatory()
4952
Assert.NotNull(pr);
5053
}
5154

55+
public class TheGetDefaultLocalBranchNameMethod
56+
{
57+
[Fact]
58+
public void ShouldReturnCorrectDefaultLocalBranchName()
59+
{
60+
var service = new PullRequestService(
61+
Substitute.For<IGitClient>(),
62+
MockGitService(),
63+
Substitute.For<IOperatingSystem>(),
64+
Substitute.For<IUsageTracker>());
65+
66+
var localRepo = Substitute.For<ILocalRepositoryModel>();
67+
var result = service.GetDefaultLocalBranchName(localRepo, 123, "Pull requests can be \"named\" all sorts of thing's (sic)");
68+
Assert.Equal("pr/123-pull-requests-can-be-named-all-sorts-of-thing-s-sic-", result);
69+
}
70+
71+
[Fact]
72+
public void DefaultLocalBranchNameShouldNotClashWithExistingBranchNames()
73+
{
74+
var service = new PullRequestService(
75+
Substitute.For<IGitClient>(),
76+
MockGitService(),
77+
Substitute.For<IOperatingSystem>(),
78+
Substitute.For<IUsageTracker>());
79+
80+
var localRepo = Substitute.For<ILocalRepositoryModel>();
81+
var result = service.GetDefaultLocalBranchName(localRepo, 123, "foo1");
82+
Assert.Equal("pr/123-foo1-3", result);
83+
}
84+
85+
private static IGitService MockGitService()
86+
{
87+
var repository = Substitute.For<IRepository>();
88+
var branches = MockBranches("pr/123-foo1", "pr/123-foo1-2");
89+
repository.Branches.Returns(branches);
90+
91+
var result = Substitute.For<IGitService>();
92+
result.GetRepository(Arg.Any<string>()).Returns(repository);
93+
return result;
94+
}
95+
}
96+
97+
private static BranchCollection MockBranches(params string[] names)
98+
{
99+
var result = Substitute.For<BranchCollection>();
100+
101+
foreach (var name in names)
102+
{
103+
var branch = Substitute.For<Branch>();
104+
branch.CanonicalName.Returns("refs/heads/" + name);
105+
result[name].Returns(branch);
106+
}
107+
108+
return result;
109+
}
52110
}

0 commit comments

Comments
 (0)