-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Fix cloning using Open From GitHub on Visual Studio 2015 #1982
Changes from all commits
1db61f0
b1a0171
a5a2b1a
8952f96
43b504e
4ce6852
c793d90
38a754a
fc2d2c1
4f527e7
567d490
9885bad
dd94727
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
|
@@ -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 | ||
| { | ||
|
|
@@ -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; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you quickly explain this bit for my edification?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a If we didn't call |
||
|
|
||
| /// <summary> | ||
| /// This MEF export requires specific versions of TeamFoundation. IGitExt is declared here so | ||
|
|
@@ -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 | ||
|
|
@@ -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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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( | ||
jcansdale marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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) | ||
jcansdale marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| // The clone progress bar appears on the GettingStartedSection of the Home page, | ||
| // so we wait for this to be hidden before continuing. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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>(); | ||
|
|
||
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
IGitRepositoriesExtservice available later on when theClonemethod attempts to retrieve it fromIGitHubServiceProvider.The problem was that it required two pieces to be in place before it wold work.
Team Explorer - Connextpage must be visibleGitServiceProvideronIGitHubServiceProviderThis was way too fragile and the reason the bug crept in.