Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/GitHub.App/Services/RepositoryCloneService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ public async Task CloneOrOpenRepository(
}
}

// Give user a chance to choose a solution
teamExplorerServices.ShowHomePage();
}

Expand Down
5 changes: 3 additions & 2 deletions src/GitHub.Exports/Services/VSServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,9 @@ public bool TryOpenRepository(string repoPath)
return false;
}

var repoDir = os.Directory.GetDirectory(repoPath);
if (!repoDir.Exists)
var gitPath = Path.Combine(repoPath, ".git");
var gitDir = os.Directory.GetDirectory(gitPath);
if (!gitDir.Exists)
{
return false;
}
Expand Down
32 changes: 0 additions & 32 deletions src/GitHub.StartPage/StartPagePackage.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
Expand All @@ -9,7 +8,6 @@
using GitHub.Primitives;
using GitHub.Services;
using GitHub.VisualStudio;
using Microsoft.TeamFoundation.Controls;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.CodeContainerManagement;
using Microsoft.VisualStudio.Threading;
Expand Down Expand Up @@ -59,7 +57,6 @@ async Task<CodeContainer> RunAcquisition(IProgress<ServiceProgressData> download
try
{
var uiProvider = await Task.Run(() => Package.GetGlobalService(typeof(IGitHubServiceProvider)) as IGitHubServiceProvider);
await ShowTeamExplorerPage(uiProvider);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this was the piece that was waiting for the Team Explorer to let us know that the clone had completed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a hack to make the IGitRepositoriesExt service available later on when the Clone method attempts to retrieve it from IGitHubServiceProvider.

The problem was that it required two pieces to be in place before it wold work.

  1. The Team Explorer - Connext page must be visible
  2. Something must have set the GitServiceProvider on IGitHubServiceProvider

This was way too fragile and the reason the bug crept in.

request = await ShowCloneDialog(uiProvider, downloadProgress, repository);
}
catch (Exception e)
Expand All @@ -84,35 +81,6 @@ async Task<CodeContainer> RunAcquisition(IProgress<ServiceProgressData> download
lastAccessed: DateTimeOffset.UtcNow);
}

async Task ShowTeamExplorerPage(IGitHubServiceProvider gitHubServiceProvider)
{
var te = gitHubServiceProvider?.GetService(typeof(ITeamExplorer)) as ITeamExplorer;

if (te != null)
{
var page = te.NavigateToPage(new Guid(TeamExplorerPageIds.Connect), null);

if (page == null)
{
var tcs = new TaskCompletionSource<ITeamExplorerPage>();
PropertyChangedEventHandler handler = null;

handler = new PropertyChangedEventHandler((s, e) =>
{
if (e.PropertyName == "CurrentPage")
{
tcs.SetResult(te.CurrentPage);
te.PropertyChanged -= handler;
}
});

te.PropertyChanged += handler;

page = await tcs.Task;
}
}
}

