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

Commit d99b34a

Browse files
committed
Fix IGitService
Make static methods that mirror the IGitService instance methods and use either a real mef instance or a fake one to get at the instance methods. Things are slightly broken getting a reference to IGitService - in real life, it's not a service, it's an exported value, but in unit tests it's treated as a service (because this stupid distinction between services and exported values is stupid). So, IGitService is nothing but a bunch of helper methods, and we want to use it as an instance and not just static methods, so that we can bypass hitting the filesystem and libgit2# methods in unit tests. Some bits that need IGitService for realz are running in places where there's no MEF. Therefore, the only time where it really makes a difference when/how the IGitService instance is created is in unit tests. When running for real, it makes zero difference if the instance is coming from mef or if it's just created on the spot, so that's what we're doing now.
1 parent 6397fbd commit d99b34a

File tree

8 files changed

+124
-88
lines changed

8 files changed

+124
-88
lines changed

src/GitHub.Exports/Extensions/GitHelpers.cs

Lines changed: 0 additions & 46 deletions
This file was deleted.

src/GitHub.Exports/Extensions/SimpleRepositoryModelExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.IO;
44
using GitHub.Models;
55
using Microsoft.VisualStudio.TeamFoundation.Git.Extensibility;
6+
using GitHub.Services;
67

