зеркало из https://github.com/github/VisualStudio.git
Merge branch 'master' into docs-changes
This commit is contained in:
Коммит
e953a1a530
|
@ -16,7 +16,7 @@ namespace GitHub.SampleData.Dialog.Clone
|
|||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
public string PathError { get; set; }
|
||||
public string PathWarning { get; set; }
|
||||
public int SelectedTabIndex { get; set; }
|
||||
public string Title => null;
|
||||
public IObservable<object> Done => null;
|
||||
|
|
|
@ -29,9 +29,13 @@ namespace GitHub.Services
|
|||
this.showDialog = showDialog;
|
||||
}
|
||||
|
||||
public async Task<CloneDialogResult> ShowCloneDialog(IConnection connection)
|
||||
public async Task<CloneDialogResult> ShowCloneDialog(IConnection connection, string url = null)
|
||||
{
|
||||
var viewModel = factory.CreateViewModel<IRepositoryCloneViewModel>();
|
||||
if (url != null)
|
||||
{
|
||||
viewModel.UrlTab.Url = url;
|
||||
}
|
||||
|
||||
if (connection != null)
|
||||
{
|
||||
|
|
|
@ -34,6 +34,7 @@ namespace GitHub.Services
|
|||
readonly IOperatingSystem operatingSystem;
|
||||
readonly string defaultClonePath;
|
||||
readonly IVSGitServices vsGitServices;
|
||||
readonly ITeamExplorerServices teamExplorerServices;
|
||||
readonly IGraphQLClientFactory graphqlFactory;
|
||||
readonly IUsageTracker usageTracker;
|
||||
ICompiledQuery<ViewerRepositoriesModel> readViewerRepositories;
|
||||
|
@ -42,11 +43,13 @@ namespace GitHub.Services
|
|||
public RepositoryCloneService(
|
||||
IOperatingSystem operatingSystem,
|
||||
IVSGitServices vsGitServices,
|
||||
ITeamExplorerServices teamExplorerServices,
|
||||
IGraphQLClientFactory graphqlFactory,
|
||||
IUsageTracker usageTracker)
|
||||
{
|
||||
this.operatingSystem = operatingSystem;
|
||||
this.vsGitServices = vsGitServices;
|
||||
this.teamExplorerServices = teamExplorerServices;
|
||||
this.graphqlFactory = graphqlFactory;
|
||||
this.usageTracker = usageTracker;
|
||||
|
||||
|
@ -103,6 +106,54 @@ namespace GitHub.Services
|
|||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task CloneOrOpenRepository(
|
||||
CloneDialogResult cloneDialogResult,
|
||||
object progress = null)
|
||||
{
|
||||
Guard.ArgumentNotNull(cloneDialogResult, nameof(cloneDialogResult));
|
||||
|
||||
var repositoryPath = cloneDialogResult.Path;
|
||||
var url = cloneDialogResult.Url;
|
||||
|
||||
if (DestinationFileExists(repositoryPath))
|
||||
{
|
||||
throw new InvalidOperationException("Can't clone or open a repository because a file exists at: " + repositoryPath);
|
||||
}
|
||||
|
||||
var repositoryUrl = url.ToRepositoryUrl();
|
||||
var isDotCom = HostAddress.IsGitHubDotComUri(repositoryUrl);
|
||||
if (DestinationDirectoryExists(repositoryPath))
|
||||
{
|
||||
teamExplorerServices.OpenRepository(repositoryPath);
|
||||
|
||||
if (isDotCom)
|
||||
{
|
||||
await usageTracker.IncrementCounter(x => x.NumberOfGitHubOpens);
|
||||
}
|
||||
else
|
||||
{
|
||||
await usageTracker.IncrementCounter(x => x.NumberOfEnterpriseOpens);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var cloneUrl = repositoryUrl.ToString();
|
||||
await CloneRepository(cloneUrl, repositoryPath, progress).ConfigureAwait(true);
|
||||
|
||||
if (isDotCom)
|
||||
{
|
||||
await usageTracker.IncrementCounter(x => x.NumberOfGitHubClones);
|
||||
}
|
||||
else
|
||||
{
|
||||
await usageTracker.IncrementCounter(x => x.NumberOfEnterpriseClones);
|
||||
}
|
||||
}
|
||||
|
||||
teamExplorerServices.ShowHomePage();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task CloneRepository(
|
||||
string cloneUrl,
|
||||
|
@ -121,19 +172,12 @@ namespace GitHub.Services
|
|||
try
|
||||
{
|
||||
await vsGitServices.Clone(cloneUrl, repositoryPath, true, progress);
|
||||
|
||||
await usageTracker.IncrementCounter(x => x.NumberOfClones);
|
||||
|
||||
var repositoryUrl = new UriString(cloneUrl).ToRepositoryUrl();
|
||||
var isDotCom = HostAddress.IsGitHubDotComUri(repositoryUrl);
|
||||
if (isDotCom)
|
||||
if (repositoryPath.StartsWith(DefaultClonePath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await usageTracker.IncrementCounter(x => x.NumberOfGitHubClones);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If it isn't a GitHub URL, assume it's an Enterprise URL
|
||||
await usageTracker.IncrementCounter(x => x.NumberOfEnterpriseClones);
|
||||
// Count the number of times users clone into the Default Repository Location
|
||||
await usageTracker.IncrementCounter(x => x.NumberOfClonesToDefaultClonePath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -144,7 +188,10 @@ namespace GitHub.Services
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool DestinationExists(string path) => Directory.Exists(path) || File.Exists(path);
|
||||
public bool DestinationDirectoryExists(string path) => operatingSystem.Directory.DirectoryExists(path);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool DestinationFileExists(string path) => operatingSystem.File.Exists(path);
|
||||
|
||||
string GetLocalClonePathFromGitProvider(string fallbackPath)
|
||||
{
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace GitHub.Services
|
|||
CannotDropFolder,
|
||||
CannotDropFolderUnauthorizedAccess,
|
||||
ClipboardFailed,
|
||||
ClonedFailed,
|
||||
CloneOrOpenFailed,
|
||||
CloneFailedNotLoggedIn,
|
||||
CommitCreateFailed,
|
||||
CommitRevertFailed,
|
||||
|
@ -123,7 +123,7 @@ namespace GitHub.Services
|
|||
},
|
||||
{ ErrorType.ClipboardFailed, Map(Defaults("Failed to copy text to the clipboard.")) },
|
||||
{
|
||||
ErrorType.ClonedFailed, Map(Defaults("Failed to clone the repository '{0}'", "Email support@github.com if you continue to have problems."),
|
||||
ErrorType.CloneOrOpenFailed, Map(Defaults("Failed to clone or open the repository '{0}'", "Email support@github.com if you continue to have problems."),
|
||||
new[]
|
||||
{
|
||||
new Translation(@"fatal: bad config file line (\d+) in (.+)", "Failed to clone the repository '{0}'", @"The config file '$2' is corrupted at line $1. You may need to open the file and try to fix any errors."),
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.App;
|
||||
using GitHub.Extensions;
|
||||
using GitHub.Logging;
|
||||
using GitHub.Models;
|
||||
|
@ -23,12 +23,13 @@ namespace GitHub.ViewModels.Dialog.Clone
|
|||
readonly IOperatingSystem os;
|
||||
readonly IConnectionManager connectionManager;
|
||||
readonly IRepositoryCloneService service;
|
||||
readonly IGitService gitService;
|
||||
readonly IUsageService usageService;
|
||||
readonly IUsageTracker usageTracker;
|
||||
readonly IReadOnlyList<IRepositoryCloneTabViewModel> tabs;
|
||||
string path;
|
||||
IRepositoryModel previousRepository;
|
||||
ObservableAsPropertyHelper<string> pathError;
|
||||
ObservableAsPropertyHelper<string> pathWarning;
|
||||
int selectedTabIndex;
|
||||
|
||||
[ImportingConstructor]
|
||||
|
@ -36,6 +37,7 @@ namespace GitHub.ViewModels.Dialog.Clone
|
|||
IOperatingSystem os,
|
||||
IConnectionManager connectionManager,
|
||||
IRepositoryCloneService service,
|
||||
IGitService gitService,
|
||||
IUsageService usageService,
|
||||
IUsageTracker usageTracker,
|
||||
IRepositorySelectViewModel gitHubTab,
|
||||
|
@ -45,6 +47,7 @@ namespace GitHub.ViewModels.Dialog.Clone
|
|||
this.os = os;
|
||||
this.connectionManager = connectionManager;
|
||||
this.service = service;
|
||||
this.gitService = gitService;
|
||||
this.usageService = usageService;
|
||||
this.usageTracker = usageTracker;
|
||||
|
||||
|
@ -59,22 +62,27 @@ namespace GitHub.ViewModels.Dialog.Clone
|
|||
Path = service.DefaultClonePath;
|
||||
repository.Subscribe(x => UpdatePath(x));
|
||||
|
||||
pathError = Observable.CombineLatest(
|
||||
pathWarning = Observable.CombineLatest(
|
||||
repository,
|
||||
this.WhenAnyValue(x => x.Path),
|
||||
ValidatePath)
|
||||
.ToProperty(this, x => x.PathError);
|
||||
ValidatePathWarning)
|
||||
.ToProperty(this, x => x.PathWarning);
|
||||
|
||||
var canClone = Observable.CombineLatest(
|
||||
repository,
|
||||
this.WhenAnyValue(x => x.PathError),
|
||||
(repo, error) => (repo, error))
|
||||
.Select(x => x.repo != null && x.error == null);
|
||||
repository, this.WhenAnyValue(x => x.Path),
|
||||
(repo, path) => repo != null && !service.DestinationFileExists(path) && !service.DestinationDirectoryExists(path));
|
||||
|
||||
var canOpen = Observable.CombineLatest(
|
||||
repository, this.WhenAnyValue(x => x.Path),
|
||||
(repo, path) => repo != null && !service.DestinationFileExists(path) && service.DestinationDirectoryExists(path));
|
||||
|
||||
Browse = ReactiveCommand.Create().OnExecuteCompleted(_ => BrowseForDirectory());
|
||||
Clone = ReactiveCommand.CreateAsyncObservable(
|
||||
canClone,
|
||||
_ => repository.Select(x => new CloneDialogResult(Path, x)));
|
||||
_ => repository.Select(x => new CloneDialogResult(Path, x?.CloneUrl)));
|
||||
Open = ReactiveCommand.CreateAsyncObservable(
|
||||
canOpen,
|
||||
_ => repository.Select(x => new CloneDialogResult(Path, x?.CloneUrl)));
|
||||
}
|
||||
|
||||
public IRepositorySelectViewModel GitHubTab { get; }
|
||||
|
@ -87,7 +95,7 @@ namespace GitHub.ViewModels.Dialog.Clone
|
|||
set => this.RaiseAndSetIfChanged(ref path, value);
|
||||
}
|
||||
|
||||
public string PathError => pathError.Value;
|
||||
public string PathWarning => pathWarning.Value;
|
||||
|
||||
public int SelectedTabIndex
|
||||
{
|
||||
|
@ -95,14 +103,16 @@ namespace GitHub.ViewModels.Dialog.Clone
|
|||
set => this.RaiseAndSetIfChanged(ref selectedTabIndex, value);
|
||||
}
|
||||
|
||||
public string Title => Resources.CloneTitle;
|
||||
public string Title => Resources.OpenFromGitHubTitle;
|
||||
|
||||
public IObservable<object> Done => Clone;
|
||||
public IObservable<object> Done => Observable.Merge(Clone, Open);
|
||||
|
||||
public ReactiveCommand<object> Browse { get; }
|
||||
|
||||
public ReactiveCommand<CloneDialogResult> Clone { get; }
|
||||
|
||||
public ReactiveCommand<CloneDialogResult> Open { get; }
|
||||
|
||||
public async Task InitializeAsync(IConnection connection)
|
||||
{
|
||||
var connections = await connectionManager.GetLoadedConnections().ConfigureAwait(false);
|
||||
|
@ -228,13 +238,39 @@ namespace GitHub.ViewModels.Dialog.Clone
|
|||
}
|
||||
}
|
||||
|
||||
string ValidatePath(IRepositoryModel repository, string path)
|
||||
string ValidatePathWarning(IRepositoryModel repositoryModel, string path)
|
||||
{
|
||||
if (repository != null)
|
||||
if (repositoryModel != null)
|
||||
{
|
||||
return service.DestinationExists(path) ?
|
||||
Resources.DestinationAlreadyExists :
|
||||
null;
|
||||
if (service.DestinationFileExists(path))
|
||||
{
|
||||
return Resources.DestinationAlreadyExists;
|
||||
}
|
||||
|
||||
if (service.DestinationDirectoryExists(path))
|
||||
{
|
||||
using (var repository = gitService.GetRepository(path))
|
||||
{
|
||||
if (repository == null)
|
||||
{
|
||||
return Resources.CantFindARepositoryAtLocalPath;
|
||||
}
|
||||
|
||||
var localUrl = gitService.GetRemoteUri(repository)?.ToRepositoryUrl();
|
||||
if (localUrl == null)
|
||||
{
|
||||
return Resources.LocalRepositoryDoesntHaveARemoteOrigin;
|
||||
}
|
||||
|
||||
var targetUrl = repositoryModel.CloneUrl?.ToRepositoryUrl();
|
||||
if (localUrl != targetUrl)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, Resources.LocalRepositoryHasARemoteOf, localUrl);
|
||||
}
|
||||
|
||||
return Resources.YouHaveAlreadyClonedToThisLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -34,13 +34,36 @@ namespace GitHub.Services
|
|||
object progress = null);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the specified destination path already exists.
|
||||
/// Clones the specified repository into the specified directory or opens it if the directory already exists.
|
||||
/// </summary>
|
||||
/// <param name="cloneDialogResult">The URL and path of the repository to clone or open.</param>
|
||||
/// <param name="progress">
|
||||
/// An object through which to report progress. This must be of type
|
||||
/// System.IProgress<Microsoft.VisualStudio.Shell.ServiceProgressData>, but
|
||||
/// as that type is only available in VS2017+ it is typed as <see cref="object"/> here.
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
Task CloneOrOpenRepository(
|
||||
CloneDialogResult cloneDialogResult,
|
||||
object progress = null);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the specified destination directory already exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The destination path.</param>
|
||||
/// <returns>
|
||||
/// true if a file or directory is already present at <paramref name="path"/>; otherwise false.
|
||||
/// true if a directory is already present at <paramref name="path"/>; otherwise false.
|
||||
/// </returns>
|
||||
bool DestinationExists(string path);
|
||||
bool DestinationDirectoryExists(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the specified destination file already exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The destination file.</param>
|
||||
/// <returns>
|
||||
/// true if a file is already present at <paramref name="path"/>; otherwise false.
|
||||
/// </returns>
|
||||
bool DestinationFileExists(string path);
|
||||
|
||||
Task<ViewerRepositoriesModel> ReadViewerRepositories(HostAddress address);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ using ReactiveUI;
|
|||
namespace GitHub.ViewModels.Dialog.Clone
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel for the the Clone Repository dialog
|
||||
/// ViewModel for the Clone Repository dialog
|
||||
/// </summary>
|
||||
public interface IRepositoryCloneViewModel : IDialogContentViewModel, IConnectionInitializedViewModel
|
||||
{
|
||||
|
@ -30,9 +30,9 @@ namespace GitHub.ViewModels.Dialog.Clone
|
|||
string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an error message that explains why <see cref="Path"/> is not valid.
|
||||
/// Gets a warning message that explains why <see cref="Path"/> is suspect.
|
||||
/// </summary>
|
||||
string PathError { get; }
|
||||
string PathWarning { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the selected tab.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using GitHub.Primitives;
|
||||
|
||||
namespace GitHub.Models
|
||||
{
|
||||
|
@ -12,10 +13,10 @@ namespace GitHub.Models
|
|||
/// </summary>
|
||||
/// <param name="path">The path to clone the repository to.</param>
|
||||
/// <param name="repository">The selected repository.</param>
|
||||
public CloneDialogResult(string path, IRepositoryModel repository)
|
||||
public CloneDialogResult(string path, UriString cloneUrl)
|
||||
{
|
||||
Path = path;
|
||||
Repository = repository;
|
||||
Url = cloneUrl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -24,8 +25,8 @@ namespace GitHub.Models
|
|||
public string Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the repository selected by the user.
|
||||
/// Gets the url selected by the user.
|
||||
/// </summary>
|
||||
public IRepositoryModel Repository { get; }
|
||||
public UriString Url { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,9 @@ namespace GitHub.Models
|
|||
public int NumberOfCloneViewUrlTab { get; set; }
|
||||
public int NumberOfGitHubClones { get; set; }
|
||||
public int NumberOfEnterpriseClones { get; set; }
|
||||
public int NumberOfGitHubOpens { get; set; }
|
||||
public int NumberOfEnterpriseOpens { get; set; }
|
||||
public int NumberOfClonesToDefaultClonePath { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,14 @@ namespace GitHub.Services
|
|||
/// The connection to use. If null, the first connection will be used, or the user promted
|
||||
/// to log in if there are no connections.
|
||||
/// </param>
|
||||
/// <param name="url">
|
||||
/// The URL to prepopulate URL field with or null.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A task that returns an instance of <see cref="CloneDialogResult"/> on success,
|
||||
/// or null if the dialog was cancelled.
|
||||
/// </returns>
|
||||
Task<CloneDialogResult> ShowCloneDialog(IConnection connection);
|
||||
Task<CloneDialogResult> ShowCloneDialog(IConnection connection, string url = null);
|
||||
|
||||
/// <summary>
|
||||
/// Shows the re-clone dialog.
|
||||
|
|
|
@ -5,7 +5,9 @@ namespace GitHub.Services
|
|||
public interface ITeamExplorerServices : INotificationService
|
||||
{
|
||||
void ShowConnectPage();
|
||||
void ShowHomePage();
|
||||
void ShowPublishSection();
|
||||
void ClearNotifications();
|
||||
void OpenRepository(string repositoryPath);
|
||||
}
|
||||
}
|
|
@ -249,6 +249,15 @@ namespace GitHub {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to There is already a directory at this location, but it doesn't contain a repository..
|
||||
/// </summary>
|
||||
public static string CantFindARepositoryAtLocalPath {
|
||||
get {
|
||||
return ResourceManager.GetString("CantFindARepositoryAtLocalPath", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Changes ({0}).
|
||||
/// </summary>
|
||||
|
@ -505,7 +514,7 @@ namespace GitHub {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The destination already exists..
|
||||
/// Looks up a localized string similar to A file exists at the destination path..
|
||||
/// </summary>
|
||||
public static string DestinationAlreadyExists {
|
||||
get {
|
||||
|
@ -873,6 +882,24 @@ namespace GitHub {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to A repository already exists at this location, but it doesn't have a remote named "origin"..
|
||||
/// </summary>
|
||||
public static string LocalRepositoryDoesntHaveARemoteOrigin {
|
||||
get {
|
||||
return ResourceManager.GetString("LocalRepositoryDoesntHaveARemoteOrigin", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to A repository already exists at this location, but it has a remote of {0}..
|
||||
/// </summary>
|
||||
public static string LocalRepositoryHasARemoteOf {
|
||||
get {
|
||||
return ResourceManager.GetString("LocalRepositoryHasARemoteOf", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Make sure to use your password and not a Personal Access token to sign in..
|
||||
/// </summary>
|
||||
|
@ -1188,6 +1215,15 @@ namespace GitHub {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open from GitHub.
|
||||
/// </summary>
|
||||
public static string OpenFromGitHubTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("OpenFromGitHubTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open in Browser.
|
||||
/// </summary>
|
||||
|
@ -2144,6 +2180,15 @@ namespace GitHub {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You have already cloned to this location. Click 'Open' to open the local repository..
|
||||
/// </summary>
|
||||
public static string YouHaveAlreadyClonedToThisLocation {
|
||||
get {
|
||||
return ResourceManager.GetString("YouHaveAlreadyClonedToThisLocation", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You must commit and push your changes to add a comment here..
|
||||
/// </summary>
|
||||
|
|
|
@ -123,6 +123,9 @@
|
|||
<data name="CloneTitle" xml:space="preserve">
|
||||
<value>Clone a Repository</value>
|
||||
</data>
|
||||
<data name="OpenFromGitHubTitle" xml:space="preserve">
|
||||
<value>Open from GitHub</value>
|
||||
</data>
|
||||
<data name="CouldNotConnectToGitHub" xml:space="preserve">
|
||||
<value>Could not connect to github.com</value>
|
||||
</data>
|
||||
|
@ -328,7 +331,7 @@ https://git-scm.com/download/win</value>
|
|||
<value>Cancel Review</value>
|
||||
</data>
|
||||
<data name="DestinationAlreadyExists" xml:space="preserve">
|
||||
<value>The destination already exists.</value>
|
||||
<value>A file exists at the destination path.</value>
|
||||
</data>
|
||||
<data name="LogoutRequired" xml:space="preserve">
|
||||
<value>Logout Required</value>
|
||||
|
@ -821,4 +824,16 @@ https://git-scm.com/download/win</value>
|
|||
<data name="NoResolveSameOwnerMessage" xml:space="preserve">
|
||||
<value>Couldn't find target URL in current repository. Try again after doing a fetch.</value>
|
||||
</data>
|
||||
<data name="CantFindARepositoryAtLocalPath" xml:space="preserve">
|
||||
<value>There is already a directory at this location, but it doesn't contain a repository.</value>
|
||||
</data>
|
||||
<data name="LocalRepositoryDoesntHaveARemoteOrigin" xml:space="preserve">
|
||||
<value>A repository already exists at this location, but it doesn't have a remote named "origin".</value>
|
||||
</data>
|
||||
<data name="LocalRepositoryHasARemoteOf" xml:space="preserve">
|
||||
<value>A repository already exists at this location, but it has a remote of {0}.</value>
|
||||
</data>
|
||||
<data name="YouHaveAlreadyClonedToThisLocation" xml:space="preserve">
|
||||
<value>You have already cloned to this location. Click 'Open' to open the local repository.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -123,6 +123,9 @@
|
|||
<data name="CloneTitle" xml:space="preserve">
|
||||
<value>克隆一个 版本库</value>
|
||||
</data>
|
||||
<data name="OpenFromGitHubTitle" xml:space="preserve">
|
||||
<value>Open from GitHub</value>
|
||||
</data>
|
||||
<data name="CouldNotConnectToGitHub" xml:space="preserve">
|
||||
<value>无法连接到github.com</value>
|
||||
</data>
|
||||
|
@ -327,10 +330,10 @@
|
|||
<value>取消评审</value>
|
||||
</data>
|
||||
<data name="DestinationAlreadyExists" xml:space="preserve">
|
||||
<value>The destination already exists.</value>
|
||||
<value>A file exists at the destination path.</value>
|
||||
</data>
|
||||
<data name="LogoutRequired" xml:space="preserve">
|
||||
<value>Logout Required</value>
|
||||
<value>需要注销</value>
|
||||
</data>
|
||||
<data name="day" xml:space="preserve">
|
||||
<value>{0:N0} 天前</value>
|
||||
|
@ -345,7 +348,7 @@
|
|||
<value>{0:N0} 小时前</value>
|
||||
</data>
|
||||
<data name="JustNow" xml:space="preserve">
|
||||
<value>just now</value>
|
||||
<value>就现在</value>
|
||||
</data>
|
||||
<data name="minute" xml:space="preserve">
|
||||
<value>{0:N0} 分钟前</value>
|
||||
|
@ -820,4 +823,16 @@
|
|||
<data name="NoResolveSameOwnerMessage" xml:space="preserve">
|
||||
<value>无法在当前存储库中找到目标URL。在进行提取后再试一次。</value>
|
||||
</data>
|
||||
<data name="CantFindARepositoryAtLocalPath" xml:space="preserve">
|
||||
<value>There is already a directory at this location, but it doesn't contain a repository.</value>
|
||||
</data>
|
||||
<data name="LocalRepositoryDoesntHaveARemoteOrigin" xml:space="preserve">
|
||||
<value>A repository already exists at this location, but it doesn't have a remote named "origin".</value>
|
||||
</data>
|
||||
<data name="LocalRepositoryHasARemoteOf" xml:space="preserve">
|
||||
<value>A repository already exists at this location, but it has a remote of {0}.</value>
|
||||
</data>
|
||||
<data name="YouHaveAlreadyClonedToThisLocation" xml:space="preserve">
|
||||
<value>You have already cloned to this location. Click 'Open' to open the local repository.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
|
@ -70,11 +70,12 @@ namespace GitHub.StartPage
|
|||
if (request == null)
|
||||
return null;
|
||||
|
||||
var uri = request.Repository.CloneUrl.ToRepositoryUrl();
|
||||
var uri = request.Url.ToRepositoryUrl();
|
||||
var repositoryName = request.Url.RepositoryName;
|
||||
return new CodeContainer(
|
||||
localProperties: new CodeContainerLocalProperties(request.Path, CodeContainerType.Folder,
|
||||
new CodeContainerSourceControlProperties(request.Repository.Name, request.Path, new Guid(Guids.GitSccProviderId))),
|
||||
remote: new RemoteCodeContainer(request.Repository.Name,
|
||||
new CodeContainerSourceControlProperties(repositoryName, request.Path, new Guid(Guids.GitSccProviderId))),
|
||||
remote: new RemoteCodeContainer(repositoryName,
|
||||
new Guid(Guids.CodeContainerProviderId),
|
||||
uri,
|
||||
new Uri(uri.ToString().TrimSuffix(".git")),
|
||||
|
@ -133,7 +134,7 @@ namespace GitHub.StartPage
|
|||
if (basePath != null)
|
||||
{
|
||||
var path = Path.Combine(basePath, repository.Name);
|
||||
result = new CloneDialogResult(path, repository);
|
||||
result = new CloneDialogResult(path, repository.CloneUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,17 +142,13 @@ namespace GitHub.StartPage
|
|||
{
|
||||
try
|
||||
{
|
||||
await cloneService.CloneRepository(
|
||||
result.Repository.CloneUrl,
|
||||
result.Path,
|
||||
progress);
|
||||
|
||||
await cloneService.CloneOrOpenRepository(result, progress);
|
||||
usageTracker.IncrementCounter(x => x.NumberOfStartPageClones).Forget();
|
||||
}
|
||||
catch
|
||||
{
|
||||
var teServices = gitHubServiceProvider.TryGetService<ITeamExplorerServices>();
|
||||
teServices.ShowError($"Failed to clone the repository '{result.Repository.Name}'");
|
||||
teServices.ShowError($"Failed to clone the repository '{result.Url.RepositoryName}'");
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,16 +151,14 @@ namespace GitHub.VisualStudio.TeamExplorer.Connect
|
|||
{
|
||||
ServiceProvider.GitServiceProvider = TEServiceProvider;
|
||||
var cloneService = ServiceProvider.GetService<IRepositoryCloneService>();
|
||||
await cloneService.CloneRepository(
|
||||
result.Repository.CloneUrl,
|
||||
result.Path);
|
||||
await cloneService.CloneOrOpenRepository(result);
|
||||
|
||||
usageTracker.IncrementCounter(x => x.NumberOfGitHubConnectSectionClones).Forget();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var teServices = ServiceProvider.TryGetService<ITeamExplorerServices>();
|
||||
teServices.ShowError(e.GetUserFriendlyErrorMessage(ErrorType.ClonedFailed, result.Repository.Name));
|
||||
teServices.ShowError(e.GetUserFriendlyErrorMessage(ErrorType.CloneOrOpenFailed, result.Url.RepositoryName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,10 @@
|
|||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\src\common\signing.props" />
|
||||
<ItemGroup>
|
||||
<Reference Include="EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<HintPath>..\..\packages\EnvDTE.8.0.2\lib\net10\EnvDTE.dll</HintPath>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
</Reference>
|
||||
<Reference Include="LibGit2Sharp, Version=0.23.1.0, Culture=neutral, PublicKeyToken=7cbde695407f0333, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\LibGit2Sharp.0.23.1\lib\net40\LibGit2Sharp.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
|
@ -131,6 +135,10 @@
|
|||
<HintPath>..\..\packages\Stateless.2.5.56.0\lib\portable-net40+sl50+win+wp80+MonoAndroid10+xamarinios10+MonoTouch10\Stateless.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="stdole, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<HintPath>..\..\packages\stdole.7.0.3302\lib\net10\stdole.dll</HintPath>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.Core" />
|
||||
|
|
|
@ -2,10 +2,9 @@ using System;
|
|||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Windows.Input;
|
||||
using GitHub.Extensions;
|
||||
using EnvDTE;
|
||||
using GitHub.VisualStudio.TeamExplorer.Sync;
|
||||
using Microsoft.TeamFoundation.Controls;
|
||||
using Microsoft.VisualStudio.Shell;
|
||||
|
||||
namespace GitHub.Services
|
||||
{
|
||||
|
@ -29,12 +28,28 @@ namespace GitHub.Services
|
|||
this.serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public void OpenRepository(string repositoryPath)
|
||||
{
|
||||
#if TEAMEXPLORER14
|
||||
var vsServices = serviceProvider.GetService<IVSServices>();
|
||||
vsServices.TryOpenRepository(repositoryPath);
|
||||
#else
|
||||
OpenFolder(repositoryPath);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void ShowConnectPage()
|
||||
{
|
||||
var te = serviceProvider.TryGetService<ITeamExplorer>();
|
||||
te.NavigateToPage(new Guid(TeamExplorerPageIds.Connect), null);
|
||||
}
|
||||
|
||||
public void ShowHomePage()
|
||||
{
|
||||
var te = serviceProvider.TryGetService<ITeamExplorer>();
|
||||
te.NavigateToPage(new Guid(TeamExplorerPageIds.Home), null);
|
||||
}
|
||||
|
||||
public void ShowPublishSection()
|
||||
{
|
||||
var te = serviceProvider.TryGetService<ITeamExplorer>();
|
||||
|
@ -89,5 +104,11 @@ namespace GitHub.Services
|
|||
manager = serviceProvider.GetService<ITeamExplorer, ITeamExplorerNotificationManager>();
|
||||
return manager?.IsNotificationVisible(guid) ?? false;
|
||||
}
|
||||
|
||||
void OpenFolder(string repositoryPath)
|
||||
{
|
||||
var dte = serviceProvider.TryGetService<DTE>();
|
||||
dte?.ExecuteCommand("File.OpenFolder", repositoryPath);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="EnvDTE" version="8.0.2" targetFramework="net461" />
|
||||
<package id="LibGit2Sharp" version="0.23.1" targetFramework="net461" />
|
||||
<package id="LibGit2Sharp.NativeBinaries" version="1.0.164" targetFramework="net461" />
|
||||
<package id="Microsoft.VisualStudio.ComponentModelHost" version="14.0.25424" targetFramework="net461" />
|
||||
|
@ -18,4 +19,5 @@
|
|||
<package id="Serilog" version="2.5.0" targetFramework="net461" />
|
||||
<package id="SerilogAnalyzer" version="0.12.0.0" targetFramework="net461" />
|
||||
<package id="Stateless" version="2.5.56.0" targetFramework="net461" />
|
||||
<package id="stdole" version="7.0.3302" targetFramework="net461" />
|
||||
</packages>
|
|
@ -52,6 +52,10 @@
|
|||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\src\common\signing.props" />
|
||||
<ItemGroup>
|
||||
<Reference Include="EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<HintPath>..\..\packages\EnvDTE.8.0.2\lib\net10\EnvDTE.dll</HintPath>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
</Reference>
|
||||
<Reference Include="LibGit2Sharp, Version=0.23.1.0, Culture=neutral, PublicKeyToken=7cbde695407f0333, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\LibGit2Sharp.0.23.1\lib\net40\LibGit2Sharp.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
|
@ -150,6 +154,10 @@
|
|||
<HintPath>..\..\packages\Stateless.2.5.56.0\lib\portable-net40+sl50+win+wp80+MonoAndroid10+xamarinios10+MonoTouch10\Stateless.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="stdole, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<HintPath>..\..\packages\stdole.7.0.3302\lib\net10\stdole.dll</HintPath>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.Core" />
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="EnvDTE" version="8.0.2" targetFramework="net461" />
|
||||
<package id="LibGit2Sharp" version="0.23.1" targetFramework="net461" />
|
||||
<package id="LibGit2Sharp.NativeBinaries" version="1.0.164" targetFramework="net461" />
|
||||
<package id="Microsoft.VisualStudio.CoreUtility" version="15.4.27004" targetFramework="net461" />
|
||||
|
@ -24,4 +25,5 @@
|
|||
<package id="Serilog" version="2.5.0" targetFramework="net461" />
|
||||
<package id="SerilogAnalyzer" version="0.12.0.0" targetFramework="net461" />
|
||||
<package id="Stateless" version="2.5.56.0" targetFramework="net461" />
|
||||
<package id="stdole" version="7.0.3302" targetFramework="net461" />
|
||||
</packages>
|
|
@ -19,7 +19,7 @@ namespace GitHub.UI.TestAutomation {
|
|||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class AutomationIDs {
|
||||
|
@ -717,6 +717,15 @@ namespace GitHub.UI.TestAutomation {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to OpenRepositoryButton.
|
||||
/// </summary>
|
||||
public static string OpenRepositoryButton {
|
||||
get {
|
||||
return ResourceManager.GetString("OpenRepositoryButton", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to PowerfulCollaborationTextBlock.
|
||||
/// </summary>
|
||||
|
|
|
@ -516,4 +516,7 @@
|
|||
<data name="TwoFactorAuthenticationInputStackPanel" xml:space="preserve">
|
||||
<value>TwoFactorAuthenticationInputStackPanel</value>
|
||||
</data>
|
||||
<data name="OpenRepositoryButton" xml:space="preserve">
|
||||
<value>OpenRepositoryButton</value>
|
||||
</data>
|
||||
</root>
|
|
@ -1,30 +1,18 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.ComponentModel.Composition;
|
||||
using GitHub.Commands;
|
||||
using GitHub.Services;
|
||||
using GitHub.ViewModels.GitHubPane;
|
||||
using GitHub.Services.Vssdk.Commands;
|
||||
using EnvDTE;
|
||||
using Microsoft.VisualStudio;
|
||||
using Microsoft.VisualStudio.Shell;
|
||||
using Microsoft.VisualStudio.Shell.Interop;
|
||||
using Task = System.Threading.Tasks.Task;
|
||||
using SVsServiceProvider = Microsoft.VisualStudio.Shell.SVsServiceProvider;
|
||||
using GitHub.VisualStudio.UI;
|
||||
|
||||
namespace GitHub.VisualStudio.Commands
|
||||
{
|
||||
[Export(typeof(IOpenFromUrlCommand))]
|
||||
public class OpenFromUrlCommand : VsCommand<string>, IOpenFromUrlCommand
|
||||
{
|
||||
readonly Lazy<IDialogService> dialogService;
|
||||
readonly Lazy<IGitHubContextService> gitHubContextService;
|
||||
readonly Lazy<IRepositoryCloneService> repositoryCloneService;
|
||||
readonly Lazy<ITeamExplorerContext> teamExplorerContext;
|
||||
readonly Lazy<IGitHubToolWindowManager> gitHubToolWindowManager;
|
||||
readonly Lazy<DTE> dte;
|
||||
readonly IServiceProvider serviceProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GUID of the group the command belongs to.
|
||||
|
@ -38,19 +26,14 @@ namespace GitHub.VisualStudio.Commands
|
|||
|
||||
[ImportingConstructor]
|
||||
public OpenFromUrlCommand(
|
||||
Lazy<IDialogService> dialogService,
|
||||
Lazy<IGitHubContextService> gitHubContextService,
|
||||
Lazy<IRepositoryCloneService> repositoryCloneService,
|
||||
Lazy<ITeamExplorerContext> teamExplorerContext,
|
||||
[Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider) :
|
||||
Lazy<IRepositoryCloneService> repositoryCloneService) :
|
||||
base(CommandSet, CommandId)
|
||||
{
|
||||
this.dialogService = dialogService;
|
||||
this.gitHubContextService = gitHubContextService;
|
||||
this.repositoryCloneService = repositoryCloneService;
|
||||
this.teamExplorerContext = teamExplorerContext;
|
||||
this.serviceProvider = serviceProvider;
|
||||
dte = new Lazy<DTE>(() => (DTE)serviceProvider.GetService(typeof(DTE)));
|
||||
gitHubToolWindowManager = new Lazy<IGitHubToolWindowManager>(
|
||||
() => (IGitHubToolWindowManager)serviceProvider.GetService(typeof(IGitHubToolWindowManager)));
|
||||
|
||||
// See https://code.msdn.microsoft.com/windowsdesktop/AllowParams-2005-9442298f
|
||||
ParametersDescription = "u"; // accept a single url
|
||||
|
@ -58,132 +41,17 @@ namespace GitHub.VisualStudio.Commands
|
|||
|
||||
public override async Task Execute(string url)
|
||||
{
|
||||
var context = string.IsNullOrEmpty(url) ? null : gitHubContextService.Value.FindContextFromUrl(url);
|
||||
if (context == null)
|
||||
if (string.IsNullOrEmpty(url))
|
||||
{
|
||||
context = gitHubContextService.Value.FindContextFromClipboard();
|
||||
var clipboardContext = gitHubContextService.Value.FindContextFromClipboard();
|
||||
url = clipboardContext?.Url;
|
||||
}
|
||||
|
||||
if (context == null)
|
||||
var cloneDialogResult = await dialogService.Value.ShowCloneDialog(null, url);
|
||||
if (cloneDialogResult != null)
|
||||
{
|
||||
// Couldn't find a URL to open
|
||||
return;
|
||||
await repositoryCloneService.Value.CloneOrOpenRepository(cloneDialogResult);
|
||||
}
|
||||
|
||||
var activeDir = teamExplorerContext.Value.ActiveRepository?.LocalPath;
|
||||
if (activeDir != null)
|
||||
{
|
||||
// Try opening file in current context
|
||||
if (gitHubContextService.Value.TryOpenFile(activeDir, context))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep repos in unique dir while testing
|
||||
var defaultSubPath = "GitHubCache";
|
||||
|
||||
var cloneUrl = gitHubContextService.Value.ToRepositoryUrl(context).ToString();
|
||||
var targetDir = Path.Combine(repositoryCloneService.Value.DefaultClonePath, defaultSubPath, context.Owner);
|
||||
var repositoryDirName = context.RepositoryName;
|
||||
var repositoryDir = Path.Combine(targetDir, repositoryDirName);
|
||||
|
||||
if (!Directory.Exists(repositoryDir))
|
||||
{
|
||||
var result = ShowInfoMessage($"Clone {cloneUrl} to '{repositoryDir}'?");
|
||||
switch (result)
|
||||
{
|
||||
case VSConstants.MessageBoxResult.IDYES:
|
||||
await repositoryCloneService.Value.CloneRepository(cloneUrl, repositoryDir);
|
||||
// Open the cloned repository
|
||||
dte.Value.ExecuteCommand("File.OpenFolder", repositoryDir);
|
||||
dte.Value.ExecuteCommand("View.TfsTeamExplorer");
|
||||
break;
|
||||
case VSConstants.MessageBoxResult.IDNO:
|
||||
// Target the current solution
|
||||
repositoryDir = FindSolutionDirectory(dte.Value.Solution);
|
||||
if (repositoryDir == null)
|
||||
{
|
||||
// No current solution to use
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case VSConstants.MessageBoxResult.IDCANCEL:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var solutionDir = FindSolutionDirectory(dte.Value.Solution);
|
||||
if (solutionDir == null || !ContainsDirectory(repositoryDir, solutionDir))
|
||||
{
|
||||
var result = ShowInfoMessage(string.Format(Resources.OpenRepositoryAtDir, repositoryDir));
|
||||
switch (result)
|
||||
{
|
||||
case VSConstants.MessageBoxResult.IDYES:
|
||||
// Open if current solution isn't in repository directory
|
||||
dte.Value.ExecuteCommand("File.OpenFolder", repositoryDir);
|
||||
dte.Value.ExecuteCommand("View.TfsTeamExplorer");
|
||||
break;
|
||||
case VSConstants.MessageBoxResult.IDNO:
|
||||
break;
|
||||
case VSConstants.MessageBoxResult.IDCANCEL:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await TryOpenPullRequest(context);
|
||||
gitHubContextService.Value.TryOpenFile(repositoryDir, context);
|
||||
}
|
||||
|
||||
VSConstants.MessageBoxResult ShowInfoMessage(string message)
|
||||
{
|
||||
return (VSConstants.MessageBoxResult)VsShellUtilities.ShowMessageBox(serviceProvider, message, null,
|
||||
OLEMSGICON.OLEMSGICON_QUERY, OLEMSGBUTTON.OLEMSGBUTTON_YESNOCANCEL, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
|
||||
}
|
||||
|
||||
static bool ContainsDirectory(string repositoryDir, string solutionDir)
|
||||
{
|
||||
if (solutionDir.Equals(repositoryDir, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (solutionDir.StartsWith(repositoryDir + '\\', StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static string FindSolutionDirectory(Solution solution)
|
||||
{
|
||||
var solutionPath = solution.FileName;
|
||||
if (File.Exists(solutionPath))
|
||||
{
|
||||
return Path.GetDirectoryName(solutionPath);
|
||||
}
|
||||
|
||||
if (Directory.Exists(solutionPath))
|
||||
{
|
||||
return solutionPath;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async Task<bool> TryOpenPullRequest(GitHubContext context)
|
||||
{
|
||||
var pullRequest = context.PullRequest;
|
||||
if (pullRequest == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var host = await gitHubToolWindowManager.Value.ShowGitHubPane();
|
||||
await host.ShowPullRequest(context.Owner, context.RepositoryName, pullRequest.Value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -362,11 +362,9 @@
|
|||
</CommandPlacement>
|
||||
|
||||
<!-- Open from GitHub command -->
|
||||
<!-- Removing from `File > Open` menu until we have a proper UI
|
||||
<CommandPlacement guid="guidGitHubCmdSet" id="openFromUrlCommand" priority="0x0100">
|
||||
<Parent guid="guidSHLMainMenu" id="IDG_VS_FILE_OPENSCC_CASCADE"/>
|
||||
</CommandPlacement>
|
||||
-->
|
||||
|
||||
<!-- Add Connection (Team Explorer) command -->
|
||||
<CommandPlacement guid="guidGitHubCmdSet" id="addConnectionCommand" priority="0x0100">
|
||||
|
|
|
@ -21,9 +21,8 @@
|
|||
</UserControl.Resources>
|
||||
|
||||
<DockPanel>
|
||||
<ghfvs:InfoPanel Message="{Binding PathError}"/>
|
||||
|
||||
<ghfvs:OcticonCircleButton DockPanel.Dock="Bottom"
|
||||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom" HorizontalAlignment="Center">
|
||||
<ghfvs:OcticonCircleButton
|
||||
Margin="12"
|
||||
HorizontalAlignment="Center"
|
||||
Icon="check"
|
||||
|
@ -32,6 +31,16 @@
|
|||
Content="Clone"
|
||||
AutomationProperties.AutomationId="{x:Static ghfvs:AutomationIDs.CloneRepositoryButton}"/>
|
||||
|
||||
<ghfvs:OcticonCircleButton
|
||||
Margin="12"
|
||||
HorizontalAlignment="Center"
|
||||
Icon="check"
|
||||
IsDefault="True"
|
||||
Command="{Binding Open}"
|
||||
Content="Open"
|
||||
AutomationProperties.AutomationId="{x:Static ghfvs:AutomationIDs.OpenRepositoryButton}"/>
|
||||
</StackPanel>
|
||||
|
||||
<DockPanel DockPanel.Dock="Bottom"
|
||||
Margin="16">
|
||||
<Label DockPanel.Dock="Left" Content="{x:Static ghfvs:Resources.localPathText}"/>
|
||||
|
@ -44,6 +53,10 @@
|
|||
<TextBox Text="{Binding Path, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</DockPanel>
|
||||
|
||||
<ghfvs:InfoPanel Message="{Binding PathWarning}"
|
||||
DockPanel.Dock="Bottom"
|
||||
Margin="0"/>
|
||||
|
||||
<TabControl SelectedIndex="{Binding SelectedTabIndex}"
|
||||
Style="{StaticResource LightModalViewTabControl}">
|
||||
<TabItem Header="GitHub.com"
|
||||
|
|
|
@ -28,19 +28,20 @@ public class RepositoryCloneServiceTests
|
|||
}
|
||||
|
||||
[TestCase("https://github.com/foo/bar", 1, nameof(UsageModel.MeasuresModel.NumberOfClones))]
|
||||
[TestCase("https://github.com/foo/bar", 1, nameof(UsageModel.MeasuresModel.NumberOfGitHubClones))]
|
||||
[TestCase("https://github.com/foo/bar", 0, nameof(UsageModel.MeasuresModel.NumberOfGitHubClones))]
|
||||
[TestCase("https://github.com/foo/bar", 0, nameof(UsageModel.MeasuresModel.NumberOfEnterpriseClones))]
|
||||
[TestCase("https://enterprise.com/foo/bar", 1, nameof(UsageModel.MeasuresModel.NumberOfClones))]
|
||||
[TestCase("https://enterprise.com/foo/bar", 1, nameof(UsageModel.MeasuresModel.NumberOfEnterpriseClones))]
|
||||
[TestCase("https://enterprise.com/foo/bar", 0, nameof(UsageModel.MeasuresModel.NumberOfEnterpriseClones))]
|
||||
[TestCase("https://enterprise.com/foo/bar", 0, nameof(UsageModel.MeasuresModel.NumberOfGitHubClones))]
|
||||
public async Task UpdatesMetricsWhenRepositoryClonedAsync(string cloneUrl, int numberOfCalls, string counterName)
|
||||
{
|
||||
var serviceProvider = Substitutes.ServiceProvider;
|
||||
var operatingSystem = serviceProvider.GetOperatingSystem();
|
||||
var vsGitServices = serviceProvider.GetVSGitServices();
|
||||
var teamExplorerServices = Substitute.For<ITeamExplorerServices>();
|
||||
var graphqlFactory = Substitute.For<IGraphQLClientFactory>();
|
||||
var usageTracker = Substitute.For<IUsageTracker>();
|
||||
var cloneService = new RepositoryCloneService(operatingSystem, vsGitServices, graphqlFactory, usageTracker);
|
||||
var cloneService = new RepositoryCloneService(operatingSystem, vsGitServices, teamExplorerServices, graphqlFactory, usageTracker);
|
||||
|
||||
await cloneService.CloneRepository(cloneUrl, @"c:\dev\bar");
|
||||
var model = UsageModel.Create(Guid.NewGuid());
|
||||
|
@ -49,5 +50,59 @@ public class RepositoryCloneServiceTests
|
|||
Arg.Is<Expression<Func<UsageModel.MeasuresModel, int>>>(x =>
|
||||
((MemberExpression)x.Body).Member.Name == counterName));
|
||||
}
|
||||
|
||||
[TestCase("https://github.com/foo/bar", false, 1, nameof(UsageModel.MeasuresModel.NumberOfClones))]
|
||||
[TestCase("https://github.com/foo/bar", false, 1, nameof(UsageModel.MeasuresModel.NumberOfGitHubClones))]
|
||||
[TestCase("https://github.com/foo/bar", false, 0, nameof(UsageModel.MeasuresModel.NumberOfEnterpriseClones))]
|
||||
[TestCase("https://enterprise.com/foo/bar", false, 1, nameof(UsageModel.MeasuresModel.NumberOfClones))]
|
||||
[TestCase("https://enterprise.com/foo/bar", false, 1, nameof(UsageModel.MeasuresModel.NumberOfEnterpriseClones))]
|
||||
[TestCase("https://enterprise.com/foo/bar", false, 0, nameof(UsageModel.MeasuresModel.NumberOfGitHubClones))]
|
||||
|
||||
[TestCase("https://github.com/foo/bar", true, 1, nameof(UsageModel.MeasuresModel.NumberOfGitHubOpens))]
|
||||
[TestCase("https://github.com/foo/bar", true, 0, nameof(UsageModel.MeasuresModel.NumberOfEnterpriseOpens))]
|
||||
[TestCase("https://enterprise.com/foo/bar", true, 1, nameof(UsageModel.MeasuresModel.NumberOfEnterpriseOpens))]
|
||||
[TestCase("https://enterprise.com/foo/bar", true, 0, nameof(UsageModel.MeasuresModel.NumberOfGitHubOpens))]
|
||||
public async Task UpdatesMetricsWhenCloneOrOpenRepositoryAsync(string cloneUrl, bool dirExists, int numberOfCalls, string counterName)
|
||||
{
|
||||
var repositoryPath = @"c:\dev\bar";
|
||||
var cloneDialogResult = new CloneDialogResult(repositoryPath, cloneUrl);
|
||||
var serviceProvider = Substitutes.ServiceProvider;
|
||||
var operatingSystem = serviceProvider.GetOperatingSystem();
|
||||
operatingSystem.Directory.DirectoryExists(repositoryPath).Returns(dirExists);
|
||||
var vsGitServices = serviceProvider.GetVSGitServices();
|
||||
var teamExplorerServices = Substitute.For<ITeamExplorerServices>();
|
||||
var graphqlFactory = Substitute.For<IGraphQLClientFactory>();
|
||||
var usageTracker = Substitute.For<IUsageTracker>();
|
||||
var cloneService = new RepositoryCloneService(operatingSystem, vsGitServices, teamExplorerServices,
|
||||
graphqlFactory, usageTracker);
|
||||
|
||||
await cloneService.CloneOrOpenRepository(cloneDialogResult);
|
||||
|
||||
await usageTracker.Received(numberOfCalls).IncrementCounter(
|
||||
Arg.Is<Expression<Func<UsageModel.MeasuresModel, int>>>(x =>
|
||||
((MemberExpression)x.Body).Member.Name == counterName));
|
||||
}
|
||||
|
||||
[TestCase(@"c:\default\repo", @"c:\default", 1, nameof(UsageModel.MeasuresModel.NumberOfClonesToDefaultClonePath))]
|
||||
[TestCase(@"c:\not_default\repo", @"c:\default", 0, nameof(UsageModel.MeasuresModel.NumberOfClonesToDefaultClonePath))]
|
||||
public async Task UpdatesMetricsWhenDefaultClonePath(string targetPath, string defaultPath, int numberOfCalls, string counterName)
|
||||
{
|
||||
var serviceProvider = Substitutes.ServiceProvider;
|
||||
var operatingSystem = serviceProvider.GetOperatingSystem();
|
||||
var vsGitServices = serviceProvider.GetVSGitServices();
|
||||
var teamExplorerServices = Substitute.For<ITeamExplorerServices>();
|
||||
vsGitServices.GetLocalClonePathFromGitProvider().Returns(defaultPath);
|
||||
var graphqlFactory = Substitute.For<IGraphQLClientFactory>();
|
||||
var usageTracker = Substitute.For<IUsageTracker>();
|
||||
var cloneService = new RepositoryCloneService(operatingSystem, vsGitServices, teamExplorerServices,
|
||||
graphqlFactory, usageTracker);
|
||||
|
||||
await cloneService.CloneRepository("https://github.com/foo/bar", targetPath);
|
||||
var model = UsageModel.Create(Guid.NewGuid());
|
||||
|
||||
await usageTracker.Received(numberOfCalls).IncrementCounter(
|
||||
Arg.Is<Expression<Func<UsageModel.MeasuresModel, int>>>(x =>
|
||||
((MemberExpression)x.Body).Member.Name == counterName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace UnitTests
|
|||
}
|
||||
|
||||
|
||||
// public static IGitRepositoriesExt IGitRepositoriesExt { get { return Substitute.For<IGitRepositoriesExt>(); } }
|
||||
// public static IGitRepositoriesExt IGitRepositoriesExt { get { return Substitute.For<IGitRepositoriesExt>(); } }
|
||||
public static IGitService IGitService { get { return Substitute.For<IGitService>(); } }
|
||||
|
||||
public static IVSGitServices IVSGitServices
|
||||
|
@ -72,7 +72,7 @@ namespace UnitTests
|
|||
/// RepositoryCloneService and RepositoryCreationService, which are real
|
||||
/// instances.
|
||||
/// </summary>
|
||||
public static IGitHubServiceProvider ServiceProvider { get { return GetServiceProvider(); } }
|
||||
public static IGitHubServiceProvider ServiceProvider { get { return GetServiceProvider(); } }
|
||||
|
||||
/// <summary>
|
||||
/// This returns a service provider with mocked IRepositoryCreationService and
|
||||
|
@ -111,7 +111,8 @@ namespace UnitTests
|
|||
|
||||
var os = OperatingSystem;
|
||||
var vsgit = IVSGitServices;
|
||||
var clone = cloneService ?? new RepositoryCloneService(os, vsgit, Substitute.For<IGraphQLClientFactory>(), Substitute.For<IUsageTracker>());
|
||||
var clone = cloneService ?? new RepositoryCloneService(os, vsgit, Substitute.For<ITeamExplorerServices>(),
|
||||
Substitute.For<IGraphQLClientFactory>(), Substitute.For<IUsageTracker>());
|
||||
var create = creationService ?? new RepositoryCreationService(clone);
|
||||
avatarProvider = avatarProvider ?? Substitute.For<IAvatarProvider>();
|
||||
//ret.GetService(typeof(IGitRepositoriesExt)).Returns(IGitRepositoriesExt);
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq.Expressions;
|
||||
using System.Numerics;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Extensions;
|
||||
using GitHub.Models;
|
||||
using GitHub.Primitives;
|
||||
using GitHub.Services;
|
||||
using GitHub.ViewModels.Dialog.Clone;
|
||||
using LibGit2Sharp;
|
||||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
using Rothko;
|
||||
|
@ -17,6 +19,10 @@ namespace GitHub.App.UnitTests.ViewModels.Dialog.Clone
|
|||
{
|
||||
public class RepositoryCloneViewModelTests
|
||||
{
|
||||
const string directoryExists = "d:\\exists\\directory";
|
||||
const string fileExists = "d:\\exists\\file";
|
||||
const string defaultPath = "d:\\default\\path";
|
||||
|
||||
[Test]
|
||||
public async Task GitHubPage_Is_Initialized()
|
||||
{
|
||||
|
@ -95,7 +101,7 @@ namespace GitHub.App.UnitTests.ViewModels.Dialog.Clone
|
|||
var cm = CreateConnectionManager("https://github.com", "https://enterprise.com");
|
||||
var target = CreateTarget(connectionManager: cm);
|
||||
|
||||
await target.InitializeAsync(null);
|
||||
await target.InitializeAsync(cm.Connections[0]);
|
||||
|
||||
await target.GitHubTab.Received(1).Activate();
|
||||
await target.EnterpriseTab.DidNotReceive().Activate();
|
||||
|
@ -146,37 +152,97 @@ namespace GitHub.App.UnitTests.ViewModels.Dialog.Clone
|
|||
{
|
||||
var target = CreateTarget();
|
||||
|
||||
Assert.That(target.Path, Is.EqualTo("d:\\efault\\path"));
|
||||
Assert.That(target.Path, Is.EqualTo(defaultPath));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Owner_And_Repository_Name_Is_Appended_To_Base_Path()
|
||||
{
|
||||
var owner = "owner";
|
||||
var repo = "repo";
|
||||
var target = CreateTarget();
|
||||
var expectPath = Path.Combine(defaultPath, owner, repo);
|
||||
|
||||
SetRepository(target.GitHubTab, CreateRepositoryModel("owner", "repo"));
|
||||
SetRepository(target.GitHubTab, CreateRepositoryModel(owner, repo));
|
||||
|
||||
Assert.That(target.Path, Is.EqualTo("d:\\efault\\path\\owner\\repo"));
|
||||
Assert.That(target.Path, Is.EqualTo(expectPath));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task PathError_Is_Not_Set_When_No_Repository_Selected()
|
||||
public async Task PathWarning_Is_Not_Set_When_No_Repository_Selected()
|
||||
{
|
||||
var target = CreateTarget();
|
||||
|
||||
target.Path = "d:\\exists";
|
||||
target.Path = directoryExists;
|
||||
|
||||
Assert.That(target.PathError, Is.Null);
|
||||
Assert.That(target.PathWarning, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task PathError_Is_Set_For_Existing_Destination()
|
||||
public async Task PathWarning_Is_Set_For_Existing_File_At_Destination()
|
||||
{
|
||||
var target = CreateTarget();
|
||||
SetRepository(target.GitHubTab, CreateRepositoryModel("owner", "repo"));
|
||||
target.Path = "d:\\exists";
|
||||
target.Path = fileExists;
|
||||
|
||||
Assert.That(target.PathError, Is.EqualTo(Resources.DestinationAlreadyExists));
|
||||
Assert.That(target.PathWarning, Is.EqualTo(Resources.DestinationAlreadyExists));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task PathWarning_Is_Set_For_Existing_Clone_At_Destination()
|
||||
{
|
||||
var owner = "owner";
|
||||
var repo = "repo";
|
||||
var remoteUrl = CreateGitHubUrl("owner", "repo");
|
||||
var gitService = CreateGitService(true, remoteUrl);
|
||||
var target = CreateTarget(gitService: gitService);
|
||||
SetRepository(target.GitHubTab, CreateRepositoryModel(owner, repo));
|
||||
target.Path = directoryExists;
|
||||
|
||||
Assert.That(target.PathWarning, Is.EqualTo(Resources.YouHaveAlreadyClonedToThisLocation));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task PathWarning_Is_Set_For_Repository_With_No_Origin()
|
||||
{
|
||||
var owner = "owner";
|
||||
var repo = "repo";
|
||||
var gitService = CreateGitService(true, null);
|
||||
var target = CreateTarget(gitService: gitService);
|
||||
SetRepository(target.GitHubTab, CreateRepositoryModel(owner, repo));
|
||||
target.Path = directoryExists;
|
||||
|
||||
Assert.That(target.PathWarning, Is.EqualTo(Resources.LocalRepositoryDoesntHaveARemoteOrigin));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task PathWarning_Is_Set_For_Directory_With_No_Repository()
|
||||
{
|
||||
var owner = "owner";
|
||||
var repo = "repo";
|
||||
var gitService = CreateGitService(false, null);
|
||||
var target = CreateTarget(gitService: gitService);
|
||||
SetRepository(target.GitHubTab, CreateRepositoryModel(owner, repo));
|
||||
target.Path = directoryExists;
|
||||
|
||||
Assert.That(target.PathWarning, Is.EqualTo(Resources.CantFindARepositoryAtLocalPath));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task PathWarning_Is_Set_For_Existing_Repository_At_Destination_With_Different_Remote()
|
||||
{
|
||||
var originalOwner = "original_Owner";
|
||||
var forkedOwner = "forked_owner";
|
||||
var repo = "repo";
|
||||
var forkedUrl = CreateGitHubUrl(forkedOwner, repo);
|
||||
var expectMessage = string.Format(CultureInfo.CurrentCulture, Resources.LocalRepositoryHasARemoteOf, forkedUrl);
|
||||
var gitService = CreateGitService(true, CreateGitHubUrl(forkedOwner, repo));
|
||||
var target = CreateTarget(gitService: gitService);
|
||||
SetRepository(target.GitHubTab, CreateRepositoryModel(originalOwner, repo));
|
||||
|
||||
target.Path = directoryExists;
|
||||
|
||||
Assert.That(target.PathWarning, Is.EqualTo(expectMessage));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -249,7 +315,7 @@ namespace GitHub.App.UnitTests.ViewModels.Dialog.Clone
|
|||
}
|
||||
|
||||
[Test]
|
||||
public async Task Clone_Is_Disabled_When_Has_PathError()
|
||||
public async Task Clone_Is_Disabled_When_Path_DirectoryExists()
|
||||
{
|
||||
var target = CreateTarget();
|
||||
|
||||
|
@ -258,11 +324,25 @@ namespace GitHub.App.UnitTests.ViewModels.Dialog.Clone
|
|||
SetRepository(target.GitHubTab, CreateRepositoryModel());
|
||||
Assert.That(target.Clone.CanExecute(null), Is.True);
|
||||
|
||||
target.Path = "d:\\exists";
|
||||
target.Path = directoryExists;
|
||||
|
||||
Assert.That(target.Clone.CanExecute(null), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Open_Is_Enabled_When_Path_DirectoryExists()
|
||||
{
|
||||
var target = CreateTarget();
|
||||
|
||||
await target.InitializeAsync(null);
|
||||
Assert.That(target.Open.CanExecute(null), Is.False);
|
||||
SetRepository(target.GitHubTab, CreateRepositoryModel());
|
||||
|
||||
target.Path = directoryExists;
|
||||
|
||||
Assert.That(target.Open.CanExecute(null), Is.True);
|
||||
}
|
||||
|
||||
static void SetRepository(IRepositoryCloneTabViewModel vm, IRepositoryModel repository)
|
||||
{
|
||||
vm.Repository.Returns(repository);
|
||||
|
@ -305,7 +385,10 @@ namespace GitHub.App.UnitTests.ViewModels.Dialog.Clone
|
|||
{
|
||||
var result = Substitute.For<IRepositoryCloneService>();
|
||||
result.DefaultClonePath.Returns(defaultClonePath);
|
||||
result.DestinationExists("d:\\exists").Returns(true);
|
||||
result.DestinationDirectoryExists(directoryExists).Returns(true);
|
||||
result.DestinationFileExists(directoryExists).Returns(false);
|
||||
result.DestinationDirectoryExists(fileExists).Returns(false);
|
||||
result.DestinationFileExists(fileExists).Returns(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -317,8 +400,9 @@ namespace GitHub.App.UnitTests.ViewModels.Dialog.Clone
|
|||
IUsageTracker usageTracker = null,
|
||||
IRepositorySelectViewModel gitHubTab = null,
|
||||
IRepositorySelectViewModel enterpriseTab = null,
|
||||
IGitService gitService = null,
|
||||
IRepositoryUrlViewModel urlTab = null,
|
||||
string defaultClonePath = "d:\\efault\\path")
|
||||
string defaultClonePath = defaultPath)
|
||||
{
|
||||
os = os ?? Substitute.For<IOperatingSystem>();
|
||||
connectionManager = connectionManager ?? CreateConnectionManager("https://github.com");
|
||||
|
@ -327,12 +411,14 @@ namespace GitHub.App.UnitTests.ViewModels.Dialog.Clone
|
|||
usageTracker = usageTracker ?? Substitute.For<IUsageTracker>();
|
||||
gitHubTab = gitHubTab ?? CreateSelectViewModel();
|
||||
enterpriseTab = enterpriseTab ?? CreateSelectViewModel();
|
||||
urlTab = urlTab ?? Substitute.For<IRepositoryUrlViewModel>();
|
||||
gitService = gitService ?? CreateGitService(true, "https://github.com/owner/repo");
|
||||
urlTab = urlTab ?? CreateRepositoryUrlViewModel();
|
||||
|
||||
return new RepositoryCloneViewModel(
|
||||
os,
|
||||
connectionManager,
|
||||
service,
|
||||
gitService,
|
||||
usageService,
|
||||
usageTracker,
|
||||
gitHubTab,
|
||||
|
@ -340,6 +426,21 @@ namespace GitHub.App.UnitTests.ViewModels.Dialog.Clone
|
|||
urlTab);
|
||||
}
|
||||
|
||||
private static IGitService CreateGitService(bool repositoryExists, UriString remoteUrl)
|
||||
{
|
||||
var gitService = Substitute.For<IGitService>();
|
||||
|
||||
IRepository repository = null;
|
||||
if (repositoryExists)
|
||||
{
|
||||
repository = Substitute.For<IRepository>();
|
||||
gitService.GetRemoteUri(repository).Returns(remoteUrl);
|
||||
}
|
||||
|
||||
gitService.GetRepository(directoryExists).Returns(repository);
|
||||
return gitService;
|
||||
}
|
||||
|
||||
static IUsageService CreateUsageService(bool isGroupA = false)
|
||||
{
|
||||
var usageService = Substitute.For<IUsageService>();
|
||||
|
@ -362,7 +463,21 @@ namespace GitHub.App.UnitTests.ViewModels.Dialog.Clone
|
|||
var repository = Substitute.For<IRepositoryModel>();
|
||||
repository.Owner.Returns(owner);
|
||||
repository.Name.Returns(name);
|
||||
repository.CloneUrl.Returns(CreateGitHubUrl(owner, name));
|
||||
return repository;
|
||||
}
|
||||
|
||||
static UriString CreateGitHubUrl(string owner, string repo)
|
||||
{
|
||||
return new UriString($"https://github.com/{owner}/{repo}");
|
||||
}
|
||||
|
||||
static IRepositoryUrlViewModel CreateRepositoryUrlViewModel()
|
||||
{
|
||||
var repositoryUrlViewModel = Substitute.For<IRepositoryUrlViewModel>();
|
||||
repositoryUrlViewModel.Repository.Returns(null as IRepositoryModel);
|
||||
repositoryUrlViewModel.Url.Returns(string.Empty);
|
||||
return repositoryUrlViewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace UnitTests
|
|||
}
|
||||
|
||||
|
||||
// public static IGitRepositoriesExt IGitRepositoriesExt { get { return Substitute.For<IGitRepositoriesExt>(); } }
|
||||
// public static IGitRepositoriesExt IGitRepositoriesExt { get { return Substitute.For<IGitRepositoriesExt>(); } }
|
||||
public static IGitService IGitService { get { return Substitute.For<IGitService>(); } }
|
||||
|
||||
public static IVSGitServices IVSGitServices
|
||||
|
@ -72,7 +72,7 @@ namespace UnitTests
|
|||
/// RepositoryCloneService and RepositoryCreationService, which are real
|
||||
/// instances.
|
||||
/// </summary>
|
||||
public static IGitHubServiceProvider ServiceProvider { get { return GetServiceProvider(); } }
|
||||
public static IGitHubServiceProvider ServiceProvider { get { return GetServiceProvider(); } }
|
||||
|
||||
/// <summary>
|
||||
/// This returns a service provider with mocked IRepositoryCreationService and
|
||||
|
@ -111,7 +111,8 @@ namespace UnitTests
|
|||
|
||||
var os = OperatingSystem;
|
||||
var vsgit = IVSGitServices;
|
||||
var clone = cloneService ?? new RepositoryCloneService(os, vsgit, Substitute.For<IGraphQLClientFactory>(), Substitute.For<IUsageTracker>());
|
||||
var clone = cloneService ?? new RepositoryCloneService(os, vsgit, Substitute.For<ITeamExplorerServices>(),
|
||||
Substitute.For<IGraphQLClientFactory>(), Substitute.For<IUsageTracker>());
|
||||
var create = creationService ?? new RepositoryCreationService(clone);
|
||||
avatarProvider = avatarProvider ?? Substitute.For<IAvatarProvider>();
|
||||
//ret.GetService(typeof(IGitRepositoriesExt)).Returns(IGitRepositoriesExt);
|
||||
|
|
Загрузка…
Ссылка в новой задаче