Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit da0fe18

Browse files
authored
Merge pull request #369 from github/fixes/360-validate-clone-directory-exists
Check if the clone target directory already exists.
2 parents 290ae08 + a611abc commit da0fe18

File tree

7 files changed

+71
-37
lines changed

7 files changed

+71
-37
lines changed

src/GitHub.App/Extensions/ValidationExtensions.cs

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/GitHub.App/GitHub.App.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@
124124
<Compile Include="Caches\ImageCache.cs" />
125125
<Compile Include="Extensions\AkavacheExtensions.cs" />
126126
<Compile Include="Extensions\EnvironmentExtensions.cs" />
127-
<Compile Include="Extensions\ValidationExtensions.cs" />
128127
<Compile Include="Factories\UIFactory.cs" />
129128
<Compile Include="GlobalSuppressions.cs" />
130129
<Compile Include="Infrastructure\LoggingConfiguration.cs" />

src/GitHub.App/SampleData/SampleViewModels.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,11 @@ public RepositoryCloneViewModelDesigner()
377377
x => x
378378
);
379379

380-
BaseRepositoryPathValidator = this.CreateBaseRepositoryPathValidator();
380+
BaseRepositoryPathValidator = ReactivePropertyValidator.ForObservable(this.WhenAny(x => x.BaseRepositoryPath, x => x.Value))
381+
.IfNullOrEmpty("Please enter a repository path")
382+
.IfTrue(x => x.Length > 200, "Path too long")
383+
.IfContainsInvalidPathChars("Path contains invalid characters")
384+
.IfPathNotRooted("Please enter a valid path");
381385
}
382386

383387
public IReactiveCommand<Unit> CloneCommand

src/GitHub.App/ViewModels/RepositoryCloneViewModel.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
using System.ComponentModel.Composition;
44
using System.Diagnostics;
55
using System.Globalization;
6+
using System.IO;
7+
using System.Linq;
68
using System.Reactive;
79
using System.Reactive.Linq;
10+
using System.Text.RegularExpressions;
811
using System.Windows.Input;
912
using GitHub.App;
1013
using GitHub.Exports;
@@ -80,7 +83,17 @@ public RepositoryCloneViewModel(
8083
signalReset: filterResetSignal
8184
);
8285

83-
BaseRepositoryPathValidator = this.CreateBaseRepositoryPathValidator();
86+
var baseRepositoryPath = this.WhenAny(
87+
x => x.BaseRepositoryPath,
88+
x => x.SelectedRepository,
89+
(x, y) => x.Value);
90+
91+
BaseRepositoryPathValidator = ReactivePropertyValidator.ForObservable(baseRepositoryPath)
92+
.IfNullOrEmpty("Please enter a repository path")
93+
.IfTrue(x => x.Length > 200, "Path too long")
94+
.IfContainsInvalidPathChars("Path contains invalid characters")
95+
.IfPathNotRooted("Please enter a valid path")
96+
.IfTrue(IsAlreadyRepoAtPath, Resources.RepositoryNameValidatorAlreadyExists);
8497

8598
var canCloneObservable = this.WhenAny(
8699
x => x.SelectedRepository,
@@ -140,6 +153,23 @@ IObservable<Unit> OnCloneRepository(object state)
140153
});
141154
}
142155

156+
bool IsAlreadyRepoAtPath(string path)
157+
{
158+
bool isAlreadyRepoAtPath = false;
159+
160+
if (SelectedRepository != null)
161+
{
162+
var validationResult = BaseRepositoryPathValidator.ValidationResult;
163+
if (validationResult != null && validationResult.IsValid)
164+
{
165+
string potentialPath = Path.Combine(path, SelectedRepository.Name);
166+
isAlreadyRepoAtPath = operatingSystem.Directory.Exists(potentialPath);
167+
}
168+
}
169+
170+
return isAlreadyRepoAtPath;
171+
}
172+
143173
/// <summary>
144174
/// Path to clone repositories into
145175
/// </summary>