async Task<CloneDialogResult> ShowCloneDialog(
IGitHubServiceProvider gitHubServiceProvider,
IProgress<ServiceProgressData> progress,
Expand Down
83 changes: 64 additions & 19 deletions src/GitHub.TeamFoundation.14/Services/VSGitServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
#endif

using System;
using System.Threading;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.ComponentModel.Composition;
using System.Globalization;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
Expand All @@ -17,15 +17,12 @@
using GitHub.Models;
using GitHub.TeamFoundation;
using GitHub.VisualStudio;
#if TEAMEXPLORER14
using Microsoft.TeamFoundation.Controls;
using Microsoft.TeamFoundation.Git.Controls.Extensibility;
using ReactiveUI;
#else
using Microsoft.VisualStudio.Shell.Interop;
using System.Threading;
#endif
using Microsoft.VisualStudio.TeamFoundation.Git.Extensibility;
using ReactiveUI;
using Serilog;
using Microsoft;

namespace GitHub.Services
{
Expand All @@ -39,6 +36,8 @@ public class VSGitServices : IVSGitServices

[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Used in VS2017")]
readonly Lazy<IStatusBarNotificationService> statusBar;
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Used in VS2015")]
readonly Lazy<IVSServices> vsServices;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you quickly explain this bit for my edification?

Copy link
Collaborator Author

@jcansdale jcansdale Oct 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a TryOpenRepository method on IVSServices that we only need to call when running on Visual Studio 2015. On Visual Studio 2017, the cloned repository automatically opens in folder view (which is new in Visual Studio 2017).

If we didn't call TryOpenRepository, you wouldn't see the URL change on Team Explorer - Home and there'd be no option to open a solution. You'd need to manually find and open a solution in the cloned folder.


/// <summary>
/// This MEF export requires specific versions of TeamFoundation. IGitExt is declared here so
Expand All @@ -49,10 +48,13 @@ public class VSGitServices : IVSGitServices
IGitExt gitExtService;

[ImportingConstructor]
public VSGitServices(IGitHubServiceProvider serviceProvider, Lazy<IStatusBarNotificationService> statusBar)
public VSGitServices(IGitHubServiceProvider serviceProvider,
Lazy<IStatusBarNotificationService> statusBar,
Lazy<IVSServices> vsServices)
{
this.serviceProvider = serviceProvider;
this.statusBar = statusBar;
this.vsServices = vsServices;
}

// The Default Repository Path that VS uses is hidden in an internal
Expand Down Expand Up @@ -81,25 +83,68 @@ public async Task Clone(
bool recurseSubmodules,
object progress = null)
{
#if TEAMEXPLORER14
var gitExt = serviceProvider.GetService<IGitRepositoriesExt>();
gitExt.Clone(cloneUrl, clonePath, recurseSubmodules ? CloneOptions.RecurseSubmodule : CloneOptions.None);
var teamExplorer = serviceProvider.TryGetService<ITeamExplorer>();
Assumes.Present(teamExplorer);

// The operation will have completed when CanClone goes false and then true again.
await gitExt.WhenAnyValue(x => x.CanClone).Where(x => !x).Take(1);
await gitExt.WhenAnyValue(x => x.CanClone).Where(x => x).Take(1);
#if TEAMEXPLORER14

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is the new bit right? So instead of waiting for the response from TE, we're starting the clone and immediately navigating to the TE Home page and then the user will see the TE change as a result of the clone completing but TE is handling that. And we can identify that change (instead of actually waiting for an update from TE)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can infer that the clone operation has completed when the progress bar is hidden and automatically change context to the newly cloned repository. If we didn't change repository context at this point, the user would know the clone had completed, but they'd be left to find and open a solution from the newly cloned folder (not a nice experience).

await StartClonenOnConnectPageAsync(teamExplorer, cloneUrl, clonePath, recurseSubmodules);
NavigateToHomePage(teamExplorer); // Show progress on Team Explorer - Home
await WaitForCloneOnHomePageAsync(teamExplorer);
vsServices.Value.TryOpenRepository(clonePath); // Show the repository on Team Explorer - Home
#else
var gitExt = serviceProvider.GetService<IGitActionsExt>();
var typedProgress = ((Progress<ServiceProgressData>)progress) ?? new Progress<ServiceProgressData>();
typedProgress.ProgressChanged += (s, e) => statusBar.Value.ShowMessage(e.ProgressText);
var cloneTask = gitExt.CloneAsync(cloneUrl, clonePath, recurseSubmodules, default(CancellationToken), typedProgress);

await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.RunAsync(async () =>
{
typedProgress.ProgressChanged += (s, e) => statusBar.Value.ShowMessage(e.ProgressText);
await gitExt.CloneAsync(cloneUrl, clonePath, recurseSubmodules, default(CancellationToken), typedProgress);
});
NavigateToHomePage(teamExplorer); // Show progress on Team Explorer - Home
await cloneTask;
#endif
}

static async Task StartClonenOnConnectPageAsync(
ITeamExplorer teamExplorer, string cloneUrl, string clonePath, bool recurseSubmodules)
{
var connectPage = await NavigateToPageAsync(teamExplorer, new Guid(TeamExplorerPageIds.Connect));
Assumes.Present(connectPage);
var gitExt = connectPage.GetService<IGitRepositoriesExt>();
Assumes.Present(gitExt);

gitExt.Clone(cloneUrl, clonePath, recurseSubmodules ? CloneOptions.RecurseSubmodule : CloneOptions.None);
}

static async Task WaitForCloneOnHomePageAsync(ITeamExplorer teamExplorer)
{
// The clone progress bar appears on the GettingStartedSection of the Home page,
// so we wait for this to be hidden before continuing.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var sectionId = new Guid("d0200918-c025-4cc3-9dee-4f5e89d0c918"); // GettingStartedSection
await teamExplorer
.WhenAnyValue(x => x.CurrentPage)
.Where(p => p.GetId() == new Guid(TeamExplorerPageIds.Home))
.Select(p => p.GetSection(sectionId))
.Where(s => s != null)
.Select(s => s.WhenAnyValue(x => x.IsVisible))
.Switch() // Watch the topmost section
.StartWith(false) // If no events arrive default to invisible
.Throttle(TimeSpan.FromSeconds(1)) // Ignore glitch where section starts invisible
.Any(x => x == false);
}

static void NavigateToHomePage(ITeamExplorer teamExplorer)
{
teamExplorer.NavigateToPage(new Guid(TeamExplorerPageIds.Home), null);
}

static async Task<ITeamExplorerPage> NavigateToPageAsync(ITeamExplorer teamExplorer, Guid pageId)
{
teamExplorer.NavigateToPage(pageId, null);
var page = await teamExplorer
.WhenAnyValue(x => x.CurrentPage)
.Where(x => x?.GetId() == pageId)
.Take(1);
return page;
}

IGitRepositoryInfo GetRepoFromVS()
{
gitExtService = serviceProvider.GetService<IGitExt>();
Expand Down
3 changes: 2 additions & 1 deletion test/GitHub.Exports.UnitTests/VSServicesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,10 @@ VSServices CreateVSServices(string repoDir, IOperatingSystem os = null, DTE dte

if (repoDir != null)
{
var gitDir = Path.Combine(repoDir, ".git");
var directoryInfo = Substitute.For<IDirectoryInfo>();
directoryInfo.Exists.Returns(repoDirExists);
os.Directory.GetDirectory(repoDir).Returns(directoryInfo);
os.Directory.GetDirectory(gitDir).Returns(directoryInfo);
}

var provider = Substitute.For<IGitHubServiceProvider>();
Expand Down