From 470ea1342b256438bfbd8ea7738b07e1bfbae0cc Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 20 Oct 2017 17:52:29 +0200 Subject: [PATCH 1/2] Removed `IConnection.Repositories`. Removed `IConnection.Repositories` and `IConnectionManager.RefreshRepositories` and replaced them with `LocalRepositories` which acts as a central place to find known repositories. As part of this, also added `IReadOnlyObservableCollection` and `ObservableCollectionEx` because the BCL's `ReadOnlyObservableCollection` sucks. Made `TrackingCollection` implement `IReadOnlyObservableCollection`. --- src/GitHub.App/GitHub.App.csproj | 1 + src/GitHub.App/Services/LocalRepositories.cs | 47 ++++++ .../Collections/TrackingCollection.cs | 2 +- .../GitHub.Exports.Reactive.csproj | 1 + .../Services/LocalRepositoriesExtensions.cs | 29 ++++ src/GitHub.Exports/GitHub.Exports.csproj | 1 + src/GitHub.Exports/Models/IConnection.cs | 3 +- src/GitHub.Exports/Services/Connection.cs | 25 ---- .../Services/IConnectionManager.cs | 1 - .../Services/ILocalRepositories.cs | 23 +++ .../GitHub.Extensions.csproj | 2 + .../IReadOnlyObservableCollection.cs | 17 +++ .../ObservableCollectionEx.cs | 42 ++++++ .../Connect/GitHubConnectSection.cs | 10 +- .../Connect/GitHubConnectSection0.cs | 5 +- .../Connect/GitHubConnectSection1.cs | 5 +- .../Services/ConnectionManager.cs | 17 --- .../Services/LocalRepositoriesTests.cs | 136 ++++++++++++++++++ .../Services/ConnectionManagerTests.cs | 2 +- 19 files changed, 315 insertions(+), 54 deletions(-) create mode 100644 src/GitHub.App/Services/LocalRepositories.cs create mode 100644 src/GitHub.Exports.Reactive/Services/LocalRepositoriesExtensions.cs create mode 100644 src/GitHub.Exports/Services/ILocalRepositories.cs create mode 100644 src/GitHub.Extensions/IReadOnlyObservableCollection.cs create mode 100644 src/GitHub.Extensions/ObservableCollectionEx.cs create mode 100644 src/UnitTests/GitHub.App/Services/LocalRepositoriesTests.cs diff --git a/src/GitHub.App/GitHub.App.csproj b/src/GitHub.App/GitHub.App.csproj index 206ece6bf9..a8372b644b 100644 --- a/src/GitHub.App/GitHub.App.csproj +++ b/src/GitHub.App/GitHub.App.csproj @@ -130,6 +130,7 @@ + diff --git a/src/GitHub.App/Services/LocalRepositories.cs b/src/GitHub.App/Services/LocalRepositories.cs new file mode 100644 index 0000000000..bd092a4b11 --- /dev/null +++ b/src/GitHub.App/Services/LocalRepositories.cs @@ -0,0 +1,47 @@ +using System; +using System.ComponentModel.Composition; +using System.Linq; +using System.Threading.Tasks; +using GitHub.Extensions; +using GitHub.Helpers; +using GitHub.Models; +using GitHub.Services; + +namespace GitHub.App.Services +{ + /// + /// Stores a collection of known local repositories. + /// + /// + /// The list of repositories exposed here is the list of local repositories known to Team + /// Explorer. + /// + [Export(typeof(ILocalRepositories))] + public class LocalRepositories : ILocalRepositories + { + readonly IVSGitServices vsGitServices; + + [ImportingConstructor] + public LocalRepositories(IVSGitServices vsGitServices) + { + this.vsGitServices = vsGitServices; + } + + /// + public async Task Refresh() + { + await ThreadingHelper.SwitchToPoolThreadAsync(); + var list = vsGitServices.GetKnownRepositories(); + await ThreadingHelper.SwitchToMainThreadAsync(); + + repositories.Except(list).ToList().ForEach(x => repositories.Remove(x)); + list.Except(repositories).ToList().ForEach(x => repositories.Add(x)); + } + + readonly ObservableCollectionEx repositories + = new ObservableCollectionEx(); + + /// + public IReadOnlyObservableCollection Repositories => repositories; + } +} diff --git a/src/GitHub.Exports.Reactive/Collections/TrackingCollection.cs b/src/GitHub.Exports.Reactive/Collections/TrackingCollection.cs index 6bb77050ee..ca82f1d802 100644 --- a/src/GitHub.Exports.Reactive/Collections/TrackingCollection.cs +++ b/src/GitHub.Exports.Reactive/Collections/TrackingCollection.cs @@ -164,7 +164,7 @@ static void UpdateStickieItems( /// for T /// /// - public class TrackingCollection : ObservableCollection, ITrackingCollection, IDisposable + public class TrackingCollection : ObservableCollection, ITrackingCollection, IReadOnlyObservableCollection, IDisposable where T : class, ICopyable { enum TheAction diff --git a/src/GitHub.Exports.Reactive/GitHub.Exports.Reactive.csproj b/src/GitHub.Exports.Reactive/GitHub.Exports.Reactive.csproj index a647c32267..3091dca509 100644 --- a/src/GitHub.Exports.Reactive/GitHub.Exports.Reactive.csproj +++ b/src/GitHub.Exports.Reactive/GitHub.Exports.Reactive.csproj @@ -112,6 +112,7 @@ + diff --git a/src/GitHub.Exports.Reactive/Services/LocalRepositoriesExtensions.cs b/src/GitHub.Exports.Reactive/Services/LocalRepositoriesExtensions.cs new file mode 100644 index 0000000000..76c808724a --- /dev/null +++ b/src/GitHub.Exports.Reactive/Services/LocalRepositoriesExtensions.cs @@ -0,0 +1,29 @@ +using System; +using GitHub.Models; +using GitHub.Primitives; +using ReactiveUI; + +namespace GitHub.Services +{ + /// + /// Implements extension methods for . + /// + public static class LocalRepositoriesExtensions + { + /// + /// Gets a derived collection that contains all known repositories with the specified + /// clone URL, ordered by name. + /// + /// The local repositories object. + /// The address. + public static IReactiveDerivedList GetRepositoriesForAddress( + this ILocalRepositories repos, + HostAddress address) + { + return repos.Repositories.CreateDerivedCollection( + x => x, + x => x.CloneUrl != null && address.Equals(HostAddress.Create(x.CloneUrl)), + OrderedComparer.OrderBy(x => x.Name).Compare); + } + } +} diff --git a/src/GitHub.Exports/GitHub.Exports.csproj b/src/GitHub.Exports/GitHub.Exports.csproj index 525fd7a02d..955132356b 100644 --- a/src/GitHub.Exports/GitHub.Exports.csproj +++ b/src/GitHub.Exports/GitHub.Exports.csproj @@ -137,6 +137,7 @@ + diff --git a/src/GitHub.Exports/Models/IConnection.cs b/src/GitHub.Exports/Models/IConnection.cs index 26e3a172e1..93d29522fe 100644 --- a/src/GitHub.Exports/Models/IConnection.cs +++ b/src/GitHub.Exports/Models/IConnection.cs @@ -6,12 +6,11 @@ namespace GitHub.Models { - public interface IConnection : IDisposable + public interface IConnection { HostAddress HostAddress { get; } string Username { get; } IObservable Login(); void Logout(); - ObservableCollection Repositories { get; } } } diff --git a/src/GitHub.Exports/Services/Connection.cs b/src/GitHub.Exports/Services/Connection.cs index 1fc3ad53a9..655d475b23 100644 --- a/src/GitHub.Exports/Services/Connection.cs +++ b/src/GitHub.Exports/Services/Connection.cs @@ -1,9 +1,6 @@ using System; using GitHub.Models; using GitHub.Primitives; -using System.Threading.Tasks; -using System.Collections.ObjectModel; -using System.Collections.Specialized; namespace GitHub.Services { @@ -16,12 +13,10 @@ public Connection(IConnectionManager cm, HostAddress hostAddress, string userNam manager = cm; HostAddress = hostAddress; Username = userName; - Repositories = new ObservableCollection(); } public HostAddress HostAddress { get; private set; } public string Username { get; private set; } - public ObservableCollection Repositories { get; } public IObservable Login() { @@ -32,25 +27,5 @@ public void Logout() { manager.RequestLogout(this); } - - #region IDisposable Support - private bool disposed = false; // To detect redundant calls - - protected virtual void Dispose(bool disposing) - { - if (!disposed) - { - if (disposing) - Repositories.Clear(); - disposed = true; - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - #endregion } } diff --git a/src/GitHub.Exports/Services/IConnectionManager.cs b/src/GitHub.Exports/Services/IConnectionManager.cs index 90599eb3be..7631ce49e7 100644 --- a/src/GitHub.Exports/Services/IConnectionManager.cs +++ b/src/GitHub.Exports/Services/IConnectionManager.cs @@ -20,6 +20,5 @@ public interface IConnectionManager // for telling IRepositoryHosts that we need to login from cache [SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly")] event Func> DoLogin; - Task RefreshRepositories(); } } diff --git a/src/GitHub.Exports/Services/ILocalRepositories.cs b/src/GitHub.Exports/Services/ILocalRepositories.cs new file mode 100644 index 0000000000..1d77fbd13c --- /dev/null +++ b/src/GitHub.Exports/Services/ILocalRepositories.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; +using GitHub.Extensions; +using GitHub.Models; + +namespace GitHub.Services +{ + /// + /// Stores a collection of known local repositories. + /// + public interface ILocalRepositories + { + /// + /// Gets the currently known local repositories. + /// + IReadOnlyObservableCollection Repositories { get; } + + /// + /// Updates . + /// + Task Refresh(); + } +} diff --git a/src/GitHub.Extensions/GitHub.Extensions.csproj b/src/GitHub.Extensions/GitHub.Extensions.csproj index 1696f0c55b..6fa1a678d6 100644 --- a/src/GitHub.Extensions/GitHub.Extensions.csproj +++ b/src/GitHub.Extensions/GitHub.Extensions.csproj @@ -59,7 +59,9 @@ + + Properties\SolutionInfo.cs diff --git a/src/GitHub.Extensions/IReadOnlyObservableCollection.cs b/src/GitHub.Extensions/IReadOnlyObservableCollection.cs new file mode 100644 index 0000000000..847512ce92 --- /dev/null +++ b/src/GitHub.Extensions/IReadOnlyObservableCollection.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; + +namespace GitHub.Extensions +{ + /// + /// Represents a read-only interface to an + /// + /// The type of elements in the collection. + public interface IReadOnlyObservableCollection : IReadOnlyList, + INotifyCollectionChanged, + INotifyPropertyChanged + { + } +} \ No newline at end of file diff --git a/src/GitHub.Extensions/ObservableCollectionEx.cs b/src/GitHub.Extensions/ObservableCollectionEx.cs new file mode 100644 index 0000000000..6e3c50c39a --- /dev/null +++ b/src/GitHub.Extensions/ObservableCollectionEx.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace GitHub.Extensions +{ + /// + /// A non-braindead way to expose a read-only view on an . + /// + /// The type of elements in the collection. + /// + /// fails in its only purpose by Not. Freaking. + /// Exposing. INotifyCollectionChanged. We define our own + /// type and use this class to expose it. Seriously. + /// + public class ObservableCollectionEx : ObservableCollection, IReadOnlyObservableCollection + { + /// + /// Initializes a new instance of the class. + /// + public ObservableCollectionEx() + { + } + + /// + /// Initializes a new instance of the class that + /// contains elements copied from the specified list. + /// + public ObservableCollectionEx(List list) + : base(list) + { + } + + /// + /// Initializes a new instance of the class that + /// contains elements copied from the specified collection. + /// + public ObservableCollectionEx(IEnumerable collection) + : base(collection) + { + } + } +} \ No newline at end of file diff --git a/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection.cs b/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection.cs index 078fdb9bf2..0862970d61 100644 --- a/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection.cs +++ b/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection.cs @@ -32,6 +32,7 @@ public class GitHubConnectSection : TeamExplorerSectionBase, IGitHubConnectSecti readonly int sectionIndex; readonly IDialogService dialogService; readonly IRepositoryCloneService cloneService; + readonly ILocalRepositories localRepositories; bool isCloning; bool isCreating; @@ -98,6 +99,7 @@ public GitHubConnectSection(IGitHubServiceProvider serviceProvider, IVSServices vsServices, IRepositoryCloneService cloneService, IDialogService dialogService, + ILocalRepositories localRepositories, int index) : base(serviceProvider, apiFactory, holder, manager) { @@ -108,6 +110,7 @@ public GitHubConnectSection(IGitHubServiceProvider serviceProvider, Guard.ArgumentNotNull(vsServices, nameof(vsServices)); Guard.ArgumentNotNull(cloneService, nameof(cloneService)); Guard.ArgumentNotNull(dialogService, nameof(dialogService)); + Guard.ArgumentNotNull(localRepositories, nameof(localRepositories)); Title = "GitHub"; IsEnabled = true; @@ -119,6 +122,7 @@ public GitHubConnectSection(IGitHubServiceProvider serviceProvider, this.vsServices = vsServices; this.cloneService = cloneService; this.dialogService = dialogService; + this.localRepositories = localRepositories; Clone = CreateAsyncCommandHack(DoClone); @@ -194,8 +198,8 @@ protected void Refresh(IConnection connection) if (connection != SectionConnection) { SectionConnection = connection; - Repositories = SectionConnection.Repositories.CreateDerivedCollection(x => x, - orderer: OrderedComparer.OrderBy(x => x.Name).Compare); + Repositories?.Dispose(); + Repositories = localRepositories.GetRepositoriesForAddress(connection.HostAddress); Repositories.CollectionChanged += UpdateRepositoryList; Title = connection.HostAddress.Title; IsVisible = true; @@ -368,7 +372,7 @@ async Task RefreshRepositories() { // TODO: This is wasteful as we can be calling it multiple times for a single changed // signal, once from each section. Needs refactoring. - await connectionManager.RefreshRepositories(); + await localRepositories.Refresh(); RaisePropertyChanged("Repositories"); // trigger a re-check of the visibility of the listview based on item count } diff --git a/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection0.cs b/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection0.cs index 05ec3a53e6..652456eaa7 100644 --- a/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection0.cs +++ b/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection0.cs @@ -21,8 +21,9 @@ public GitHubConnectSection0(IGitHubServiceProvider serviceProvider, IPackageSettings settings, IVSServices vsServices, IRepositoryCloneService cloneService, - IDialogService dialogService) - : base(serviceProvider, apiFactory, holder, manager, settings, vsServices, cloneService, dialogService, 0) + IDialogService dialogService, + ILocalRepositories localRepositories) + : base(serviceProvider, apiFactory, holder, manager, settings, vsServices, cloneService, dialogService, localRepositories, 0) { } } diff --git a/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection1.cs b/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection1.cs index 5866f5958c..e2c7070521 100644 --- a/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection1.cs +++ b/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection1.cs @@ -21,8 +21,9 @@ public GitHubConnectSection1(IGitHubServiceProvider serviceProvider, IPackageSettings settings, IVSServices vsServices, IRepositoryCloneService cloneService, - IDialogService dialogService) - : base(serviceProvider, apiFactory, holder, manager, settings, vsServices, cloneService, dialogService, 1) + IDialogService dialogService, + ILocalRepositories localRepositories) + : base(serviceProvider, apiFactory, holder, manager, settings, vsServices, cloneService, dialogService, localRepositories, 1) { } } diff --git a/src/GitHub.VisualStudio/Services/ConnectionManager.cs b/src/GitHub.VisualStudio/Services/ConnectionManager.cs index 08498332bf..fc3ae56adc 100644 --- a/src/GitHub.VisualStudio/Services/ConnectionManager.cs +++ b/src/GitHub.VisualStudio/Services/ConnectionManager.cs @@ -17,7 +17,6 @@ namespace GitHub.VisualStudio [PartCreationPolicy(CreationPolicy.Shared)] public class ConnectionManager : IConnectionManager { - readonly IVSGitServices vsGitServices; readonly IConnectionCache cache; readonly IKeychain keychain; readonly ILoginManager loginManager; @@ -27,13 +26,11 @@ public class ConnectionManager : IConnectionManager [ImportingConstructor] public ConnectionManager( - IVSGitServices vsGitServices, IConnectionCache cache, IKeychain keychain, ILoginManager loginManager, IApiClientFactory apiClientFactory) { - this.vsGitServices = vsGitServices; this.cache = cache; this.keychain = keychain; this.loginManager = loginManager; @@ -87,19 +84,6 @@ public void RequestLogout(IConnection connection) Connections.Remove(connection); } - public async Task RefreshRepositories() - { - var list = await Task.Run(() => vsGitServices.GetKnownRepositories()); - list.GroupBy(r => Connections.FirstOrDefault(c => r.CloneUrl != null && c.HostAddress.Equals(HostAddress.Create(r.CloneUrl)))) - .Where(g => g.Key != null) - .ForEach(g => - { - var repos = g.Key.Repositories; - repos.Except(g).ToList().ForEach(c => repos.Remove(c)); - g.Except(repos).ToList().ForEach(c => repos.Add(c)); - }); - } - IConnection SetupConnection(HostAddress address, string username) { var conn = new Connection(this, address, username); @@ -115,7 +99,6 @@ void RefreshConnections(object sender, System.Collections.Specialized.NotifyColl // RepositoryHosts hasn't been loaded so it can't handle logging out, we have to do it ourselves if (DoLogin == null) keychain.Delete(c.HostAddress).Forget(); - c.Dispose(); } } diff --git a/src/UnitTests/GitHub.App/Services/LocalRepositoriesTests.cs b/src/UnitTests/GitHub.App/Services/LocalRepositoriesTests.cs new file mode 100644 index 0000000000..8ec590721d --- /dev/null +++ b/src/UnitTests/GitHub.App/Services/LocalRepositoriesTests.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using GitHub.App.Services; +using GitHub.Models; +using GitHub.Primitives; +using GitHub.Services; +using NSubstitute; +using Xunit; + +public class LocalRepositoriesTests : TestBaseClass +{ + const string GitHubAddress = "https://github.com"; + + [Fact] + public void RepositoriesShouldInitiallyBeEmpty() + { + var service = CreateVSGitServices("repo1", "repo2"); + var target = new LocalRepositories(service); + + Assert.Empty(target.Repositories); + } + + [Fact] + public async Task RefreshShouldLoadRepositories() + { + var service = CreateVSGitServices("repo1", "repo2"); + var target = new LocalRepositories(service); + + await target.Refresh(); + + Assert.Equal( + new[] { "repo1", "repo2" }, + target.Repositories.Select(x => x.Name).ToList()); + } + + [Fact] + public async Task RefreshShouldAddNewRepository() + { + var service = CreateVSGitServices("repo1", "repo2"); + var target = new LocalRepositories(service); + + await target.Refresh(); + + Assert.Equal(2, target.Repositories.Count); + + var existing = service.GetKnownRepositories(); + var newRepo = CreateRepository("new"); + service.GetKnownRepositories().Returns(existing.Concat(new[] { newRepo })); + + await target.Refresh(); + + Assert.Equal( + new[] { "repo1", "repo2", "new" }, + target.Repositories.Select(x => x.Name).ToList()); + } + + [Fact] + public async Task RefreshShouldRemoveRepository() + { + var service = CreateVSGitServices("repo1", "repo2"); + var target = new LocalRepositories(service); + + await target.Refresh(); + + Assert.Equal(2, target.Repositories.Count); + + var existing = service.GetKnownRepositories(); + service.GetKnownRepositories().Returns(existing.Skip(1).Take(1)); + + await target.Refresh(); + + Assert.Equal( + new[] { "repo2" }, + target.Repositories.Select(x => x.Name).ToList()); + } + + [Fact] + public async Task GetRepositoriesForAddressShouldFilterRepositories() + { + var service = CreateVSGitServices( + Tuple.Create("repo1", GitHubAddress), + Tuple.Create("repo2", GitHubAddress), + Tuple.Create("repo2", "https://another.com")); + var target = new LocalRepositories(service); + + await target.Refresh(); + + Assert.Equal(3, target.Repositories.Count); + + var result = target.GetRepositoriesForAddress(HostAddress.Create(GitHubAddress)); + + Assert.Equal(2, result.Count); + } + + [Fact] + public async Task GetRepositoriesForAddressShouldSortRepositories() + { + var service = CreateVSGitServices("c", "a", "b"); + var target = new LocalRepositories(service); + + await target.Refresh(); + var result = target.GetRepositoriesForAddress(HostAddress.Create(GitHubAddress)); + + Assert.Equal( + new[] { "a", "b", "c" }, + result.Select(x => x.Name).ToList()); + } + + static IVSGitServices CreateVSGitServices(params string[] names) + { + return CreateVSGitServices(names.Select(x => Tuple.Create(x, GitHubAddress)).ToArray()); + } + + static IVSGitServices CreateVSGitServices(params Tuple[] namesAndAddresses) + { + var result = Substitute.For(); + var repositories = new List(namesAndAddresses.Select(CreateRepository)); + result.GetKnownRepositories().Returns(repositories); + return result; + } + + static ILocalRepositoryModel CreateRepository(string name) + { + return CreateRepository(Tuple.Create(name, "https://github.com")); + } + + static ILocalRepositoryModel CreateRepository(Tuple nameAndAddress) + { + var result = Substitute.For(); + result.Name.Returns(nameAndAddress.Item1); + result.CloneUrl.Returns(new UriString(nameAndAddress.Item2)); + return result; + } +} diff --git a/src/UnitTests/GitHub.VisualStudio/Services/ConnectionManagerTests.cs b/src/UnitTests/GitHub.VisualStudio/Services/ConnectionManagerTests.cs index 269d61ff60..0671760d28 100644 --- a/src/UnitTests/GitHub.VisualStudio/Services/ConnectionManagerTests.cs +++ b/src/UnitTests/GitHub.VisualStudio/Services/ConnectionManagerTests.cs @@ -31,7 +31,7 @@ public void IsSavedToDiskWhenConnectionAdded() var keychain = Substitute.For(); var loginManager = Substitute.For(); var apiClientFactory = Substitute.For(); - var manager = new ConnectionManager(Substitutes.IVSGitServices, cache, keychain, loginManager, apiClientFactory); + var manager = new ConnectionManager(cache, keychain, loginManager, apiClientFactory); manager.Connections.Add(new Connection(manager, HostAddress.GitHubDotComHostAddress, "coolio")); From 213a7fc7269e1244908815863d8c9553e2ba5837 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 24 Oct 2017 12:20:08 +0200 Subject: [PATCH 2/2] Moved LocalRepositories to GitHub.VisualStudio. Following advice by @shana. --- src/GitHub.App/GitHub.App.csproj | 1 - src/GitHub.VisualStudio/GitHub.VisualStudio.csproj | 1 + .../Services/LocalRepositories.cs | 3 +-- 3 files changed, 2 insertions(+), 3 deletions(-) rename src/{GitHub.App => GitHub.VisualStudio}/Services/LocalRepositories.cs (96%) diff --git a/src/GitHub.App/GitHub.App.csproj b/src/GitHub.App/GitHub.App.csproj index a8372b644b..206ece6bf9 100644 --- a/src/GitHub.App/GitHub.App.csproj +++ b/src/GitHub.App/GitHub.App.csproj @@ -130,7 +130,6 @@ - diff --git a/src/GitHub.VisualStudio/GitHub.VisualStudio.csproj b/src/GitHub.VisualStudio/GitHub.VisualStudio.csproj index 6ff30377c2..65c3ccb241 100644 --- a/src/GitHub.VisualStudio/GitHub.VisualStudio.csproj +++ b/src/GitHub.VisualStudio/GitHub.VisualStudio.csproj @@ -303,6 +303,7 @@ + diff --git a/src/GitHub.App/Services/LocalRepositories.cs b/src/GitHub.VisualStudio/Services/LocalRepositories.cs similarity index 96% rename from src/GitHub.App/Services/LocalRepositories.cs rename to src/GitHub.VisualStudio/Services/LocalRepositories.cs index bd092a4b11..d413b39422 100644 --- a/src/GitHub.App/Services/LocalRepositories.cs +++ b/src/GitHub.VisualStudio/Services/LocalRepositories.cs @@ -5,9 +5,8 @@ using GitHub.Extensions; using GitHub.Helpers; using GitHub.Models; -using GitHub.Services; -namespace GitHub.App.Services +namespace GitHub.Services { /// /// Stores a collection of known local repositories.