src/GitHub.App/ViewModels/RepositoryCreationViewModel.cs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,11 @@ public RepositoryCreationViewModel(
7878

7979
browseForDirectoryCommand.Subscribe(_ => ShowBrowseForDirectoryDialog());
8080

81-
BaseRepositoryPathValidator = this.CreateBaseRepositoryPathValidator();
81+
BaseRepositoryPathValidator = ReactivePropertyValidator.ForObservable(this.WhenAny(x => x.BaseRepositoryPath, x => x.Value))
82+
.IfNullOrEmpty("Please enter a repository path")
83+
.IfTrue(x => x.Length > 200, "Path too long")
84+
.IfContainsInvalidPathChars("Path contains invalid characters")
85+
.IfPathNotRooted("Please enter a valid path");
8286

8387
var nonNullRepositoryName = this.WhenAny(
8488
x => x.RepositoryName,
@@ -251,23 +255,11 @@ bool IsAlreadyRepoAtPath(string potentialRepositoryName)
251255
if (Path.GetInvalidPathChars().Any(chr => BaseRepositoryPath.Contains(chr)))
252256
return false;
253257
string potentialPath = Path.Combine(BaseRepositoryPath, safeRepositoryName);
254-
isAlreadyRepoAtPath = IsGitRepo(potentialPath);
258+
isAlreadyRepoAtPath = operatingSystem.Directory.Exists(potentialPath);
255259
}
256260
return isAlreadyRepoAtPath;
257261
}
258262

259-
bool IsGitRepo(string path)
260-
{
261-
try
262-
{
263-
return operatingSystem.File.Exists(Path.Combine(path, ".git", "HEAD"));
264-
}
265-
catch (PathTooLongException)
266-
{
267-
return false;
268-
}
269-
}
270-
271263
IObservable<Unit> OnCreateRepository(object state)
272264
{
273265
var newRepository = GatherRepositoryInfo();

src/UnitTests/GitHub.App/ViewModels/RepositoryCloneViewModelTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Threading.Tasks;
66
using GitHub.Models;
77
using GitHub.Services;
8+
using GitHub.Validation;
89
using GitHub.ViewModels;
910
using NSubstitute;
1011
using Rothko;
@@ -196,6 +197,33 @@ public void IsTrueIfLoadingReposFails()
196197
}
197198
}
198199

200+
public class TheBaseRepositoryPathValidator
201+
{
202+
[Fact]
203+
public void IsInvalidWhenDestinationRepositoryExists()
204+
{
205+
var repo = Substitute.For<IRepositoryModel>();
206+
repo.Name.Returns("bar");
207+
var repositoryHost = Substitute.For<IRepositoryHost>();
208+
repositoryHost.ModelService.GetRepositories().Returns(Observable.Return(new[] { repo }));
209+
var cloneService = Substitute.For<IRepositoryCloneService>();
210+
var os = Substitute.For<IOperatingSystem>();
211+
var directories = Substitute.For<IDirectoryFacade>();
212+
os.Directory.Returns(directories);
213+
directories.Exists(@"c:\foo\bar").Returns(true);
214+
var vm = new RepositoryCloneViewModel(
215+
repositoryHost,
216+
cloneService,
217+
os,
218+
Substitute.For<INotificationService>());
219+
220+
vm.BaseRepositoryPath = @"c:\foo";
221+
vm.SelectedRepository = repo;
222+
223+
Assert.Equal(ValidationStatus.Invalid, vm.BaseRepositoryPathValidator.ValidationResult.Status);
224+
}
225+
}
226+
199227
public class TheCloneCommand : TestBaseClass
200228
{
201229
[Fact]

src/UnitTests/GitHub.App/ViewModels/RepositoryCreationViewModelTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ public void IsFalseWhenRepositoryAlreadyExists(bool exists, bool expected)
283283
{
284284
var provider = Substitutes.ServiceProvider;
285285
var operatingSystem = provider.GetOperatingSystem();
286-
operatingSystem.File.Exists(@"c:\fake\foo\.git\HEAD").Returns(exists);
286+
operatingSystem.Directory.Exists(@"c:\fake\foo").Returns(exists);
287287
var vm = GetMeAViewModel(provider);
288288
vm.BaseRepositoryPath = @"c:\fake\";
289289

0 commit comments

Comments
 (0)