diff --git a/src/GitHub.App/Services/RepositoryCloneService.cs b/src/GitHub.App/Services/RepositoryCloneService.cs index 7fc5e6e3e4..4b543f6daa 100644 --- a/src/GitHub.App/Services/RepositoryCloneService.cs +++ b/src/GitHub.App/Services/RepositoryCloneService.cs @@ -129,7 +129,7 @@ public async Task CloneOrOpenRepository( var repositoryUrl = url.ToRepositoryUrl(); var isDotCom = HostAddress.IsGitHubDotComUri(repositoryUrl); - if (DestinationDirectoryExists(repositoryPath)) + if (DestinationDirectoryExists(repositoryPath) && !DestinationDirectoryEmpty(repositoryPath)) { if (!IsSolutionInRepository(repositoryPath)) { @@ -206,9 +206,12 @@ public async Task CloneRepository( // Switch to a thread pool thread for IO then back to the main thread to call // vsGitServices.Clone() as this must be called on the main thread. - await ThreadingHelper.SwitchToPoolThreadAsync(); - operatingSystem.Directory.CreateDirectory(repositoryPath); - await ThreadingHelper.SwitchToMainThreadAsync(); + if (!DestinationDirectoryExists(repositoryPath)) + { + await ThreadingHelper.SwitchToPoolThreadAsync(); + operatingSystem.Directory.CreateDirectory(repositoryPath); + await ThreadingHelper.SwitchToMainThreadAsync(); + } try { @@ -232,6 +235,9 @@ public async Task CloneRepository( /// public bool DestinationDirectoryExists(string path) => operatingSystem.Directory.DirectoryExists(path); + /// + public bool DestinationDirectoryEmpty(string path) => operatingSystem.Directory.GetDirectory(path).IsEmpty; + /// public bool DestinationFileExists(string path) => operatingSystem.File.Exists(path); diff --git a/src/GitHub.App/ViewModels/Dialog/Clone/RepositoryCloneViewModel.cs b/src/GitHub.App/ViewModels/Dialog/Clone/RepositoryCloneViewModel.cs index 585f57c5cb..ff30629bfa 100644 --- a/src/GitHub.App/ViewModels/Dialog/Clone/RepositoryCloneViewModel.cs +++ b/src/GitHub.App/ViewModels/Dialog/Clone/RepositoryCloneViewModel.cs @@ -67,11 +67,13 @@ public RepositoryCloneViewModel( var canClone = Observable.CombineLatest( repository, this.WhenAnyValue(x => x.Path), - (repo, path) => repo != null && !service.DestinationFileExists(path) && !service.DestinationDirectoryExists(path)); + (repo, path) => repo != null && !service.DestinationFileExists(path) && + (!service.DestinationDirectoryExists(path)) || service.DestinationDirectoryEmpty(path)); var canOpen = Observable.CombineLatest( repository, this.WhenAnyValue(x => x.Path), - (repo, path) => repo != null && !service.DestinationFileExists(path) && service.DestinationDirectoryExists(path)); + (repo, path) => repo != null && !service.DestinationFileExists(path) && service.DestinationDirectoryExists(path) + && !service.DestinationDirectoryEmpty(path)); Browse = ReactiveCommand.Create(() => BrowseForDirectory()); Clone = ReactiveCommand.CreateFromObservable( @@ -236,13 +238,13 @@ string ValidatePathWarning(RepositoryModel repositoryModel, string path) return Resources.DestinationAlreadyExists; } - if (service.DestinationDirectoryExists(path)) + if (service.DestinationDirectoryExists(path) && !service.DestinationDirectoryEmpty(path)) { using (var repository = gitService.GetRepository(path)) { if (repository == null) { - return Resources.CantFindARepositoryAtLocalPath; + return Resources.DirectoryAtDestinationNotEmpty; } var localUrl = gitService.GetRemoteUri(repository)?.ToRepositoryUrl(); @@ -254,7 +256,8 @@ string ValidatePathWarning(RepositoryModel repositoryModel, string path) var targetUrl = repositoryModel.CloneUrl?.ToRepositoryUrl(); if (localUrl != targetUrl) { - return string.Format(CultureInfo.CurrentCulture, Resources.LocalRepositoryHasARemoteOf, localUrl); + return string.Format(CultureInfo.CurrentCulture, Resources.LocalRepositoryHasARemoteOf, + localUrl); } return Resources.YouHaveAlreadyClonedToThisLocation; diff --git a/src/GitHub.Exports.Reactive/Services/IRepositoryCloneService.cs b/src/GitHub.Exports.Reactive/Services/IRepositoryCloneService.cs index 152a203d60..791aa25734 100644 --- a/src/GitHub.Exports.Reactive/Services/IRepositoryCloneService.cs +++ b/src/GitHub.Exports.Reactive/Services/IRepositoryCloneService.cs @@ -58,6 +58,15 @@ Task CloneOrOpenRepository( /// bool DestinationDirectoryExists(string path); + /// + /// Checks whether the specified destination directory is empty. + /// + /// The destination path. + /// + /// true if a directory is empty ; otherwise false. + /// + bool DestinationDirectoryEmpty(string path); + /// /// Checks whether the specified destination file already exists. /// diff --git a/src/GitHub.Resources/Resources.Designer.cs b/src/GitHub.Resources/Resources.Designer.cs index 52383bf054..5aa9144ed9 100644 --- a/src/GitHub.Resources/Resources.Designer.cs +++ b/src/GitHub.Resources/Resources.Designer.cs @@ -267,15 +267,6 @@ public static string CancelPendingReviewConfirmationCaption { } } - /// - /// Looks up a localized string similar to There is already a directory at this location, but it doesn't contain a repository.. - /// - public static string CantFindARepositoryAtLocalPath { - get { - return ResourceManager.GetString("CantFindARepositoryAtLocalPath", resourceCulture); - } - } - /// /// Looks up a localized string similar to Can't find GitHub URL for repository. /// @@ -603,6 +594,15 @@ public static string DifferentRepositoryMessage { } } + /// + /// Looks up a localized string similar to The directory at the destination path is not empty.. + /// + public static string DirectoryAtDestinationNotEmpty { + get { + return ResourceManager.GetString("DirectoryAtDestinationNotEmpty", resourceCulture); + } + } + /// /// Looks up a localized string similar to Don’t have an account? . /// diff --git a/src/GitHub.Resources/Resources.cs-CZ.resx b/src/GitHub.Resources/Resources.cs-CZ.resx index 787a8a0323..89d9096aed 100644 --- a/src/GitHub.Resources/Resources.cs-CZ.resx +++ b/src/GitHub.Resources/Resources.cs-CZ.resx @@ -809,8 +809,8 @@ https://git-scm.com/download/win V aktuálním úložišti se nepodařilo najít cílovou adresu URL. Zkuste to znovu po načtení změn. - - V tomto umístění je už adresář, ale neobsahuje úložiště. + + The directory at the destination path is not empty. Úložiště už v tomto umístění existuje, ale nemá vzdálené úložiště s názvem origin. diff --git a/src/GitHub.Resources/Resources.de-DE.resx b/src/GitHub.Resources/Resources.de-DE.resx index c66c0bfd50..1b0e34db11 100644 --- a/src/GitHub.Resources/Resources.de-DE.resx +++ b/src/GitHub.Resources/Resources.de-DE.resx @@ -809,8 +809,8 @@ https://git-scm.com/download/win Die Ziel-URL wurde im aktuellen Repository nicht gefunden. Versuchen Sie es nach einem Abrufvorgang (fetch) noch einmal. - - An diesem Speicherort liegt bereits ein Verzeichnis vor, aber es enthält kein Repository. + + The directory at the destination path is not empty. An diesem Speicherort ist bereits ein Repository vorhanden, das aber kein Remoterepository namens "origin" aufweist. diff --git a/src/GitHub.Resources/Resources.es-ES.resx b/src/GitHub.Resources/Resources.es-ES.resx index d416d9875c..c243ffb369 100644 --- a/src/GitHub.Resources/Resources.es-ES.resx +++ b/src/GitHub.Resources/Resources.es-ES.resx @@ -809,8 +809,8 @@ https://git-scm.com/download/win No se encontró la dirección URL de destino en el repositorio actual. Vuelva a intentarlo tras una recuperación de cambios. - - Ya hay un directorio en esta ubicación, pero no contiene un repositorio. + + The directory at the destination path is not empty. Ya existe un repositorio en esta ubicación, pero no tiene un repositorio remoto con el nombre "origen". diff --git a/src/GitHub.Resources/Resources.fr-FR.resx b/src/GitHub.Resources/Resources.fr-FR.resx index b30b2784b1..9642a486c4 100644 --- a/src/GitHub.Resources/Resources.fr-FR.resx +++ b/src/GitHub.Resources/Resources.fr-FR.resx @@ -809,8 +809,8 @@ https://git-scm.com/download/win L'URL cible est introuvable dans le dépôt actuel. Réessayez après une récupération (fetch). - - Un répertoire est déjà présent à cet emplacement, mais il ne contient pas de dépôt. + + The directory at the destination path is not empty. Un dépôt existe déjà à cet emplacement, mais il n'a pas de dépôt distant nommé « origin ». diff --git a/src/GitHub.Resources/Resources.it-IT.resx b/src/GitHub.Resources/Resources.it-IT.resx index c825f5bae4..493973019b 100644 --- a/src/GitHub.Resources/Resources.it-IT.resx +++ b/src/GitHub.Resources/Resources.it-IT.resx @@ -809,8 +809,8 @@ https://git-scm.com/download/win Non è stato trovato alcun URL di destinazione nel repository corrente. Riprovare dopo aver eseguito il comando fetch. - - In questo percorso esiste già una directory che però non contiene un repository. + + The directory at the destination path is not empty. In questo percorso esiste già un repository che però non include un repository remoto denominato "origin". diff --git a/src/GitHub.Resources/Resources.ja-JP.resx b/src/GitHub.Resources/Resources.ja-JP.resx index a20cf8c660..f117b67c15 100644 --- a/src/GitHub.Resources/Resources.ja-JP.resx +++ b/src/GitHub.Resources/Resources.ja-JP.resx @@ -809,8 +809,8 @@ https://git-scm.com/download/win 現在のリポジトリにターゲット URL が見つかりませんでした。フェッチを実行した後、もう一度お試しください。 - - この場所に既にディレクトリがありますが、リポジトリが含まれていません。 + + The directory at the destination path is not empty. この場所にリポジトリが既に存在しますが、そのリポジトリには "origin" という名前のリモートがありません。 diff --git a/src/GitHub.Resources/Resources.ko-KR.resx b/src/GitHub.Resources/Resources.ko-KR.resx index 6825c48f1e..b97c8af695 100644 --- a/src/GitHub.Resources/Resources.ko-KR.resx +++ b/src/GitHub.Resources/Resources.ko-KR.resx @@ -809,8 +809,8 @@ https://git-scm.com/download/win 현재 리포지토리에서 대상 URL을 찾을 수 없습니다. 페치를 수행한 후 다시 시도하세요. - - 이 위치에 이미 디렉터리가 있지만, 디렉터리에 리포지토리가 없습니다. + + The directory at the destination path is not empty. 리포지토리가 이 위치에 이미 있지만, "origin"이라는 원격 항목을 포함하지 않습니다. diff --git a/src/GitHub.Resources/Resources.pl-PL.resx b/src/GitHub.Resources/Resources.pl-PL.resx index c8366cd109..7b019685b2 100644 --- a/src/GitHub.Resources/Resources.pl-PL.resx +++ b/src/GitHub.Resources/Resources.pl-PL.resx @@ -809,8 +809,8 @@ https://git-scm.com/download/win Nie można odnaleźć docelowego adresu URL w bieżącym repozytorium. Spróbuj ponownie po zakończeniu pobierania. - - W tej lokalizacji istnieje już katalog, ale nie zawiera on repozytorium. + + The directory at the destination path is not empty. Repozytorium już istnieje w tej lokalizacji, ale nie ma zdalnego repozytorium o nazwie „origin”. diff --git a/src/GitHub.Resources/Resources.pt-BR.resx b/src/GitHub.Resources/Resources.pt-BR.resx index f15054ec12..9acb25c9db 100644 --- a/src/GitHub.Resources/Resources.pt-BR.resx +++ b/src/GitHub.Resources/Resources.pt-BR.resx @@ -809,8 +809,8 @@ https://git-scm.com/download/win Não foi possível encontrar a URL de destino no repositório atual. Tente novamente depois de efetuar um fetch. - - Já existe um diretório neste local, mas ele não contém um repositório. + + The directory at the destination path is not empty. Já existe um repositório neste local, mas ele não tem um repositório remoto com o nome "origem". diff --git a/src/GitHub.Resources/Resources.resx b/src/GitHub.Resources/Resources.resx index c9cde7d183..acc5d80621 100644 --- a/src/GitHub.Resources/Resources.resx +++ b/src/GitHub.Resources/Resources.resx @@ -809,8 +809,8 @@ https://git-scm.com/download/win Couldn't find target URL in current repository. Try again after doing a fetch. - - There is already a directory at this location, but it doesn't contain a repository. + + The directory at the destination path is not empty. A repository already exists at this location, but it doesn't have a remote named "origin". @@ -878,4 +878,4 @@ https://git-scm.com/download/win Contributed to repositories - \ No newline at end of file + diff --git a/src/GitHub.Resources/Resources.ru-RU.resx b/src/GitHub.Resources/Resources.ru-RU.resx index 0be42538bd..3a6e991a9c 100644 --- a/src/GitHub.Resources/Resources.ru-RU.resx +++ b/src/GitHub.Resources/Resources.ru-RU.resx @@ -809,8 +809,8 @@ https://git-scm.com/download/win Не удалось найти целевой URL-адрес в текущем репозитории. Выполните принесение и повторите попытку. - - В этом расположении уже существует каталог, но он не содержит репозиторий. + + The directory at the destination path is not empty. В этом расположении уже существует репозиторий, но у него отсутствует удаленный репозиторий с именем "origin". diff --git a/src/GitHub.Resources/Resources.tr-TR.resx b/src/GitHub.Resources/Resources.tr-TR.resx index d9b282c804..9c6a2e687e 100644 --- a/src/GitHub.Resources/Resources.tr-TR.resx +++ b/src/GitHub.Resources/Resources.tr-TR.resx @@ -809,8 +809,8 @@ https://git-scm.com/download/win Hedef URL geçerli depoda bulunamadı. Getirme işlemi gerçekleştirdikten sonra yeniden deneyin. - - Bu konumda zaten bir dizin var ancak bir depo içermiyor. + + The directory at the destination path is not empty. Bu konumda bir depo zaten var ancak bu deponun "origin" adlı bir uzak deposu yok. diff --git a/src/GitHub.Resources/Resources.zh-CN.resx b/src/GitHub.Resources/Resources.zh-CN.resx index 398d8b0478..057bc9bcf4 100644 --- a/src/GitHub.Resources/Resources.zh-CN.resx +++ b/src/GitHub.Resources/Resources.zh-CN.resx @@ -809,8 +809,8 @@ https://git-scm.com/download/win 当前存储库中找不到目标 URL。请执行提取后重试。 - - 在此位置已存在一个目录,但它不包含存储库。 + + The directory at the destination path is not empty. 此位置已存在一个存储库,但该存储库不具有名为 "origin" 的远程库。 diff --git a/src/GitHub.Resources/Resources.zh-TW.resx b/src/GitHub.Resources/Resources.zh-TW.resx index 64db419743..3ef86dfdd2 100644 --- a/src/GitHub.Resources/Resources.zh-TW.resx +++ b/src/GitHub.Resources/Resources.zh-TW.resx @@ -809,8 +809,8 @@ https://git-scm.com/download/win 在目前存放庫中找不到目標 URL。請在執行擷取後再試一次。 - - 此位置已經有一個目錄,但它並未包含存放庫。 + + The directory at the destination path is not empty. 存放庫已存在於此位置,但其不具有名為 "origin" 的遠端存放庫。 diff --git a/test/GitHub.App.UnitTests/Services/RepositoryCloneServiceTests.cs b/test/GitHub.App.UnitTests/Services/RepositoryCloneServiceTests.cs index 28ca979afe..dc77d5dd1f 100644 --- a/test/GitHub.App.UnitTests/Services/RepositoryCloneServiceTests.cs +++ b/test/GitHub.App.UnitTests/Services/RepositoryCloneServiceTests.cs @@ -121,9 +121,11 @@ await usageTracker.Received(numberOfCalls).IncrementCounter( ((MemberExpression)x.Body).Member.Name == counterName)); } - [TestCase("https://github.com/failing/url", @"c:\dev\bar")] - public async Task CleansDirectoryOnCloneFailed(string cloneUrl, string clonePath) + [Test] + public async Task CleansDirectoryOnCloneFailed() { + var cloneUrl = "https://github.com/failing/url"; + var clonePath = @"c:\dev\bar"; var operatingSystem = Substitute.For(); var vsGitServices = Substitute.For(); vsGitServices.Clone(cloneUrl, clonePath, true).Returns(x => { throw new Exception(); }); @@ -136,6 +138,22 @@ public async Task CleansDirectoryOnCloneFailed(string cloneUrl, string clonePath await vsGitServices.Received().Clone(cloneUrl, clonePath, true); } + [Test] + public async Task CloneIntoEmptyDirectory() + { + var cloneUrl = "https://github.com/foo/bar"; + var clonePath = @"c:\empty\directory"; + var operatingSystem = Substitute.For(); + operatingSystem.Directory.DirectoryExists(clonePath).Returns(true); + operatingSystem.Directory.IsEmpty(clonePath).Returns(true); + var vsGitServices = Substitute.For(); + var cloneService = CreateRepositoryCloneService(operatingSystem: operatingSystem, vsGitServices: vsGitServices); + await cloneService.CloneRepository(cloneUrl, clonePath); + + operatingSystem.Directory.DidNotReceive().CreateDirectory(clonePath); + await vsGitServices.Received().Clone(cloneUrl, clonePath, true); + } + static RepositoryCloneService CreateRepositoryCloneService(IOperatingSystem operatingSystem = null, IVSGitServices vsGitServices = null, IUsageTracker usageTracker = null, ITeamExplorerServices teamExplorerServices = null, IGitHubServiceProvider serviceProvider = null) diff --git a/test/GitHub.App.UnitTests/ViewModels/Dialog/Clone/RepositoryCloneViewModelTests.cs b/test/GitHub.App.UnitTests/ViewModels/Dialog/Clone/RepositoryCloneViewModelTests.cs index 8d3b26f210..4ee276d691 100644 --- a/test/GitHub.App.UnitTests/ViewModels/Dialog/Clone/RepositoryCloneViewModelTests.cs +++ b/test/GitHub.App.UnitTests/ViewModels/Dialog/Clone/RepositoryCloneViewModelTests.cs @@ -17,6 +17,7 @@ namespace GitHub.App.UnitTests.ViewModels.Dialog.Clone { public class RepositoryCloneViewModelTests { + const string directoryEmpty = "d:\\empty\\directory"; const string directoryExists = "d:\\exists\\directory"; const string fileExists = "d:\\exists\\file"; const string defaultPath = "d:\\default\\path"; @@ -224,7 +225,7 @@ public void PathWarning_Is_Set_For_Directory_With_No_Repository() SetRepository(target.GitHubTab, CreateRepositoryModel(owner, repo)); target.Path = directoryExists; - Assert.That(target.PathWarning, Is.EqualTo(Resources.CantFindARepositoryAtLocalPath)); + Assert.That(target.PathWarning, Is.EqualTo(Resources.DirectoryAtDestinationNotEmpty)); } [Test] @@ -244,6 +245,16 @@ public void PathWarning_Is_Set_For_Existing_Repository_At_Destination_With_Diffe Assert.That(target.PathWarning, Is.EqualTo(expectMessage)); } + [Test] + public void PathWarning_Is_Not_Set_When_EmptyDirectoryExists_Selected() + { + var target = CreateTarget(); + + target.Path = directoryEmpty; + + Assert.That(target.PathWarning, Is.Null); + } + [Test] public void Repository_Name_Replaces_Last_Part_Of_Non_Base_Path() { @@ -342,6 +353,21 @@ public async Task Open_Is_Enabled_When_Path_DirectoryExists() Assert.That(target.Open.CanExecute(null), Is.True); } + [Test] + public async Task Clone_Is_Enabled_When_Path_EmptyDirectoryExists() + { + var target = CreateTarget(); + + await target.InitializeAsync(null); + + SetRepository(target.GitHubTab, CreateRepositoryModel()); + Assert.That(target.Clone.CanExecute(null), Is.True); + + target.Path = directoryEmpty; + + Assert.That(target.Clone.CanExecute(null), Is.True); + } + static void SetRepository(IRepositoryCloneTabViewModel vm, RepositoryModel repository) { vm.Repository.Returns(repository); @@ -385,6 +411,8 @@ static IRepositoryCloneService CreateRepositoryCloneService(string defaultCloneP var result = Substitute.For(); result.DefaultClonePath.Returns(defaultClonePath); result.DestinationDirectoryExists(directoryExists).Returns(true); + result.DestinationDirectoryExists(directoryEmpty).Returns(true); + result.DestinationDirectoryEmpty(directoryEmpty).Returns(true); result.DestinationFileExists(directoryExists).Returns(false); result.DestinationDirectoryExists(fileExists).Returns(false); result.DestinationFileExists(fileExists).Returns(true);