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

Commit 54e4182

Browse files
committed
Merge branch 'master' into 480-fix-pr-refresh-showing-create-form
2 parents d5d9e5a + 18fbdfc commit 54e4182

File tree

14 files changed

+152
-47
lines changed

14 files changed

+152
-47
lines changed

src/GitHub.App/Services/GitClient.cs

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq;
44
using System.Reactive;
55
using System.Reactive.Linq;
6+
using System.Threading.Tasks;
67
using GitHub.Extensions;
78
using GitHub.Primitives;
89
using LibGit2Sharp;
@@ -23,78 +24,91 @@ public GitClient(IGitHubCredentialProvider credentialProvider)
2324
fetchOptions = new FetchOptions { CredentialsProvider = credentialProvider.HandleCredentials };
2425
}
2526

26-
public IObservable<Unit> Push(IRepository repository, string branchName, string remoteName)
27+
public Task Push(IRepository repository, string branchName, string remoteName)
2728
{
2829
Guard.ArgumentNotEmptyString(branchName, nameof(branchName));
2930
Guard.ArgumentNotEmptyString(remoteName, nameof(remoteName));
3031

31-
return Observable.Defer(() =>
32+
return Task.Factory.StartNew(() =>
3233
{
3334
if (repository.Head?.Commits != null && repository.Head.Commits.Any())
3435
{
3536
var remote = repository.Network.Remotes[remoteName];
3637
repository.Network.Push(remote, "HEAD", @"refs/heads/" + branchName, pushOptions);
3738
}
38-
return Observable.Return(Unit.Default);
3939
});
4040
}
4141

42-
public IObservable<Unit> Fetch(IRepository repository, string remoteName)
42+
public Task Fetch(IRepository repository, string remoteName)
4343
{
4444
Guard.ArgumentNotEmptyString(remoteName, nameof(remoteName));
4545

46-
return Observable.Defer(() =>
46+
return Task.Factory.StartNew(() =>
4747
{
4848
var remote = repository.Network.Remotes[remoteName];
4949
repository.Network.Fetch(remote, fetchOptions);
50-
return Observable.Return(Unit.Default);
5150
});
5251
}
5352

54-
public IObservable<Unit> SetRemote(IRepository repository, string remoteName, Uri url)
53+
public Task Fetch(IRepository repository, string remoteName, params string[] refspecs)
5554
{
5655
Guard.ArgumentNotEmptyString(remoteName, nameof(remoteName));
5756

58-
return Observable.Defer(() =>
57+
return Task.Factory.StartNew(() =>
5958
{
59+
var remote = repository.Network.Remotes[remoteName];
60+
repository.Network.Fetch(remote, refspecs, fetchOptions);
61+
});
62+
}
63+
64+
public Task SetRemote(IRepository repository, string remoteName, Uri url)
65+
{
66+
Guard.ArgumentNotEmptyString(remoteName, nameof(remoteName));
67+
68+
return Task.Factory.StartNew(() =>
69+
{
70+
6071
repository.Config.Set("remote." + remoteName + ".url", url.ToString());
6172
repository.Config.Set("remote." + remoteName + ".fetch", "+refs/heads/*:refs/remotes/" + remoteName + "/*");
62-
63-
return Observable.Return(Unit.Default);
6473
});
6574
}
6675

67-
public IObservable<Unit> SetTrackingBranch(IRepository repository, string branchName, string remoteName)
76+
public Task SetTrackingBranch(IRepository repository, string branchName, string remoteName)
6877
{
6978
Guard.ArgumentNotEmptyString(branchName, nameof(branchName));
7079
Guard.ArgumentNotEmptyString(remoteName, nameof(remoteName));
7180

72-
return Observable.Defer(() =>
81+
return Task.Factory.StartNew(() =>
7382
{
74-
var remoteBranchName = "refs/remotes/" + remoteName + "/" + branchName;
83+
var remoteBranchName = IsCanonical(remoteName) ? remoteName : "refs/remotes/" + remoteName + "/" + branchName;
7584
var remoteBranch = repository.Branches[remoteBranchName];
7685
// if it's null, it's because nothing was pushed
7786
if (remoteBranch != null)
7887
{
79-
var localBranchName = "refs/heads/" + branchName;
88+
var localBranchName = IsCanonical(branchName) ? branchName : "refs/heads/" + branchName;
8089
var localBranch = repository.Branches[localBranchName];
8190
repository.Branches.Update(localBranch, b => b.TrackedBranch = remoteBranch.CanonicalName);
8291
}
83-
return Observable.Return(Unit.Default);
8492
});
8593
}
8694

87-
public IObservable<Remote> GetHttpRemote(IRepository repo, string remote)
95+
public Task<Remote> GetHttpRemote(IRepository repo, string remote)
8896
{
89-
return Observable.Defer(() => Observable.Return(GitService.GitServiceHelper.GetRemoteUri(repo, remote)))
90-
.Select(uri => new { Remote = uri.IsHypertextTransferProtocol ? remote : remote + "-http", Uri = uri })
91-
.Select(r =>
92-
{
93-
var ret = repo.Network.Remotes[r.Remote];
94-
if (ret == null)
95-
ret = repo.Network.Remotes.Add(r.Remote, UriString.ToUriString(r.Uri.ToRepositoryUrl()));
96-
return ret;
97-
});
97+
return Task.Factory.StartNew(() =>
98+
{
99+
100+
var uri = GitService.GitServiceHelper.GetRemoteUri(repo, remote);
101+
var remoteName = uri.IsHypertextTransferProtocol ? remote : remote + "-http";
102+
var ret = repo.Network.Remotes[remoteName];
103+
if (ret == null)
104+
ret = repo.Network.Remotes.Add(remoteName, UriString.ToUriString(uri.ToRepositoryUrl()));
105+
return ret;
106+
});
107+
}
108+
109+
static bool IsCanonical(string s)
110+
{
111+
return s.StartsWith("refs/", StringComparison.Ordinal);
98112
}
99113
}
100114
}

src/GitHub.App/Services/RepositoryPublishService.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.ComponentModel.Composition;
33
using System.IO;
44
using System.Reactive.Linq;
5+
using System.Reactive.Threading.Tasks;
56
using GitHub.Api;
67
using GitHub.Models;
78
using LibGit2Sharp;
@@ -38,11 +39,16 @@ public string LocalRepositoryName
3839
IApiClient apiClient)
3940
{
4041
return Observable.Defer(() => apiClient.CreateRepository(newRepository, account.Login, account.IsUser)
41-
.Select(remoteRepo => new { RemoteRepo = remoteRepo, LocalRepo = activeRepository }))
42-
.SelectMany(repo => gitClient.SetRemote(repo.LocalRepo, "origin", new Uri(repo.RemoteRepo.CloneUrl)).Select(_ => repo))
43-
.SelectMany(repo => gitClient.Push(repo.LocalRepo, "master", "origin").Select(_ => repo))
44-
.SelectMany(repo => gitClient.Fetch(repo.LocalRepo, "origin").Select(_ => repo))
45-
.SelectMany(repo => gitClient.SetTrackingBranch(repo.LocalRepo, "master", "origin").Select(_ => repo.RemoteRepo));
42+
.Select(remoteRepo => new { RemoteRepo = remoteRepo, LocalRepo = activeRepository }))
43+
.Select(async repo =>
44+
{
45+
await gitClient.SetRemote(repo.LocalRepo, "origin", new Uri(repo.RemoteRepo.CloneUrl));
46+
await gitClient.Push(repo.LocalRepo, "master", "origin");
47+
await gitClient.Fetch(repo.LocalRepo, "origin");
48+
await gitClient.SetTrackingBranch(repo.LocalRepo, "master", "origin");
49+
return repo.RemoteRepo;
50+
})
51+
.Select(t => t.Result);
4652
}
4753
}
4854
}
Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using System;
2-
using System.Reactive;
2+
using System.Threading.Tasks;
33
using LibGit2Sharp;
44

