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

Commit ad6b434

Browse files
authored
Merge pull request #1021 from github/fixes/863-fork-pull-requests
Allow selection of fork/parent pull requests.
2 parents 76296ed + 82bf5ce commit ad6b434

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+671
-230
lines changed

GitHubVS.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ Global
231231
{E4ED0537-D1D9-44B6-9212-3096D7C3F7A1}.Release|x86.ActiveCfg = Release|Any CPU
232232
{E4ED0537-D1D9-44B6-9212-3096D7C3F7A1}.Release|x86.Build.0 = Release|Any CPU
233233
{B1F5C227-456F-437D-BD5F-4C11B7A8D1A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
234+
{B1F5C227-456F-437D-BD5F-4C11B7A8D1A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
234235
{B1F5C227-456F-437D-BD5F-4C11B7A8D1A0}.Debug|x86.ActiveCfg = Debug|Any CPU
235236
{B1F5C227-456F-437D-BD5F-4C11B7A8D1A0}.Debug|x86.Build.0 = Debug|Any CPU
236237
{B1F5C227-456F-437D-BD5F-4C11B7A8D1A0}.Publish|Any CPU.ActiveCfg = Release|Any CPU

src/GitHub.App/GitHub.App.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
<ItemGroup>
135135
<Compile Include="Models\IssueCommentModel.cs" />
136136
<Compile Include="Models\PullRequestReviewCommentModel.cs" />
137+
<Compile Include="Models\PullRequestDetailArgument.cs" />
137138
<Compile Include="ViewModels\ViewModelBase.cs" />
138139
<None Include="..\..\script\Key.snk" Condition="$(Buildtype) == 'Internal'">
139140
<Link>Key.snk</Link>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using GitHub.ViewModels;
3+
4+
namespace GitHub.Models
5+
{
6+
/// <summary>
7+
/// Passes arguments to a <see cref="PullRequestDetailViewModel"/>
8+
/// </summary>
9+
public class PullRequestDetailArgument
10+
{
11+
/// <summary>
12+
/// Gets or sets the repository containing the pull request.
13+
/// </summary>
14+
public IRemoteRepositoryModel Repository { get; set; }
15+
16+
/// <summary>
17+
/// Gets or sets the number of the pull request.
18+
/// </summary>
19+
public int Number { get; set; }
20+
}
21+
}

src/GitHub.App/Models/RemoteRepositoryModel.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ public class RemoteRepositoryModel : RepositoryModel, IRemoteRepositoryModel,
2020
/// <param name="isPrivate">Whether the repository is private.</param>
2121
/// <param name="isFork">Whether the repository is a fork.</param>
2222
/// <param name="ownerAccount">The repository owner account.</param>
23-
public RemoteRepositoryModel(long id, string name, UriString cloneUrl, bool isPrivate, bool isFork, IAccount ownerAccount)
23+
/// <param name="parent">The parent repository if this repository is a fork.</param>
24+
public RemoteRepositoryModel(long id, string name, UriString cloneUrl, bool isPrivate, bool isFork, IAccount ownerAccount, IRemoteRepositoryModel parent)
2425
: base(name, cloneUrl)
2526
{
2627
Guard.ArgumentNotEmptyString(name, nameof(name));
@@ -33,6 +34,7 @@ public RemoteRepositoryModel(long id, string name, UriString cloneUrl, bool isPr
3334
// this is an assumption, we'd have to load the repo information from octokit to know for sure
3435
// probably not worth it for this ctor
3536
DefaultBranch = new BranchModel("master", this);
37+
Parent = parent;
3638
}
3739

3840
/// <summary>

src/GitHub.App/SampleData/PullRequestDetailViewModelDesigner.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ public PullRequestDetailViewModelDesigner()
7373

7474
public IPullRequestModel Model { get; }
7575
public IPullRequestSession Session { get; }
76-
public ILocalRepositoryModel Repository { get; }
76+
public ILocalRepositoryModel LocalRepository { get; }
77+
public IRemoteRepositoryModel RemoteRepository { get; }
7778
public string SourceBranchDisplayName { get; set; }
7879
public string TargetBranchDisplayName { get; set; }
7980
public int CommentCount { get; set; }

src/GitHub.App/SampleData/PullRequestListViewModelDesigner.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ public PullRequestListViewModelDesigner()
5555
SelectedAuthor = Authors.ElementAt(1);
5656
}
5757

58+
public IReadOnlyList<IRemoteRepositoryModel> Repositories { get; }
59+
public IRemoteRepositoryModel SelectedRepository { get; set; }
60+
5861
public ITrackingCollection<IPullRequestModel> PullRequests { get; set; }
5962
public IPullRequestModel SelectedPullRequest { get; set; }
6063

@@ -63,6 +66,8 @@ public PullRequestListViewModelDesigner()
6366

6467
public ObservableCollection<IAccount> Authors { get; set; }
6568
public IAccount SelectedAuthor { get; set; }
69+
public bool RepositoryIsFork { get; set; } = true;
70+
public bool ShowPullRequestsForFork { get; set; }
6671

6772
public ObservableCollection<IAccount> Assignees { get; set; }
6873
public IAccount SelectedAssignee { get; set; }

src/GitHub.App/SampleData/SampleViewModels.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ public static IRemoteRepositoryModel Create(string name = null, string owner = n
321321
{
322322
name = name ?? "octocat";
323323
owner = owner ?? "github";
324-
return new RemoteRepositoryModel(0, name, new UriString("http://github.com/" + name + "/" + owner), false, false, new AccountDesigner() { Login = owner });
324+
return new RemoteRepositoryModel(0, name, new UriString("http://github.com/" + name + "/" + owner), false, false, new AccountDesigner() { Login = owner }, null);
325325
}
326326
}
327327

src/GitHub.App/Services/GitClient.cs

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ namespace GitHub.Services
1515
[PartCreationPolicy(CreationPolicy.Shared)]
1616
public class GitClient : IGitClient
1717
{
18+
const string defaultOriginName = "origin";
19+
1820
static readonly Logger log = LogManager.GetCurrentClassLogger();
1921
readonly PullOptions pullOptions;
2022
readonly PushOptions pushOptions;
@@ -370,37 +372,43 @@ public Task<bool> IsModified(IRepository repository, string path, byte[] content
370372
});
371373
}
372374

373-
public async Task<string> GetPullRequestMergeBase(IRepository repo, string remoteName, string baseSha, string headSha, string baseRef, int pullNumber)
375+
public async Task<string> GetPullRequestMergeBase(IRepository repo,
376+
UriString baseCloneUrl, UriString headCloneUrl, string baseSha, string headSha, string baseRef, string headRef)
374377
{
375378
Guard.ArgumentNotNull(repo, nameof(repo));
376-
Guard.ArgumentNotEmptyString(remoteName, nameof(remoteName));
379+
Guard.ArgumentNotNull(baseCloneUrl, nameof(baseCloneUrl));
380+
Guard.ArgumentNotNull(headCloneUrl, nameof(headCloneUrl));
377381
Guard.ArgumentNotEmptyString(baseRef, nameof(baseRef));
378382

379-
var mergeBase = GetMergeBase(repo, baseSha, headSha);
380-
if (mergeBase == null)
383+
var baseCommit = repo.Lookup<Commit>(baseSha);
384+
if (baseCommit == null)
381385
{
382-
var pullHeadRef = $"refs/pull/{pullNumber}/head";
383-
await Fetch(repo, remoteName, baseRef, pullHeadRef);
384-
385-
mergeBase = GetMergeBase(repo, baseSha, headSha);
386+
await Fetch(repo, baseCloneUrl, baseRef);
387+
baseCommit = repo.Lookup<Commit>(baseSha);
388+
if (baseCommit == null)
389+
{
390+
return null;
391+
}
386392
}
387393

388-
return mergeBase;
389-
}
390-
391-
static string GetMergeBase(IRepository repo, string a, string b)
392-
{
393-
Guard.ArgumentNotNull(repo, nameof(repo));
394+
var headCommit = repo.Lookup<Commit>(headSha);
395+
if (headCommit == null)
396+
{
397+
await Fetch(repo, headCloneUrl, headRef);
398+
headCommit = repo.Lookup<Commit>(headSha);
399+
if (headCommit == null)
400+
{
401+
return null;
402+
}
403+
}
394404

395-
var aCommit = repo.Lookup<Commit>(a);
396-
var bCommit = repo.Lookup<Commit>(b);
397-
if (aCommit == null || bCommit == null)
405+
var mergeBaseCommit = repo.ObjectDatabase.FindMergeBase(baseCommit, headCommit);
406+
if(mergeBaseCommit == null)
398407
{
399408
return null;
400409
}
401410

402-
var baseCommit = repo.ObjectDatabase.FindMergeBase(aCommit, bCommit);
403-
return baseCommit?.Sha;
411+
return mergeBaseCommit.Sha;
404412
}
405413

406414
public Task<bool> IsHeadPushed(IRepository repo)
@@ -422,6 +430,26 @@ public Task<bool> IsHeadPushed(IRepository repo)
422430
});
423431
}
424432