78
namespace GitHub.Extensions
89
{
@@ -18,7 +19,7 @@ public static ISimpleRepositoryModel ToModel(this IGitRepositoryInfo repo)
1819

1920
public static bool HasCommits(this ISimpleRepositoryModel repository)
2021
{
21-
var repo = VisualStudio.Services.IGitService.GetRepo(repository.LocalPath);
22+
var repo = GitService.GitServiceHelper.GetRepo(repository.LocalPath);
2223
return repo?.Commits.Any() ?? false;
2324
}
2425

src/GitHub.Exports/GitHub.Exports.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@
9999
<None Include="..\..\script\Key.snk" Condition="$(Buildtype) == 'Internal'">
100100
<Link>Key.snk</Link>
101101
</None>
102-
<Compile Include="Extensions\GitHelpers.cs" />
103102
<Compile Include="Helpers\INotifyPropertySource.cs" />
104103
<Compile Include="Extensions\PropertyNotifierExtensions.cs" />
105104
<Compile Include="Extensions\SimpleRepositoryModelExtensions.cs" />

src/GitHub.Exports/Models/SimpleRepositoryModel.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using GitHub.Primitives;
66
using GitHub.UI;
77
using GitHub.VisualStudio.Helpers;
8+
using GitHub.Services;
89

910
namespace GitHub.Models
1011
{
@@ -26,7 +27,7 @@ public SimpleRepositoryModel(string path)
2627
var dir = new DirectoryInfo(path);
2728
if (!dir.Exists)
2829
throw new ArgumentException("Path does not exist", nameof(path));
29-
var uri = VisualStudio.Services.IGitService.GetUri(path);
30+
var uri = GitService.GitServiceHelper.GetUri(path);
3031
var name = uri?.NameWithOwner ?? dir.Name;
3132
Name = name;
3233
LocalPath = path;
@@ -47,7 +48,7 @@ public void Refresh()
4748
{
4849
if (LocalPath == null)
4950
return;
50-
var uri = VisualStudio.Services.IGitService.GetUri(LocalPath);
51+
var uri = GitService.GitServiceHelper.GetUri(LocalPath);
5152
if (CloneUrl != uri)
5253
CloneUrl = uri;
5354
}

src/GitHub.Exports/Services/GitService.cs

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,55 +4,98 @@
44
using GitHub.Primitives;
55
using LibGit2Sharp;
66
using Microsoft.VisualStudio.TeamFoundation.Git.Extensibility;
7+
using GitHub.Extensions;
78

89
namespace GitHub.Services
910
{
10-
[Export(typeof(IVSServices))]
11-
[PartCreationPolicy(CreationPolicy.Shared)]
11+
[Export(typeof(IGitService))]
12+
[PartCreationPolicy(CreationPolicy.NonShared)]
1213
public class GitService : IGitService
1314
{
1415
/// <summary>
1516
/// Returns the URL of the remote named "origin" for the specified <see cref="repository"/>. If the repository
1617
/// is null or no remote named origin exists, this method returns null
1718
/// </summary>
1819
/// <param name="repository">The repository to look at for the remote.</param>
19-
/// <returns>A <see cref="UriString"/> representing the origin or null if none found.</returns>
20+
/// <returns>Returns a <see cref="UriString"/> representing the uri of the "origin" remote normalized to a GitHub repository url or null if none found.</returns>
2021
public UriString GetUri(IRepository repository)
2122
{
22-
return UriString.ToUriString(GetUriFromRepository(repository)?.ToRepositoryUrl());
23+
return UriString.ToUriString(GetOriginUri(repository)?.ToRepositoryUrl());
24+
2325
}
2426

2527
/// <summary>
26-
/// Probes for a git repository and if one is found, returns a <see cref="UriString"/> for the repository's
27-
/// remote named "origin" if one is found
28+
/// Returns a <see cref="UriString"/> representing the uri of the "origin" remote normalized to a GitHub repository url or null if none found.
29+
/// </summary>
30+
/// <param name="repository"></param>
31+
/// <returns>Returns a <see cref="UriString"/> representing the uri of the "origin" remote normalized to a GitHub repository url or null if none found.</returns>
32+
public static UriString GetGitHubUri(IRepository repository)
33+
{
34+
return GitServiceHelper.GetUri(repository);
35+
}
36+
37+
/// <summary>
38+
/// Probes for a git repository and if one is found, returns a normalized GitHub uri <see cref="UriString"/>
39+
/// for the repository's remote named "origin" if one is found
2840
/// </summary>
2941
/// <remarks>
3042
/// The lookup checks to see if the specified <paramref name="path"/> is a repository. If it's not, it then
3143
/// walks up the parent directories until it either finds a repository, or reaches the root disk.
3244
/// </remarks>
3345
/// <param name="path">The path to start probing</param>
34-
/// <returns>A <see cref="UriString"/> representing the origin or null if none found.</returns>
46+
/// <returns>Returns a <see cref="UriString"/> representing the uri of the "origin" remote normalized to a GitHub repository url or null if none found.</returns>
3547
public UriString GetUri(string path)
3648
{
3749
return GetUri(GetRepo(path));
3850
}
3951

4052
/// <summary>
41-
/// Probes for a git repository and if one is found, returns a <see cref="UriString"/> for the repository's
42-
/// remote named "origin" if one is found
53+
/// Probes for a git repository and if one is found, returns a normalized GitHub uri <see cref="UriString"/>
54+
/// for the repository's remote named "origin" if one is found
55+
/// </summary>
56+
/// <remarks>
57+
/// The lookup checks to see if the specified <paramref name="path"/> is a repository. If it's not, it then
58+
/// walks up the parent directories until it either finds a repository, or reaches the root disk.
59+
/// </remarks>
60+
/// <param name="path">The path to start probing</param>
61+
/// <returns>Returns a <see cref="UriString"/> representing the uri of the "origin" remote normalized to a GitHub repository url or null if none found.</returns>
62+
public static UriString GetUriFromPath(string path)
63+
{
64+
return GitServiceHelper.GetUri(path);
65+
}
66+
67+
/// <summary>
68+
/// Probes for a git repository and if one is found, returns a normalized GitHub uri
69+
/// <see cref="UriString"/> for the repository's remote named "origin" if one is found
4370
/// </summary>
4471
/// <remarks>
4572
/// The lookup checks to see if the path specified by the RepositoryPath property of the specified
4673
/// <see cref="repoInfo"/> is a repository. If it's not, it then walks up the parent directories until it
4774
/// either finds a repository, or reaches the root disk.
4875
/// </remarks>
4976
/// <param name="repoInfo">The repository information containing the path to start probing</param>
50-
/// <returns>A <see cref="UriString"/> representing the origin or null if none found.</returns>
77+
/// <returns>Returns a <see cref="UriString"/> representing the uri of the "origin" remote normalized to a GitHub repository url or null if none found.</returns>
5178
public UriString GetUri(IGitRepositoryInfo repoInfo)
5279
{
5380
return GetUri(GetRepo(repoInfo));
5481
}
5582

83+
/// <summary>
84+
/// Probes for a git repository and if one is found, returns a normalized GitHub uri
85+
/// <see cref="UriString"/> for the repository's remote named "origin" if one is found
86+
/// </summary>
87+
/// <remarks>
88+
/// The lookup checks to see if the path specified by the RepositoryPath property of the specified
89+
/// <see cref="repoInfo"/> is a repository. If it's not, it then walks up the parent directories until it
90+
/// either finds a repository, or reaches the root disk.
91+
/// </remarks>
92+
/// <param name="repoInfo">The repository information containing the path to start probing</param>
93+
/// <returns>Returns a <see cref="UriString"/> representing the uri of the "origin" remote normalized to a GitHub repository url or null if none found.</returns>
94+
public static UriString GetUriFromVSGit(IGitRepositoryInfo repoInfo)
95+
{
96+
return GitServiceHelper.GetUri(repoInfo);
97+
}
98+
5699
/// <summary>
57100
/// Probes for a git repository and if one is found, returns a <see cref="IRepository"/> instance for the
58101
/// repository.
@@ -70,6 +113,22 @@ public IRepository GetRepo(IGitRepositoryInfo repoInfo)
70113
return GetRepo(repoInfo?.RepositoryPath);
71114
}
72115

116+
/// <summary>
117+
/// Probes for a git repository and if one is found, returns a <see cref="IRepository"/> instance for the
118+
/// repository.
119+
/// </summary>
120+
/// <remarks>
121+
/// The lookup checks to see if the path specified by the RepositoryPath property of the specified
122+
/// <see cref="repoInfo"/> is a repository. If it's not, it then walks up the parent directories until it
123+
/// either finds a repository, or reaches the root disk.
124+
/// </remarks>
125+
/// <param name="repoInfo">The repository information containing the path to start probing</param>
126+
/// <returns>An instance of <see cref="IRepository"/> or null</returns>
127+
public static IRepository GetRepoFromVSGit(IGitRepositoryInfo repoInfo)
128+
{
129+
return GitServiceHelper.GetRepo(repoInfo);
130+
}
131+
73132
/// <summary>
74133
/// Probes for a git repository and if one is found, returns a <see cref="IRepository"/> instance for the
75134
/// repository.
@@ -86,13 +145,35 @@ public IRepository GetRepo(string path)
86145
return repoPath == null ? null : new Repository(repoPath);
87146
}
88147

89-
internal static UriString GetUriFromRepository(IRepository repo)
148+
/// <summary>
149+
/// Probes for a git repository and if one is found, returns a <see cref="IRepository"/> instance for the
150+
/// repository.
151+
/// </summary>
152+
/// <remarks>
153+
/// The lookup checks to see if the specified <paramref name="path"/> is a repository. If it's not, it then
154+
/// walks up the parent directories until it either finds a repository, or reaches the root disk.
155+
/// </remarks>
156+
/// <param name="path">The path to start probing</param>
157+
/// <returns>An instance of <see cref="IRepository"/> or null</returns>
158+
public static IRepository GetRepoFromPath(string path)
159+
{
160+
return GitServiceHelper.GetRepo(path);
161+
}
162+
163+
/// <summary>
164+
/// Returns a <see cref="UriString"/> representing the uri of the "origin" remote with no modifications.
165+
/// </summary>
166+
/// <param name="repo"></param>
167+
/// <returns></returns>
168+
public static UriString GetOriginUri(IRepository repo)
90169
{
91170
return repo
92171
?.Network
93172
.Remotes
94173
.FirstOrDefault(x => x.Name.Equals("origin", StringComparison.Ordinal))
95174
?.Url;
96175
}
176+
177+
public static IGitService GitServiceHelper => VisualStudio.Services.DefaultExportProvider.GetExportedValueOrDefault<IGitService>() ?? new GitService();
97178
}
98179
}