55
namespace GitHub.Services
@@ -13,15 +13,24 @@ public interface IGitClient
1313
/// <param name="remoteName">The name of the remote</param>
1414
/// <param name="branchName">the branch to pull</param>
1515
/// <returns></returns>
16-
IObservable<Unit> Push(IRepository repository, string branchName, string remoteName);
16+
Task Push(IRepository repository, string branchName, string remoteName);
1717

1818
/// <summary>
1919
/// Fetches the remote.
2020
/// </summary>
2121
/// <param name="repository">The repository to pull</param>
2222
/// <param name="remoteName">The name of the remote</param>
2323
/// <returns></returns>
24-
IObservable<Unit> Fetch(IRepository repository, string remoteName);
24+
Task Fetch(IRepository repository, string remoteName);
25+
26+
/// <summary>
27+
/// Fetches from the remote, using custom refspecs.
28+
/// </summary>
29+
/// <param name="repository">The repository to pull</param>
30+
/// <param name="remoteName">The name of the remote</param>
31+
/// <param name="refspecs">The custom refspecs</param>
32+
/// <returns></returns>
33+
Task Fetch(IRepository repository, string remoteName, params string[] refspecs);
2534

2635
/// <summary>
2736
/// Sets the specified remote to the specified URL.
@@ -30,17 +39,17 @@ public interface IGitClient
3039
/// <param name="remoteName">The name of the remote</param>
3140
/// <param name="url">The URL to set as the remote</param>
3241
/// <returns></returns>
33-
IObservable<Unit> SetRemote(IRepository repository, string remoteName, Uri url);
42+
Task SetRemote(IRepository repository, string remoteName, Uri url);
3443

