diff --git a/src/GitHub.Exports/Extensions/GitHelpers.cs b/src/GitHub.Exports/Extensions/GitHelpers.cs
deleted file mode 100644
index 8925fbb75a..0000000000
--- a/src/GitHub.Exports/Extensions/GitHelpers.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using GitHub.Primitives;
-using LibGit2Sharp;
-using Microsoft.VisualStudio.TeamFoundation.Git.Extensibility;
-using System;
-using System.Linq;
-
-namespace GitHub.Extensions
-{
- public static class GitHelpers
- {
- public static Repository GetRepoFromIGit(this IGitRepositoryInfo repoInfo)
- {
- var repoPath = Repository.Discover(repoInfo.RepositoryPath);
- if (repoPath == null)
- return null;
- return new Repository(repoPath);
- }
-
- public static UriString GetUriFromRepository(this IGitRepositoryInfo repoInfo)
- {
- return repoInfo.GetRepoFromIGit()?.GetUri();
- }
-
- public static UriString GetUri(this Repository repo)
- {
- return UriString.ToUriString(GetUriFromRepository(repo)?.ToRepositoryUrl());
- }
-
- static UriString GetUriFromRepository(Repository repo)
- {
- return repo
- ?.Network
- .Remotes
- .FirstOrDefault(x => x.Name.Equals("origin", StringComparison.Ordinal))
- ?.Url;
- }
-
- public static Repository GetRepoFromPath(string path)
- {
- var repoPath = Repository.Discover(path);
- if (repoPath == null)
- return null;
- return new Repository(repoPath);
- }
- }
-}
diff --git a/src/GitHub.Exports/Extensions/SimpleRepositoryModelExtensions.cs b/src/GitHub.Exports/Extensions/SimpleRepositoryModelExtensions.cs
index 939818e4fd..c5089e3d82 100644
--- a/src/GitHub.Exports/Extensions/SimpleRepositoryModelExtensions.cs
+++ b/src/GitHub.Exports/Extensions/SimpleRepositoryModelExtensions.cs
@@ -3,6 +3,7 @@
using System.IO;
using GitHub.Models;
using Microsoft.VisualStudio.TeamFoundation.Git.Extensibility;
+using GitHub.Services;
namespace GitHub.Extensions
{
@@ -18,7 +19,7 @@ public static ISimpleRepositoryModel ToModel(this IGitRepositoryInfo repo)
public static bool HasCommits(this ISimpleRepositoryModel repository)
{
- var repo = VisualStudio.Services.IGitService.GetRepo(repository.LocalPath);
+ var repo = GitService.GitServiceHelper.GetRepo(repository.LocalPath);
return repo?.Commits.Any() ?? false;
}
diff --git a/src/GitHub.Exports/GitHub.Exports.csproj b/src/GitHub.Exports/GitHub.Exports.csproj
index 2256606870..d38af1ce82 100644
--- a/src/GitHub.Exports/GitHub.Exports.csproj
+++ b/src/GitHub.Exports/GitHub.Exports.csproj
@@ -99,7 +99,6 @@
Key.snk
-
diff --git a/src/GitHub.Exports/Models/SimpleRepositoryModel.cs b/src/GitHub.Exports/Models/SimpleRepositoryModel.cs
index 419411f96a..9bb2672660 100644
--- a/src/GitHub.Exports/Models/SimpleRepositoryModel.cs
+++ b/src/GitHub.Exports/Models/SimpleRepositoryModel.cs
@@ -5,6 +5,7 @@
using GitHub.Primitives;
using GitHub.UI;
using GitHub.VisualStudio.Helpers;
+using GitHub.Services;
namespace GitHub.Models
{
@@ -26,7 +27,7 @@ public SimpleRepositoryModel(string path)
var dir = new DirectoryInfo(path);
if (!dir.Exists)
throw new ArgumentException("Path does not exist", nameof(path));
- var uri = VisualStudio.Services.IGitService.GetUri(path);
+ var uri = GitService.GitServiceHelper.GetUri(path);
var name = uri?.NameWithOwner ?? dir.Name;
Name = name;
LocalPath = path;
@@ -47,7 +48,7 @@ public void Refresh()
{
if (LocalPath == null)
return;
- var uri = VisualStudio.Services.IGitService.GetUri(LocalPath);
+ var uri = GitService.GitServiceHelper.GetUri(LocalPath);
if (CloneUrl != uri)
CloneUrl = uri;
}
diff --git a/src/GitHub.Exports/Services/GitService.cs b/src/GitHub.Exports/Services/GitService.cs
index ecaa487003..2800aa869a 100644
--- a/src/GitHub.Exports/Services/GitService.cs
+++ b/src/GitHub.Exports/Services/GitService.cs
@@ -7,8 +7,8 @@
namespace GitHub.Services
{
- [Export(typeof(IVSServices))]
- [PartCreationPolicy(CreationPolicy.Shared)]
+ [Export(typeof(IGitService))]
+ [PartCreationPolicy(CreationPolicy.NonShared)]
public class GitService : IGitService
{
///
@@ -16,30 +16,55 @@ public class GitService : IGitService
/// is null or no remote named origin exists, this method returns null
///
/// The repository to look at for the remote.
- /// A representing the origin or null if none found.
+ /// Returns a representing the uri of the "origin" remote normalized to a GitHub repository url or null if none found.
public UriString GetUri(IRepository repository)
{
- return UriString.ToUriString(GetUriFromRepository(repository)?.ToRepositoryUrl());
+ return UriString.ToUriString(GetOriginUri(repository)?.ToRepositoryUrl());
}
///
- /// Probes for a git repository and if one is found, returns a for the repository's
- /// remote named "origin" if one is found
+ /// Returns a representing the uri of the "origin" remote normalized to a GitHub repository url or null if none found.
+ ///
+ ///
+ /// Returns a representing the uri of the "origin" remote normalized to a GitHub repository url or null if none found.
+ public static UriString GetGitHubUri(IRepository repository)
+ {
+ return GitServiceHelper.GetUri(repository);
+ }
+
+ ///
+ /// Probes for a git repository and if one is found, returns a normalized GitHub uri
+ /// for the repository's remote named "origin" if one is found
///
///
/// The lookup checks to see if the specified is a repository. If it's not, it then
/// walks up the parent directories until it either finds a repository, or reaches the root disk.
///
/// The path to start probing
- /// A representing the origin or null if none found.
+ /// Returns a representing the uri of the "origin" remote normalized to a GitHub repository url or null if none found.
public UriString GetUri(string path)
{
return GetUri(GetRepo(path));
}
///
- /// Probes for a git repository and if one is found, returns a for the repository's
- /// remote named "origin" if one is found
+ /// Probes for a git repository and if one is found, returns a normalized GitHub uri
+ /// for the repository's remote named "origin" if one is found
+ ///
+ ///
+ /// The lookup checks to see if the specified is a repository. If it's not, it then
+ /// walks up the parent directories until it either finds a repository, or reaches the root disk.
+ ///
+ /// The path to start probing
+ /// Returns a representing the uri of the "origin" remote normalized to a GitHub repository url or null if none found.
+ public static UriString GetUriFromPath(string path)
+ {
+ return GitServiceHelper.GetUri(path);
+ }
+
+ ///
+ /// Probes for a git repository and if one is found, returns a normalized GitHub uri
+ /// for the repository's remote named "origin" if one is found
///
///
/// The lookup checks to see if the path specified by the RepositoryPath property of the specified
@@ -47,12 +72,28 @@ public UriString GetUri(string path)
/// either finds a repository, or reaches the root disk.
///
/// The repository information containing the path to start probing
- /// A representing the origin or null if none found.
+ /// Returns a representing the uri of the "origin" remote normalized to a GitHub repository url or null if none found.
public UriString GetUri(IGitRepositoryInfo repoInfo)
{
return GetUri(GetRepo(repoInfo));
}
+ ///
+ /// Probes for a git repository and if one is found, returns a normalized GitHub uri
+ /// for the repository's remote named "origin" if one is found
+ ///
+ ///
+ /// The lookup checks to see if the path specified by the RepositoryPath property of the specified
+ /// is a repository. If it's not, it then walks up the parent directories until it
+ /// either finds a repository, or reaches the root disk.
+ ///
+ /// The repository information containing the path to start probing
+ /// Returns a representing the uri of the "origin" remote normalized to a GitHub repository url or null if none found.
+ public static UriString GetUriFromVSGit(IGitRepositoryInfo repoInfo)
+ {
+ return GitServiceHelper.GetUri(repoInfo);
+ }
+
///
/// Probes for a git repository and if one is found, returns a instance for the
/// repository.
@@ -70,6 +111,22 @@ public IRepository GetRepo(IGitRepositoryInfo repoInfo)
return GetRepo(repoInfo?.RepositoryPath);
}
+ ///
+ /// Probes for a git repository and if one is found, returns a instance for the
+ /// repository.
+ ///
+ ///
+ /// The lookup checks to see if the path specified by the RepositoryPath property of the specified
+ /// is a repository. If it's not, it then walks up the parent directories until it
+ /// either finds a repository, or reaches the root disk.
+ ///
+ /// The repository information containing the path to start probing
+ /// An instance of or null
+ public static IRepository GetRepoFromVSGit(IGitRepositoryInfo repoInfo)
+ {
+ return GitServiceHelper.GetRepo(repoInfo);
+ }
+
///
/// Probes for a git repository and if one is found, returns a instance for the
/// repository.
@@ -86,7 +143,27 @@ public IRepository GetRepo(string path)
return repoPath == null ? null : new Repository(repoPath);
}
- internal static UriString GetUriFromRepository(IRepository repo)
+ ///
+ /// Probes for a git repository and if one is found, returns a instance for the
+ /// repository.
+ ///
+ ///
+ /// The lookup checks to see if the specified is a repository. If it's not, it then
+ /// walks up the parent directories until it either finds a repository, or reaches the root disk.
+ ///
+ /// The path to start probing
+ /// An instance of or null
+ public static IRepository GetRepoFromPath(string path)
+ {
+ return GitServiceHelper.GetRepo(path);
+ }
+
+ ///
+ /// Returns a representing the uri of the "origin" remote with no modifications.
+ ///
+ ///
+ ///
+ public static UriString GetOriginUri(IRepository repo)
{
return repo
?.Network
@@ -94,5 +171,7 @@ internal static UriString GetUriFromRepository(IRepository repo)
.FirstOrDefault(x => x.Name.Equals("origin", StringComparison.Ordinal))
?.Url;
}
+
+ public static IGitService GitServiceHelper => VisualStudio.Services.DefaultExportProvider.GetExportedValueOrDefault() ?? new GitService();
}
}
diff --git a/src/GitHub.Exports/Services/Services.cs b/src/GitHub.Exports/Services/Services.cs
index ceeaf640a4..717df6c6a8 100644
--- a/src/GitHub.Exports/Services/Services.cs
+++ b/src/GitHub.Exports/Services/Services.cs
@@ -1,7 +1,6 @@
using System;
using EnvDTE;
using EnvDTE80;
-using GitHub.Extensions;
using GitHub.Info;
using GitHub.Primitives;
using GitHub.Services;
@@ -9,6 +8,8 @@
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.Shell.Interop;
+using Microsoft.VisualStudio.Shell;
+using System.ComponentModel.Composition.Hosting;
namespace GitHub.VisualStudio
{
@@ -17,10 +18,11 @@ public static class Services
public static IServiceProvider PackageServiceProvider { get; set; }
///
- /// Two ways of getting a service. First, trying the passed-in ,
- /// then
- /// If the passed-in provider returns null, try PackageServiceProvider, returning the fetched value
- /// regardless of whether it's null or not.
+ /// Three ways of getting a service. First, trying the passed-in ,
+ /// then , then
+ /// If the passed-in provider returns null, try PackageServiceProvider or Package, returning the fetched value
+ /// regardless of whether it's null or not. Package.GetGlobalService is never called if PackageServiceProvider is set.
+ /// This is on purpose, to support easy unit testing outside VS.
///
///
///
@@ -33,10 +35,13 @@ static Ret GetGlobalService(IServiceProvider provider = null) where T :
ret = provider.GetService(typeof(T)) as Ret;
if (ret != null)
return ret;
- return PackageServiceProvider.GetService(typeof(T)) as Ret;
+ if (PackageServiceProvider != null)
+ return PackageServiceProvider.GetService(typeof(T)) as Ret;
+ return Package.GetGlobalService(typeof(T)) as Ret;
}
public static IComponentModel ComponentModel => GetGlobalService();
+ public static ExportProvider DefaultExportProvider => ComponentModel.DefaultExportProvider;
public static IVsWebBrowsingService GetWebBrowsingService(this IServiceProvider provider)
{
@@ -45,7 +50,7 @@ public static IVsWebBrowsingService GetWebBrowsingService(this IServiceProvider
public static IVsOutputWindow OutputWindow => GetGlobalService();
- static IVsOutputWindowPane outputWindowPane = null;
+ static IVsOutputWindowPane outputWindowPane;
public static IVsOutputWindowPane OutputWindowPane
{
get
@@ -56,7 +61,7 @@ public static IVsOutputWindowPane OutputWindowPane
var uiShell = GetGlobalService();
// Get the frame of the output window
var outputWindowGuid = new Guid("{34e76e81-ee4a-11d0-ae2e-00a0c90fffc3}");
- IVsWindowFrame outputWindowFrame = null;
+ IVsWindowFrame outputWindowFrame;
ErrorHandler.ThrowOnFailure(uiShell.FindToolWindow((uint)__VSCREATETOOLWIN.CTW_fForceCreate, ref outputWindowGuid, out outputWindowFrame));
// Show the output window
if (outputWindowFrame != null)
@@ -73,6 +78,7 @@ public static IVsOutputWindowPane OutputWindowPane
public static DTE Dte => GetGlobalService();
+ // ReSharper disable once SuspiciousTypeConversion.Global
public static DTE2 Dte2 => Dte as DTE2;
public static IVsActivityLog GetActivityLog(this IServiceProvider provider)
@@ -92,13 +98,7 @@ public static UriString GetRepoUrlFromSolution(IVsSolution solution)
return null;
if (solutionDir == null)
return null;
- var repoPath = Repository.Discover(solutionDir);
- if (repoPath == null)
- return null;
- using (var repo = new Repository(repoPath))
- {
- return GetUri(repo);
- }
+ return GitService.GitServiceHelper.GetUri(solutionDir);
}
public static IRepository GetRepoFromSolution(this IVsSolution solution)
@@ -108,15 +108,7 @@ public static IRepository GetRepoFromSolution(this IVsSolution solution)
return null;
if (solutionDir == null)
return null;
- var repoPath = Repository.Discover(solutionDir);
- if (repoPath == null)
- return null;
- return new Repository(repoPath);
- }
- static UriString GetUri(IRepository repo)
- {
- return UriString.ToUriString(GitService.GetUriFromRepository(repo)?.ToRepositoryUrl());
+ return GitService.GitServiceHelper.GetRepo(solutionDir);
}
- public static IGitService IGitService => PackageServiceProvider.GetService();
}
}
diff --git a/src/UnitTests/GitHub.App/Models/RepositoryModelTests.cs b/src/UnitTests/GitHub.App/Models/RepositoryModelTests.cs
index 6dc3a7e0d3..64577d00b8 100644
--- a/src/UnitTests/GitHub.App/Models/RepositoryModelTests.cs
+++ b/src/UnitTests/GitHub.App/Models/RepositoryModelTests.cs
@@ -60,8 +60,7 @@ public void NoRemoteUrl()
{
var provider = Substitutes.ServiceProvider;
Services.PackageServiceProvider = provider;
- var gitservice = Substitutes.IGitService;
- provider.GetService(typeof(IGitService)).Returns(gitservice);
+ var gitservice = provider.GetGitService();
var repo = Substitute.For();
var path = Directory.CreateSubdirectory("repo-name");
gitservice.GetUri(path.FullName).Returns((UriString)null);
@@ -74,8 +73,7 @@ public void WithRemoteUrl()
{
var provider = Substitutes.ServiceProvider;
Services.PackageServiceProvider = provider;
- var gitservice = Substitutes.IGitService;
- provider.GetService(typeof(IGitService)).Returns(gitservice);
+ var gitservice = provider.GetGitService();
var repo = Substitute.For();
var path = Directory.CreateSubdirectory("repo-name");
gitservice.GetUri(path.FullName).Returns(new UriString("https://github.com/user/repo-name"));
diff --git a/src/UnitTests/Substitutes.cs b/src/UnitTests/Substitutes.cs
index 2a6de0d842..003587c60e 100644
--- a/src/UnitTests/Substitutes.cs
+++ b/src/UnitTests/Substitutes.cs
@@ -2,10 +2,13 @@
using GitHub.Models;
using GitHub.Services;
using Microsoft.TeamFoundation.Git.Controls.Extensibility;
+using Microsoft.VisualStudio.ComponentModelHost;
using NSubstitute;
using Rothko;
using System;
using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -82,13 +85,21 @@ public static IServiceProvider GetServiceProvider(
IAvatarProvider avatarProvider = null)
{
var ret = Substitute.For();
+
+ var gitservice = IGitService;
+ var cm = Substitute.For();
+ var cc = new CompositionContainer(CompositionOptions.IsThreadSafe | CompositionOptions.DisableSilentRejection);
+ cc.ComposeExportedValue(gitservice);
+ ((IComponentModel)cm).DefaultExportProvider.Returns(cc);
+ ret.GetService(typeof(SComponentModel)).Returns(cm);
+
var os = OperatingSystem;
var vs = IVSServices;
var clone = cloneService ?? new RepositoryCloneService(os, vs);
var create = creationService ?? new RepositoryCreationService(clone);
avatarProvider = avatarProvider ?? Substitute.For();
ret.GetService(typeof(IGitRepositoriesExt)).Returns(IGitRepositoriesExt);
- ret.GetService(typeof(IGitService)).Returns(IGitService);
+ ret.GetService(typeof(IGitService)).Returns(gitservice);
ret.GetService(typeof(IVSServices)).Returns(vs);
ret.GetService(typeof(IOperatingSystem)).Returns(os);
ret.GetService(typeof(IRepositoryCloneService)).Returns(clone);