src/GitHub.Exports/Services/Services.cs

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
using System;
22
using EnvDTE;
33
using EnvDTE80;
4-
using GitHub.Extensions;
54
using GitHub.Info;
65
using GitHub.Primitives;
76
using GitHub.Services;
87
using LibGit2Sharp;
98
using Microsoft.VisualStudio;
109
using Microsoft.VisualStudio.ComponentModelHost;
1110
using Microsoft.VisualStudio.Shell.Interop;
11+
using Microsoft.VisualStudio.Shell;
12+
using System.ComponentModel.Composition.Hosting;
1213

1314
namespace GitHub.VisualStudio
1415
{
@@ -17,10 +18,11 @@ public static class Services
1718
public static IServiceProvider PackageServiceProvider { get; set; }
1819

1920
/// <summary>
20-
/// Two ways of getting a service. First, trying the passed-in <paramref name="provider"/>,
21-
/// then <see cref="PackageServiceProvider"/>
22-
/// If the passed-in provider returns null, try PackageServiceProvider, returning the fetched value
23-
/// regardless of whether it's null or not.
21+
/// Three ways of getting a service. First, trying the passed-in <paramref name="provider"/>,
22+
/// then <see cref="PackageServiceProvider"/>, then <see cref="T:Microsoft.VisualStudio.Shell.Package"/>
23+
/// If the passed-in provider returns null, try PackageServiceProvider or Package, returning the fetched value
24+
/// regardless of whether it's null or not. Package.GetGlobalService is never called if PackageServiceProvider is set.
25+
/// This is on purpose, to support easy unit testing outside VS.
2426
/// </summary>
2527
/// <typeparam name="T"></typeparam>
2628
/// <typeparam name="Ret"></typeparam>
@@ -33,10 +35,13 @@ static Ret GetGlobalService<T, Ret>(IServiceProvider provider = null) where T :
3335
ret = provider.GetService(typeof(T)) as Ret;
3436
if (ret != null)
3537
return ret;
36-
return PackageServiceProvider.GetService(typeof(T)) as Ret;
38+
if (PackageServiceProvider != null)
39+
return PackageServiceProvider.GetService(typeof(T)) as Ret;
40+
return Package.GetGlobalService(typeof(T)) as Ret;
3741
}
3842