3544
/// <summary>
3645
/// Sets the remote branch that the local branch tracks
3746
/// </summary>
3847
/// <param name="repository">The repository to set</param>
39-
/// <param name="branchName">The name of the remote</param>
40-
/// <param name="remoteName">The name of the branch (local and remote)</param>
48+
/// <param name="branchName">The name of the local remote</param>
49+
/// <param name="remoteName">The name of the remote branch</param>
4150
/// <returns></returns>
42-
IObservable<Unit> SetTrackingBranch(IRepository repository, string branchName, string remoteName);
51+
Task SetTrackingBranch(IRepository repository, string branchName, string remoteName);
4352

44-
IObservable<Remote> GetHttpRemote(IRepository repo, string remote);
53+
Task<Remote> GetHttpRemote(IRepository repo, string remote);
4554
}
4655
}

src/GitHub.Exports/Models/ILocalRepositoryModel.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using GitHub.Primitives;
22
using System.ComponentModel;
3+
using System.Threading.Tasks;
34

45
namespace GitHub.Models
56
{
@@ -31,6 +32,6 @@ public interface ILocalRepositoryModel : IRepositoryModel, INotifyPropertyChange
3132
/// <param name="startLine">A specific line, or (if specifying the <paramref name="endLine"/> as well) the start of a range</param>
3233
/// <param name="endLine">The end of a line range on the specified file.</param>
3334
/// <returns>An UriString with the generated url, or null if the repository has no remote server configured or if it can't be found locally</returns>
34-
UriString GenerateUrl(string path = null, int startLine = -1, int endLine = -1);
35+
Task<UriString> GenerateUrl(string path = null, int startLine = -1, int endLine = -1);
3536
}
3637
}

src/GitHub.Exports/Models/LocalRepositoryModel.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using GitHub.UI;
88
using GitHub.Services;
99
using GitHub.Extensions;
10+
using System.Threading.Tasks;
1011

