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

Commit 5a3f114

Browse files
authored
Merge pull request #553 from github/fixes/343-latest-remote-commit
Generate Copy/Open link based on the latest commit
2 parents b5cdef1 + 1b8e445 commit 5a3f114

File tree

8 files changed

+63
-8
lines changed

8 files changed

+63
-8
lines changed

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>();

src/UnitTests/GitHub.Exports/LocalRepositoryModelTests.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
using Xunit;
88
using GitHub.Primitives;
99
using Xunit.Abstractions;
10+
using System.Collections.Generic;
11+
using System.Threading.Tasks;
1012

1113
[Collection("PackageServiceProvider global data tests")]
1214
public class LocalRepositoryModelTests : TestBaseClass
@@ -24,8 +26,13 @@ static void SetupRepository(string sha)
2426
var gitservice = provider.GetGitService();
2527
var repo = Substitute.For<IRepository>();
2628
gitservice.GetRepository(Args.String).Returns(repo);
29+
gitservice.GetLatestPushedSha(Args.String).Returns(Task.FromResult(sha));
2730
if (!String.IsNullOrEmpty(sha))
2831
{
32+
var refs = Substitute.For<ReferenceCollection>();
33+
var refrence = Substitute.For<Reference>();
34+
refs.ReachableFrom(Arg.Any<IEnumerable<Reference>>(), Arg.Any<IEnumerable<Commit>>()).Returns(new Reference[] { refrence });
35+
repo.Refs.Returns(refs);
2936
var commit = Substitute.For<Commit>();
3037
commit.Sha.Returns(sha);
3138
repo.Commits.Returns(new FakeCommitLog { commit });
@@ -54,7 +61,7 @@ static void SetupRepository(string sha)
5461
[InlineData(19, false, "[email protected]/foo/bar", "123123", @"src\dir\file1.cs", -1, -1, "https://github.com/foo/bar/blob/123123/src/dir/file1.cs")]
5562
[InlineData(20, false, "[email protected]/foo/bar", "123123", @"src\dir\File1.cs", -1, -1, "https://github.com/foo/bar/blob/123123/src/dir/File1.cs")]
5663
[InlineData(21, false, "[email protected]/foo/bar", "123123", @"src\dir\ThisIsFile1.cs", -1, -1, "https://github.com/foo/bar/blob/123123/src/dir/ThisIsFile1.cs")]
57-
public void GenerateUrl(int testid, bool createRootedPath, string baseUrl, string sha, string path, int startLine, int endLine, string expected)
64+
public async void GenerateUrl(int testid, bool createRootedPath, string baseUrl, string sha, string path, int startLine, int endLine, string expected)
5865
{
5966
using (var temp = new TempDirectory())
6067
{
@@ -68,7 +75,7 @@ public void GenerateUrl(int testid, bool createRootedPath, string baseUrl, strin
6875
model = new LocalRepositoryModel("bar", new UriString(baseUrl), basePath.FullName);
6976
else
7077
model = new LocalRepositoryModel(basePath.FullName);
71-
var result = model.GenerateUrl(path, startLine, endLine);
78+
var result = await model.GenerateUrl(path, startLine, endLine);
7279
Assert.Equal(expected, result?.ToString());
7380
}
7481
}

0 commit comments

Comments
 (0)