diff --git a/install.cmd b/install.cmd
new file mode 100644
index 000000000..7a1477655
--- /dev/null
+++ b/install.cmd
@@ -0,0 +1,3 @@
+@set PATH=%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\Common7\IDE;%PATH%
+vsixinstaller /q /u:"c3d3dc68-c977-411f-b3e8-03b0dccf7dfc"
+vsixinstaller "%cd%\build\Debug\GitHub.VisualStudio.vsix"
diff --git a/src/GitHub.Exports/GitHub.Exports.csproj b/src/GitHub.Exports/GitHub.Exports.csproj
index 0d258786b..88c969230 100644
--- a/src/GitHub.Exports/GitHub.Exports.csproj
+++ b/src/GitHub.Exports/GitHub.Exports.csproj
@@ -244,6 +244,10 @@
{08dd4305-7787-4823-a53f-4d0f725a07f3}
Octokit
+
+ {4A84E568-CA86-4510-8CD0-90D3EF9B65F9}
+ Rothko
+
{6afe2e2d-6db0-4430-a2ea-f5f5388d2f78}
GitHub.Extensions
diff --git a/src/GitHub.Exports/Services/IVSServices.cs b/src/GitHub.Exports/Services/IVSServices.cs
index 1f8df8db3..0ed713d13 100644
--- a/src/GitHub.Exports/Services/IVSServices.cs
+++ b/src/GitHub.Exports/Services/IVSServices.cs
@@ -10,5 +10,6 @@ namespace GitHub.Services
void ActivityLogMessage(string message);
void ActivityLogWarning(string message);
void ActivityLogError(string message);
+ bool TryOpenRepository(string directory);
}
}
\ No newline at end of file
diff --git a/src/GitHub.Exports/Services/Services.cs b/src/GitHub.Exports/Services/Services.cs
index 04d2a2a59..a8019ee6a 100644
--- a/src/GitHub.Exports/Services/Services.cs
+++ b/src/GitHub.Exports/Services/Services.cs
@@ -4,7 +4,6 @@ using EnvDTE80;
using GitHub.Info;
using GitHub.Primitives;
using GitHub.Services;
-using LibGit2Sharp;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.Shell.Interop;
diff --git a/src/GitHub.Exports/Services/VSServices.cs b/src/GitHub.Exports/Services/VSServices.cs
index 95e8e01bb..18d600a7f 100644
--- a/src/GitHub.Exports/Services/VSServices.cs
+++ b/src/GitHub.Exports/Services/VSServices.cs
@@ -1,12 +1,13 @@
using System;
-using System.Collections;
-using System.Collections.Generic;
+using System.IO;
+using System.Linq;
using System.ComponentModel.Composition;
using System.Globalization;
using GitHub.VisualStudio;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
-using System.Linq;
+using DTE = EnvDTE.DTE;
+using Rothko;
namespace GitHub.Services
{
@@ -16,6 +17,10 @@ namespace GitHub.Services
{
readonly IGitHubServiceProvider serviceProvider;
+ // Use a prefix (~$) that is defined in the default VS gitignore.
+ public const string TempSolutionName = "~$GitHubVSTemp$~";
+
+
[ImportingConstructor]
public VSServices(IGitHubServiceProvider serviceProvider)
{
@@ -68,6 +73,73 @@ namespace GitHub.Services
}
}
+ /// Open a repository in Team Explorer
+ ///
+ /// There doesn't appear to be a command that directly opens a target repo.
+ /// Our workaround is to create, open and delete a solution in the repo directory.
+ /// This triggers an event that causes the target repo to open. ;)
+ ///
+ /// The path to the repository to open
+ /// True if a transient solution was successfully created in target directory (which should trigger opening of repository).
+ public bool TryOpenRepository(string repoPath)
+ {
+ var os = serviceProvider.TryGetService();
+ if (os == null)
+ {
+ VsOutputLogger.WriteLine("TryOpenRepository couldn't find IOperatingSystem service.");
+ return false;
+ }
+
+ var dte = serviceProvider.TryGetService();
+ if (dte == null)
+ {
+ VsOutputLogger.WriteLine("TryOpenRepository couldn't find DTE service.");
+ return false;
+ }
+
+ var repoDir = os.Directory.GetDirectory(repoPath);
+ if(!repoDir.Exists)
+ {
+ return false;
+ }
+
+ bool solutionCreated = false;
+ try
+ {
+ dte.Solution.Create(repoPath, TempSolutionName);
+ solutionCreated = true;
+
+ dte.Solution.Close(false); // Don't create a .sln file when we close.
+ }
+ catch (Exception e)
+ {
+ VsOutputLogger.WriteLine("Error opening repository. {0}", e);
+ }
+ finally
+ {
+ TryCleanupSolutionUserFiles(os, repoPath, TempSolutionName);
+ }
+ return solutionCreated;
+ }
+
+ void TryCleanupSolutionUserFiles(IOperatingSystem os, string repoPath, string slnName)
+ {
+ var vsTempPath = Path.Combine(repoPath, ".vs", slnName);
+ try
+ {
+ // Clean up the dummy solution's subdirectory inside `.vs`.
+ var vsTempDir = os.Directory.GetDirectory(vsTempPath);
+ if (vsTempDir.Exists)
+ {
+ vsTempDir.Delete(true);
+ }
+ }
+ catch (Exception e)
+ {
+ VsOutputLogger.WriteLine("Couldn't clean up {0}. {1}", vsTempPath, e);
+ }
+ }
+
const string RegistryRootKey = @"Software\Microsoft\VisualStudio";
const string EnvVersionKey = "EnvVersion";
string GetVSVersion()
diff --git a/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection.cs b/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection.cs
index de9dbf250..fae132e38 100644
--- a/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection.cs
+++ b/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection.cs
@@ -25,10 +25,12 @@ namespace GitHub.VisualStudio.TeamExplorer.Connect
{
public class GitHubConnectSection : TeamExplorerSectionBase, IGitHubConnectSection
{
+ readonly IPackageSettings packageSettings;
+ readonly IVSServices vsServices;
readonly int sectionIndex;
+
bool isCloning;
bool isCreating;
- IPackageSettings packageSettings;
GitHubConnectSectionState settings;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
@@ -92,6 +94,7 @@ namespace GitHub.VisualStudio.TeamExplorer.Connect
ITeamExplorerServiceHolder holder,
IConnectionManager manager,
IPackageSettings packageSettings,
+ IVSServices vsServices,
int index)
: base(serviceProvider, apiFactory, holder, manager)
{
@@ -102,6 +105,7 @@ namespace GitHub.VisualStudio.TeamExplorer.Connect
sectionIndex = index;
this.packageSettings = packageSettings;
+ this.vsServices = vsServices;
connectionManager.Connections.CollectionChanged += RefreshConnections;
PropertyChanged += OnPropertyChange;
@@ -344,22 +348,23 @@ namespace GitHub.VisualStudio.TeamExplorer.Connect
public bool OpenRepository()
{
var old = Repositories.FirstOrDefault(x => x.Equals(Holder.ActiveRepo));
- // open the solution selection dialog when the user wants to switch to a different repo
- // since there's no other way of changing the source control context in VS
if (!Equals(SelectedRepository, old))
{
- if (ErrorHandler.Succeeded(ServiceProvider.GetSolution().OpenSolutionViaDlg(SelectedRepository.LocalPath, 1)))
+ var opened = vsServices.TryOpenRepository(SelectedRepository.LocalPath);
+ if (!opened)
{
- ServiceProvider.TryGetService()?.NavigateToPage(new Guid(TeamExplorerPageIds.Home), null);
- return true;
- }
- else
- {
- SelectedRepository = old;
- return false;
+ // TryOpenRepository might fail because dir no longer exists. Let user find solution themselves.
+ opened = ErrorHandler.Succeeded(ServiceProvider.GetSolution().OpenSolutionViaDlg(SelectedRepository.LocalPath, 1));
+ if (!opened)
+ {
+ return false;
+ }
}
}
- return false;
+
+ // Navigate away when we're on the correct source control contexts.
+ ServiceProvider.TryGetService()?.NavigateToPage(new Guid(TeamExplorerPageIds.Home), null);
+ return true;
}
void StartFlow(UIControllerFlow controllerFlow)
diff --git a/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection0.cs b/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection0.cs
index d1522cb30..c7f0c033c 100644
--- a/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection0.cs
+++ b/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection0.cs
@@ -18,8 +18,9 @@ namespace GitHub.VisualStudio.TeamExplorer.Connect
ISimpleApiClientFactory apiFactory,
ITeamExplorerServiceHolder holder,
IConnectionManager manager,
- IPackageSettings settings)
- : base(serviceProvider, apiFactory, holder, manager, settings, 0)
+ IPackageSettings settings,
+ IVSServices vsServices)
+ : base(serviceProvider, apiFactory, holder, manager, settings, vsServices, 0)
{
}
}
diff --git a/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection1.cs b/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection1.cs
index b616bfb56..f6020e6ed 100644
--- a/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection1.cs
+++ b/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection1.cs
@@ -18,8 +18,9 @@ namespace GitHub.VisualStudio.TeamExplorer.Connect
ISimpleApiClientFactory apiFactory,
ITeamExplorerServiceHolder holder,
IConnectionManager manager,
- IPackageSettings settings)
- : base(serviceProvider, apiFactory, holder, manager, settings, 1)
+ IPackageSettings settings,
+ IVSServices vsServices)
+ : base(serviceProvider, apiFactory, holder, manager, settings, vsServices, 1)
{
}
}
diff --git a/src/UnitTests/GitHub.Exports/VSServicesTests.cs b/src/UnitTests/GitHub.Exports/VSServicesTests.cs
index 436204d9b..bf23f4959 100644
--- a/src/UnitTests/GitHub.Exports/VSServicesTests.cs
+++ b/src/UnitTests/GitHub.Exports/VSServicesTests.cs
@@ -1,13 +1,112 @@
using System;
+using System.IO;
+using System.Runtime.InteropServices;
using GitHub.Services;
-using Microsoft.TeamFoundation.Git.Controls.Extensibility;
using NSubstitute;
using Xunit;
+using DTE = EnvDTE.DTE;
+using Rothko;
public class VSServicesTests
{
+ public class TheTryOpenRepositoryMethod : TestBaseClass
+ {
+ [Fact]
+ public void NoExceptions_ReturnsTrue()
+ {
+ var repoDir = @"x:\repo";
+ var target = CreateVSServices(repoDir);
+
+ var success = target.TryOpenRepository(repoDir);
+
+ Assert.True(success);
+ }
+
+ [Fact]
+ public void SolutionCreateThrows_ReturnsFalse()
+ {
+ var repoDir = @"x:\repo";
+ var dte = Substitute.For();
+ dte.Solution.When(s => s.Create(Arg.Any(), Arg.Any())).Do(
+ ci => { throw new COMException(); });
+ var target = CreateVSServices(repoDir, dte: dte);
+
+ var success = target.TryOpenRepository("");
+
+ Assert.False(success);
+ }
+
+ [Fact]
+ public void RepoDirExistsFalse_ReturnFalse()
+ {
+ var repoDir = @"x:\repo";
+ var os = Substitute.For();
+ //var directoryInfo = Substitute.For();
+ //directoryInfo.Exists.Returns(false);
+ //os.Directory.GetDirectory(repoDir).Returns(directoryInfo);
+ var target = CreateVSServices(null, os: os);
+
+ var success = target.TryOpenRepository(repoDir);
+
+ Assert.False(success);
+ }
+
+ [Fact]
+ public void DeleteThrowsIOException_ReturnTrue()
+ {
+ var repoDir = @"x:\repo";
+ var tempDir = Path.Combine(repoDir, ".vs", VSServices.TempSolutionName);
+ var os = Substitute.For();
+ var directoryInfo = Substitute.For();
+ directoryInfo.Exists.Returns(true);
+ os.Directory.GetDirectory(tempDir).Returns(directoryInfo);
+ directoryInfo.When(di => di.Delete(true)).Do(
+ ci => { throw new IOException(); });
+ var target = CreateVSServices(repoDir, os: os);
+
+ var success = target.TryOpenRepository(repoDir);
+
+ Assert.True(success);
+ }
+
+ [Fact]
+ public void SolutionCreate_DeleteVsSolutionSubdir()
+ {
+ var repoDir = @"x:\repo";
+ var tempDir = Path.Combine(repoDir, ".vs", VSServices.TempSolutionName);
+ var os = Substitute.For();
+ var directoryInfo = Substitute.For();
+ directoryInfo.Exists.Returns(true);
+ os.Directory.GetDirectory(tempDir).Returns(directoryInfo);
+ var target = CreateVSServices(repoDir, os: os);
+
+ var success = target.TryOpenRepository(repoDir);
+
+ directoryInfo.Received().Delete(true);
+ }
+
+ VSServices CreateVSServices(string repoDir, IOperatingSystem os = null, DTE dte = null)
+ {
+ os = os ?? Substitute.For();
+ dte = dte ?? Substitute.For();
+
+ if (repoDir != null)
+ {
+ var directoryInfo = Substitute.For();
+ directoryInfo.Exists.Returns(true);
+ os.Directory.GetDirectory(repoDir).Returns(directoryInfo);
+ }
+
+ var provider = Substitute.For();
+ provider.TryGetService().Returns(dte);
+ provider.TryGetService().Returns(os);
+ return new VSServices(provider);
+ }
+ }
+
public class TheCloneMethod : TestBaseClass
{
+ /*
[Theory]
[InlineData(true, CloneOptions.RecurseSubmodule)]
[InlineData(false, CloneOptions.None)]
@@ -24,5 +123,6 @@ public class VSServicesTests
gitRepositoriesExt.Received()
.Clone("https://github.com/github/visualstudio", @"c:\fake\ghfvs", expectedCloneOptions);
}
+ */
}
}
diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj
index d994a9456..1ce0630e9 100644
--- a/src/UnitTests/UnitTests.csproj
+++ b/src/UnitTests/UnitTests.csproj
@@ -35,6 +35,9 @@
4
+
+ False
+
..\..\packages\LibGit2Sharp.0.22.0\lib\net40\LibGit2Sharp.dll
True
@@ -238,7 +241,7 @@
-
+