3943
public static IComponentModel ComponentModel => GetGlobalService<SComponentModel, IComponentModel>();
44+
public static ExportProvider DefaultExportProvider => ComponentModel.DefaultExportProvider;
4045

4146
public static IVsWebBrowsingService GetWebBrowsingService(this IServiceProvider provider)
4247
{
@@ -92,13 +97,7 @@ public static UriString GetRepoUrlFromSolution(IVsSolution solution)
9297
return null;
9398
if (solutionDir == null)
9499
return null;
95-
var repoPath = Repository.Discover(solutionDir);
96-
if (repoPath == null)
97-
return null;
98-
using (var repo = new Repository(repoPath))
99-
{
100-
return GetUri(repo);
101-
}
100+
return GitService.GitServiceHelper.GetUri(solutionDir);
102101
}
103102

104103
public static IRepository GetRepoFromSolution(this IVsSolution solution)
@@ -108,15 +107,7 @@ public static IRepository GetRepoFromSolution(this IVsSolution solution)
108107
return null;
109108
if (solutionDir == null)
110109
return null;
111-
var repoPath = Repository.Discover(solutionDir);
112-
if (repoPath == null)
113-
return null;
114-
return new Repository(repoPath);
115-
}
116-
static UriString GetUri(IRepository repo)
117-
{
118-
return UriString.ToUriString(GitService.GetUriFromRepository(repo)?.ToRepositoryUrl());
110+
return GitService.GitServiceHelper.GetRepo(solutionDir);
119111
}
120-
public static IGitService IGitService => PackageServiceProvider.GetService<IGitService>();
121112
}
122113
}

src/UnitTests/GitHub.App/Models/RepositoryModelTests.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ public void NoRemoteUrl()
6060
{
6161
var provider = Substitutes.ServiceProvider;
6262
Services.PackageServiceProvider = provider;
63-
var gitservice = Substitutes.IGitService;
64-
provider.GetService(typeof(IGitService)).Returns(gitservice);
63+
var gitservice = provider.GetGitService();
6564
var repo = Substitute.For<IRepository>();
6665
var path = Directory.CreateSubdirectory("repo-name");
6766
gitservice.GetUri(path.FullName).Returns((UriString)null);
@@ -74,8 +73,7 @@ public void WithRemoteUrl()
7473
{
7574
var provider = Substitutes.ServiceProvider;
7675
Services.PackageServiceProvider = provider;
77-
var gitservice = Substitutes.IGitService;
78-
provider.GetService(typeof(IGitService)).Returns(gitservice);
76+
var gitservice = provider.GetGitService();
7977
var repo = Substitute.For<IRepository>();
8078
var path = Directory.CreateSubdirectory("repo-name");
8179
gitservice.GetUri(path.FullName).Returns(new UriString("https://github.com/user/repo-name"));

0 commit comments

Comments
 (0)