1112
namespace GitHub.Models
1213
{
@@ -60,12 +61,12 @@ public void Refresh()
6061
/// <param name="startLine">A specific line, or (if specifying the <paramref name="endLine"/> as well) the start of a range</param>
6162
/// <param name="endLine">The end of a line range on the specified file.</param>
6263
/// <returns>An UriString with the generated url, or null if the repository has no remote server configured or if it can't be found locally</returns>
63-
public UriString GenerateUrl(string path = null, int startLine = -1, int endLine = -1)
64+
public async Task<UriString> GenerateUrl(string path = null, int startLine = -1, int endLine = -1)
6465
{
6566
if (CloneUrl == null)
6667
return null;
6768

68-
var sha = HeadSha;
69+
var sha = await GitService.GitServiceHelper.GetLatestPushedSha(path ?? LocalPath);
6970
// this also incidentally checks whether the repo has a valid LocalPath
7071
if (String.IsNullOrEmpty(sha))
7172
return CloneUrl.ToRepositoryUrl().AbsoluteUri;

src/GitHub.Exports/Services/GitService.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using System;
55
using System.Threading.Tasks;
66
using GitHub.Models;
7+
using System.Linq;
8+
using GitHub.Extensions;
79

810
namespace GitHub.Services
911
{
@@ -70,5 +72,39 @@ public UriString GetRemoteUri(IRepository repo, string remote = "origin")
7072
}
7173

7274
public static IGitService GitServiceHelper => VisualStudio.Services.DefaultExportProvider.GetExportedValueOrDefault<IGitService>() ?? new GitService();
75+
76+
/// <summary>
77+
/// Finds the latest pushed commit of a file and returns the sha of that commit. Returns null when no commits have
78+
/// been found in any remote branches or the current local branch.
79+
/// </summary>
80+
/// <param name="path">The local path of a repository or a file inside a repository. This cannot be null.</param>
81+
/// <returns></returns>
82+
public Task<string> GetLatestPushedSha(string path)
83+
{
84+
Guard.ArgumentNotNull(path, nameof(path));
85+
var repo = GetRepository(path);
86+
87+
if (repo == null)
88+
return null;
89+
90+
if (repo.Head.IsTracking && repo.Head.Tip.Sha == repo.Head.TrackedBranch.Tip.Sha)
91+
{
92+
return Task.FromResult(repo.Head.Tip.Sha);
93+
}
94+
95+
return Task.Factory.StartNew(() =>
96+
{
97+
var remoteHeads = repo.Refs.Where(r => r.IsRemoteTrackingBranch).ToList();
98+
99+
foreach (var c in repo.Commits)
100+
{
101+
if (repo.Refs.ReachableFrom(remoteHeads, new[] { c }).Any())
102+
{
103+
return c.Sha;
104+
}
105+
}
106+
return null;
107+
});
108+
}
73109
}
74110
}

src/GitHub.Exports/Services/IGitService.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Threading.Tasks;
12
using GitHub.Models;
23
using GitHub.Primitives;
34
using LibGit2Sharp;
@@ -44,5 +45,13 @@ public interface IGitService
4445
/// <param name="repo"></param>
4546
/// <returns></returns>
4647
UriString GetRemoteUri(IRepository repo, string remote = "origin");
48+
49+
/// <summary>
50+
/// Finds the latest pushed commit of a file and returns the sha of that commit. Returns null when no commits have
51+
/// been found in any remote branches or the current local branch.
52+
/// </summary>
53+
/// <param name="path">The local path of a repository or a file inside a repository. This cannot be null.</param>
54+
/// <returns></returns>
55+
Task<string> GetLatestPushedSha(string path);
4756
}
4857
}

src/GitHub.VisualStudio/Menus/CopyLink.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public async void Activate([AllowNull]object data = null)
3333
if (!isgithub)
3434
return;
3535

36-
var link = GenerateLink();
36+
var link = await GenerateLink();
3737
if (link == null)
3838
return;
3939
try

src/GitHub.VisualStudio/Menus/LinkMenuBase.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using GitHub.Extensions;
33
using GitHub.Primitives;
44
using System;
5+
using System.Threading.Tasks;
56

67
namespace GitHub.VisualStudio.Menus
78
{
@@ -12,7 +13,7 @@ public LinkMenuBase(IServiceProvider serviceProvider, ISimpleApiClientFactory ap
1213
{
1314
}
1415

15-
protected UriString GenerateLink()
16+
protected Task<UriString> GenerateLink()
1617
{
1718
var repo = ActiveRepo;
1819
var activeDocument = ServiceProvider.GetExportedValue<IActiveDocumentSnapshot>();

src/GitHub.VisualStudio/Menus/OpenLink.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public async void Activate([AllowNull]object data = null)
3131
if (!isgithub)
3232
return;
3333

34-
var link = GenerateLink();
34+
var link = await GenerateLink();
3535
if (link == null)
3636
return;
3737
var browser = ServiceProvider.GetExportedValue<IVisualStudioBrowser>();

0 commit comments

Comments
 (0)