433+
public Task Fetch(IRepository repo, UriString cloneUrl, params string[] refspecs)
434+
{
435+
var httpsUrl = UriString.ToUriString(cloneUrl.ToRepositoryUrl());
436+
if (repo.Network.Remotes[defaultOriginName]?.Url == httpsUrl)
437+
{
438+
return Fetch(repo, defaultOriginName, refspecs);
439+
}
440+
441+
var tempRemoteName = cloneUrl.Owner + "-" + Guid.NewGuid();
442+
repo.Network.Remotes.Add(tempRemoteName, httpsUrl);
443+
try
444+
{
445+
return Fetch(repo, tempRemoteName, refspecs);
446+
}
447+
finally
448+
{
449+
repo.Network.Remotes.Remove(tempRemoteName);
450+
}
451+
}
452+
425453
static bool IsCanonical(string s)
426454
{
427455
Guard.ArgumentNotEmptyString(s, nameof(s));

src/GitHub.App/Services/ModelService.cs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ IObservable<AccountCacheItem> GetUserFromCache()
153153
/// <param name="repo"></param>
154154
/// <param name="collection"></param>
155155
/// <returns></returns>
156-
public ITrackingCollection<IPullRequestModel> GetPullRequests(ILocalRepositoryModel repo,
156+
public ITrackingCollection<IPullRequestModel> GetPullRequests(IRepositoryModel repo,
157157
ITrackingCollection<IPullRequestModel> collection)
158158
{
159159
// Since the api to list pull requests returns all the data for each pr, cache each pr in its own entry
@@ -163,7 +163,7 @@ public ITrackingCollection<IPullRequestModel> GetPullRequests(ILocalRepositoryMo
163163
// and replaces it instead of appending, so items get refreshed in-place as they come in.
164164

165165
var keyobs = GetUserFromCache()
166-
.Select(user => string.Format(CultureInfo.InvariantCulture, "{0}|{1}:{2}", CacheIndex.PRPrefix, user.Login, repo.Name));
166+
.Select(user => string.Format(CultureInfo.InvariantCulture, "{0}|{1}:{2}", CacheIndex.PRPrefix, repo.Owner, repo.Name));
167167

168168
var source = Observable.Defer(() => keyobs
169169
.SelectMany(key =>
@@ -188,16 +188,16 @@ public ITrackingCollection<IPullRequestModel> GetPullRequests(ILocalRepositoryMo
188188
return collection;
189189
}
190190

191-
public IObservable<IPullRequestModel> GetPullRequest(ILocalRepositoryModel repo, int number)
191+
public IObservable<IPullRequestModel> GetPullRequest(string owner, string name, int number)
192192
{
193193
return Observable.Defer(() =>
194194
{
195195
return hostCache.GetAndRefreshObject(PRPrefix + '|' + number, () =>
196196
Observable.CombineLatest(
197-
apiClient.GetPullRequest(repo.CloneUrl.Owner, repo.CloneUrl.RepositoryName, number),
198-
apiClient.GetPullRequestFiles(repo.CloneUrl.Owner, repo.CloneUrl.RepositoryName, number).ToList(),
199-
apiClient.GetIssueComments(repo.CloneUrl.Owner, repo.CloneUrl.RepositoryName, number).ToList(),
200-
apiClient.GetPullRequestReviewComments(repo.CloneUrl.Owner, repo.CloneUrl.RepositoryName, number).ToList(),
197+
apiClient.GetPullRequest(owner, name, number),
198+
apiClient.GetPullRequestFiles(owner, name, number).ToList(),
199+
apiClient.GetIssueComments(owner, name, number).ToList(),
200+
apiClient.GetPullRequestReviewComments(owner, name, number).ToList(),
201201
(pr, files, comments, reviewComments) => new
202202
{
203203
PullRequest = pr,
@@ -216,6 +216,19 @@ public IObservable<IPullRequestModel> GetPullRequest(ILocalRepositoryModel repo,
216216
});
217217
}
218218

219+
public IObservable<IRemoteRepositoryModel> GetRepository(string owner, string repo)
220+
{
221+
var keyobs = GetUserFromCache()
222+
.Select(user => string.Format(CultureInfo.InvariantCulture, "{0}|{1}", CacheIndex.RepoPrefix, user.Login));
223+
224+
return Observable.Defer(() => keyobs
225+
.SelectMany(key =>
226+
hostCache.GetAndFetchLatest(
227+
key,
228+
() => apiClient.GetRepository(owner, repo).Select(RepositoryCacheItem.Create))
229+
.Select(Create)));
230+
}
231+
219232
public ITrackingCollection<IRemoteRepositoryModel> GetRepositories(ITrackingCollection<IRemoteRepositoryModel> collection)
220233
{
221234
var keyobs = GetUserFromCache()
@@ -390,7 +403,8 @@ IRemoteRepositoryModel Create(RepositoryCacheItem item)
390403
new UriString(item.CloneUrl),
391404
item.Private,
392405
item.Fork,
393-
Create(item.Owner))
406+
Create(item.Owner),
407+
item.Parent != null ? Create(item.Parent) : null)
394408
{
395409
CreatedAt = item.CreatedAt,
396410
UpdatedAt = item.UpdatedAt
@@ -504,6 +518,7 @@ public RepositoryCacheItem(Repository apiRepository)
504518
CreatedAt = apiRepository.CreatedAt;
505519
UpdatedAt = apiRepository.UpdatedAt;
506520
Timestamp = apiRepository.UpdatedAt;
521+
Parent = apiRepository.Parent != null ? new RepositoryCacheItem(apiRepository.Parent) : null;
507522
}
508523

509524
public long Id { get; set; }
@@ -515,6 +530,7 @@ public RepositoryCacheItem(Repository apiRepository)
515530
public bool Fork { get; set; }
516531
public DateTimeOffset CreatedAt { get; set; }
517532
public DateTimeOffset UpdatedAt { get; set; }
533+
public RepositoryCacheItem Parent { get; set; }
518534
}
519535

520536
public class PullRequestCacheItem : CacheItem

0 commit comments

Comments
 (0)