diff --git a/src/GitHub.App/GlobalSuppressions.cs b/src/GitHub.App/GlobalSuppressions.cs index f635f8ea1d..d3dcbf45d6 100644 --- a/src/GitHub.App/GlobalSuppressions.cs +++ b/src/GitHub.App/GlobalSuppressions.cs @@ -6,6 +6,8 @@ [assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "Git", Scope = "resource", Target = "GitHub.App.Resources.resources")] [assembly: SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object,System.Object)", Scope = "member", Target = "GitHub.Services.PullRequestService.#CreateTempFile(System.String,System.String,System.String)")] [assembly: SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object,System.Object)", Scope = "member", Target = "GitHub.Services.PullRequestService.#CreateTempFile(System.String,System.String,System.String,System.Text.Encoding)")] +[assembly: SuppressMessage("Reliability", "CA2007:Do not directly await a Task", Justification = "We always need to think about what thread we're running on")] + // This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given diff --git a/src/GitHub.App/SampleData/Dialog/Clone/SelectPageViewModelDesigner.cs b/src/GitHub.App/SampleData/Dialog/Clone/SelectPageViewModelDesigner.cs index ff6fc6e49e..82df1bfae9 100644 --- a/src/GitHub.App/SampleData/Dialog/Clone/SelectPageViewModelDesigner.cs +++ b/src/GitHub.App/SampleData/Dialog/Clone/SelectPageViewModelDesigner.cs @@ -38,7 +38,7 @@ public SelectPageViewModelDesigner() public IReadOnlyList Items { get; } public ICollectionView ItemsView { get; } public IRepositoryItemViewModel SelectedItem { get; set; } - public IRepositoryModel Repository { get; } + public RepositoryModel Repository { get; } public void Initialize(IConnection connection) { diff --git a/src/GitHub.App/SampleData/ForkRepositoryExecuteViewModelDesigner.cs b/src/GitHub.App/SampleData/ForkRepositoryExecuteViewModelDesigner.cs index 1a3cf201cd..74d05021ab 100644 --- a/src/GitHub.App/SampleData/ForkRepositoryExecuteViewModelDesigner.cs +++ b/src/GitHub.App/SampleData/ForkRepositoryExecuteViewModelDesigner.cs @@ -35,9 +35,9 @@ public ForkRepositoryExecuteViewModelDesigner() public string Title => null; - public IRepositoryModel SourceRepository { get; set; } + public RepositoryModel SourceRepository { get; set; } - public IRepositoryModel DestinationRepository { get; set; } + public RepositoryModel DestinationRepository { get; set; } public IAccount DestinationAccount { get; } @@ -57,7 +57,7 @@ public ForkRepositoryExecuteViewModelDesigner() public string Error { get; } = "I AM ERROR"; - public Task InitializeAsync(ILocalRepositoryModel sourceRepository, IAccount destinationAccount, IConnection connection) + public Task InitializeAsync(LocalRepositoryModel sourceRepository, IAccount destinationAccount, IConnection connection) { return Task.CompletedTask; } diff --git a/src/GitHub.App/SampleData/ForkRepositorySelectViewModelDesigner.cs b/src/GitHub.App/SampleData/ForkRepositorySelectViewModelDesigner.cs index a43ed6421a..ddbdaf80fd 100644 --- a/src/GitHub.App/SampleData/ForkRepositorySelectViewModelDesigner.cs +++ b/src/GitHub.App/SampleData/ForkRepositorySelectViewModelDesigner.cs @@ -33,7 +33,7 @@ public ForkRepositorySelectViewModelDesigner() public IObservable Done => null; - public IReadOnlyList ExistingForks { get; set; } + public IReadOnlyList ExistingForks { get; set; } public bool IsLoading { get; set; } @@ -41,9 +41,9 @@ public ForkRepositorySelectViewModelDesigner() public ReactiveCommand SelectedAccount => null; - public ReactiveCommand SwitchOrigin => null; + public ReactiveCommand SwitchOrigin => null; - public Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection) + public Task InitializeAsync(LocalRepositoryModel repository, IConnection connection) { return Task.CompletedTask; } diff --git a/src/GitHub.App/SampleData/ForkRepositorySwitchViewModelDesigner.cs b/src/GitHub.App/SampleData/ForkRepositorySwitchViewModelDesigner.cs index 7d895dbb31..2fa298fa05 100644 --- a/src/GitHub.App/SampleData/ForkRepositorySwitchViewModelDesigner.cs +++ b/src/GitHub.App/SampleData/ForkRepositorySwitchViewModelDesigner.cs @@ -29,9 +29,9 @@ public ForkRepositorySwitchViewModelDesigner() public IObservable Done => null; - public IRepositoryModel SourceRepository { get; } + public RepositoryModel SourceRepository { get; } - public IRepositoryModel DestinationRepository { get; } + public RepositoryModel DestinationRepository { get; } public ReactiveCommand SwitchFork => null; @@ -41,7 +41,7 @@ public ForkRepositorySwitchViewModelDesigner() public bool UpdateOrigin { get; set; } = true; - public void Initialize(ILocalRepositoryModel sourceRepository, IRemoteRepositoryModel remoteRepository) + public void Initialize(LocalRepositoryModel sourceRepository, RemoteRepositoryModel remoteRepository) { } } diff --git a/src/GitHub.App/SampleData/GitServiceDesigner.cs b/src/GitHub.App/SampleData/GitServiceDesigner.cs index de81803439..cabbf30a4c 100644 --- a/src/GitHub.App/SampleData/GitServiceDesigner.cs +++ b/src/GitHub.App/SampleData/GitServiceDesigner.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using GitHub.Models; using GitHub.Primitives; using GitHub.Services; using LibGit2Sharp; @@ -7,6 +8,8 @@ namespace GitHub.SampleData { class GitServiceDesigner : IGitService { + public LocalRepositoryModel CreateLocalRepositoryModel(string localPath) => null; + public BranchModel GetBranch(LocalRepositoryModel model) => null; public Task GetLatestPushedSha(string path, string remote = "origin") => Task.FromResult(null); public UriString GetRemoteUri(IRepository repo, string remote = "origin") => null; public IRepository GetRepository(string path) => null; diff --git a/src/GitHub.App/SampleData/LocalRepositoryModelDesigner.cs b/src/GitHub.App/SampleData/LocalRepositoryModelDesigner.cs index dbcac88908..1a1b4f7189 100644 --- a/src/GitHub.App/SampleData/LocalRepositoryModelDesigner.cs +++ b/src/GitHub.App/SampleData/LocalRepositoryModelDesigner.cs @@ -1,39 +1,16 @@ -using System; -using System.ComponentModel; -using System.Threading.Tasks; -using GitHub.Models; +using GitHub.Models; using GitHub.Primitives; using GitHub.UI; -using GitHub.Exports; namespace GitHub.App.SampleData { - public class LocalRepositoryModelDesigner : ILocalRepositoryModel + public class LocalRepositoryModelDesigner : LocalRepositoryModel { - public UriString CloneUrl { get; set; } - public IBranch CurrentBranch { get; set; } - public Octicon Icon { get; set; } - public string LocalPath { get; set; } - public string Name { get; set; } - public string Owner { get; set; } - -#pragma warning disable CS0067 - public event PropertyChangedEventHandler PropertyChanged; -#pragma warning restore CS0067 - - public Task GenerateUrl(LinkType linkType, string path = null, int startLine = -1, int endLine = -1) - { - throw new NotImplementedException(); - } - - public void Refresh() - { - throw new NotImplementedException(); - } - - public void SetIcon(bool isPrivate, bool isFork) - { - throw new NotImplementedException(); - } + public new UriString CloneUrl { get; set; } + public BranchModel CurrentBranch { get; set; } + public new Octicon Icon { get; set; } + public new string LocalPath { get; set; } + public new string Name { get; set; } + public new string Owner { get; set; } } } diff --git a/src/GitHub.App/SampleData/PullRequestCreationViewModelDesigner.cs b/src/GitHub.App/SampleData/PullRequestCreationViewModelDesigner.cs index 99f05b86f9..32922438d8 100644 --- a/src/GitHub.App/SampleData/PullRequestCreationViewModelDesigner.cs +++ b/src/GitHub.App/SampleData/PullRequestCreationViewModelDesigner.cs @@ -15,15 +15,21 @@ public class PullRequestCreationViewModelDesigner : PanePageViewModelBase, IPull { public PullRequestCreationViewModelDesigner() { - Branches = new List + var repositoryModel = new LocalRepositoryModel { - new BranchModel("master", new LocalRepositoryModel("http://github.com/user/repo", new GitServiceDesigner())), - new BranchModel("don/stub-ui", new LocalRepositoryModel("http://github.com/user/repo", new GitServiceDesigner())), - new BranchModel("feature/pr/views", new LocalRepositoryModel("http://github.com/user/repo", new GitServiceDesigner())), - new BranchModel("release-1.0.17.0", new LocalRepositoryModel("http://github.com/user/repo", new GitServiceDesigner())), + Name = "repo", + CloneUrl = "http://github.com/user/repo" + }; + + Branches = new List + { + new BranchModel("master", repositoryModel), + new BranchModel("don/stub-ui", repositoryModel), + new BranchModel("feature/pr/views", repositoryModel), + new BranchModel("release-1.0.17.0", repositoryModel), }.AsReadOnly(); - TargetBranch = new BranchModel("master", new LocalRepositoryModel("http://github.com/user/repo", new GitServiceDesigner())); + TargetBranch = new BranchModel("master", repositoryModel); SourceBranch = Branches[2]; SelectedAssignee = "Haacked (Phil Haack)"; @@ -34,9 +40,9 @@ public PullRequestCreationViewModelDesigner() }; } - public IBranch SourceBranch { get; set; } - public IBranch TargetBranch { get; set; } - public IReadOnlyList Branches { get; set; } + public BranchModel SourceBranch { get; set; } + public BranchModel TargetBranch { get; set; } + public IReadOnlyList Branches { get; set; } public string SelectedAssignee { get; set; } public List Users { get; set; } @@ -50,6 +56,6 @@ public PullRequestCreationViewModelDesigner() public ReactivePropertyValidator BranchValidator { get; } - public Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection) => Task.CompletedTask; + public Task InitializeAsync(LocalRepositoryModel repository, IConnection connection) => Task.CompletedTask; } } \ No newline at end of file diff --git a/src/GitHub.App/SampleData/PullRequestDetailViewModelDesigner.cs b/src/GitHub.App/SampleData/PullRequestDetailViewModelDesigner.cs index 6d8310a773..0bb87d5d37 100644 --- a/src/GitHub.App/SampleData/PullRequestDetailViewModelDesigner.cs +++ b/src/GitHub.App/SampleData/PullRequestDetailViewModelDesigner.cs @@ -101,7 +101,7 @@ public PullRequestDetailViewModelDesigner() public PullRequestDetailModel Model { get; } public IPullRequestSession Session { get; } - public ILocalRepositoryModel LocalRepository { get; } + public LocalRepositoryModel LocalRepository { get; } public string RemoteRepositoryOwner { get; } public int Number { get; set; } public IActorViewModel Author { get; set; } @@ -127,7 +127,7 @@ public PullRequestDetailViewModelDesigner() public IReadOnlyList Checks { get; } - public Task InitializeAsync(ILocalRepositoryModel localRepository, IConnection connection, string owner, string repo, int number) => Task.CompletedTask; + public Task InitializeAsync(LocalRepositoryModel localRepository, IConnection connection, string owner, string repo, int number) => Task.CompletedTask; public string GetLocalFilePath(IPullRequestFileNode file) { diff --git a/src/GitHub.App/SampleData/PullRequestListViewModelDesigner.cs b/src/GitHub.App/SampleData/PullRequestListViewModelDesigner.cs index 34c90b8525..46b572c5d9 100644 --- a/src/GitHub.App/SampleData/PullRequestListViewModelDesigner.cs +++ b/src/GitHub.App/SampleData/PullRequestListViewModelDesigner.cs @@ -53,10 +53,10 @@ public PullRequestListViewModelDesigner() public IUserFilterViewModel AuthorFilter { get; set; } public IReadOnlyList Items { get; } public ICollectionView ItemsView { get; } - public ILocalRepositoryModel LocalRepository { get; set; } + public LocalRepositoryModel LocalRepository { get; set; } public IssueListMessage Message { get; set; } - public IRepositoryModel RemoteRepository { get; set; } - public IReadOnlyList Forks { get; } + public RepositoryModel RemoteRepository { get; set; } + public IReadOnlyList Forks { get; } public string SearchQuery { get; set; } public string SelectedState { get; set; } public string StateCaption { get; set; } @@ -66,6 +66,6 @@ public PullRequestListViewModelDesigner() public ReactiveCommand OpenItem { get; } public ReactiveCommand OpenItemInBrowser { get; } - public Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection) => Task.CompletedTask; + public Task InitializeAsync(LocalRepositoryModel repository, IConnection connection) => Task.CompletedTask; } } \ No newline at end of file diff --git a/src/GitHub.App/SampleData/PullRequestReviewAuthoringViewModelDesigner.cs b/src/GitHub.App/SampleData/PullRequestReviewAuthoringViewModelDesigner.cs index 9082f69785..cfc8eb23e4 100644 --- a/src/GitHub.App/SampleData/PullRequestReviewAuthoringViewModelDesigner.cs +++ b/src/GitHub.App/SampleData/PullRequestReviewAuthoringViewModelDesigner.cs @@ -43,7 +43,7 @@ public PullRequestReviewAuthoringViewModelDesigner() public bool CanApproveRequestChanges { get; set; } public IReadOnlyList FileComments { get; } public IPullRequestFilesViewModel Files { get; } - public ILocalRepositoryModel LocalRepository { get; set; } + public LocalRepositoryModel LocalRepository { get; set; } public PullRequestReviewModel Model { get; set; } public ReactiveCommand NavigateToPullRequest { get; } public string OperationError { get; set; } @@ -55,7 +55,7 @@ public PullRequestReviewAuthoringViewModelDesigner() public ReactiveCommand Cancel { get; } public Task InitializeAsync( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, IConnection connection, string owner, string repo, diff --git a/src/GitHub.App/SampleData/PullRequestReviewViewModelDesigner.cs b/src/GitHub.App/SampleData/PullRequestReviewViewModelDesigner.cs index 2df22707dc..ca4ce82404 100644 --- a/src/GitHub.App/SampleData/PullRequestReviewViewModelDesigner.cs +++ b/src/GitHub.App/SampleData/PullRequestReviewViewModelDesigner.cs @@ -64,7 +64,7 @@ public PullRequestReviewViewModelDesigner() public IReadOnlyList FileComments { get; set; } public bool IsExpanded { get; set; } public bool HasDetails { get; set; } - public ILocalRepositoryModel LocalRepository { get; set; } + public LocalRepositoryModel LocalRepository { get; set; } public PullRequestReviewModel Model { get; set; } public ReactiveCommand NavigateToPullRequest { get; } public IReadOnlyList OutdatedFileComments { get; set; } diff --git a/src/GitHub.App/SampleData/PullRequestUserReviewsViewModelDesigner.cs b/src/GitHub.App/SampleData/PullRequestUserReviewsViewModelDesigner.cs index 0575871e59..a1aec8c4c3 100644 --- a/src/GitHub.App/SampleData/PullRequestUserReviewsViewModelDesigner.cs +++ b/src/GitHub.App/SampleData/PullRequestUserReviewsViewModelDesigner.cs @@ -63,7 +63,7 @@ public PullRequestUserReviewsViewModelDesigner() }; } - public ILocalRepositoryModel LocalRepository { get; set; } + public LocalRepositoryModel LocalRepository { get; set; } public string RemoteRepositoryOwner { get; set; } public int PullRequestNumber { get; set; } public IActorViewModel User { get; set; } @@ -71,7 +71,7 @@ public PullRequestUserReviewsViewModelDesigner() public string PullRequestTitle { get; set; } public ReactiveCommand NavigateToPullRequest { get; } - public Task InitializeAsync(ILocalRepositoryModel localRepository, IConnection connection, string owner, string repo, int pullRequestNumber, string login) + public Task InitializeAsync(LocalRepositoryModel localRepository, IConnection connection, string owner, string repo, int pullRequestNumber, string login) { return Task.CompletedTask; } diff --git a/src/GitHub.App/SampleData/RemoteRepositoryModelDesigner.cs b/src/GitHub.App/SampleData/RemoteRepositoryModelDesigner.cs index d9179f1cb5..a87fe8d8db 100644 --- a/src/GitHub.App/SampleData/RemoteRepositoryModelDesigner.cs +++ b/src/GitHub.App/SampleData/RemoteRepositoryModelDesigner.cs @@ -6,37 +6,18 @@ namespace GitHub.SampleData { [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1036:OverrideMethodsOnComparableTypes")] - public class RemoteRepositoryModelDesigner : IRemoteRepositoryModel + public class RemoteRepositoryModelDesigner : RemoteRepositoryModel { - public UriString CloneUrl { get; set; } - public DateTimeOffset CreatedAt { get; set; } - public IBranch DefaultBranch { get; set; } - public Octicon Icon { get; set; } - public long Id { get; set; } - public bool IsFork { get; set; } - public string Name { get; set; } - public string Owner { get; set; } - public IAccount OwnerAccount { get; set; } - public IRemoteRepositoryModel Parent { get; set; } - public DateTimeOffset UpdatedAt { get; set; } - - public int CompareTo(IRemoteRepositoryModel other) - { - return 0; - } - - public void CopyFrom(IRemoteRepositoryModel other) - { - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public bool Equals(IRemoteRepositoryModel other) - { - return false; - } - - public void SetIcon(bool isPrivate, bool isFork) - { - } + public new UriString CloneUrl { get; set; } + public new DateTimeOffset CreatedAt { get; set; } + public new BranchModel DefaultBranch { get; set; } + public new Octicon Icon { get; set; } + public new long Id { get; set; } + public new bool IsFork { get; set; } + public new string Name { get; set; } + public new string Owner { get; set; } + public new IAccount OwnerAccount { get; set; } + public new RemoteRepositoryModel Parent { get; set; } + public new DateTimeOffset UpdatedAt { get; set; } } } diff --git a/src/GitHub.App/SampleData/RepositoryRecloneViewModelDesigner.cs b/src/GitHub.App/SampleData/RepositoryRecloneViewModelDesigner.cs index 477ab7cb7b..274f58d9c1 100644 --- a/src/GitHub.App/SampleData/RepositoryRecloneViewModelDesigner.cs +++ b/src/GitHub.App/SampleData/RepositoryRecloneViewModelDesigner.cs @@ -17,7 +17,7 @@ public class RepositoryRecloneViewModelDesigner : ViewModelBase, IRepositoryRecl public ReactivePropertyValidator BaseRepositoryPathValidator { get; } public ICommand BrowseForDirectory { get; } public ReactiveCommand CloneCommand { get; } - public IRepositoryModel SelectedRepository { get; set; } + public RepositoryModel SelectedRepository { get; set; } public IObservable Done { get; } public Task InitializeAsync(IConnection connection) => Task.CompletedTask; diff --git a/src/GitHub.App/SampleData/SampleViewModels.cs b/src/GitHub.App/SampleData/SampleViewModels.cs index 552392c2de..9a2a430e9f 100644 --- a/src/GitHub.App/SampleData/SampleViewModels.cs +++ b/src/GitHub.App/SampleData/SampleViewModels.cs @@ -254,7 +254,7 @@ public IConnection SelectedConnection [ExcludeFromCodeCoverage] public static class RepositoryModelDesigner { - public static IRemoteRepositoryModel Create(string name = null, string owner = null) + public static RemoteRepositoryModel Create(string name = null, string owner = null) { name = name ?? "octocat"; owner = owner ?? "github"; diff --git a/src/GitHub.App/Services/DialogService.cs b/src/GitHub.App/Services/DialogService.cs index a96d532893..ecce4a1cfa 100644 --- a/src/GitHub.App/Services/DialogService.cs +++ b/src/GitHub.App/Services/DialogService.cs @@ -52,7 +52,7 @@ public async Task ShowCloneDialog(IConnection connection, str } } - public async Task ShowReCloneDialog(IRepositoryModel repository) + public async Task ShowReCloneDialog(RepositoryModel repository) { Guard.ArgumentNotNull(repository, nameof(repository)); @@ -91,7 +91,7 @@ public async Task ShowLoginDialog() return (IConnection)await showDialog.Show(viewModel); } - public async Task ShowForkDialog(ILocalRepositoryModel repository, IConnection connection) + public async Task ShowForkDialog(LocalRepositoryModel repository, IConnection connection) { Guard.ArgumentNotNull(repository, nameof(repository)); Guard.ArgumentNotNull(connection, nameof(connection)); diff --git a/src/GitHub.App/Services/ModelService.cs b/src/GitHub.App/Services/ModelService.cs index e35169d29c..79fb0d0b6c 100644 --- a/src/GitHub.App/Services/ModelService.cs +++ b/src/GitHub.App/Services/ModelService.cs @@ -104,10 +104,25 @@ public IObservable> GetAccounts() .ToReadOnlyList(Create); } - public IObservable GetForks(IRepositoryModel repository) + public IObservable GetForks(RepositoryModel repository) { return ApiClient.GetForks(repository.Owner, repository.Name) - .Select(x => new RemoteRepositoryModel(x)); + .Select(x => CreateRemoteRepositoryModel(x)); + } + + static RemoteRepositoryModel CreateRemoteRepositoryModel(Repository repository) + { + var ownerAccount = new Models.Account(repository.Owner); + var parent = repository.Parent != null ? CreateRemoteRepositoryModel(repository.Parent) : null; + var model = new RemoteRepositoryModel(repository.Id, repository.Name, repository.CloneUrl, + repository.Private, repository.Fork, ownerAccount, parent, repository.DefaultBranch); + + if (parent != null) + { + parent.DefaultBranch.DisplayName = parent.DefaultBranch.Id; + } + + return model; } IObservable GetLicensesFromApi() @@ -156,7 +171,7 @@ IObservable> GetUserOrganizations() }); } - public IObservable> GetRepositories() + public IObservable> GetRepositories() { return GetUserRepositories(RepositoryType.Owner) .TakeLast(1) @@ -175,7 +190,7 @@ IObservable GetUserFromCache() /// /// /// - public ITrackingCollection GetPullRequests(IRepositoryModel repo, + public ITrackingCollection GetPullRequests(RepositoryModel repo, ITrackingCollection collection) { // Since the api to list pull requests returns all the data for each pr, cache each pr in its own entry @@ -215,7 +230,7 @@ public IObservable GetPullRequest(string owner, string name, throw new NotImplementedException(); } - public IObservable GetRepository(string owner, string repo) + public IObservable GetRepository(string owner, string repo) { var keyobs = GetUserFromCache() .Select(user => string.Format(CultureInfo.InvariantCulture, "{0}|{1}|{2}/{3}", CacheIndex.RepoPrefix, user.Login, owner, repo)); @@ -228,7 +243,7 @@ public IObservable GetRepository(string owner, string re .Select(Create))); } - public ITrackingCollection GetRepositories(ITrackingCollection collection) + public ITrackingCollection GetRepositories(ITrackingCollection collection) { var keyobs = GetUserFromCache() .Select(user => string.Format(CultureInfo.InvariantCulture, "{0}|{1}", CacheIndex.RepoPrefix, user.Login)); @@ -256,8 +271,8 @@ public ITrackingCollection GetRepositories(ITrackingColl return collection; } - public IObservable CreatePullRequest(ILocalRepositoryModel sourceRepository, IRepositoryModel targetRepository, - IBranch sourceBranch, IBranch targetBranch, + public IObservable CreatePullRequest(LocalRepositoryModel sourceRepository, RepositoryModel targetRepository, + BranchModel sourceBranch, BranchModel targetBranch, string title, string body) { var keyobs = GetUserFromCache() @@ -270,7 +285,7 @@ public IObservable CreatePullRequest(ILocalRepositoryModel so new NewPullRequest(title, string.Format(CultureInfo.InvariantCulture, "{0}:{1}", sourceRepository.Owner, sourceBranch.Name), targetBranch.Name) - { Body = body }, + { Body = body }, targetRepository.Owner, targetRepository.Name) .Select(PullRequestCacheItem.Create) @@ -286,7 +301,7 @@ public IObservable InvalidateAll() return hostCache.InvalidateAll().ContinueAfter(() => hostCache.Vacuum()); } - public IObservable GetFileContents(IRepositoryModel repo, string commitSha, string path, string fileSha) + public IObservable GetFileContents(RepositoryModel repo, string commitSha, string path, string fileSha) { return Observable.Defer(() => Task.Run(async () => { @@ -305,7 +320,7 @@ public IObservable GetFileContents(IRepositoryModel repo, string commitS })); } - IObservable> GetUserRepositories(RepositoryType repositoryType) + IObservable> GetUserRepositories(RepositoryType repositoryType) { return Observable.Defer(() => GetUserFromCache().SelectMany(user => hostCache.GetAndRefreshObject(string.Format(CultureInfo.InvariantCulture, "{0}|{1}:repos", user.Login, repositoryType), @@ -313,14 +328,14 @@ IObservable> GetUserRepositories(Repositor TimeSpan.FromMinutes(2), TimeSpan.FromDays(7))) .ToReadOnlyList(Create)) - .Catch, KeyNotFoundException>( + .Catch, KeyNotFoundException>( // This could in theory happen if we try to call this before the user is logged in. e => { log.Error(e, "Retrieving {RepositoryType} user repositories failed because user is not stored in the cache", repositoryType); - return Observable.Return(Array.Empty()); + return Observable.Return(Array.Empty()); }); } @@ -333,14 +348,14 @@ IObservable> GetUserRepositoriesFromApi(Reposit .Catch, Exception>(_ => Observable.Return(Enumerable.Empty())); } - IObservable> GetAllRepositoriesForAllOrganizations() + IObservable> GetAllRepositoriesForAllOrganizations() { return GetUserOrganizations() .SelectMany(org => org.ToObservable()) .SelectMany(org => GetOrganizationRepositories(org.Login).TakeLast(1)); } - IObservable> GetOrganizationRepositories(string organization) + IObservable> GetOrganizationRepositories(string organization) { return Observable.Defer(() => GetUserFromCache().SelectMany(user => hostCache.GetAndRefreshObject(string.Format(CultureInfo.InvariantCulture, "{0}|{1}|repos", user.Login, organization), @@ -349,24 +364,24 @@ IObservable> GetOrganizationRepositories(s TimeSpan.FromMinutes(2), TimeSpan.FromDays(7))) .ToReadOnlyList(Create)) - .Catch, KeyNotFoundException>( + .Catch, KeyNotFoundException>( // This could in theory happen if we try to call this before the user is logged in. e => { log.Error(e, "Retrieveing {Organization} org repositories failed because user is not stored in the cache", organization); - return Observable.Return(Array.Empty()); + return Observable.Return(Array.Empty()); }); } - public IObservable GetBranches(IRepositoryModel repo) + public IObservable GetBranches(RepositoryModel repo) { var keyobs = GetUserFromCache() .Select(user => string.Format(CultureInfo.InvariantCulture, "{0}|{1}|branch", user.Login, repo.Name)); return Observable.Defer(() => keyobs .SelectMany(key => ApiClient.GetBranches(repo.CloneUrl.Owner, repo.CloneUrl.RepositoryName))) - .Select(x => new BranchModel(x, repo)); + .Select(x => new BranchModel(x.Name, repo)); } static GitIgnoreItem Create(GitIgnoreCacheItem item) @@ -403,7 +418,7 @@ IAccount Create(string login, string avatarUrl) avatarProvider.GetAvatar(avatarUrl)); } - IRemoteRepositoryModel Create(RepositoryCacheItem item) + RemoteRepositoryModel Create(RepositoryCacheItem item) { return new RemoteRepositoryModel( item.Id, @@ -452,7 +467,7 @@ public IObservable InsertUser(AccountCacheItem user) } protected virtual void Dispose(bool disposing) - {} + { } public void Dispose() { @@ -493,7 +508,7 @@ public static RepositoryCacheItem Create(Repository apiRepository) return new RepositoryCacheItem(apiRepository); } - public RepositoryCacheItem() {} + public RepositoryCacheItem() { } public RepositoryCacheItem(Repository apiRepository) { @@ -529,7 +544,7 @@ public static PullRequestCacheItem Create(PullRequest pr) return new PullRequestCacheItem(pr); } - public PullRequestCacheItem() {} + public PullRequestCacheItem() { } public PullRequestCacheItem(PullRequest pr) { @@ -563,7 +578,7 @@ public PullRequestCacheItem(PullRequest pr) Timestamp = UpdatedAt; } - public string Title {get; set; } + public string Title { get; set; } public int Number { get; set; } public GitReferenceCacheItem Base { get; set; } public GitReferenceCacheItem Head { get; set; } @@ -574,7 +589,7 @@ public PullRequestCacheItem(PullRequest pr) public DateTimeOffset CreatedAt { get; set; } public DateTimeOffset UpdatedAt { get; set; } public string Body { get; set; } - + // Nullable for compatibility with old caches. public PullRequestStateEnum? State { get; set; } diff --git a/src/GitHub.App/Services/PullRequestEditorService.cs b/src/GitHub.App/Services/PullRequestEditorService.cs index 3135c29730..aaf44c50ad 100644 --- a/src/GitHub.App/Services/PullRequestEditorService.cs +++ b/src/GitHub.App/Services/PullRequestEditorService.cs @@ -472,7 +472,7 @@ public int FindNearestMatchingLine(IList fromLines, IList toLine return matchingLine; } - static string GetAbsolutePath(ILocalRepositoryModel localRepository, string relativePath) + static string GetAbsolutePath(LocalRepositoryModel localRepository, string relativePath) { var localPath = localRepository.LocalPath; relativePath = relativePath.Replace('/', Path.DirectorySeparatorChar); diff --git a/src/GitHub.App/Services/PullRequestService.cs b/src/GitHub.App/Services/PullRequestService.cs index 9b8015cb43..9e141857fa 100644 --- a/src/GitHub.App/Services/PullRequestService.cs +++ b/src/GitHub.App/Services/PullRequestService.cs @@ -231,8 +231,8 @@ public async Task> ReadPullRequests( if (hasCheckRuns) { checksHasFailure = checkRuns - .Any(model => model.Conclusion.HasValue - && (model.Conclusion.Value == CheckConclusionState.Failure + .Any(model => model.Conclusion.HasValue + && (model.Conclusion.Value == CheckConclusionState.Failure || model.Conclusion.Value == CheckConclusionState.ActionRequired)); if (!checksHasFailure) @@ -317,8 +317,8 @@ public async Task> ReadAssignableUsers( } public IObservable CreatePullRequest(IModelService modelService, - ILocalRepositoryModel sourceRepository, IRepositoryModel targetRepository, - IBranch sourceBranch, IBranch targetBranch, + LocalRepositoryModel sourceRepository, RepositoryModel targetRepository, + BranchModel sourceBranch, BranchModel targetBranch, string title, string body ) { @@ -333,7 +333,7 @@ public IObservable CreatePullRequest(IModelService modelServi return PushAndCreatePR(modelService, sourceRepository, targetRepository, sourceBranch, targetBranch, title, body).ToObservable(); } - public IObservable GetPullRequestTemplate(ILocalRepositoryModel repository) + public IObservable GetPullRequestTemplate(LocalRepositoryModel repository) { Extensions.Guard.ArgumentNotNull(repository, nameof(repository)); @@ -353,7 +353,7 @@ public IObservable GetPullRequestTemplate(ILocalRepositoryModel reposito } public IObservable> GetMessagesForUniqueCommits( - ILocalRepositoryModel repository, + LocalRepositoryModel repository, string baseBranch, string compareBranch, int maxCommits) @@ -369,7 +369,7 @@ public IObservable> GetMessagesForUniqueCommits( }); } - public IObservable CountSubmodulesToSync(ILocalRepositoryModel repository) + public IObservable CountSubmodulesToSync(LocalRepositoryModel repository) { using (var repo = gitService.GetRepository(repository.LocalPath)) { @@ -399,7 +399,7 @@ public IObservable CountSubmodulesToSync(ILocalRepositoryModel repository) } } - public IObservable IsWorkingDirectoryClean(ILocalRepositoryModel repository) + public IObservable IsWorkingDirectoryClean(LocalRepositoryModel repository) { // The `using` appears to resolve this issue: // https://github.com/github/VisualStudio/issues/1306 @@ -440,7 +440,7 @@ static bool IsCheckoutBlockingChange(StatusEntry entry) } } - public IObservable Pull(ILocalRepositoryModel repository) + public IObservable Pull(LocalRepositoryModel repository) { return Observable.Defer(async () => { @@ -452,7 +452,7 @@ public IObservable Pull(ILocalRepositoryModel repository) }); } - public IObservable Push(ILocalRepositoryModel repository) + public IObservable Push(LocalRepositoryModel repository) { return Observable.Defer(async () => { @@ -466,7 +466,7 @@ public IObservable Push(ILocalRepositoryModel repository) }); } - public async Task SyncSubmodules(ILocalRepositoryModel repository, Action progress) + public async Task SyncSubmodules(LocalRepositoryModel repository, Action progress) { var exitCode = await Where("git"); if (exitCode != 0) @@ -529,7 +529,7 @@ static async Task ReadLinesAsync(TextReader reader, Action progress) } } - public IObservable Checkout(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest, string localBranchName) + public IObservable Checkout(LocalRepositoryModel repository, PullRequestDetailModel pullRequest, string localBranchName) { return Observable.Defer(async () => { @@ -567,7 +567,7 @@ public IObservable Checkout(ILocalRepositoryModel repository, PullRequestD }); } - public IObservable GetDefaultLocalBranchName(ILocalRepositoryModel repository, int pullRequestNumber, string pullRequestTitle) + public IObservable GetDefaultLocalBranchName(LocalRepositoryModel repository, int pullRequestNumber, string pullRequestTitle) { return Observable.Defer(() => { @@ -587,7 +587,7 @@ public IObservable GetDefaultLocalBranchName(ILocalRepositoryModel repos }); } - public IObservable CalculateHistoryDivergence(ILocalRepositoryModel repository, int pullRequestNumber) + public IObservable CalculateHistoryDivergence(LocalRepositoryModel repository, int pullRequestNumber) { return Observable.Defer(async () => { @@ -606,7 +606,7 @@ public IObservable CalculateHistoryDivergence(ILocalRepos }); } - public async Task GetMergeBase(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest) + public async Task GetMergeBase(LocalRepositoryModel repository, PullRequestDetailModel pullRequest) { using (var repo = gitService.GetRepository(repository.LocalPath)) { @@ -620,7 +620,7 @@ public async Task GetMergeBase(ILocalRepositoryModel repository, PullReq } } - public IObservable GetTreeChanges(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest) + public IObservable GetTreeChanges(LocalRepositoryModel repository, PullRequestDetailModel pullRequest) { return Observable.Defer(async () => { @@ -635,7 +635,7 @@ public IObservable GetTreeChanges(ILocalRepositoryModel repository, }); } - public IObservable GetLocalBranches(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest) + public IObservable GetLocalBranches(LocalRepositoryModel repository, PullRequestDetailModel pullRequest) { return Observable.Defer(() => { @@ -648,7 +648,7 @@ public IObservable GetLocalBranches(ILocalRepositoryModel repository, P }); } - public IObservable EnsureLocalBranchesAreMarkedAsPullRequests(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest) + public IObservable EnsureLocalBranchesAreMarkedAsPullRequests(LocalRepositoryModel repository, PullRequestDetailModel pullRequest) { return Observable.Defer(async () => { @@ -671,12 +671,12 @@ public IObservable EnsureLocalBranchesAreMarkedAsPullRequests(ILocalReposi }); } - public bool IsPullRequestFromRepository(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest) + public bool IsPullRequestFromRepository(LocalRepositoryModel repository, PullRequestDetailModel pullRequest) { return string.Equals(repository.CloneUrl?.Owner, pullRequest.HeadRepositoryOwner, StringComparison.OrdinalIgnoreCase); } - public IObservable SwitchToBranch(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest) + public IObservable SwitchToBranch(LocalRepositoryModel repository, PullRequestDetailModel pullRequest) { return Observable.Defer(async () => { @@ -718,7 +718,7 @@ public IObservable SwitchToBranch(ILocalRepositoryModel repository, PullRe }); } - public IObservable> GetPullRequestForCurrentBranch(ILocalRepositoryModel repository) + public IObservable<(string owner, int number)> GetPullRequestForCurrentBranch(LocalRepositoryModel repository) { return Observable.Defer(async () => { @@ -736,7 +736,7 @@ public IObservable> GetPullRequestForCurrentBranch(ILocalRepo } public async Task ExtractToTempFile( - ILocalRepositoryModel repository, + LocalRepositoryModel repository, PullRequestDetailModel pullRequest, string relativePath, string commitSha, @@ -756,7 +756,7 @@ public async Task ExtractToTempFile( return tempFilePath; } - public Encoding GetEncoding(ILocalRepositoryModel repository, string relativePath) + public Encoding GetEncoding(LocalRepositoryModel repository, string relativePath) { var fullPath = Path.Combine(repository.LocalPath, relativePath); @@ -788,7 +788,7 @@ static bool HasPreamble(string file, Encoding encoding) return true; } - public IObservable RemoveUnusedRemotes(ILocalRepositoryModel repository) + public IObservable RemoveUnusedRemotes(LocalRepositoryModel repository) { return Observable.Defer(async () => { @@ -889,7 +889,7 @@ async Task ExtractToTempFile( } IEnumerable GetLocalBranchesInternal( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, IRepository repository, PullRequestDetailModel pullRequest) { @@ -912,7 +912,7 @@ async Task IsBranchMarkedAsPullRequest(IRepository repo, string branchName { var prConfigKey = $"branch.{branchName}.{SettingGHfVSPullRequest}"; var value = ParseGHfVSConfigKeyValue(await gitClient.GetConfig(repo, prConfigKey)); - return value != null && + return value != default && value.Item1 == pullRequest.BaseRepositoryOwner && value.Item2 == pullRequest.Number; } @@ -925,8 +925,8 @@ async Task MarkBranchAsPullRequest(IRepository repo, string branchName, string o } async Task PushAndCreatePR(IModelService modelService, - ILocalRepositoryModel sourceRepository, IRepositoryModel targetRepository, - IBranch sourceBranch, IBranch targetBranch, + LocalRepositoryModel sourceRepository, RepositoryModel targetRepository, + BranchModel sourceBranch, BranchModel targetBranch, string title, string body) { // PullRequestModel doesn't keep a reference to repo @@ -983,7 +983,7 @@ static string BuildGHfVSConfigKeyValue(string owner, int number) return owner + '#' + number.ToString(CultureInfo.InvariantCulture); } - static Tuple ParseGHfVSConfigKeyValue(string value) + static (string owner, int number) ParseGHfVSConfigKeyValue(string value) { if (value != null) { @@ -996,12 +996,12 @@ static Tuple ParseGHfVSConfigKeyValue(string value) if (int.TryParse(value.Substring(separator + 1), NumberStyles.None, CultureInfo.InvariantCulture, out number)) { - return Tuple.Create(owner, number); + return (owner, number); } } } - return null; + return default; } class ListItemAdapter : PullRequestListItemModel diff --git a/src/GitHub.App/Services/RepositoryForkService.cs b/src/GitHub.App/Services/RepositoryForkService.cs index f272ca4155..b6f1a00ab5 100644 --- a/src/GitHub.App/Services/RepositoryForkService.cs +++ b/src/GitHub.App/Services/RepositoryForkService.cs @@ -37,7 +37,7 @@ public RepositoryForkService(IGitClient gitClient, IVSGitServices vsGitServices, this.usageTracker = usageTracker; } - public IObservable ForkRepository(IApiClient apiClient, IRepositoryModel sourceRepository, NewRepositoryFork repositoryFork, bool updateOrigin, bool addUpstream, bool trackMasterUpstream) + public IObservable ForkRepository(IApiClient apiClient, RepositoryModel sourceRepository, NewRepositoryFork repositoryFork, bool updateOrigin, bool addUpstream, bool trackMasterUpstream) { log.Verbose("ForkRepository Source:{SourceOwner}/{SourceName} To:{DestinationOwner}", sourceRepository.Owner, sourceRepository.Name, repositoryFork.Organization ?? "[Current User]"); log.Verbose("ForkRepository updateOrigin:{UpdateOrigin} addUpstream:{AddUpstream} trackMasterUpstream:{TrackMasterUpstream}", updateOrigin, addUpstream, trackMasterUpstream); @@ -64,7 +64,7 @@ public IObservable ForkRepository(IApiClient apiClient, IRepositoryM }); } - public IObservable SwitchRemotes(IRepositoryModel destinationRepository, bool updateOrigin, bool addUpstream, bool trackMasterUpstream) + public IObservable SwitchRemotes(RepositoryModel destinationRepository, bool updateOrigin, bool addUpstream, bool trackMasterUpstream) { return Observable.Defer(() => Observable.Return(new object()) .ObserveOn(RxApp.MainThreadScheduler) diff --git a/src/GitHub.App/Services/TeamExplorerContext.cs b/src/GitHub.App/Services/TeamExplorerContext.cs index 6d443e4dfc..3f3b1caf7e 100644 --- a/src/GitHub.App/Services/TeamExplorerContext.cs +++ b/src/GitHub.App/Services/TeamExplorerContext.cs @@ -40,12 +40,9 @@ public class TeamExplorerContext : ITeamExplorerContext string solutionPath; string repositoryPath; UriString cloneUrl; - string branchName; - string headSha; - string trackedSha; - Tuple pullRequest; + (string owner, int number) pullRequest; - ILocalRepositoryModel repositoryModel; + LocalRepositoryModel repositoryModel; JoinableTask refreshJoinableTask; [ImportingConstructor] @@ -113,10 +110,7 @@ async Task RefreshAsync() { var newRepositoryPath = repo?.LocalPath; var newCloneUrl = repo?.CloneUrl; - var newBranchName = repo?.CurrentBranch?.Name; - var newHeadSha = repo?.CurrentBranch?.Sha; - var newTrackedSha = repo?.CurrentBranch?.TrackedSha; - var newPullRequest = repo != null ? await pullRequestService.GetPullRequestForCurrentBranch(repo) : null; + var newPullRequest = repo != null ? await pullRequestService.GetPullRequestForCurrentBranch(repo) : default; if (newRepositoryPath != repositoryPath) { @@ -128,33 +122,21 @@ async Task RefreshAsync() log.Debug("ActiveRepository changed to {CloneUrl} @ {Path}", repo?.CloneUrl, newRepositoryPath); ActiveRepository = repo; } - else if (newBranchName != branchName) - { - log.Debug("Fire StatusChanged event when BranchName changes for ActiveRepository"); - StatusChanged?.Invoke(this, EventArgs.Empty); - } - else if (newHeadSha != headSha) - { - log.Debug("Fire StatusChanged event when HeadSha changes for ActiveRepository"); - StatusChanged?.Invoke(this, EventArgs.Empty); - } - else if (newTrackedSha != trackedSha) + else if (newPullRequest != pullRequest) { - log.Debug("Fire StatusChanged event when TrackedSha changes for ActiveRepository"); + log.Debug("Fire StatusChanged event when PullRequest changes for ActiveRepository"); StatusChanged?.Invoke(this, EventArgs.Empty); } - else if (newPullRequest != pullRequest) + else if (newRepositoryPath != null) { - log.Debug("Fire StatusChanged event when PullRequest changes for ActiveRepository"); + // For example, this will fire when the HEAD commit changes + log.Debug("Fire StatusChanged event if anything about an active repository has changed"); StatusChanged?.Invoke(this, EventArgs.Empty); } repositoryPath = newRepositoryPath; cloneUrl = newCloneUrl; - branchName = newBranchName; - headSha = newHeadSha; solutionPath = newSolutionPath; - trackedSha = newTrackedSha; pullRequest = newPullRequest; } } @@ -174,7 +156,7 @@ async Task GetSolutionPath() /// /// The active repository or null if not in a repository. /// - public ILocalRepositoryModel ActiveRepository + public LocalRepositoryModel ActiveRepository { get { diff --git a/src/GitHub.App/ViewModels/Dialog/Clone/RepositoryCloneViewModel.cs b/src/GitHub.App/ViewModels/Dialog/Clone/RepositoryCloneViewModel.cs index bfc2d3e78f..12c51171e8 100644 --- a/src/GitHub.App/ViewModels/Dialog/Clone/RepositoryCloneViewModel.cs +++ b/src/GitHub.App/ViewModels/Dialog/Clone/RepositoryCloneViewModel.cs @@ -29,7 +29,7 @@ public class RepositoryCloneViewModel : ViewModelBase, IRepositoryCloneViewModel readonly IUsageTracker usageTracker; readonly IReadOnlyList tabs; string path; - IRepositoryModel previousRepository; + RepositoryModel previousRepository; ObservableAsPropertyHelper pathWarning; int selectedTabIndex; @@ -183,7 +183,7 @@ void BrowseForDirectory() } } - void UpdatePath(IRepositoryModel repository) + void UpdatePath(RepositoryModel repository) { if (repository != null) { @@ -239,7 +239,7 @@ string FindDirWithout(string dir, string match, int levels) } } - string ValidatePathWarning(IRepositoryModel repositoryModel, string path) + string ValidatePathWarning(RepositoryModel repositoryModel, string path) { if (repositoryModel != null) { diff --git a/src/GitHub.App/ViewModels/Dialog/Clone/RepositorySelectViewModel.cs b/src/GitHub.App/ViewModels/Dialog/Clone/RepositorySelectViewModel.cs index e48a0b9822..539b5d7f0a 100644 --- a/src/GitHub.App/ViewModels/Dialog/Clone/RepositorySelectViewModel.cs +++ b/src/GitHub.App/ViewModels/Dialog/Clone/RepositorySelectViewModel.cs @@ -30,7 +30,7 @@ public class RepositorySelectViewModel : ViewModelBase, IRepositorySelectViewMod bool loadingStarted; IReadOnlyList items; ICollectionView itemsView; - ObservableAsPropertyHelper repository; + ObservableAsPropertyHelper repository; IRepositoryItemViewModel selectedItem; [ImportingConstructor] @@ -88,7 +88,7 @@ public IRepositoryItemViewModel SelectedItem set => this.RaiseAndSetIfChanged(ref selectedItem, value); } - public IRepositoryModel Repository => repository.Value; + public RepositoryModel Repository => repository.Value; public void Initialize(IConnection connection) { @@ -152,7 +152,7 @@ bool FilterItem(object obj) return true; } - IRepositoryModel CreateRepository(IRepositoryItemViewModel item) + RepositoryModel CreateRepository(IRepositoryItemViewModel item) { return item != null ? new RepositoryModel(item.Name, UriString.ToUriString(item.Url)) : diff --git a/src/GitHub.App/ViewModels/Dialog/Clone/RepositoryUrlViewModel.cs b/src/GitHub.App/ViewModels/Dialog/Clone/RepositoryUrlViewModel.cs index cb4f4abb30..0c2d69caee 100644 --- a/src/GitHub.App/ViewModels/Dialog/Clone/RepositoryUrlViewModel.cs +++ b/src/GitHub.App/ViewModels/Dialog/Clone/RepositoryUrlViewModel.cs @@ -14,7 +14,7 @@ namespace GitHub.App.ViewModels.Dialog.Clone [PartCreationPolicy(CreationPolicy.NonShared)] public class RepositoryUrlViewModel : ViewModelBase, IRepositoryUrlViewModel { - ObservableAsPropertyHelper repository; + ObservableAsPropertyHelper repository; string url; public RepositoryUrlViewModel() @@ -30,11 +30,11 @@ public string Url public bool IsEnabled => true; - public IRepositoryModel Repository => repository.Value; + public RepositoryModel Repository => repository.Value; public Task Activate() => Task.CompletedTask; - IRepositoryModel ParseUrl(string s) + RepositoryModel ParseUrl(string s) { if (s != null) { diff --git a/src/GitHub.App/ViewModels/Dialog/ForkRepositoryExecuteViewModel.cs b/src/GitHub.App/ViewModels/Dialog/ForkRepositoryExecuteViewModel.cs index b1a0804572..9211b06b4c 100644 --- a/src/GitHub.App/ViewModels/Dialog/ForkRepositoryExecuteViewModel.cs +++ b/src/GitHub.App/ViewModels/Dialog/ForkRepositoryExecuteViewModel.cs @@ -49,11 +49,11 @@ IRepositoryForkService repositoryForkService BackCommand = ReactiveCommand.Create(() => { }); } - public IRepositoryModel SourceRepository { get; private set; } + public RepositoryModel SourceRepository { get; private set; } public IAccount DestinationAccount { get; private set; } - public IRepositoryModel DestinationRepository { get; private set; } + public RepositoryModel DestinationRepository { get; private set; } public ReactiveCommand CreateFork { get; } @@ -65,7 +65,7 @@ IRepositoryForkService repositoryForkService public IObservable Back => BackCommand; - public async Task InitializeAsync(ILocalRepositoryModel sourceRepository, IAccount destinationAccount, IConnection connection) + public async Task InitializeAsync(LocalRepositoryModel sourceRepository, IAccount destinationAccount, IConnection connection) { var modelService = await modelServiceFactory.CreateAsync(connection); apiClient = modelService.ApiClient; diff --git a/src/GitHub.App/ViewModels/Dialog/ForkRepositorySelectViewModel.cs b/src/GitHub.App/ViewModels/Dialog/ForkRepositorySelectViewModel.cs index 83c6c33499..fc5a597f47 100644 --- a/src/GitHub.App/ViewModels/Dialog/ForkRepositorySelectViewModel.cs +++ b/src/GitHub.App/ViewModels/Dialog/ForkRepositorySelectViewModel.cs @@ -23,7 +23,7 @@ public class ForkRepositorySelectViewModel : ViewModelBase, IForkRepositorySelec readonly IModelServiceFactory modelServiceFactory; IReadOnlyList accounts; - IReadOnlyList existingForks; + IReadOnlyList existingForks; bool isLoading; [ImportingConstructor] @@ -31,7 +31,7 @@ public ForkRepositorySelectViewModel(IModelServiceFactory modelServiceFactory) { this.modelServiceFactory = modelServiceFactory; SelectedAccount = ReactiveCommand.Create(_ => { }); - SwitchOrigin = ReactiveCommand.Create(_ => { }); + SwitchOrigin = ReactiveCommand.Create(_ => { }); } public string Title => Resources.ForkRepositoryTitle; @@ -42,7 +42,7 @@ public IReadOnlyList Accounts private set { this.RaiseAndSetIfChanged(ref accounts, value); } } - public IReadOnlyList ExistingForks + public IReadOnlyList ExistingForks { get { return existingForks; } private set { this.RaiseAndSetIfChanged(ref existingForks, value); } @@ -56,11 +56,11 @@ public bool IsLoading public ReactiveCommand SelectedAccount { get; } - public ReactiveCommand SwitchOrigin { get; } + public ReactiveCommand SwitchOrigin { get; } public IObservable Done => SelectedAccount.SelectNull(); - public async Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection) + public async Task InitializeAsync(LocalRepositoryModel repository, IConnection connection) { IsLoading = true; @@ -78,7 +78,7 @@ public async Task InitializeAsync(ILocalRepositoryModel repository, IConnection { var forks = x.Forks; - var parents = new List(); + var parents = new List(); var current = x.Respoitory; while (current.Parent != null) { @@ -97,7 +97,7 @@ public async Task InitializeAsync(ILocalRepositoryModel repository, IConnection } } - void BuildAccounts(IReadOnlyList accessibleAccounts, ILocalRepositoryModel currentRepository, IList forks, List parents) + void BuildAccounts(IReadOnlyList accessibleAccounts, LocalRepositoryModel currentRepository, IList forks, List parents) { log.Verbose("BuildAccounts: {AccessibleAccounts} accessibleAccounts, {Forks} forks, {Parents} parents", accessibleAccounts.Count, forks.Count, parents.Count); @@ -105,7 +105,7 @@ void BuildAccounts(IReadOnlyList accessibleAccounts, ILocalRepositoryM var readOnlyList = accessibleAccounts .Where(account => account.Login != currentRepository.Owner) - .Select(account => new {Account = account, Fork = existingForksAndParents.ContainsKey(account.Login) ? existingForksAndParents[account.Login] : null }) + .Select(account => new { Account = account, Fork = existingForksAndParents.ContainsKey(account.Login) ? existingForksAndParents[account.Login] : null }) .ToArray(); Accounts = readOnlyList.Where(arg => arg.Fork == null).Select(arg => arg.Account).ToList(); diff --git a/src/GitHub.App/ViewModels/Dialog/ForkRepositorySwitchViewModel.cs b/src/GitHub.App/ViewModels/Dialog/ForkRepositorySwitchViewModel.cs index cff2a303a6..74777a7a05 100644 --- a/src/GitHub.App/ViewModels/Dialog/ForkRepositorySwitchViewModel.cs +++ b/src/GitHub.App/ViewModels/Dialog/ForkRepositorySwitchViewModel.cs @@ -31,9 +31,9 @@ public ForkRepositorySwitchViewModel(IRepositoryForkService repositoryForkServic SwitchFork = ReactiveCommand.CreateFromObservable(OnSwitchFork); } - public IRepositoryModel SourceRepository { get; private set; } + public RepositoryModel SourceRepository { get; private set; } - public IRepositoryModel DestinationRepository { get; private set; } + public RepositoryModel DestinationRepository { get; private set; } public ReactiveCommand SwitchFork { get; } @@ -41,7 +41,7 @@ public ForkRepositorySwitchViewModel(IRepositoryForkService repositoryForkServic public IObservable Done => SwitchFork.Where(value => value != null).SelectNull(); - public void Initialize(ILocalRepositoryModel sourceRepository, IRemoteRepositoryModel remoteRepository) + public void Initialize(LocalRepositoryModel sourceRepository, RemoteRepositoryModel remoteRepository) { SourceRepository = sourceRepository; DestinationRepository = remoteRepository; diff --git a/src/GitHub.App/ViewModels/Dialog/ForkRepositoryViewModel.cs b/src/GitHub.App/ViewModels/Dialog/ForkRepositoryViewModel.cs index 6907afa224..28f0fed260 100644 --- a/src/GitHub.App/ViewModels/Dialog/ForkRepositoryViewModel.cs +++ b/src/GitHub.App/ViewModels/Dialog/ForkRepositoryViewModel.cs @@ -37,7 +37,7 @@ public ForkRepositoryViewModel( executePage.Back.Subscribe(x => ShowSelectPage().Forget()); } - public ILocalRepositoryModel Repository { get; private set; } + public LocalRepositoryModel Repository { get; private set; } public IConnection Connection { get; private set; } @@ -45,7 +45,7 @@ public ForkRepositoryViewModel( public override IObservable Done => executePage.Done; - public async Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection) + public async Task InitializeAsync(LocalRepositoryModel repository, IConnection connection) { Repository = repository; Connection = connection; @@ -64,7 +64,7 @@ async Task ShowExecutePage(IAccount account) Content = executePage; } - void ShowSwitchRepositoryPath(IRemoteRepositoryModel remoteRepository) + void ShowSwitchRepositoryPath(RemoteRepositoryModel remoteRepository) { switchPage.Initialize(Repository, remoteRepository); Content = switchPage; diff --git a/src/GitHub.App/ViewModels/Dialog/RepositoryRecloneViewModel.cs b/src/GitHub.App/ViewModels/Dialog/RepositoryRecloneViewModel.cs index d54fb65bba..f996cd5b70 100644 --- a/src/GitHub.App/ViewModels/Dialog/RepositoryRecloneViewModel.cs +++ b/src/GitHub.App/ViewModels/Dialog/RepositoryRecloneViewModel.cs @@ -131,11 +131,11 @@ public string BaseRepositoryPath /// public ReactiveCommand CloneCommand { get; private set; } - IRepositoryModel selectedRepository; + RepositoryModel selectedRepository; /// /// Selected repository to clone /// - public IRepositoryModel SelectedRepository + public RepositoryModel SelectedRepository { get { return selectedRepository; } set { this.RaiseAndSetIfChanged(ref selectedRepository, value); } diff --git a/src/GitHub.App/ViewModels/GitHubPane/GitHubPaneViewModel.cs b/src/GitHub.App/ViewModels/GitHubPane/GitHubPaneViewModel.cs index 6620b8a064..05f70c788c 100644 --- a/src/GitHub.App/ViewModels/GitHubPane/GitHubPaneViewModel.cs +++ b/src/GitHub.App/ViewModels/GitHubPane/GitHubPaneViewModel.cs @@ -56,7 +56,7 @@ public sealed class GitHubPaneViewModel : ViewModelBase, IGitHubPaneViewModel, I IDisposable connectionSubscription; Task initializeTask; IViewModel content; - ILocalRepositoryModel localRepository; + LocalRepositoryModel localRepository; string searchQuery; [ImportingConstructor] @@ -191,7 +191,7 @@ public IViewModel Content public bool IsSearchEnabled => isSearchEnabled.Value; /// - public ILocalRepositoryModel LocalRepository + public LocalRepositoryModel LocalRepository { get { return localRepository; } private set { this.RaiseAndSetIfChanged(ref localRepository, value); } @@ -390,7 +390,7 @@ async Task NavigateTo(Func initialize, Func forks; + RepositoryModel remoteRepository; + IReadOnlyList forks; string searchQuery; string selectedState; ObservableAsPropertyHelper stateCaption; @@ -65,7 +65,7 @@ public IUserFilterViewModel AuthorFilter } /// - public IReadOnlyList Forks + public IReadOnlyList Forks { get { return forks; } set { this.RaiseAndSetIfChanged(ref forks, value); } @@ -86,7 +86,7 @@ public ICollectionView ItemsView } /// - public ILocalRepositoryModel LocalRepository { get; private set; } + public LocalRepositoryModel LocalRepository { get; private set; } /// public IssueListMessage Message @@ -96,7 +96,7 @@ public IssueListMessage Message } /// - public IRepositoryModel RemoteRepository + public RepositoryModel RemoteRepository { get { return remoteRepository; } set { this.RaiseAndSetIfChanged(ref remoteRepository, value); } @@ -126,7 +126,7 @@ public string SelectedState public ReactiveCommand OpenItem { get; } /// - public async Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection) + public async Task InitializeAsync(LocalRepositoryModel repository, IConnection connection) { try { @@ -151,7 +151,7 @@ public async Task InitializeAsync(ILocalRepositoryModel repository, IConnection repository.Name, UriString.ToUriString(repository.CloneUrl.ToRepositoryUrl(parent.Value.owner))); - Forks = new IRepositoryModel[] + Forks = new RepositoryModel[] { RemoteRepository, repository, diff --git a/src/GitHub.App/ViewModels/GitHubPane/PullRequestCreationViewModel.cs b/src/GitHub.App/ViewModels/GitHubPane/PullRequestCreationViewModel.cs index f72bc605fe..16c93ede76 100644 --- a/src/GitHub.App/ViewModels/GitHubPane/PullRequestCreationViewModel.cs +++ b/src/GitHub.App/ViewModels/GitHubPane/PullRequestCreationViewModel.cs @@ -38,10 +38,11 @@ public class PullRequestCreationViewModel : PanePageViewModelBase, IPullRequestC readonly IPullRequestService service; readonly IModelServiceFactory modelServiceFactory; readonly IMessageDraftStore draftStore; + readonly IGitService gitService; readonly IScheduler timerScheduler; readonly CompositeDisposable disposables = new CompositeDisposable(); - ILocalRepositoryModel activeLocalRepo; - ObservableAsPropertyHelper githubRepository; + LocalRepositoryModel activeLocalRepo; + ObservableAsPropertyHelper githubRepository; IModelService modelService; [ImportingConstructor] @@ -49,8 +50,9 @@ public PullRequestCreationViewModel( IModelServiceFactory modelServiceFactory, IPullRequestService service, INotificationService notifications, - IMessageDraftStore draftStore) - : this(modelServiceFactory, service, notifications, draftStore, DefaultScheduler.Instance) + IMessageDraftStore draftStore, + IGitService gitService) + : this(modelServiceFactory, service, notifications, draftStore, gitService, DefaultScheduler.Instance) { } @@ -59,17 +61,20 @@ public PullRequestCreationViewModel( IPullRequestService service, INotificationService notifications, IMessageDraftStore draftStore, + IGitService gitService, IScheduler timerScheduler) { Guard.ArgumentNotNull(modelServiceFactory, nameof(modelServiceFactory)); Guard.ArgumentNotNull(service, nameof(service)); Guard.ArgumentNotNull(notifications, nameof(notifications)); Guard.ArgumentNotNull(draftStore, nameof(draftStore)); + Guard.ArgumentNotNull(gitService, nameof(gitService)); Guard.ArgumentNotNull(timerScheduler, nameof(timerScheduler)); this.service = service; this.modelServiceFactory = modelServiceFactory; this.draftStore = draftStore; + this.gitService = gitService; this.timerScheduler = timerScheduler; this.WhenAnyValue(x => x.Branches) @@ -77,8 +82,12 @@ public PullRequestCreationViewModel( .Where(_ => TargetBranch != null) .Subscribe(x => { + //// HACK: Why is `t` null? + //if (!x.Any(t => t != null && t.Equals(TargetBranch))) if (!x.Any(t => t.Equals(TargetBranch))) + { TargetBranch = GitHubRepository.IsFork ? GitHubRepository.Parent.DefaultBranch : GitHubRepository.DefaultBranch; + } }); SetupValidators(); @@ -133,14 +142,14 @@ public PullRequestCreationViewModel( .Subscribe(x => IsBusy = x); } - public async Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection) + public async Task InitializeAsync(LocalRepositoryModel repository, IConnection connection) { modelService = await modelServiceFactory.CreateAsync(connection); activeLocalRepo = repository; - SourceBranch = repository.CurrentBranch; + SourceBranch = gitService.GetBranch(repository); var obs = modelService.ApiClient.GetRepository(repository.Owner, repository.Name) - .Select(r => new RemoteRepositoryModel(r)) + .Select(r => CreateRemoteRepositoryModel(r)) .PublishLast(); disposables.Add(obs.Connect()); var githubObs = obs; @@ -156,7 +165,7 @@ public async Task InitializeAsync(ILocalRepositoryModel repository, IConnection githubObs.SelectMany(r => { - var b = Observable.Empty(); + var b = Observable.Empty(); if (r.IsFork) { b = modelService.GetBranches(r.Parent).Select(x => @@ -187,6 +196,21 @@ public async Task InitializeAsync(ILocalRepositoryModel repository, IConnection Initialized = true; } + static RemoteRepositoryModel CreateRemoteRepositoryModel(Repository repository) + { + var ownerAccount = new Models.Account(repository.Owner); + var parent = repository.Parent != null ? CreateRemoteRepositoryModel(repository.Parent) : null; + var model = new RemoteRepositoryModel(repository.Id, repository.Name, repository.CloneUrl, + repository.Private, repository.Fork, ownerAccount, parent, repository.DefaultBranch); + + if (parent != null) + { + parent.DefaultBranch.DisplayName = parent.DefaultBranch.Id; + } + + return model; + } + async Task LoadInitialState(string draftKey) { if (activeLocalRepo.CloneUrl == null) @@ -207,7 +231,7 @@ async Task LoadInitialState(string draftKey) void LoadDescriptionFromCommits() { - SourceBranch = activeLocalRepo.CurrentBranch; + SourceBranch = gitService.GetBranch(activeLocalRepo); var uniqueCommits = this.WhenAnyValue( x => x.SourceBranch, @@ -312,7 +336,7 @@ protected string GetDraftKey() SourceBranch.Name); } - public IRemoteRepositoryModel GitHubRepository { get { return githubRepository?.Value; } } + public RemoteRepositoryModel GitHubRepository { get { return githubRepository?.Value; } } bool IsExecuting { get { return isExecuting.Value; } } bool initialized; @@ -322,22 +346,22 @@ bool Initialized set { this.RaiseAndSetIfChanged(ref initialized, value); } } - IBranch sourceBranch; - public IBranch SourceBranch + BranchModel sourceBranch; + public BranchModel SourceBranch { get { return sourceBranch; } set { this.RaiseAndSetIfChanged(ref sourceBranch, value); } } - IBranch targetBranch; - public IBranch TargetBranch + BranchModel targetBranch; + public BranchModel TargetBranch { get { return targetBranch; } set { this.RaiseAndSetIfChanged(ref targetBranch, value); } } - IReadOnlyList branches; - public IReadOnlyList Branches + IReadOnlyList branches; + public IReadOnlyList Branches { get { return branches; } set { this.RaiseAndSetIfChanged(ref branches, value); } diff --git a/src/GitHub.App/ViewModels/GitHubPane/PullRequestDetailViewModel.cs b/src/GitHub.App/ViewModels/GitHubPane/PullRequestDetailViewModel.cs index 32d4dbe2d7..f968b391c3 100644 --- a/src/GitHub.App/ViewModels/GitHubPane/PullRequestDetailViewModel.cs +++ b/src/GitHub.App/ViewModels/GitHubPane/PullRequestDetailViewModel.cs @@ -39,6 +39,7 @@ public sealed class PullRequestDetailViewModel : PanePageViewModelBase, IPullReq readonly ITeamExplorerContext teamExplorerContext; readonly ISyncSubmodulesCommand syncSubmodulesCommand; readonly IViewViewModelFactory viewViewModelFactory; + readonly IGitService gitService; IModelService modelService; PullRequestDetailModel model; IActorViewModel author; @@ -77,7 +78,8 @@ public PullRequestDetailViewModel( ITeamExplorerContext teamExplorerContext, IPullRequestFilesViewModel files, ISyncSubmodulesCommand syncSubmodulesCommand, - IViewViewModelFactory viewViewModelFactory) + IViewViewModelFactory viewViewModelFactory, + IGitService gitService) { Guard.ArgumentNotNull(pullRequestsService, nameof(pullRequestsService)); Guard.ArgumentNotNull(sessionManager, nameof(sessionManager)); @@ -86,6 +88,7 @@ public PullRequestDetailViewModel( Guard.ArgumentNotNull(teamExplorerContext, nameof(teamExplorerContext)); Guard.ArgumentNotNull(syncSubmodulesCommand, nameof(syncSubmodulesCommand)); Guard.ArgumentNotNull(viewViewModelFactory, nameof(viewViewModelFactory)); + Guard.ArgumentNotNull(gitService, nameof(gitService)); this.pullRequestsService = pullRequestsService; this.sessionManager = sessionManager; @@ -94,6 +97,7 @@ public PullRequestDetailViewModel( this.teamExplorerContext = teamExplorerContext; this.syncSubmodulesCommand = syncSubmodulesCommand; this.viewViewModelFactory = viewViewModelFactory; + this.gitService = gitService; Files = files; Checkout = ReactiveCommand.CreateFromObservable( @@ -158,7 +162,7 @@ private set /// /// Gets the local repository. /// - public ILocalRepositoryModel LocalRepository { get; private set; } + public LocalRepositoryModel LocalRepository { get; private set; } /// /// Gets the owner of the remote repository that contains the pull request. @@ -329,7 +333,7 @@ public IReadOnlyList Checks /// The pull request's repository name. /// The pull request number. public async Task InitializeAsync( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, IConnection connection, string owner, string repo, @@ -401,7 +405,8 @@ public async Task Load(PullRequestDetailModel pullRequest) var localBranches = await pullRequestsService.GetLocalBranches(LocalRepository, pullRequest).ToList(); - IsCheckedOut = localBranches.Contains(LocalRepository.CurrentBranch); + var currentBranch = gitService.GetBranch(LocalRepository); + IsCheckedOut = localBranches.Contains(currentBranch); if (IsCheckedOut) { diff --git a/src/GitHub.App/ViewModels/GitHubPane/PullRequestReviewAuthoringViewModel.cs b/src/GitHub.App/ViewModels/GitHubPane/PullRequestReviewAuthoringViewModel.cs index fc80765fd4..c263af4585 100644 --- a/src/GitHub.App/ViewModels/GitHubPane/PullRequestReviewAuthoringViewModel.cs +++ b/src/GitHub.App/ViewModels/GitHubPane/PullRequestReviewAuthoringViewModel.cs @@ -96,7 +96,7 @@ public PullRequestReviewAuthoringViewModel( } /// - public ILocalRepositoryModel LocalRepository { get; private set; } + public LocalRepositoryModel LocalRepository { get; private set; } /// public string RemoteRepositoryOwner { get; private set; } @@ -152,7 +152,7 @@ public IReadOnlyList FileComments public ReactiveCommand Cancel { get; } public async Task InitializeAsync( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, IConnection connection, string owner, string repo, diff --git a/src/GitHub.App/ViewModels/GitHubPane/PullRequestUserReviewsViewModel.cs b/src/GitHub.App/ViewModels/GitHubPane/PullRequestUserReviewsViewModel.cs index 2faafcb3f9..718a906f1a 100644 --- a/src/GitHub.App/ViewModels/GitHubPane/PullRequestUserReviewsViewModel.cs +++ b/src/GitHub.App/ViewModels/GitHubPane/PullRequestUserReviewsViewModel.cs @@ -50,7 +50,7 @@ public PullRequestUserReviewsViewModel( } /// - public ILocalRepositoryModel LocalRepository { get; private set; } + public LocalRepositoryModel LocalRepository { get; private set; } /// public string RemoteRepositoryOwner { get; private set; } @@ -84,7 +84,7 @@ public string PullRequestTitle /// [SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "login")] public async Task InitializeAsync( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, IConnection connection, string owner, string repo, diff --git a/src/GitHub.Exports.Reactive/Caches/AccountCacheItem.cs b/src/GitHub.Exports.Reactive/Caches/AccountCacheItem.cs index 36ad661261..6d3a0eec10 100644 --- a/src/GitHub.Exports.Reactive/Caches/AccountCacheItem.cs +++ b/src/GitHub.Exports.Reactive/Caches/AccountCacheItem.cs @@ -2,6 +2,7 @@ using GitHub.Models; using GitHub.Primitives; using Octokit; +using Account = Octokit.Account; namespace GitHub.Caches { diff --git a/src/GitHub.Exports.Reactive/Extensions/ConnectionManagerExtensions.cs b/src/GitHub.Exports.Reactive/Extensions/ConnectionManagerExtensions.cs index a13cd327d2..36737b61aa 100644 --- a/src/GitHub.Exports.Reactive/Extensions/ConnectionManagerExtensions.cs +++ b/src/GitHub.Exports.Reactive/Extensions/ConnectionManagerExtensions.cs @@ -10,7 +10,7 @@ public static class ConnectionManagerExtensions { public static async Task GetModelService( this IConnectionManager cm, - ILocalRepositoryModel repository, + LocalRepositoryModel repository, IModelServiceFactory factory) { var connection = await cm.GetConnection(repository); diff --git a/src/GitHub.App/Models/Account.cs b/src/GitHub.Exports.Reactive/Models/Account.cs similarity index 100% rename from src/GitHub.App/Models/Account.cs rename to src/GitHub.Exports.Reactive/Models/Account.cs diff --git a/src/GitHub.App/Models/RemoteRepositoryModel.cs b/src/GitHub.Exports.Reactive/Models/RemoteRepositoryModel.cs similarity index 70% rename from src/GitHub.App/Models/RemoteRepositoryModel.cs rename to src/GitHub.Exports.Reactive/Models/RemoteRepositoryModel.cs index 64ec5bda4a..838dda5b8e 100644 --- a/src/GitHub.App/Models/RemoteRepositoryModel.cs +++ b/src/GitHub.Exports.Reactive/Models/RemoteRepositoryModel.cs @@ -1,14 +1,15 @@ -using GitHub.Primitives; -using System; +using System; using System.Globalization; +using GitHub.Primitives; using GitHub.Extensions; +using GitHub.Collections; namespace GitHub.Models { /// /// A repository read from the GitHub API. /// - public class RemoteRepositoryModel : RepositoryModel, IRemoteRepositoryModel, + public class RemoteRepositoryModel : RepositoryModel, ICopyable, IEquatable, IComparable { /// @@ -21,7 +22,9 @@ public class RemoteRepositoryModel : RepositoryModel, IRemoteRepositoryModel, /// Whether the repository is a fork. /// The repository owner account. /// The parent repository if this repository is a fork. - public RemoteRepositoryModel(long id, string name, UriString cloneUrl, bool isPrivate, bool isFork, IAccount ownerAccount, IRemoteRepositoryModel parent) + /// The default branch name (or "master" if undefined). + public RemoteRepositoryModel(long id, string name, UriString cloneUrl, bool isPrivate, bool isFork, IAccount ownerAccount, + RemoteRepositoryModel parent, string defaultBranchName = "master") : base(name, cloneUrl) { Guard.ArgumentNotEmptyString(name, nameof(name)); @@ -31,33 +34,19 @@ public RemoteRepositoryModel(long id, string name, UriString cloneUrl, bool isPr OwnerAccount = ownerAccount; IsFork = isFork; SetIcon(isPrivate, isFork); - // this is an assumption, we'd have to load the repo information from octokit to know for sure - // probably not worth it for this ctor - DefaultBranch = new BranchModel("master", this); + DefaultBranch = new BranchModel(defaultBranchName, this); Parent = parent; } /// - /// Initializes a new instance of the class. + /// This is used by . /// - /// The source octokit repository. - public RemoteRepositoryModel(Octokit.Repository repository) - : base(repository.Name, repository.CloneUrl) + protected RemoteRepositoryModel() { - Guard.ArgumentNotNull(repository, nameof(repository)); - - Id = repository.Id; - IsFork = repository.Fork; - SetIcon(repository.Private, IsFork); - OwnerAccount = new Account(repository.Owner); - DefaultBranch = new BranchModel(repository.DefaultBranch, this); - Parent = repository.Parent != null ? new RemoteRepositoryModel(repository.Parent) : null; - if (Parent != null) - Parent.DefaultBranch.DisplayName = Parent.DefaultBranch.Id; } -#region Equality Things - public void CopyFrom(IRemoteRepositoryModel other) + #region Equality Things + public void CopyFrom(RemoteRepositoryModel other) { if (!Equals(other)) throw new ArgumentException("Instance to copy from doesn't match this instance. this:(" + this + ") other:(" + other + ")", nameof(other)); @@ -77,13 +66,6 @@ public override bool Equals(object obj) return Equals(other); } - public bool Equals(IRemoteRepositoryModel other) - { - if (ReferenceEquals(this, other)) - return true; - return other != null && Id == other.Id; - } - public bool Equals(RemoteRepositoryModel other) { if (ReferenceEquals(this, other)) @@ -91,11 +73,6 @@ public bool Equals(RemoteRepositoryModel other) return other != null && Id == other.Id; } - public int CompareTo(IRemoteRepositoryModel other) - { - return other != null ? UpdatedAt.CompareTo(other.UpdatedAt) : 1; - } - public int CompareTo(RemoteRepositoryModel other) { return other != null ? UpdatedAt.CompareTo(other.UpdatedAt) : 1; @@ -124,7 +101,7 @@ public int CompareTo(RemoteRepositoryModel other) { return !(lhs == rhs); } -#endregion + #endregion /// /// Gets the account that is the ower of the repository. @@ -154,12 +131,12 @@ public int CompareTo(RemoteRepositoryModel other) /// /// Gets the repository from which this repository was forked, if any. /// - public IRemoteRepositoryModel Parent { get; } + public RemoteRepositoryModel Parent { get; } /// /// Gets the default branch for the repository. /// - public IBranch DefaultBranch { get; } + public BranchModel DefaultBranch { get; } internal string DebuggerDisplay { diff --git a/src/GitHub.Exports.Reactive/Services/IModelService.cs b/src/GitHub.Exports.Reactive/Services/IModelService.cs index 76f970248a..aacc5164c5 100644 --- a/src/GitHub.Exports.Reactive/Services/IModelService.cs +++ b/src/GitHub.Exports.Reactive/Services/IModelService.cs @@ -20,18 +20,18 @@ public interface IModelService : IDisposable IObservable GetUser(string login); IObservable InsertUser(AccountCacheItem user); IObservable> GetAccounts(); - IObservable GetRepository(string owner, string repo); - ITrackingCollection GetRepositories(ITrackingCollection collection); - IObservable GetForks(IRepositoryModel repository); + IObservable GetRepository(string owner, string repo); + ITrackingCollection GetRepositories(ITrackingCollection collection); + IObservable GetForks(RepositoryModel repository); IObservable GetLicenses(); IObservable GetGitIgnoreTemplates(); IObservable GetPullRequest(string owner, string name, int number); - ITrackingCollection GetPullRequests(IRepositoryModel repo, ITrackingCollection collection); - IObservable CreatePullRequest(ILocalRepositoryModel sourceRepository, IRepositoryModel targetRepository, - IBranch sourceBranch, IBranch targetBranch, + ITrackingCollection GetPullRequests(RepositoryModel repo, ITrackingCollection collection); + IObservable CreatePullRequest(LocalRepositoryModel sourceRepository, RepositoryModel targetRepository, + BranchModel sourceBranch, BranchModel targetBranch, string title, string body); - IObservable GetBranches(IRepositoryModel repo); + IObservable GetBranches(RepositoryModel repo); IObservable InvalidateAll(); - IObservable GetFileContents(IRepositoryModel repo, string commitSha, string path, string fileSha); + IObservable GetFileContents(RepositoryModel repo, string commitSha, string path, string fileSha); } } diff --git a/src/GitHub.Exports.Reactive/Services/IPullRequestService.cs b/src/GitHub.Exports.Reactive/Services/IPullRequestService.cs index 48837ce2d6..29e8e1d5f4 100644 --- a/src/GitHub.Exports.Reactive/Services/IPullRequestService.cs +++ b/src/GitHub.Exports.Reactive/Services/IPullRequestService.cs @@ -42,8 +42,8 @@ Task> ReadAssignableUsers( string after); IObservable CreatePullRequest(IModelService modelService, - ILocalRepositoryModel sourceRepository, IRepositoryModel targetRepository, - IBranch sourceBranch, IBranch targetBranch, + LocalRepositoryModel sourceRepository, RepositoryModel targetRepository, + BranchModel sourceBranch, BranchModel targetBranch, string title, string body); /// @@ -51,14 +51,14 @@ IObservable CreatePullRequest(IModelService modelService, /// /// The repository. /// - IObservable IsWorkingDirectoryClean(ILocalRepositoryModel repository); + IObservable IsWorkingDirectoryClean(LocalRepositoryModel repository); /// /// Count the number of submodules that require syncing. /// /// The repository. /// The number of submodules that need to be synced. - IObservable CountSubmodulesToSync(ILocalRepositoryModel repository); + IObservable CountSubmodulesToSync(LocalRepositoryModel repository); /// /// Checks out a pull request to a local branch. @@ -67,26 +67,26 @@ IObservable CreatePullRequest(IModelService modelService, /// The pull request details. /// The name of the local branch. /// - IObservable Checkout(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest, string localBranchName); + IObservable Checkout(LocalRepositoryModel repository, PullRequestDetailModel pullRequest, string localBranchName); /// /// Carries out a pull on the current branch. /// /// The repository. - IObservable Pull(ILocalRepositoryModel repository); + IObservable Pull(LocalRepositoryModel repository); /// /// Carries out a push of the current branch. /// /// The repository. - IObservable Push(ILocalRepositoryModel repository); + IObservable Push(LocalRepositoryModel repository); /// /// Sync submodules on the current branch. /// /// The repository. /// A method that will be called with progress messages - Task SyncSubmodules(ILocalRepositoryModel repository, Action progress); + Task SyncSubmodules(LocalRepositoryModel repository, Action progress); /// /// Calculates the name of a local branch for a pull request avoiding clashes with existing branches. @@ -95,7 +95,7 @@ IObservable CreatePullRequest(IModelService modelService, /// The pull request number. /// The pull request title. /// - IObservable GetDefaultLocalBranchName(ILocalRepositoryModel repository, int pullRequestNumber, string pullRequestTitle); + IObservable GetDefaultLocalBranchName(LocalRepositoryModel repository, int pullRequestNumber, string pullRequestTitle); /// /// Gets the local branches that exist for the specified pull request. @@ -103,7 +103,7 @@ IObservable CreatePullRequest(IModelService modelService, /// The repository. /// The pull request details. /// - IObservable GetLocalBranches(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest); + IObservable GetLocalBranches(LocalRepositoryModel repository, PullRequestDetailModel pullRequest); /// /// Ensures that all local branches for the specified pull request are marked as PR branches. @@ -119,7 +119,7 @@ IObservable CreatePullRequest(IModelService modelService, /// for the specified pull request are indeed marked and returns a value indicating whether any branches /// have had the mark added. /// - IObservable EnsureLocalBranchesAreMarkedAsPullRequests(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest); + IObservable EnsureLocalBranchesAreMarkedAsPullRequests(LocalRepositoryModel repository, PullRequestDetailModel pullRequest); /// /// Determines whether the specified pull request is from the specified repository. @@ -127,7 +127,7 @@ IObservable CreatePullRequest(IModelService modelService, /// The repository. /// The pull request details. /// - bool IsPullRequestFromRepository(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest); + bool IsPullRequestFromRepository(LocalRepositoryModel repository, PullRequestDetailModel pullRequest); /// /// Switches to an existing branch for the specified pull request. @@ -135,7 +135,7 @@ IObservable CreatePullRequest(IModelService modelService, /// The repository. /// The pull request details. /// - IObservable SwitchToBranch(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest); + IObservable SwitchToBranch(LocalRepositoryModel repository, PullRequestDetailModel pullRequest); /// /// Gets the history divergence between the current HEAD and the specified pull request. @@ -143,7 +143,7 @@ IObservable CreatePullRequest(IModelService modelService, /// The repository. /// The pull request number. /// - IObservable CalculateHistoryDivergence(ILocalRepositoryModel repository, int pullRequestNumber); + IObservable CalculateHistoryDivergence(LocalRepositoryModel repository, int pullRequestNumber); /// /// Gets the SHA of the merge base for a pull request. @@ -151,7 +151,7 @@ IObservable CreatePullRequest(IModelService modelService, /// The repository. /// The pull request details. /// - Task GetMergeBase(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest); + Task GetMergeBase(LocalRepositoryModel repository, PullRequestDetailModel pullRequest); /// /// Gets the changes between the pull request base and head. @@ -159,7 +159,7 @@ IObservable CreatePullRequest(IModelService modelService, /// The repository. /// The pull request details. /// - IObservable GetTreeChanges(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest); + IObservable GetTreeChanges(LocalRepositoryModel repository, PullRequestDetailModel pullRequest); /// /// Gets the pull request associated with the current branch. @@ -171,9 +171,9 @@ IObservable CreatePullRequest(IModelService modelService, /// /// /// This method does not do an API request - it simply checks the mark left in the git - /// config by . + /// config by . /// - IObservable> GetPullRequestForCurrentBranch(ILocalRepositoryModel repository); + IObservable<(string owner, int number)> GetPullRequestForCurrentBranch(LocalRepositoryModel repository); /// /// Gets the encoding for the specified local file. @@ -183,7 +183,7 @@ IObservable CreatePullRequest(IModelService modelService, /// /// The file's encoding or null if the file doesn't exist locally. /// - Encoding GetEncoding(ILocalRepositoryModel repository, string relativePath); + Encoding GetEncoding(LocalRepositoryModel repository, string relativePath); /// /// Extracts a file at the specified commit to a temporary file. @@ -197,7 +197,7 @@ IObservable CreatePullRequest(IModelService modelService, /// /// The path to the temporary file. Task ExtractToTempFile( - ILocalRepositoryModel repository, + LocalRepositoryModel repository, PullRequestDetailModel pullRequest, string relativePath, string commitSha, @@ -209,9 +209,9 @@ Task ExtractToTempFile( /// /// The repository. /// - IObservable RemoveUnusedRemotes(ILocalRepositoryModel repository); + IObservable RemoveUnusedRemotes(LocalRepositoryModel repository); - IObservable GetPullRequestTemplate(ILocalRepositoryModel repository); + IObservable GetPullRequestTemplate(LocalRepositoryModel repository); /// /// Gets the unique commits from to the merge base of @@ -224,7 +224,7 @@ Task ExtractToTempFile( /// The maximum number of commits to return. /// An enumerable of unique commits from the merge base to the compareBranch. IObservable> GetMessagesForUniqueCommits( - ILocalRepositoryModel repository, + LocalRepositoryModel repository, string baseBranch, string compareBranch, int maxCommits); diff --git a/src/GitHub.Exports.Reactive/Services/IPullRequestSession.cs b/src/GitHub.Exports.Reactive/Services/IPullRequestSession.cs index dcc60b4d6e..05a497afdc 100644 --- a/src/GitHub.Exports.Reactive/Services/IPullRequestSession.cs +++ b/src/GitHub.Exports.Reactive/Services/IPullRequestSession.cs @@ -39,7 +39,7 @@ public interface IPullRequestSession /// /// Gets the local repository. /// - ILocalRepositoryModel LocalRepository { get; } + LocalRepositoryModel LocalRepository { get; } /// /// Gets the owner of the repository that contains the pull request. diff --git a/src/GitHub.Exports.Reactive/Services/IRepositoryForkService.cs b/src/GitHub.Exports.Reactive/Services/IRepositoryForkService.cs index 7cb3b79e2e..8f0b12c3b9 100644 --- a/src/GitHub.Exports.Reactive/Services/IRepositoryForkService.cs +++ b/src/GitHub.Exports.Reactive/Services/IRepositoryForkService.cs @@ -9,7 +9,7 @@ namespace GitHub.Services { public interface IRepositoryForkService { - IObservable ForkRepository(IApiClient apiClient, IRepositoryModel sourceRepository, NewRepositoryFork repositoryFork, bool updateOrigin, bool addUpstream, bool trackMasterUpstream); - IObservable SwitchRemotes(IRepositoryModel destinationRepository, bool updateOrigin, bool addUpstream, bool trackMasterUpstream); + IObservable ForkRepository(IApiClient apiClient, RepositoryModel sourceRepository, NewRepositoryFork repositoryFork, bool updateOrigin, bool addUpstream, bool trackMasterUpstream); + IObservable SwitchRemotes(RepositoryModel destinationRepository, bool updateOrigin, bool addUpstream, bool trackMasterUpstream); } } diff --git a/src/GitHub.Exports.Reactive/Services/LocalRepositoriesExtensions.cs b/src/GitHub.Exports.Reactive/Services/LocalRepositoriesExtensions.cs index 76c808724a..02237f87dc 100644 --- a/src/GitHub.Exports.Reactive/Services/LocalRepositoriesExtensions.cs +++ b/src/GitHub.Exports.Reactive/Services/LocalRepositoriesExtensions.cs @@ -16,14 +16,14 @@ public static class LocalRepositoriesExtensions /// /// The local repositories object. /// The address. - public static IReactiveDerivedList GetRepositoriesForAddress( + 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); + OrderedComparer.OrderBy(x => x.Name).Compare); } } } diff --git a/src/GitHub.Exports.Reactive/Services/ModelServiceExtensions.cs b/src/GitHub.Exports.Reactive/Services/ModelServiceExtensions.cs index abe5e35a88..b9413d4ab2 100644 --- a/src/GitHub.Exports.Reactive/Services/ModelServiceExtensions.cs +++ b/src/GitHub.Exports.Reactive/Services/ModelServiceExtensions.cs @@ -5,7 +5,7 @@ namespace GitHub.Services { public static class ModelServiceExtensions { - public static IObservable GetPullRequest(this IModelService service, IRepositoryModel repo, int number) + public static IObservable GetPullRequest(this IModelService service, RepositoryModel repo, int number) { return service.GetPullRequest(repo.Owner, repo.Name, number); } diff --git a/src/GitHub.Exports.Reactive/ViewModels/Dialog/Clone/IRepositoryCloneTabViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/Dialog/Clone/IRepositoryCloneTabViewModel.cs index 6f6c7d6b43..0eb678d828 100644 --- a/src/GitHub.Exports.Reactive/ViewModels/Dialog/Clone/IRepositoryCloneTabViewModel.cs +++ b/src/GitHub.Exports.Reactive/ViewModels/Dialog/Clone/IRepositoryCloneTabViewModel.cs @@ -20,7 +20,7 @@ public interface IRepositoryCloneTabViewModel : IViewModel /// /// Gets the selected repository, or null if no repository has been selected. /// - IRepositoryModel Repository { get; } + RepositoryModel Repository { get; } /// /// Activates the tab. diff --git a/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositoryExecuteViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositoryExecuteViewModel.cs index 251f3fd66f..330138c3cc 100644 --- a/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositoryExecuteViewModel.cs +++ b/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositoryExecuteViewModel.cs @@ -13,9 +13,9 @@ namespace GitHub.ViewModels.Dialog /// public interface IForkRepositoryExecuteViewModel : IDialogContentViewModel { - IRepositoryModel SourceRepository { get; } + RepositoryModel SourceRepository { get; } - IRepositoryModel DestinationRepository { get; } + RepositoryModel DestinationRepository { get; } IAccount DestinationAccount { get; } @@ -46,7 +46,7 @@ public interface IForkRepositoryExecuteViewModel : IDialogContentViewModel /// The account to fork to. /// The connection to use. Task InitializeAsync( - ILocalRepositoryModel sourceRepository, + LocalRepositoryModel sourceRepository, IAccount destinationAccount, IConnection connection); } diff --git a/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositorySelectViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositorySelectViewModel.cs index 21f7b9b7f2..28e4a813cb 100644 --- a/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositorySelectViewModel.cs +++ b/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositorySelectViewModel.cs @@ -20,7 +20,7 @@ public interface IForkRepositorySelectViewModel : IDialogContentViewModel /// /// Gets a list of existing forks for accounts that the owner has access to. /// - IReadOnlyList ExistingForks { get; } + IReadOnlyList ExistingForks { get; } /// /// Gets a value indicating whether the view model is loading. @@ -35,13 +35,13 @@ public interface IForkRepositorySelectViewModel : IDialogContentViewModel /// /// Gets a command that is executed when the user selects an item in . /// - ReactiveCommand SwitchOrigin { get; } + ReactiveCommand SwitchOrigin { get; } /// /// Initializes the view model. /// /// The repository to fork. /// The connection to use. - Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection); + Task InitializeAsync(LocalRepositoryModel repository, IConnection connection); } } diff --git a/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositorySwitchViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositorySwitchViewModel.cs index d908c1bb61..f820955bc0 100644 --- a/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositorySwitchViewModel.cs +++ b/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositorySwitchViewModel.cs @@ -9,9 +9,9 @@ namespace GitHub.ViewModels.Dialog /// public interface IForkRepositorySwitchViewModel : IDialogContentViewModel { - IRepositoryModel SourceRepository { get; } + RepositoryModel SourceRepository { get; } - IRepositoryModel DestinationRepository { get; } + RepositoryModel DestinationRepository { get; } /// /// Gets a command that is executed when the user clicks the "Fork" button. @@ -29,6 +29,6 @@ public interface IForkRepositorySwitchViewModel : IDialogContentViewModel /// /// The repository to fork. /// - void Initialize(ILocalRepositoryModel sourceRepository, IRemoteRepositoryModel remoteRepository); + void Initialize(LocalRepositoryModel sourceRepository, RemoteRepositoryModel remoteRepository); } } \ No newline at end of file diff --git a/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositoryViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositoryViewModel.cs index e02e3e64a6..cb9d8c5d3d 100644 --- a/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositoryViewModel.cs +++ b/src/GitHub.Exports.Reactive/ViewModels/Dialog/IForkRepositoryViewModel.cs @@ -19,6 +19,6 @@ public interface IForkRepositoryViewModel : IDialogContentViewModel /// /// The repository to fork. /// The connection to use. - Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection); + Task InitializeAsync(LocalRepositoryModel repository, IConnection connection); } } diff --git a/src/GitHub.Exports.Reactive/ViewModels/Dialog/IRepositoryRecloneViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/Dialog/IRepositoryRecloneViewModel.cs index 9feef83bf2..1929a198e4 100644 --- a/src/GitHub.Exports.Reactive/ViewModels/Dialog/IRepositoryRecloneViewModel.cs +++ b/src/GitHub.Exports.Reactive/ViewModels/Dialog/IRepositoryRecloneViewModel.cs @@ -8,6 +8,6 @@ public interface IRepositoryRecloneViewModel : IDialogContentViewModel, IConnect /// /// Gets or sets the repository to clone. /// - IRepositoryModel SelectedRepository { get; set; } + RepositoryModel SelectedRepository { get; set; } } } diff --git a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IIssueListViewModelBase.cs b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IIssueListViewModelBase.cs index 164677ef13..1330142fc7 100644 --- a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IIssueListViewModelBase.cs +++ b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IIssueListViewModelBase.cs @@ -18,7 +18,7 @@ public enum IssueListMessage /// No message should be displayed; the items should be displayed. /// None, - + /// /// A "No Open Items" message should be displayed. /// @@ -47,7 +47,7 @@ public interface IIssueListViewModelBase : ISearchablePageViewModel /// /// Returns null if the current repository is not a fork. /// - IReadOnlyList Forks { get; } + IReadOnlyList Forks { get; } /// /// Gets the list of issues or pull requests. @@ -63,7 +63,7 @@ public interface IIssueListViewModelBase : ISearchablePageViewModel /// /// Gets the local repository. /// - ILocalRepositoryModel LocalRepository { get; } + LocalRepositoryModel LocalRepository { get; } /// /// Gets an enum indicating a message that should be displayed in place of a list of items. @@ -77,7 +77,7 @@ public interface IIssueListViewModelBase : ISearchablePageViewModel /// This may differ from if is /// a fork. /// - IRepositoryModel RemoteRepository { get; set; } + RepositoryModel RemoteRepository { get; set; } /// /// Gets the currently selected item in . @@ -105,6 +105,6 @@ public interface IIssueListViewModelBase : ISearchablePageViewModel /// The local repository. /// The connection/ /// A task tracking the operation. - Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection); + Task InitializeAsync(LocalRepositoryModel repository, IConnection connection); } } diff --git a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestCreationViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestCreationViewModel.cs index 47f1aaf31b..444c5bd191 100644 --- a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestCreationViewModel.cs +++ b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestCreationViewModel.cs @@ -9,14 +9,14 @@ namespace GitHub.ViewModels.GitHubPane { public interface IPullRequestCreationViewModel : IPanePageViewModel { - IBranch SourceBranch { get; set; } - IBranch TargetBranch { get; set; } - IReadOnlyList Branches { get; } + BranchModel SourceBranch { get; set; } + BranchModel TargetBranch { get; set; } + IReadOnlyList Branches { get; } ReactiveCommand CreatePullRequest { get; } ReactiveCommand Cancel { get; } string PRTitle { get; set; } ReactivePropertyValidator TitleValidator { get; } - Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection); + Task InitializeAsync(LocalRepositoryModel repository, IConnection connection); } } diff --git a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestDetailViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestDetailViewModel.cs index 37e87ed78b..c10c6b2376 100644 --- a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestDetailViewModel.cs +++ b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestDetailViewModel.cs @@ -79,7 +79,7 @@ public interface IPullRequestDetailViewModel : IPanePageViewModel, IOpenInBrowse /// /// Gets the local repository. /// - ILocalRepositoryModel LocalRepository { get; } + LocalRepositoryModel LocalRepository { get; } /// /// Gets the owner of the remote repository that contains the pull request. @@ -189,7 +189,7 @@ public interface IPullRequestDetailViewModel : IPanePageViewModel, IOpenInBrowse /// The pull request's repository name. /// The pull request number. Task InitializeAsync( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, IConnection connection, string owner, string repo, diff --git a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestReviewAuthoringViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestReviewAuthoringViewModel.cs index 14034778a3..e0be2242d9 100644 --- a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestReviewAuthoringViewModel.cs +++ b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestReviewAuthoringViewModel.cs @@ -16,7 +16,7 @@ public interface IPullRequestReviewAuthoringViewModel : IPanePageViewModel, IDis /// /// Gets the local repository. /// - ILocalRepositoryModel LocalRepository { get; } + LocalRepositoryModel LocalRepository { get; } /// /// Gets the owner of the remote repository that contains the pull request. @@ -96,7 +96,7 @@ public interface IPullRequestReviewAuthoringViewModel : IPanePageViewModel, IDis /// The pull request's repository name. /// The pull request number. Task InitializeAsync( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, IConnection connection, string owner, string repo, diff --git a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestUserReviewsViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestUserReviewsViewModel.cs index 4ceae3b8d3..fa21f2753d 100644 --- a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestUserReviewsViewModel.cs +++ b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestUserReviewsViewModel.cs @@ -14,7 +14,7 @@ public interface IPullRequestUserReviewsViewModel : IPanePageViewModel /// /// Gets the local repository. /// - ILocalRepositoryModel LocalRepository { get; } + LocalRepositoryModel LocalRepository { get; } /// /// Gets the owner of the remote repository that contains the pull request. @@ -60,7 +60,7 @@ public interface IPullRequestUserReviewsViewModel : IPanePageViewModel /// The pull request number. /// The user's login. Task InitializeAsync( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, IConnection connection, string owner, string repo, diff --git a/src/GitHub.Exports/Extensions/ConnectionManagerExtensions.cs b/src/GitHub.Exports/Extensions/ConnectionManagerExtensions.cs index 50ede8930b..1763f728f7 100644 --- a/src/GitHub.Exports/Extensions/ConnectionManagerExtensions.cs +++ b/src/GitHub.Exports/Extensions/ConnectionManagerExtensions.cs @@ -34,7 +34,7 @@ public static async Task GetFirstLoggedInConnection(this IConnectio return connections.FirstOrDefault(x => x.IsLoggedIn); } - public static Task GetConnection(this IConnectionManager cm, IRepositoryModel repository) + public static Task GetConnection(this IConnectionManager cm, RepositoryModel repository) { if (repository?.CloneUrl != null) { diff --git a/src/GitHub.Exports/Extensions/LocalRepositoryModelExtensions.cs b/src/GitHub.Exports/Extensions/LocalRepositoryModelExtensions.cs index 4cb75caa17..f11d093947 100644 --- a/src/GitHub.Exports/Extensions/LocalRepositoryModelExtensions.cs +++ b/src/GitHub.Exports/Extensions/LocalRepositoryModelExtensions.cs @@ -8,7 +8,7 @@ namespace GitHub.Extensions { public static class LocalRepositoryModelExtensions { - public static bool HasCommits(this ILocalRepositoryModel repository) + public static bool HasCommits(this LocalRepositoryModel repository) { using (var repo = GitService.GitServiceHelper.GetRepository(repository.LocalPath)) { @@ -16,7 +16,7 @@ public static bool HasCommits(this ILocalRepositoryModel repository) } } - public static bool MightContainSolution(this ILocalRepositoryModel repository) + public static bool MightContainSolution(this LocalRepositoryModel repository) { var dir = new DirectoryInfo(repository.LocalPath); return dir.EnumerateFileSystemInfos("*", SearchOption.TopDirectoryOnly) diff --git a/src/GitHub.Exports/Models/BranchModel.cs b/src/GitHub.Exports/Models/BranchModel.cs index 9a02091871..e72280f5c2 100644 --- a/src/GitHub.Exports/Models/BranchModel.cs +++ b/src/GitHub.Exports/Models/BranchModel.cs @@ -1,55 +1,40 @@ using System; using System.Globalization; -using GitHub.Services; +using GitHub.Collections; namespace GitHub.Models { - public class BranchModel : IBranch + public class BranchModel : ICopyable, + IEquatable, IComparable { - public BranchModel(string name, IRepositoryModel repo) + public BranchModel(string name, RepositoryModel repo, string sha, bool isTracking, string trackedSha) : + this(name, repo) { - Extensions.Guard.ArgumentNotEmptyString(name, nameof(name)); - Extensions.Guard.ArgumentNotNull(repo, nameof(repo)); - - Name = DisplayName = name; - Repository = repo; - Id = String.Format(CultureInfo.InvariantCulture, "{0}/{1}", Repository.Owner, Name); + IsTracking = isTracking; + Sha = sha; + TrackedSha = trackedSha; } - public BranchModel(Octokit.Branch branch, IRepositoryModel repo) + public BranchModel(string name, RepositoryModel repo) { - Extensions.Guard.ArgumentNotNull(branch, nameof(branch)); + Extensions.Guard.ArgumentNotEmptyString(name, nameof(name)); Extensions.Guard.ArgumentNotNull(repo, nameof(repo)); - Name = DisplayName = branch.Name; + Name = DisplayName = name; Repository = repo; - Id = String.Format(CultureInfo.InvariantCulture, "{0}/{1}", Repository.Owner, Name); - } - - public BranchModel(LibGit2Sharp.Branch branch, IRepositoryModel repo, IGitService gitService) - { - Extensions.Guard.ArgumentNotNull(branch, nameof(branch)); - Extensions.Guard.ArgumentNotNull(repo, nameof(repo)); - Name = DisplayName = branch.FriendlyName; -#pragma warning disable 0618 // TODO: Replace `Branch.Remote` with `Repository.Network.Remotes[branch.RemoteName]`. - Repository = branch.IsRemote ? new LocalRepositoryModel(branch.Remote.Url, gitService) : repo; -#pragma warning restore 0618 - IsTracking = branch.IsTracking; - Sha = branch.Tip?.Sha; - TrackedSha = branch.TrackedBranch?.Tip?.Sha; - Id = String.Format(CultureInfo.InvariantCulture, "{0}/{1}", Repository.Owner, Name); + Id = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", Repository.Owner, Name); } public string Id { get; private set; } public string Name { get; private set; } - public IRepositoryModel Repository { get; private set; } + public RepositoryModel Repository { get; private set; } public bool IsTracking { get; private set; } public string DisplayName { get; set; } public string Sha { get; private set; } public string TrackedSha { get; private set; } #region Equality things - public void CopyFrom(IBranch other) + public void CopyFrom(BranchModel other) { if (!Equals(other)) throw new ArgumentException("Instance to copy from doesn't match this instance. this:(" + this + ") other:(" + other + ")", nameof(other)); @@ -73,16 +58,16 @@ public override int GetHashCode() return Id.GetHashCode(); } - bool IEquatable.Equals(IBranch other) + bool IEquatable.Equals(BranchModel other) { if (ReferenceEquals(this, other)) return true; return other != null && Id == other.Id; } - public int CompareTo(IBranch other) + public int CompareTo(BranchModel other) { - return other != null ? String.Compare(Id, other.Id, StringComparison.CurrentCulture) : 1; + return other != null ? string.Compare(Id, other.Id, StringComparison.CurrentCulture) : 1; } public static bool operator >(BranchModel lhs, BranchModel rhs) diff --git a/src/GitHub.Exports/Models/IBranch.cs b/src/GitHub.Exports/Models/IBranch.cs deleted file mode 100644 index 223172e8fe..0000000000 --- a/src/GitHub.Exports/Models/IBranch.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using GitHub.Collections; - -namespace GitHub.Models -{ - public interface IBranch : ICopyable, - IEquatable, IComparable - { - string Id { get; } - string Name { get; } - IRepositoryModel Repository { get; } - bool IsTracking { get; } - string DisplayName { get; set; } - string Sha { get; } - string TrackedSha { get; } - } -} diff --git a/src/GitHub.Exports/Models/ILocalRepositoryModel.cs b/src/GitHub.Exports/Models/ILocalRepositoryModel.cs deleted file mode 100644 index 1295599b6a..0000000000 --- a/src/GitHub.Exports/Models/ILocalRepositoryModel.cs +++ /dev/null @@ -1,39 +0,0 @@ -using GitHub.Exports; -using GitHub.Primitives; -using System.ComponentModel; -using System.Threading.Tasks; - -namespace GitHub.Models -{ - /// - /// Represents a locally cloned repository. - /// - public interface ILocalRepositoryModel : IRepositoryModel, INotifyPropertyChanged - { - /// - /// Gets the path to the repository on the filesystem. - /// - string LocalPath { get; } - - /// - /// Gets the current branch. - /// - IBranch CurrentBranch { get; } - - /// - /// Updates the url information based on the local path - /// - void Refresh(); - - /// - /// Generates a http(s) url to the repository in the remote server, optionally - /// pointing to a specific file and specific line range in it. - /// - /// The type of repository link to create - /// The file to generate an url to. Optional. - /// A specific line, or (if specifying the as well) the start of a range - /// The end of a line range on the specified file. - /// An UriString with the generated url, or null if the repository has no remote server configured or if it can't be found locally - Task GenerateUrl(LinkType linkType, string path = null, int startLine = -1, int endLine = -1); - } -} diff --git a/src/GitHub.Exports/Models/ILocalRepositoryModelFactory.cs b/src/GitHub.Exports/Models/ILocalRepositoryModelFactory.cs deleted file mode 100644 index 3c8dfed0ce..0000000000 --- a/src/GitHub.Exports/Models/ILocalRepositoryModelFactory.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace GitHub.Models -{ - /// - /// A factory for objects. - /// - public interface ILocalRepositoryModelFactory - { - /// - /// Construct a new . - /// - /// The local path for the repository. - /// A new repository model. - ILocalRepositoryModel Create(string localPath); - } -} diff --git a/src/GitHub.Exports/Models/IRemoteRepositoryModel.cs b/src/GitHub.Exports/Models/IRemoteRepositoryModel.cs deleted file mode 100644 index d6c3224cbd..0000000000 --- a/src/GitHub.Exports/Models/IRemoteRepositoryModel.cs +++ /dev/null @@ -1,47 +0,0 @@ -using GitHub.Collections; -using System; - -namespace GitHub.Models -{ - /// - /// Represents a repository read from the GitHub API. - /// - public interface IRemoteRepositoryModel : IRepositoryModel, ICopyable, - IEquatable, IComparable - { - /// - /// Gets the repository's API ID. - /// - long Id { get; } - - /// - /// Gets the account that is the ower of the repository. - /// - IAccount OwnerAccount { get; } - - /// - /// Gets the date and time at which the repository was created. - /// - DateTimeOffset CreatedAt { get; } - - /// - /// Gets the repository's last update date and time. - /// - DateTimeOffset UpdatedAt { get; } - - /// - /// Gets a value indicating whether the repository is a fork. - /// - bool IsFork { get; } - - /// - /// Gets the repository from which this repository was forked, if any. - /// - IRemoteRepositoryModel Parent { get; } - - /// - /// Gets the default branch for the repository. - /// - IBranch DefaultBranch { get; } - } -} diff --git a/src/GitHub.Exports/Models/IRepositoryModel.cs b/src/GitHub.Exports/Models/IRepositoryModel.cs deleted file mode 100644 index c3c1d577ae..0000000000 --- a/src/GitHub.Exports/Models/IRepositoryModel.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using GitHub.Primitives; -using GitHub.UI; - -namespace GitHub.Models -{ - /// - /// Represents a repository, either local or retreived via the GitHub API. - /// - public interface IRepositoryModel - { - /// - /// Gets the name of the repository. - /// - string Name { get; } - - /// - /// Gets the repository clone URL. - /// - UriString CloneUrl { get; } - - /// - /// Gets the name of the owner of the repository, taken from the clone URL. - /// - string Owner { get; } - - /// - /// Gets an icon for the repository that displays its private and fork state. - /// - Octicon Icon { get; } - - /// - /// Sets the based on a private and fork state. - /// - /// Whether the repository is a private repository. - /// Whether the repository is a fork. - void SetIcon(bool isPrivate, bool isFork); - } -} diff --git a/src/GitHub.Exports/Models/LocalRepositoryModel.cs b/src/GitHub.Exports/Models/LocalRepositoryModel.cs index fe07ef5ab8..edaf63a559 100644 --- a/src/GitHub.Exports/Models/LocalRepositoryModel.cs +++ b/src/GitHub.Exports/Models/LocalRepositoryModel.cs @@ -1,191 +1,25 @@ using System; using System.Diagnostics; using System.Globalization; -using System.IO; -using System.Linq; -using GitHub.Primitives; -using GitHub.UI; -using GitHub.Exports; -using GitHub.Services; -using GitHub.Extensions; -using System.Threading.Tasks; +using System.ComponentModel; namespace GitHub.Models { + /// + /// Represents a locally cloned repository. + /// /// /// A locally cloned repository. /// [DebuggerDisplay("{DebuggerDisplay,nq}")] - public class LocalRepositoryModel : RepositoryModel, ILocalRepositoryModel, IEquatable + public class LocalRepositoryModel : RepositoryModel, INotifyPropertyChanged, IEquatable { - readonly IGitService gitService; - - /// - /// Initializes a new instance of the class. - /// - /// The repository name. - /// The repository's clone URL. - /// The repository's local path. - /// The service used to refresh the repository's URL. - public LocalRepositoryModel(string name, UriString cloneUrl, string localPath, IGitService gitService) - : base(name, cloneUrl) - { - Guard.ArgumentNotEmptyString(localPath, nameof(localPath)); - Guard.ArgumentNotNull(gitService, nameof(gitService)); - - this.gitService = gitService; - LocalPath = localPath; - Icon = Octicon.repo; - } - - /// - /// Initializes a new instance of the class. - /// - /// The repository's local path. - /// The service used to find the repository's URL. - public LocalRepositoryModel(string path, IGitService gitService) - : base(path, gitService) - { - Guard.ArgumentNotNull(gitService, nameof(gitService)); - - this.gitService = gitService; - LocalPath = path; - Icon = Octicon.repo; - } - - /// - /// Updates the clone URL from the local repository. - /// - public void Refresh() - { - if (LocalPath == null) - return; - CloneUrl = gitService.GetUri(LocalPath); - } - - /// - /// Generates a http(s) url to the repository in the remote server, optionally - /// pointing to a specific file and specific line range in it. - /// - /// Type of link to generate - /// The file to generate an url to. Optional. - /// A specific line, or (if specifying the as well) the start of a range - /// The end of a line range on the specified file. - /// An UriString with the generated url, or null if the repository has no remote server configured or if it can't be found locally - public async Task GenerateUrl(LinkType linkType, string path = null, int startLine = -1, int endLine = -1) - { - if (CloneUrl == null) - return null; - - var sha = await gitService.GetLatestPushedSha(path ?? LocalPath); - // this also incidentally checks whether the repo has a valid LocalPath - if (String.IsNullOrEmpty(sha)) - return CloneUrl.ToRepositoryUrl().AbsoluteUri; - - if (path != null && Path.IsPathRooted(path)) - { - // if the path root doesn't match the repository local path, then ignore it - if (!path.StartsWith(LocalPath, StringComparison.OrdinalIgnoreCase)) - { - Debug.Assert(false, String.Format(CultureInfo.CurrentCulture, "GenerateUrl: path {0} doesn't match repository {1}", path, LocalPath)); - path = null; - } - else - path = path.Substring(LocalPath.Length + 1); - } - - if (startLine > 0 && endLine > 0 && startLine > endLine) - { - // if startLine is greater than endLine and both are set, swap them - var temp = startLine; - startLine = endLine; - endLine = temp; - } - - if (startLine == endLine) - { - // if startLine is the same as endLine don't generate a range link - endLine = -1; - } - - return new UriString(GenerateUrl(linkType, CloneUrl.ToRepositoryUrl().AbsoluteUri, sha, path, startLine, endLine)); - } - - const string CommitFormat = "{0}/commit/{1}"; - const string BlobFormat = "{0}/blob/{1}/{2}"; - const string BlameFormat = "{0}/blame/{1}/{2}"; - const string StartLineFormat = "{0}#L{1}"; - const string EndLineFormat = "{0}-L{1}"; - static string GenerateUrl(LinkType linkType, string basePath, string sha, string path, int startLine = -1, int endLine = -1) - { - if (sha == null) - return basePath; - - if (String.IsNullOrEmpty(path)) - return String.Format(CultureInfo.InvariantCulture, CommitFormat, basePath, sha); - - var ret = String.Format(CultureInfo.InvariantCulture, GetLinkFormat(linkType), basePath, sha, path.Replace(@"\", "/")); - - if (startLine < 0) - return ret; - ret = String.Format(CultureInfo.InvariantCulture, StartLineFormat, ret, startLine); - if (endLine < 0) - return ret; - return String.Format(CultureInfo.InvariantCulture, EndLineFormat, ret, endLine); - } - - /// - /// Selects the proper format for the link type, defaults to the blob url when link type is not selected. - /// - /// Type of link to generate - /// The string format of the selected link type - static string GetLinkFormat(LinkType linkType) - { - switch (linkType) - { - case LinkType.Blame: - return BlameFormat; - - case LinkType.Blob: - return BlobFormat; - - default: - return BlobFormat; - } - } - /// /// Gets the local path of the repository. /// - public string LocalPath { get; } - - /// - /// Gets the head SHA of the repository. - /// - public string HeadSha - { - get - { - using (var repo = gitService.GetRepository(LocalPath)) - { - return repo?.Commits.FirstOrDefault()?.Sha ?? string.Empty; - } - } - } - - /// - /// Gets the current branch of the repository. - /// - public IBranch CurrentBranch + public string LocalPath { - get - { - // BranchModel doesn't keep a reference to Repository - using (var repo = gitService.GetRepository(LocalPath)) - { - return new BranchModel(repo?.Head, this, gitService); - } - } + get; set; } /// @@ -211,13 +45,13 @@ public bool Equals(LocalRepositoryModel other) if (ReferenceEquals(this, other)) return true; return other != null && - String.Equals(Name, other.Name) && - String.Equals(Owner, other.Owner) && - String.Equals(CloneUrl, other.CloneUrl) && - String.Equals(LocalPath?.TrimEnd('\\'), other.LocalPath?.TrimEnd('\\'), StringComparison.CurrentCultureIgnoreCase); + string.Equals(Name, other.Name) && + string.Equals(Owner, other.Owner) && + string.Equals(CloneUrl, other.CloneUrl) && + string.Equals(LocalPath?.TrimEnd('\\'), other.LocalPath?.TrimEnd('\\'), StringComparison.CurrentCultureIgnoreCase); } - internal string DebuggerDisplay => String.Format( + internal string DebuggerDisplay => string.Format( CultureInfo.InvariantCulture, "{4}\tOwner: {0} Name: {1} CloneUrl: {2} LocalPath: {3}", Owner, diff --git a/src/GitHub.Exports/Models/RepositoryModel.cs b/src/GitHub.Exports/Models/RepositoryModel.cs index 3ec99829b5..c30488cdfc 100644 --- a/src/GitHub.Exports/Models/RepositoryModel.cs +++ b/src/GitHub.Exports/Models/RepositoryModel.cs @@ -1,9 +1,7 @@ using System; using System.Diagnostics; -using System.IO; using GitHub.Extensions; using GitHub.Primitives; -using GitHub.Services; using GitHub.UI; namespace GitHub.Models @@ -12,7 +10,7 @@ namespace GitHub.Models /// The base class for local and remote repository models. /// [DebuggerDisplay("{DebuggerDisplay,nq}")] - public class RepositoryModel : NotificationAwareObject, IRepositoryModel + public class RepositoryModel : NotificationAwareObject { UriString cloneUrl; Octicon icon; @@ -33,30 +31,14 @@ public RepositoryModel( CloneUrl = cloneUrl; } - /// - /// Initializes a new instance of the class. - /// - /// - /// The path to the local repository from which repository name and clone URL will be - /// extracted. - /// - /// The service used to find the repository's and . - protected RepositoryModel(string path, IGitService gitService) + protected RepositoryModel() { - Guard.ArgumentNotNull(path, nameof(path)); - - var dir = new DirectoryInfo(path); - if (!dir.Exists) - throw new ArgumentException("Path does not exist", nameof(path)); - var uri = gitService.GetUri(path); - Name = uri?.RepositoryName ?? dir.Name; - CloneUrl = gitService.GetUri(path); } /// /// Gets the name of the repository. /// - public string Name { get; } + public string Name { get; set; } /// /// Gets the repository clone URL. @@ -64,7 +46,7 @@ protected RepositoryModel(string path, IGitService gitService) public UriString CloneUrl { get { return cloneUrl; } - protected set + set { if (cloneUrl != value) { @@ -85,7 +67,7 @@ protected set public Octicon Icon { get { return icon; } - protected set + set { if (icon != value) { diff --git a/src/GitHub.Exports/Services/GitService.cs b/src/GitHub.Exports/Services/GitService.cs index a02700f972..310863f0c9 100644 --- a/src/GitHub.Exports/Services/GitService.cs +++ b/src/GitHub.Exports/Services/GitService.cs @@ -1,11 +1,13 @@ -using System.ComponentModel.Composition; -using GitHub.Primitives; -using LibGit2Sharp; -using System; +using System; +using System.IO; +using System.Linq; using System.Threading.Tasks; +using System.ComponentModel.Composition; +using GitHub.UI; using GitHub.Models; -using System.Linq; +using GitHub.Primitives; using GitHub.Extensions; +using LibGit2Sharp; namespace GitHub.Services { @@ -21,6 +23,55 @@ public GitService(IRepositoryFacade repositoryFacade) this.repositoryFacade = repositoryFacade; } + /// + /// Initializes a new instance of the class. + /// + /// The repository's local path. + /// A repository model. + public LocalRepositoryModel CreateLocalRepositoryModel(string localPath) + { + Guard.ArgumentNotNull(localPath, nameof(localPath)); + + var dir = new DirectoryInfo(localPath); + if (!dir.Exists) + { + throw new ArgumentException("Path does not exist", nameof(localPath)); + } + + var cloneUrl = GetUri(localPath); + var name = cloneUrl?.RepositoryName ?? dir.Name; + + var model = new LocalRepositoryModel + { + LocalPath = localPath, + CloneUrl = cloneUrl, + Name = name, + Icon = Octicon.repo + }; + + return model; + } + + public BranchModel GetBranch(LocalRepositoryModel model) + { + var localPath = model.LocalPath; + using (var repo = GetRepository(localPath)) + { + var branch = repo?.Head; + if (branch == null) + { + return null; + } + + return new BranchModel( + name: branch.FriendlyName, + repo: model, + sha: branch.Tip?.Sha, + isTracking: branch.IsTracking, + trackedSha: branch.TrackedBranch?.Tip?.Sha); + } + } + /// /// Returns the URL of the remote for the specified . If the repository /// is null or no remote named origin exists, this method returns null @@ -53,7 +104,7 @@ public UriString GetUri(string path, string remote = "origin") } /// - /// Probes for a git repository and if one is found, returns a instance for the + /// Probes for a git repository and if one is found, returns a instance for the /// repository. /// /// @@ -61,7 +112,7 @@ public UriString GetUri(string path, string remote = "origin") /// walks up the parent directories until it either finds a repository, or reaches the root disk. /// /// The path to start probing - /// An instance of or null + /// An instance of or null public IRepository GetRepository(string path) { var repoPath = repositoryFacade.Discover(path); diff --git a/src/GitHub.Exports/Services/IDialogService.cs b/src/GitHub.Exports/Services/IDialogService.cs index 55a09c1493..ea26dd71e1 100644 --- a/src/GitHub.Exports/Services/IDialogService.cs +++ b/src/GitHub.Exports/Services/IDialogService.cs @@ -37,7 +37,7 @@ public interface IDialogService /// The re-clone dialog is shown from the VS2017+ start page when the user wants to check /// out a repository that was previously checked out on another machine. /// - Task ShowReCloneDialog(IRepositoryModel repository); + Task ShowReCloneDialog(RepositoryModel repository); /// /// Shows the Create Gist dialog. @@ -70,6 +70,6 @@ public interface IDialogService /// /// The repository to fork. /// The connection to use. May not be null. - Task ShowForkDialog(ILocalRepositoryModel repository, IConnection connection); + Task ShowForkDialog(LocalRepositoryModel repository, IConnection connection); } } diff --git a/src/GitHub.Exports/Services/IGitService.cs b/src/GitHub.Exports/Services/IGitService.cs index 91453ad792..4ff850c21b 100644 --- a/src/GitHub.Exports/Services/IGitService.cs +++ b/src/GitHub.Exports/Services/IGitService.cs @@ -7,6 +7,20 @@ namespace GitHub.Services { public interface IGitService { + /// + /// Initializes a new instance of the class. + /// + /// The repository's local path. + /// A repository model. + LocalRepositoryModel CreateLocalRepositoryModel(string localPath); + + /// + /// Creates a new branch model for the current branch. + /// + /// The to create a current branch model for. + /// A new branch model. + BranchModel GetBranch(LocalRepositoryModel model); + /// /// Returns the URL of the remote for the specified . If the repository /// is null or no remote exists, this method returns null @@ -30,7 +44,7 @@ public interface IGitService UriString GetUri(string path, string remote = "origin"); /// - /// Probes for a git repository and if one is found, returns a instance for the + /// Probes for a git repository and if one is found, returns a instance for the /// repository. /// /// @@ -38,7 +52,7 @@ public interface IGitService /// walks up the parent directories until it either finds a repository, or reaches the root disk. /// /// The path to start probing - /// An instance of or null + /// An instance of or null IRepository GetRepository(string path); /// diff --git a/src/GitHub.Exports/Services/ILocalRepositories.cs b/src/GitHub.Exports/Services/ILocalRepositories.cs index 1d77fbd13c..6a463f1b7f 100644 --- a/src/GitHub.Exports/Services/ILocalRepositories.cs +++ b/src/GitHub.Exports/Services/ILocalRepositories.cs @@ -13,7 +13,7 @@ public interface ILocalRepositories /// /// Gets the currently known local repositories. /// - IReadOnlyObservableCollection Repositories { get; } + IReadOnlyObservableCollection Repositories { get; } /// /// Updates . diff --git a/src/GitHub.Exports/Services/ITeamExplorerContext.cs b/src/GitHub.Exports/Services/ITeamExplorerContext.cs index d90c64e867..e09d9aa3b4 100644 --- a/src/GitHub.Exports/Services/ITeamExplorerContext.cs +++ b/src/GitHub.Exports/Services/ITeamExplorerContext.cs @@ -20,7 +20,7 @@ public interface ITeamExplorerContext : INotifyPropertyChanged /// /// This property might be changed by a non-UI thread. /// - ILocalRepositoryModel ActiveRepository { get; } + LocalRepositoryModel ActiveRepository { get; } /// /// Fired when the CurrentBranch or HeadSha changes. diff --git a/src/GitHub.Exports/Services/ITeamExplorerServiceHolder.cs b/src/GitHub.Exports/Services/ITeamExplorerServiceHolder.cs index dd6ca75bd1..16316aceea 100644 --- a/src/GitHub.Exports/Services/ITeamExplorerServiceHolder.cs +++ b/src/GitHub.Exports/Services/ITeamExplorerServiceHolder.cs @@ -1,6 +1,7 @@ using System; using GitHub.Primitives; using GitHub.Models; +using Microsoft.VisualStudio.Threading; namespace GitHub.Services { @@ -17,6 +18,7 @@ public interface ITeamExplorerServiceHolder /// changes in the source control context. /// IServiceProvider ServiceProvider { get; set; } + /// /// Clears the current ServiceProvider if it matches the one that is passed in. /// This is usually called on Dispose, which might happen after another section @@ -25,28 +27,23 @@ public interface ITeamExplorerServiceHolder /// /// If the current ServiceProvider matches this, clear it void ClearServiceProvider(IServiceProvider provider); + /// - /// A IGitRepositoryInfo representing the currently active repository - /// - ILocalRepositoryModel ActiveRepo { get; } - /// - /// Subscribe to be notified when the active repository is set and Notify is called. + /// A service that can be used for repository changed events. /// - /// The instance that is interested in being called (or a unique key/object for that instance) - /// The handler to call when ActiveRepo is set - void Subscribe(object who, Action handler); + ITeamExplorerContext TeamExplorerContext { get; } + /// - /// Unsubscribe from notifications + /// A service for avoiding deadlocks and marshaling tasks onto the UI thread. /// - /// The instance/key that previously subscribed to notifications - void Unsubscribe(object who); + JoinableTaskFactory JoinableTaskFactory { get; } IGitAwareItem HomeSection { get; } } public interface IGitAwareItem { - ILocalRepositoryModel ActiveRepo { get; } + LocalRepositoryModel ActiveRepo { get; } /// /// Represents the web URL of the repository on GitHub.com, even if the origin is an SSH address. diff --git a/src/GitHub.Exports/Services/IVSGitExt.cs b/src/GitHub.Exports/Services/IVSGitExt.cs index 3491aef125..076191f0a3 100644 --- a/src/GitHub.Exports/Services/IVSGitExt.cs +++ b/src/GitHub.Exports/Services/IVSGitExt.cs @@ -6,7 +6,7 @@ namespace GitHub.Services { public interface IVSGitExt { - IReadOnlyList ActiveRepositories { get; } + IReadOnlyList ActiveRepositories { get; } event Action ActiveRepositoriesChanged; void RefreshActiveRepositories(); } diff --git a/src/GitHub.Exports/Services/IVSGitServices.cs b/src/GitHub.Exports/Services/IVSGitServices.cs index 3795cd5974..8343ac92b2 100644 --- a/src/GitHub.Exports/Services/IVSGitServices.cs +++ b/src/GitHub.Exports/Services/IVSGitServices.cs @@ -30,7 +30,7 @@ Task Clone( string GetActiveRepoPath(); LibGit2Sharp.IRepository GetActiveRepo(); - IEnumerable GetKnownRepositories(); + IEnumerable GetKnownRepositories(); string SetDefaultProjectPath(string path); } } \ No newline at end of file diff --git a/src/GitHub.Exports/ViewModels/GitHubPane/IGitHubPaneViewModel.cs b/src/GitHub.Exports/ViewModels/GitHubPane/IGitHubPaneViewModel.cs index 6e44ca5bf0..ecab58b39d 100644 --- a/src/GitHub.Exports/ViewModels/GitHubPane/IGitHubPaneViewModel.cs +++ b/src/GitHub.Exports/ViewModels/GitHubPane/IGitHubPaneViewModel.cs @@ -55,7 +55,7 @@ public interface IGitHubPaneViewModel : IViewModel /// /// Gets the local repository. /// - ILocalRepositoryModel LocalRepository { get; } + LocalRepositoryModel LocalRepository { get; } /// /// Gets or sets the search query for the current page. diff --git a/src/GitHub.InlineReviews/Services/IPullRequestSessionService.cs b/src/GitHub.InlineReviews/Services/IPullRequestSessionService.cs index a867a4393a..7a3d9c845d 100644 --- a/src/GitHub.InlineReviews/Services/IPullRequestSessionService.cs +++ b/src/GitHub.InlineReviews/Services/IPullRequestSessionService.cs @@ -23,7 +23,7 @@ public interface IPullRequestSessionService /// The relative path to the file. /// Task> Diff( - ILocalRepositoryModel repository, + LocalRepositoryModel repository, string baseSha, string headSha, string relativePath); @@ -38,7 +38,7 @@ Task> Diff( /// The contents of the file. /// Task> Diff( - ILocalRepositoryModel repository, + LocalRepositoryModel repository, string baseSha, string headSha, string relativePath, @@ -83,7 +83,7 @@ IReadOnlyList> UpdateCommentThreads( /// pushed to origin; otherwise false. /// Task IsUnmodifiedAndPushed( - ILocalRepositoryModel repository, + LocalRepositoryModel repository, string relativePath, byte[] contents); @@ -98,7 +98,7 @@ Task IsUnmodifiedAndPushed( /// The contents of the file, or null if the file was not found at the specified commit. /// Task ExtractFileFromGit( - ILocalRepositoryModel repository, + LocalRepositoryModel repository, int pullRequestNumber, string sha, string relativePath); @@ -124,7 +124,7 @@ Task ExtractFileFromGit( /// /// The repository. /// The tip SHA. - Task GetTipSha(ILocalRepositoryModel repository); + Task GetTipSha(LocalRepositoryModel repository); /// /// Asynchronously reads the contents of a file. @@ -163,7 +163,7 @@ Task ExtractFileFromGit( /// /// The merge base SHA for the PR. /// - Task GetPullRequestMergeBase(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest); + Task GetPullRequestMergeBase(LocalRepositoryModel repository, PullRequestDetailModel pullRequest); /// /// Gets the GraphQL ID for a pull request. @@ -173,7 +173,7 @@ Task ExtractFileFromGit( /// The pull request number. /// Task GetGraphQLPullRequestId( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string repositoryOwner, int number); @@ -199,7 +199,7 @@ Task GetGraphQLPullRequestId( /// The GraphQL ID of the pull request. /// The updated state of the pull request. Task CreatePendingReview( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string pullRequestId); /// @@ -209,7 +209,7 @@ Task CreatePendingReview( /// The GraphQL ID of the review. /// The updated state of the pull request. Task CancelPendingReview( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string reviewId); /// @@ -222,7 +222,7 @@ Task CancelPendingReview( /// The review event. /// The updated state of the pull request. Task PostReview( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string pullRequestId, string commitId, string body, @@ -237,7 +237,7 @@ Task PostReview( /// The review event. /// The updated state of the pull request. Task SubmitPendingReview( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string pendingReviewId, string body, PullRequestReviewEvent e); @@ -254,10 +254,10 @@ Task SubmitPendingReview( /// The updated state of the pull request. /// /// This method posts a new pull request comment to a pending review started by - /// . + /// . /// Task PostPendingReviewComment( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string pendingReviewId, string body, string commitId, @@ -274,10 +274,10 @@ Task PostPendingReviewComment( /// The updated state of the pull request. /// /// The method posts a new pull request comment to a pending review started by - /// . + /// . /// Task PostPendingReviewCommentReply( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string pendingReviewId, string body, string inReplyTo); @@ -297,7 +297,7 @@ Task PostPendingReviewCommentReply( /// pull request review. /// Task PostStandaloneReviewComment( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string pullRequestId, string body, string commitId, @@ -313,7 +313,7 @@ Task PostStandaloneReviewComment( /// The GraphQL ID of the comment to reply to. /// The updated state of the pull request. Task PostStandaloneReviewCommentReply( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string pullRequestId, string body, string inReplyTo); @@ -326,7 +326,7 @@ Task PostStandaloneReviewCommentReply( /// The pull request id of the comment /// The pull request comment number. /// The updated state of the pull request. - Task DeleteComment(ILocalRepositoryModel localRepository, + Task DeleteComment(LocalRepositoryModel localRepository, string remoteRepositoryOwner, int pullRequestId, int commentDatabaseId); @@ -339,7 +339,7 @@ Task DeleteComment(ILocalRepositoryModel localRepository /// The pull request comment node id. /// The replacement comment body. /// The updated state of the pull request. - Task EditComment(ILocalRepositoryModel localRepository, + Task EditComment(LocalRepositoryModel localRepository, string remoteRepositoryOwner, string commentNodeId, string body); diff --git a/src/GitHub.InlineReviews/Services/PullRequestSession.cs b/src/GitHub.InlineReviews/Services/PullRequestSession.cs index 0759b996d2..88f2995824 100644 --- a/src/GitHub.InlineReviews/Services/PullRequestSession.cs +++ b/src/GitHub.InlineReviews/Services/PullRequestSession.cs @@ -42,7 +42,7 @@ public PullRequestSession( IPullRequestSessionService service, ActorModel user, PullRequestDetailModel pullRequest, - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string repositoryOwner, bool isCheckedOut) { @@ -402,7 +402,7 @@ private set public IObservable PullRequestChanged => pullRequestChanged; /// - public ILocalRepositoryModel LocalRepository { get; } + public LocalRepositoryModel LocalRepository { get; } /// public string RepositoryOwner { get; } diff --git a/src/GitHub.InlineReviews/Services/PullRequestSessionManager.cs b/src/GitHub.InlineReviews/Services/PullRequestSessionManager.cs index 0c62775f9a..d9f31eba5e 100644 --- a/src/GitHub.InlineReviews/Services/PullRequestSessionManager.cs +++ b/src/GitHub.InlineReviews/Services/PullRequestSessionManager.cs @@ -35,7 +35,7 @@ public class PullRequestSessionManager : ReactiveObject, IPullRequestSessionMana new Dictionary, WeakReference>(); TaskCompletionSource initialized; IPullRequestSession currentSession; - ILocalRepositoryModel repository; + LocalRepositoryModel repository; /// /// Initializes a new instance of the class. @@ -182,7 +182,7 @@ public PullRequestTextBufferInfo GetTextBufferInfo(ITextBuffer buffer) return null; } - async Task RepoChanged(ILocalRepositoryModel localRepositoryModel) + async Task RepoChanged(LocalRepositoryModel localRepositoryModel) { repository = localRepositoryModel; CurrentSession = null; @@ -203,7 +203,7 @@ async Task StatusChanged() var session = CurrentSession; var pr = await service.GetPullRequestForCurrentBranch(repository).FirstOrDefaultAsync(); - if (pr != null) + if (pr != default) { var changePR = pr.Item1 != (session?.PullRequest.BaseRepositoryOwner) || diff --git a/src/GitHub.InlineReviews/Services/PullRequestSessionService.cs b/src/GitHub.InlineReviews/Services/PullRequestSessionService.cs index 56f8570903..17342cab4e 100644 --- a/src/GitHub.InlineReviews/Services/PullRequestSessionService.cs +++ b/src/GitHub.InlineReviews/Services/PullRequestSessionService.cs @@ -80,7 +80,7 @@ public PullRequestSessionService( } /// - public virtual async Task> Diff(ILocalRepositoryModel repository, string baseSha, string headSha, string relativePath) + public virtual async Task> Diff(LocalRepositoryModel repository, string baseSha, string headSha, string relativePath) { using (var repo = await GetRepository(repository)) { @@ -89,7 +89,7 @@ public virtual async Task> Diff(ILocalRepositoryModel r } /// - public virtual async Task> Diff(ILocalRepositoryModel repository, string baseSha, string headSha, string relativePath, byte[] contents) + public virtual async Task> Diff(LocalRepositoryModel repository, string baseSha, string headSha, string relativePath, byte[] contents) { using (var repo = await GetRepository(repository)) { @@ -217,7 +217,7 @@ public ITextDocument GetDocument(ITextBuffer buffer) } /// - public virtual async Task GetTipSha(ILocalRepositoryModel repository) + public virtual async Task GetTipSha(LocalRepositoryModel repository) { using (var repo = await GetRepository(repository)) { @@ -226,7 +226,7 @@ public virtual async Task GetTipSha(ILocalRepositoryModel repository) } /// - public async Task IsUnmodifiedAndPushed(ILocalRepositoryModel repository, string relativePath, byte[] contents) + public async Task IsUnmodifiedAndPushed(LocalRepositoryModel repository, string relativePath, byte[] contents) { using (var repo = await GetRepository(repository)) { @@ -238,7 +238,7 @@ public async Task IsUnmodifiedAndPushed(ILocalRepositoryModel repository, } public async Task ExtractFileFromGit( - ILocalRepositoryModel repository, + LocalRepositoryModel repository, int pullRequestNumber, string sha, string relativePath) @@ -387,7 +387,7 @@ public virtual async Task ReadViewer(HostAddress address) } public async Task GetGraphQLPullRequestId( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string repositoryOwner, int number) { @@ -403,7 +403,7 @@ public async Task GetGraphQLPullRequestId( } /// - public virtual async Task GetPullRequestMergeBase(ILocalRepositoryModel repository, PullRequestDetailModel pullRequest) + public virtual async Task GetPullRequestMergeBase(LocalRepositoryModel repository, PullRequestDetailModel pullRequest) { var baseSha = pullRequest.BaseRefSha; var headSha = pullRequest.HeadRefSha; @@ -447,7 +447,7 @@ public virtual ISubject CreateRebuildSignal() /// public async Task CreatePendingReview( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string pullRequestId) { var address = HostAddress.Create(localRepository.CloneUrl.Host); @@ -462,7 +462,7 @@ public async Task CreatePendingReview( /// public async Task CancelPendingReview( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string reviewId) { var address = HostAddress.Create(localRepository.CloneUrl.Host); @@ -487,7 +487,7 @@ public async Task CancelPendingReview( /// public async Task PostReview( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string pullRequestId, string commitId, string body, @@ -518,7 +518,7 @@ public async Task PostReview( } public async Task SubmitPendingReview( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string pendingReviewId, string body, PullRequestReviewEvent e) @@ -548,7 +548,7 @@ public async Task SubmitPendingReview( /// public async Task PostPendingReviewComment( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string pendingReviewId, string body, string commitId, @@ -582,7 +582,7 @@ public async Task PostPendingReviewComment( /// public async Task PostPendingReviewCommentReply( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string pendingReviewId, string body, string inReplyTo) @@ -612,7 +612,7 @@ public async Task PostPendingReviewCommentReply( /// public async Task PostStandaloneReviewComment( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string pullRequestId, string body, string commitId, @@ -654,7 +654,7 @@ public async Task PostStandaloneReviewComment( /// public async Task PostStandaloneReviewCommentReply( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string pullRequestId, string body, string inReplyTo) @@ -666,7 +666,7 @@ public async Task PostStandaloneReviewCommentReply( /// public async Task DeleteComment( - ILocalRepositoryModel localRepository, + LocalRepositoryModel localRepository, string remoteRepositoryOwner, int pullRequestId, int commentDatabaseId) @@ -684,7 +684,7 @@ await apiClient.DeletePullRequestReviewComment( } /// - public async Task EditComment(ILocalRepositoryModel localRepository, + public async Task EditComment(LocalRepositoryModel localRepository, string remoteRepositoryOwner, string commentNodeId, string body) @@ -710,7 +710,7 @@ public async Task EditComment(ILocalRepositoryModel loca return await ReadPullRequestDetail(address, result.Login, localRepository.Name, result.Number); } - async Task<(string id, string owner, int number)> CreatePendingReviewCore(ILocalRepositoryModel localRepository, string pullRequestId) + async Task<(string id, string owner, int number)> CreatePendingReviewCore(LocalRepositoryModel localRepository, string pullRequestId) { var address = HostAddress.Create(localRepository.CloneUrl.Host); var graphql = await graphqlFactory.CreateConnection(address); @@ -747,7 +747,7 @@ int GetUpdatedLineNumber(IInlineCommentThreadModel thread, IEnumerable GetRepository(ILocalRepositoryModel repository) + Task GetRepository(LocalRepositoryModel repository) { return Task.Factory.StartNew(() => gitService.GetRepository(repository.LocalPath)); } diff --git a/src/GitHub.InlineReviews/Services/PullRequestStatusBarManager.cs b/src/GitHub.InlineReviews/Services/PullRequestStatusBarManager.cs index 8bbc3ed148..94583183df 100644 --- a/src/GitHub.InlineReviews/Services/PullRequestStatusBarManager.cs +++ b/src/GitHub.InlineReviews/Services/PullRequestStatusBarManager.cs @@ -80,7 +80,7 @@ public void StartShowingStatus() } } - async Task RefreshCurrentSession(ILocalRepositoryModel repository, IPullRequestSession session) + async Task RefreshCurrentSession(LocalRepositoryModel repository, IPullRequestSession session) { var showStatus = await IsDotComOrEnterpriseRepository(repository); if (!showStatus) @@ -93,7 +93,7 @@ async Task RefreshCurrentSession(ILocalRepositoryModel repository, IPullRequestS ShowStatus(viewModel); } - async Task IsDotComOrEnterpriseRepository(ILocalRepositoryModel repository) + async Task IsDotComOrEnterpriseRepository(LocalRepositoryModel repository) { var cloneUrl = repository?.CloneUrl; if (cloneUrl == null) diff --git a/src/GitHub.StartPage/StartPagePackage.cs b/src/GitHub.StartPage/StartPagePackage.cs index 245746f08a..15069c9779 100644 --- a/src/GitHub.StartPage/StartPagePackage.cs +++ b/src/GitHub.StartPage/StartPagePackage.cs @@ -50,7 +50,7 @@ public async Task AcquireCodeContainerAsync(RemoteCodeContainer o } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "cancellationToken")] - async Task RunAcquisition(IProgress downloadProgress, CancellationToken cancellationToken, IRepositoryModel repository) + async Task RunAcquisition(IProgress downloadProgress, CancellationToken cancellationToken, RepositoryModel repository) { CloneDialogResult request = null; @@ -84,7 +84,7 @@ async Task RunAcquisition(IProgress download async Task ShowCloneDialog( IGitHubServiceProvider gitHubServiceProvider, IProgress progress, - IRepositoryModel repository = null) + RepositoryModel repository = null) { var dialogService = gitHubServiceProvider.GetService(); var cloneService = gitHubServiceProvider.GetService(); diff --git a/src/GitHub.TeamFoundation.14/Base/TeamExplorerNavigationItemBase.cs b/src/GitHub.TeamFoundation.14/Base/TeamExplorerNavigationItemBase.cs index f8b73819db..f01f657860 100644 --- a/src/GitHub.TeamFoundation.14/Base/TeamExplorerNavigationItemBase.cs +++ b/src/GitHub.TeamFoundation.14/Base/TeamExplorerNavigationItemBase.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Drawing; +using System.ComponentModel; using GitHub.Api; using GitHub.Extensions; using GitHub.Services; @@ -14,6 +15,7 @@ namespace GitHub.VisualStudio.Base { public class TeamExplorerNavigationItemBase : TeamExplorerItemBase, ITeamExplorerNavigationItem2 { + readonly ITeamExplorerServiceHolder holder; readonly Octicon octicon; public TeamExplorerNavigationItemBase(IGitHubServiceProvider serviceProvider, @@ -24,6 +26,7 @@ public TeamExplorerNavigationItemBase(IGitHubServiceProvider serviceProvider, Guard.ArgumentNotNull(apiFactory, nameof(apiFactory)); Guard.ArgumentNotNull(holder, nameof(holder)); + this.holder = holder; this.octicon = octicon; IsVisible = false; @@ -36,7 +39,31 @@ public TeamExplorerNavigationItemBase(IGitHubServiceProvider serviceProvider, Invalidate(); }; - holder.Subscribe(this, UpdateRepo); + ActiveRepo = holder.TeamExplorerContext.ActiveRepository; + holder.TeamExplorerContext.PropertyChanged += TeamExplorerContext_PropertyChanged; + holder.TeamExplorerContext.StatusChanged += TeamExplorerContext_StatusChanged; + } + + void TeamExplorerContext_StatusChanged(object sender, EventArgs e) + { + UpdateRepoOnMainThread(holder.TeamExplorerContext.ActiveRepository); + } + + void TeamExplorerContext_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(holder.TeamExplorerContext.ActiveRepository)) + { + UpdateRepoOnMainThread(holder.TeamExplorerContext.ActiveRepository); + } + } + + void UpdateRepoOnMainThread(LocalRepositoryModel repo) + { + holder.JoinableTaskFactory.RunAsync(async () => + { + await holder.JoinableTaskFactory.SwitchToMainThreadAsync(); + UpdateRepo(repo); + }).Task.Forget(); } public override async void Invalidate() @@ -52,7 +79,7 @@ void OnThemeChanged() Icon = SharedResources.GetDrawingForIcon(octicon, dark ? Colors.DarkThemeNavigationItem : Colors.LightThemeNavigationItem, theme); } - void UpdateRepo(ILocalRepositoryModel repo) + void UpdateRepo(LocalRepositoryModel repo) { var changed = ActiveRepo != repo; ActiveRepo = repo; @@ -75,7 +102,8 @@ protected void OpenInBrowser(Lazy browser, string endpoint void Unsubscribe() { - holder.Unsubscribe(this); + holder.TeamExplorerContext.PropertyChanged -= TeamExplorerContext_PropertyChanged; + holder.TeamExplorerContext.StatusChanged -= TeamExplorerContext_StatusChanged; ; } bool disposed; @@ -109,7 +137,7 @@ public object Icon Image image; public Image Image { - get{ return image; } + get { return image; } set { image = value; this.RaisePropertyChange(); } } } diff --git a/src/GitHub.TeamFoundation.14/Base/TeamExplorerServiceHolder.cs b/src/GitHub.TeamFoundation.14/Base/TeamExplorerServiceHolder.cs index 20879c8fbe..8d6f002b69 100644 --- a/src/GitHub.TeamFoundation.14/Base/TeamExplorerServiceHolder.cs +++ b/src/GitHub.TeamFoundation.14/Base/TeamExplorerServiceHolder.cs @@ -1,16 +1,10 @@ using System; -using System.Collections.Generic; using System.ComponentModel.Composition; -using System.Linq; using GitHub.Extensions; -using GitHub.Logging; -using GitHub.Models; using GitHub.Services; -using Serilog; using Microsoft.TeamFoundation.Controls; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Threading; -using System.Windows; namespace GitHub.VisualStudio.Base { @@ -18,21 +12,15 @@ namespace GitHub.VisualStudio.Base [PartCreationPolicy(CreationPolicy.Shared)] public class TeamExplorerServiceHolder : ITeamExplorerServiceHolder { - static readonly ILogger log = LogManager.ForContext(); - - readonly Dictionary> activeRepoHandlers = new Dictionary>(); - ILocalRepositoryModel activeRepo; - bool activeRepoNotified = false; - IServiceProvider serviceProvider; - readonly IVSGitExt gitService; /// /// This class relies on IVSGitExt that provides information when VS switches repositories. /// - /// Used for monitoring the active repository. + /// Used for monitoring the active repository. [ImportingConstructor] - TeamExplorerServiceHolder(IVSGitExt gitService) : this(gitService, ThreadHelper.JoinableTaskContext) + TeamExplorerServiceHolder(ITeamExplorerContext teamExplorerContext) : + this(teamExplorerContext, ThreadHelper.JoinableTaskContext) { } @@ -41,19 +29,13 @@ public class TeamExplorerServiceHolder : ITeamExplorerServiceHolder /// /// Used for monitoring the active repository. /// Used for switching to the Main thread. - public TeamExplorerServiceHolder(IVSGitExt gitService, JoinableTaskContext joinableTaskContext) + public TeamExplorerServiceHolder(ITeamExplorerContext teamExplorerContext, JoinableTaskContext joinableTaskContext) { JoinableTaskCollection = joinableTaskContext.CreateCollection(); JoinableTaskCollection.DisplayName = nameof(TeamExplorerServiceHolder); JoinableTaskFactory = joinableTaskContext.CreateFactory(JoinableTaskCollection); - // This might be null in Blend or SafeMode - if (gitService != null) - { - this.gitService = gitService; - UpdateActiveRepo(); - gitService.ActiveRepositoriesChanged += UpdateActiveRepo; - } + TeamExplorerContext = teamExplorerContext; } // set by the sections when they get initialized @@ -71,58 +53,6 @@ public IServiceProvider ServiceProvider } } - public ILocalRepositoryModel ActiveRepo - { - get { return activeRepo; } - private set - { - if (activeRepo == value) - return; - if (activeRepo != null) - activeRepo.PropertyChanged -= ActiveRepoPropertyChanged; - activeRepo = value; - if (activeRepo != null) - activeRepo.PropertyChanged += ActiveRepoPropertyChanged; - NotifyActiveRepo(); - } - } - - public void Subscribe(object who, Action handler) - { - Guard.ArgumentNotNull(who, nameof(who)); - Guard.ArgumentNotNull(handler, nameof(handler)); - - bool notificationsExist; - ILocalRepositoryModel repo; - lock (activeRepoHandlers) - { - repo = ActiveRepo; - notificationsExist = activeRepoNotified; - if (!activeRepoHandlers.ContainsKey(who)) - activeRepoHandlers.Add(who, handler); - else - activeRepoHandlers[who] = handler; - } - - // the repo url might have changed and we don't get notifications - // for that, so this is a good place to refresh it in case that happened - repo?.Refresh(); - - // if the active repo hasn't changed and there's notifications queued up, - // notify the subscriber. If the repo has changed, the set above will trigger - // notifications so we don't have to do it here. - if (repo == ActiveRepo && notificationsExist) - handler(repo); - } - - public void Unsubscribe(object who) - { - Guard.ArgumentNotNull(who, nameof(who)); - - if (activeRepoHandlers.ContainsKey(who)) - activeRepoHandlers.Remove(who); - } - /// /// Clears the current ServiceProvider if it matches the one that is passed in. /// This is usually called on Dispose, which might happen after another section @@ -140,39 +70,6 @@ public void ClearServiceProvider(IServiceProvider provider) ServiceProvider = null; } - void NotifyActiveRepo() - { - lock (activeRepoHandlers) - { - activeRepoNotified = true; - foreach (var handler in activeRepoHandlers.Values) - handler(activeRepo); - } - } - - void UpdateActiveRepo() - { - var repo = gitService.ActiveRepositories.FirstOrDefault(); - - if (!Equals(repo, ActiveRepo)) - { - // Fire property change events on Main thread - JoinableTaskFactory.RunAsync(async () => - { - await JoinableTaskFactory.SwitchToMainThreadAsync(); - ActiveRepo = repo; - }).Task.Forget(log); - } - } - - void ActiveRepoPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) - { - Guard.ArgumentNotNull(e, nameof(e)); - - if (e.PropertyName == "CloneUrl") - ActiveRepo = sender as ILocalRepositoryModel; - } - public IGitAwareItem HomeSection { get @@ -192,6 +89,7 @@ ITeamExplorerPage PageService } public JoinableTaskCollection JoinableTaskCollection { get; } - JoinableTaskFactory JoinableTaskFactory { get; } + public JoinableTaskFactory JoinableTaskFactory { get; } + public ITeamExplorerContext TeamExplorerContext { get; } } } diff --git a/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection.cs b/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection.cs index bc0ef22a01..7c18915ed2 100644 --- a/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection.cs +++ b/src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection.cs @@ -28,6 +28,8 @@ namespace GitHub.VisualStudio.TeamExplorer.Connect public class GitHubConnectSection : TeamExplorerSectionBase, IGitHubConnectSection { static readonly ILogger log = LogManager.ForContext(); + readonly ISimpleApiClientFactory apiFactory; + readonly ITeamExplorerServiceHolder holder; readonly IPackageSettings packageSettings; readonly ITeamExplorerServices teamExplorerServices; readonly int sectionIndex; @@ -85,15 +87,15 @@ public bool ShowRetry private set { showRetry = value; this.RaisePropertyChange(); } } - IReactiveDerivedList repositories; - public IReactiveDerivedList Repositories + IReactiveDerivedList repositories; + public IReactiveDerivedList Repositories { get { return repositories; } private set { repositories = value; this.RaisePropertyChange(); } } - ILocalRepositoryModel selectedRepository; - public ILocalRepositoryModel SelectedRepository + LocalRepositoryModel selectedRepository; + public LocalRepositoryModel SelectedRepository { get { return selectedRepository; } set { selectedRepository = value; this.RaisePropertyChange(); } @@ -101,8 +103,6 @@ public ILocalRepositoryModel SelectedRepository public ICommand Clone { get; } - internal ITeamExplorerServiceHolder Holder => holder; - public GitHubConnectSection(IGitHubServiceProvider serviceProvider, ISimpleApiClientFactory apiFactory, ITeamExplorerServiceHolder holder, @@ -127,6 +127,8 @@ public GitHubConnectSection(IGitHubServiceProvider serviceProvider, IsVisible = false; sectionIndex = index; + this.apiFactory = apiFactory; + this.holder = holder; this.packageSettings = packageSettings; this.teamExplorerServices = teamExplorerServices; this.localRepositories = localRepositories; @@ -312,7 +314,7 @@ async void UpdateRepositoryList(object sender, NotifyCollectionChangedEventArgs // so we can handle just one new entry separately if (isCloning || isCreating) { - var newrepo = e.NewItems.Cast().First(); + var newrepo = e.NewItems.Cast().First(); SelectedRepository = newrepo; if (isCreating) @@ -325,7 +327,7 @@ async void UpdateRepositoryList(object sender, NotifyCollectionChangedEventArgs try { // TODO: Cache the icon state. - var api = await ApiFactory.Create(newrepo.CloneUrl); + var api = await apiFactory.Create(newrepo.CloneUrl); var repo = await api.GetRepository(); newrepo.SetIcon(repo.Private, repo.Fork); } @@ -341,16 +343,16 @@ async void UpdateRepositoryList(object sender, NotifyCollectionChangedEventArgs else { e.NewItems - .Cast() + .Cast() .ForEach(async r => { - if (Equals(Holder.ActiveRepo, r)) + if (Equals(holder.TeamExplorerContext.ActiveRepository, r)) SelectedRepository = r; try { // TODO: Cache the icon state. - var api = await ApiFactory.Create(r.CloneUrl); + var api = await apiFactory.Create(r.CloneUrl); var repo = await api.GetRepository(); r.SetIcon(repo.Private, repo.Fork); } @@ -366,7 +368,7 @@ async void UpdateRepositoryList(object sender, NotifyCollectionChangedEventArgs } } - void HandleCreatedRepo(ILocalRepositoryModel newrepo) + void HandleCreatedRepo(LocalRepositoryModel newrepo) { Guard.ArgumentNotNull(newrepo, nameof(newrepo)); @@ -375,7 +377,7 @@ void HandleCreatedRepo(ILocalRepositoryModel newrepo) ShowNotification(newrepo, msg); } - void HandleClonedRepo(ILocalRepositoryModel newrepo) + void HandleClonedRepo(LocalRepositoryModel newrepo) { Guard.ArgumentNotNull(newrepo, nameof(newrepo)); @@ -387,7 +389,7 @@ void HandleClonedRepo(ILocalRepositoryModel newrepo) ShowNotification(newrepo, msg); } - void ShowNotification(ILocalRepositoryModel newrepo, string msg) + void ShowNotification(LocalRepositoryModel newrepo, string msg) { Guard.ArgumentNotNull(newrepo, nameof(newrepo)); @@ -457,7 +459,7 @@ public void Retry() public bool OpenRepository() { - var old = Repositories.FirstOrDefault(x => x.Equals(Holder.ActiveRepo)); + var old = Repositories.FirstOrDefault(x => x.Equals(holder.TeamExplorerContext.ActiveRepository)); if (!Equals(SelectedRepository, old)) { try diff --git a/src/GitHub.TeamFoundation.14/GitHub.TeamFoundation.14.csproj b/src/GitHub.TeamFoundation.14/GitHub.TeamFoundation.14.csproj index a27bc63e3a..d4b56fc610 100644 --- a/src/GitHub.TeamFoundation.14/GitHub.TeamFoundation.14.csproj +++ b/src/GitHub.TeamFoundation.14/GitHub.TeamFoundation.14.csproj @@ -163,7 +163,6 @@ - diff --git a/src/GitHub.TeamFoundation.14/RegistryHelper.cs b/src/GitHub.TeamFoundation.14/RegistryHelper.cs index f4fa44eb31..e36aa9f536 100644 --- a/src/GitHub.TeamFoundation.14/RegistryHelper.cs +++ b/src/GitHub.TeamFoundation.14/RegistryHelper.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Globalization; using System.IO; @@ -23,13 +24,13 @@ static RegistryKey OpenGitKey(string path) return Registry.CurrentUser.OpenSubKey(keyName, true); } - internal static IEnumerable PokeTheRegistryForRepositoryList() + internal static IEnumerable PokeTheRegistryForRepositoryList() { using (var key = OpenGitKey("Repositories")) { if (key == null) { - return Enumerable.Empty(); + return Enumerable.Empty(); } return key.GetSubKeyNames().Select(x => @@ -40,7 +41,7 @@ internal static IEnumerable PokeTheRegistryForRepositoryL { var path = subkey?.GetValue("Path") as string; if (path != null && Directory.Exists(path)) - return new LocalRepositoryModel(path, GitService.GitServiceHelper); + return GitService.GitServiceHelper.CreateLocalRepositoryModel(path); } catch (Exception) { diff --git a/src/GitHub.TeamFoundation.14/Services/LocalRepositoryModelFactory.cs b/src/GitHub.TeamFoundation.14/Services/LocalRepositoryModelFactory.cs deleted file mode 100644 index f6d7dc4a54..0000000000 --- a/src/GitHub.TeamFoundation.14/Services/LocalRepositoryModelFactory.cs +++ /dev/null @@ -1,13 +0,0 @@ -using GitHub.Models; -using GitHub.Services; - -namespace GitHub.TeamFoundation.Services -{ - class LocalRepositoryModelFactory : ILocalRepositoryModelFactory - { - public ILocalRepositoryModel Create(string localPath) - { - return new LocalRepositoryModel(localPath, GitService.GitServiceHelper); - } - } -} diff --git a/src/GitHub.TeamFoundation.14/Services/VSGitExt.cs b/src/GitHub.TeamFoundation.14/Services/VSGitExt.cs index d996b9e170..004b9704df 100644 --- a/src/GitHub.TeamFoundation.14/Services/VSGitExt.cs +++ b/src/GitHub.TeamFoundation.14/Services/VSGitExt.cs @@ -27,18 +27,18 @@ public class VSGitExt : IVSGitExt static readonly ILogger log = LogManager.ForContext(); readonly IServiceProvider serviceProvider; - readonly ILocalRepositoryModelFactory repositoryFactory; + readonly IGitService gitService; readonly object refreshLock = new object(); - IGitExt gitService; - IReadOnlyList activeRepositories; + IGitExt gitExt; + IReadOnlyList activeRepositories; - public VSGitExt(IServiceProvider serviceProvider) - : this(serviceProvider, new VSUIContextFactory(), new LocalRepositoryModelFactory(), ThreadHelper.JoinableTaskContext) + public VSGitExt(IServiceProvider serviceProvider, IGitService gitService) + : this(serviceProvider, new VSUIContextFactory(), gitService, ThreadHelper.JoinableTaskContext) { } - public VSGitExt(IServiceProvider serviceProvider, IVSUIContextFactory factory, ILocalRepositoryModelFactory repositoryFactory, + public VSGitExt(IServiceProvider serviceProvider, IVSUIContextFactory factory, IGitService gitService, JoinableTaskContext joinableTaskContext) { JoinableTaskCollection = joinableTaskContext.CreateCollection(); @@ -46,10 +46,10 @@ public VSGitExt(IServiceProvider serviceProvider, IVSUIContextFactory factory, I JoinableTaskFactory = joinableTaskContext.CreateFactory(JoinableTaskCollection); this.serviceProvider = serviceProvider; - this.repositoryFactory = repositoryFactory; + this.gitService = gitService; // Start with empty array until we have a chance to initialize. - ActiveRepositories = Array.Empty(); + ActiveRepositories = Array.Empty(); // The IGitExt service isn't available when a TFS based solution is opened directly. // It will become available when moving to a Git based solution (and cause a UIContext event to fire). @@ -64,7 +64,7 @@ public VSGitExt(IServiceProvider serviceProvider, IVSUIContextFactory factory, I async Task InitializeAsync() { - gitService = await GetServiceAsync(); + gitExt = await GetServiceAsync(); if (gitService == null) { log.Error("Couldn't find IGitExt service"); @@ -74,9 +74,9 @@ async Task InitializeAsync() // Refresh on background thread await Task.Run(() => RefreshActiveRepositories()); - gitService.PropertyChanged += (s, e) => + gitExt.PropertyChanged += (s, e) => { - if (e.PropertyName == nameof(gitService.ActiveRepositories)) + if (e.PropertyName == nameof(gitExt.ActiveRepositories)) { RefreshActiveRepositories(); } @@ -91,20 +91,20 @@ public void RefreshActiveRepositories() { log.Debug( "IGitExt.ActiveRepositories (#{Id}) returned {Repositories}", - gitService.GetHashCode(), - gitService.ActiveRepositories.Select(x => x.RepositoryPath)); + gitExt.GetHashCode(), + gitExt.ActiveRepositories.Select(x => x.RepositoryPath)); - ActiveRepositories = gitService?.ActiveRepositories.Select(x => repositoryFactory.Create(x.RepositoryPath)).ToList(); + ActiveRepositories = gitExt?.ActiveRepositories.Select(x => gitService.CreateLocalRepositoryModel(x.RepositoryPath)).ToList(); } } catch (Exception e) { log.Error(e, "Error refreshing repositories"); - ActiveRepositories = Array.Empty(); + ActiveRepositories = Array.Empty(); } } - public IReadOnlyList ActiveRepositories + public IReadOnlyList ActiveRepositories { get { diff --git a/src/GitHub.TeamFoundation.14/Services/VSGitServices.cs b/src/GitHub.TeamFoundation.14/Services/VSGitServices.cs index 106d76875d..0696a6f7f1 100644 --- a/src/GitHub.TeamFoundation.14/Services/VSGitServices.cs +++ b/src/GitHub.TeamFoundation.14/Services/VSGitServices.cs @@ -188,7 +188,7 @@ public string GetActiveRepoPath() return ret ?? String.Empty; } - public IEnumerable GetKnownRepositories() + public IEnumerable GetKnownRepositories() { try { @@ -197,7 +197,7 @@ public IEnumerable GetKnownRepositories() catch (Exception ex) { log.Error(ex, "Error loading the repository list from the registry"); - return Enumerable.Empty(); + return Enumerable.Empty(); } } diff --git a/src/GitHub.TeamFoundation.14/Sync/GitHubPublishSection.cs b/src/GitHub.TeamFoundation.14/Sync/GitHubPublishSection.cs index 3ce2d7c286..203e10c684 100644 --- a/src/GitHub.TeamFoundation.14/Sync/GitHubPublishSection.cs +++ b/src/GitHub.TeamFoundation.14/Sync/GitHubPublishSection.cs @@ -132,14 +132,14 @@ public void ShowPublish() }); } - void HandleCreatedRepo(ILocalRepositoryModel newrepo) + void HandleCreatedRepo(LocalRepositoryModel newrepo) { var msg = String.Format(CultureInfo.CurrentCulture, Constants.Notification_RepoCreated, newrepo.Name, newrepo.CloneUrl); msg += " " + String.Format(CultureInfo.CurrentCulture, Constants.Notification_CreateNewProject, newrepo.LocalPath); ShowNotification(newrepo, msg); } - private void ShowNotification(ILocalRepositoryModel newrepo, string msg) + private void ShowNotification(LocalRepositoryModel newrepo, string msg) { var teServices = ServiceProvider.TryGetService(); diff --git a/src/GitHub.TeamFoundation.15/GitHub.TeamFoundation.15.csproj b/src/GitHub.TeamFoundation.15/GitHub.TeamFoundation.15.csproj index 0f872555d1..b8a8b29374 100644 --- a/src/GitHub.TeamFoundation.15/GitHub.TeamFoundation.15.csproj +++ b/src/GitHub.TeamFoundation.15/GitHub.TeamFoundation.15.csproj @@ -180,9 +180,6 @@ RegistryHelper.cs - - Services\LocalRepositoryModelFactory.cs - Services\VSGitExt.cs diff --git a/src/GitHub.TeamFoundation.16/GitHub.TeamFoundation.16.csproj b/src/GitHub.TeamFoundation.16/GitHub.TeamFoundation.16.csproj index 5e727e3fed..5f7b3d4c9a 100644 --- a/src/GitHub.TeamFoundation.16/GitHub.TeamFoundation.16.csproj +++ b/src/GitHub.TeamFoundation.16/GitHub.TeamFoundation.16.csproj @@ -180,9 +180,6 @@ RegistryHelper.cs - - Services\LocalRepositoryModelFactory.cs - Services\VSGitExt.cs diff --git a/src/GitHub.UI/Converters/BranchNameConverter.cs b/src/GitHub.UI/Converters/BranchNameConverter.cs index 0db0f51c3e..800c959382 100644 --- a/src/GitHub.UI/Converters/BranchNameConverter.cs +++ b/src/GitHub.UI/Converters/BranchNameConverter.cs @@ -15,12 +15,12 @@ public class BranchNameConverter : IMultiValueConverter public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - var branch = values.OfType().FirstOrDefault(); - var activeRepo = values.OfType().FirstOrDefault(); + var branch = values.OfType().FirstOrDefault(); + var activeRepo = values.OfType().FirstOrDefault(); if (branch != null && activeRepo != null) { - var repo = (IRemoteRepositoryModel)branch.Repository; + var repo = (RemoteRepositoryModel)branch.Repository; if (repo.Parent == null && activeRepo.Owner != repo.Owner) { diff --git a/src/GitHub.UI/GitHub.UI.csproj b/src/GitHub.UI/GitHub.UI.csproj index 6a622c6772..1b8eb681cc 100644 --- a/src/GitHub.UI/GitHub.UI.csproj +++ b/src/GitHub.UI/GitHub.UI.csproj @@ -29,6 +29,7 @@ + diff --git a/src/GitHub.VisualStudio.UI/Base/TeamExplorerGitRepoInfo.cs b/src/GitHub.VisualStudio.UI/Base/TeamExplorerGitRepoInfo.cs index f8e0ca43ed..b93a5bdd76 100644 --- a/src/GitHub.VisualStudio.UI/Base/TeamExplorerGitRepoInfo.cs +++ b/src/GitHub.VisualStudio.UI/Base/TeamExplorerGitRepoInfo.cs @@ -14,8 +14,8 @@ public TeamExplorerGitRepoInfo(IGitHubServiceProvider serviceProvider) : base(se activeRepoName = string.Empty; } - ILocalRepositoryModel activeRepo; - public ILocalRepositoryModel ActiveRepo + LocalRepositoryModel activeRepo; + public LocalRepositoryModel ActiveRepo { get { return activeRepo; } set diff --git a/src/GitHub.VisualStudio.UI/Base/TeamExplorerItemBase.cs b/src/GitHub.VisualStudio.UI/Base/TeamExplorerItemBase.cs index 917a0b7fee..d38c679fcb 100644 --- a/src/GitHub.VisualStudio.UI/Base/TeamExplorerItemBase.cs +++ b/src/GitHub.VisualStudio.UI/Base/TeamExplorerItemBase.cs @@ -9,13 +9,14 @@ using GitHub.ViewModels; using GitHub.VisualStudio.UI; using GitHub.Extensions; +using System.ComponentModel; namespace GitHub.VisualStudio.Base { public class TeamExplorerItemBase : TeamExplorerGitRepoInfo, IServiceProviderAware { + readonly ITeamExplorerServiceHolder holder; readonly ISimpleApiClientFactory apiFactory; - protected ITeamExplorerServiceHolder holder; ISimpleApiClient simpleApiClient; public ISimpleApiClient SimpleApiClient @@ -29,8 +30,6 @@ public ISimpleApiClient SimpleApiClient } } - protected ISimpleApiClientFactory ApiFactory => apiFactory; - public TeamExplorerItemBase(IGitHubServiceProvider serviceProvider, ITeamExplorerServiceHolder holder) : base(serviceProvider) { @@ -75,21 +74,50 @@ public virtual void Invalidate() void SubscribeToRepoChanges() { - holder.Subscribe(this, (ILocalRepositoryModel repo) => + UpdateRepo(holder.TeamExplorerContext.ActiveRepository); + holder.TeamExplorerContext.PropertyChanged += TeamExplorerContext_PropertyChanged; + holder.TeamExplorerContext.StatusChanged += TeamExplorerContext_StatusChanged; + } + + void TeamExplorerContext_StatusChanged(object sender, EventArgs e) + { + UpdateRepoOnMainThread(holder.TeamExplorerContext.ActiveRepository); + } + + void TeamExplorerContext_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(holder.TeamExplorerContext.ActiveRepository)) { - var changed = !Equals(ActiveRepo, repo); - ActiveRepo = repo; - RepoChanged(changed); - }); + UpdateRepoOnMainThread(holder.TeamExplorerContext.ActiveRepository); + } + } + + void UpdateRepoOnMainThread(LocalRepositoryModel repo) + { + holder.JoinableTaskFactory.RunAsync(async () => + { + await holder.JoinableTaskFactory.SwitchToMainThreadAsync(); + UpdateRepo(repo); + }).Task.Forget(); } void Unsubscribe() { - holder.Unsubscribe(this); + holder.TeamExplorerContext.PropertyChanged -= TeamExplorerContext_PropertyChanged; + holder.TeamExplorerContext.StatusChanged -= TeamExplorerContext_StatusChanged; + if (TEServiceProvider != null) holder.ClearServiceProvider(TEServiceProvider); } + void UpdateRepo(LocalRepositoryModel repo) + { + var changed = ActiveRepo != repo; + ActiveRepo = repo; + RepoChanged(changed); + Invalidate(); + } + protected virtual void RepoChanged(bool changed) { var repo = ActiveRepo; @@ -198,6 +226,5 @@ public string Text get { return text; } set { text = value; this.RaisePropertyChange(); } } - } } \ No newline at end of file diff --git a/src/GitHub.VisualStudio.UI/UI/Views/GitHubConnectContent.xaml.cs b/src/GitHub.VisualStudio.UI/UI/Views/GitHubConnectContent.xaml.cs index 7fb8770e05..72c140b07b 100644 --- a/src/GitHub.VisualStudio.UI/UI/Views/GitHubConnectContent.xaml.cs +++ b/src/GitHub.VisualStudio.UI/UI/Views/GitHubConnectContent.xaml.cs @@ -73,7 +73,7 @@ public object Convert(object[] values, Type targetType, object parameter, Cultur if (values.Length != 2 || parameter as string != "FormatRepositoryName") return String.Empty; - var item = values[0] as ILocalRepositoryModel; + var item = values[0] as LocalRepositoryModel; var context = values[1] as IGitHubConnectSection; if (item == null) return String.Empty; @@ -98,7 +98,7 @@ public object Convert(object[] values, Type targetType, object parameter, Cultur if (values.Length != 2 || parameter as string != "IsCurrentRepository") return false; - var item = values[0] as ILocalRepositoryModel; + var item = values[0] as LocalRepositoryModel; var context = values[1] as IGitAwareItem; return context?.ActiveRepoUri == item?.CloneUrl && String.Equals(context?.ActiveRepo?.LocalPath, item?.LocalPath, StringComparison.OrdinalIgnoreCase); } diff --git a/src/GitHub.VisualStudio.UI/Views/Dialog/ForkRepositorySelectView.xaml.cs b/src/GitHub.VisualStudio.UI/Views/Dialog/ForkRepositorySelectView.xaml.cs index 718d2990ab..3bca75bf59 100644 --- a/src/GitHub.VisualStudio.UI/Views/Dialog/ForkRepositorySelectView.xaml.cs +++ b/src/GitHub.VisualStudio.UI/Views/Dialog/ForkRepositorySelectView.xaml.cs @@ -30,7 +30,7 @@ private void accountsListBox_SelectionChanged(object sender, SelectionChangedEve private void existingForksListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { - var repository = e.AddedItems.OfType().FirstOrDefault(); + var repository = e.AddedItems.OfType().FirstOrDefault(); if (repository != null) { ((IForkRepositorySelectViewModel)DataContext).SwitchOrigin.Execute(repository); diff --git a/src/GitHub.VisualStudio/Commands/BlameLinkCommand.cs b/src/GitHub.VisualStudio/Commands/BlameLinkCommand.cs index 0d55d951be..40d0352dab 100644 --- a/src/GitHub.VisualStudio/Commands/BlameLinkCommand.cs +++ b/src/GitHub.VisualStudio/Commands/BlameLinkCommand.cs @@ -15,8 +15,8 @@ namespace GitHub.VisualStudio.Commands public class BlameLinkCommand : LinkCommandBase, IBlameLinkCommand { [ImportingConstructor] - protected BlameLinkCommand(IGitHubServiceProvider serviceProvider) - : base(CommandSet, CommandId, serviceProvider) + protected BlameLinkCommand(IGitHubServiceProvider serviceProvider, Lazy gitService) + : base(CommandSet, CommandId, serviceProvider, gitService) { } diff --git a/src/GitHub.VisualStudio/Commands/CopyLinkCommand.cs b/src/GitHub.VisualStudio/Commands/CopyLinkCommand.cs index c01aedfdfc..b195d110cf 100644 --- a/src/GitHub.VisualStudio/Commands/CopyLinkCommand.cs +++ b/src/GitHub.VisualStudio/Commands/CopyLinkCommand.cs @@ -17,8 +17,8 @@ namespace GitHub.VisualStudio.Commands public class CopyLinkCommand : LinkCommandBase, ICopyLinkCommand { [ImportingConstructor] - protected CopyLinkCommand(IGitHubServiceProvider serviceProvider) - : base(CommandSet, CommandId, serviceProvider) + protected CopyLinkCommand(IGitHubServiceProvider serviceProvider, Lazy gitService) + : base(CommandSet, CommandId, serviceProvider, gitService) { } diff --git a/src/GitHub.VisualStudio/Commands/LinkCommandBase.cs b/src/GitHub.VisualStudio/Commands/LinkCommandBase.cs index 7326e306b8..d7a1a56008 100644 --- a/src/GitHub.VisualStudio/Commands/LinkCommandBase.cs +++ b/src/GitHub.VisualStudio/Commands/LinkCommandBase.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.IO; using System.Threading.Tasks; using GitHub.Api; @@ -20,33 +21,36 @@ public abstract class LinkCommandBase : VsCommand static readonly ILogger log = LogManager.ForContext(); readonly Lazy apiFactory; readonly Lazy usageTracker; + readonly Lazy lazyGitService; protected LinkCommandBase( Guid commandSet, int commandId, - IGitHubServiceProvider serviceProvider) + IGitHubServiceProvider serviceProvider, + Lazy gitService) : base(commandSet, commandId) { ServiceProvider = serviceProvider; apiFactory = new Lazy(() => ServiceProvider.TryGetService()); usageTracker = new Lazy(() => serviceProvider.TryGetService()); + lazyGitService = gitService; } - protected ILocalRepositoryModel ActiveRepo { get; private set; } + protected LocalRepositoryModel ActiveRepo { get; private set; } protected ISimpleApiClientFactory ApiFactory => apiFactory.Value; protected IGitHubServiceProvider ServiceProvider { get; } protected IUsageTracker UsageTracker => usageTracker.Value; public abstract override Task Execute(); - protected ILocalRepositoryModel GetRepositoryByPath(string path) + protected LocalRepositoryModel GetRepositoryByPath(string path) { try { if (!string.IsNullOrEmpty(path)) { var repo = ServiceProvider.TryGetService().GetRepository(path); - return new LocalRepositoryModel(repo.Info.WorkingDirectory.TrimEnd('\\'), GitService.GitServiceHelper); + return GitService.GitServiceHelper.CreateLocalRepositoryModel(repo.Info.WorkingDirectory.TrimEnd('\\')); } } catch (Exception ex) @@ -57,16 +61,16 @@ protected ILocalRepositoryModel GetRepositoryByPath(string path) return null; } - protected ILocalRepositoryModel GetActiveRepo() + protected LocalRepositoryModel GetActiveRepo() { - var activeRepo = ServiceProvider.TryGetService()?.ActiveRepo; + var activeRepo = ServiceProvider.TryGetService()?.TeamExplorerContext.ActiveRepository; // activeRepo can be null at this point because it is set elsewhere as the result of async operation that may not have completed yet. if (activeRepo == null) { var path = ServiceProvider.TryGetService()?.GetActiveRepoPath() ?? String.Empty; try { - activeRepo = !string.IsNullOrEmpty(path) ? new LocalRepositoryModel(path, GitService.GitServiceHelper) : null; + activeRepo = !string.IsNullOrEmpty(path) ? GitService.GitServiceHelper.CreateLocalRepositoryModel(path) : null; } catch (Exception ex) { @@ -78,7 +82,7 @@ protected ILocalRepositoryModel GetActiveRepo() void RefreshRepo() { - ActiveRepo = ServiceProvider.TryGetService().ActiveRepo; + ActiveRepo = ServiceProvider.TryGetService()?.TeamExplorerContext.ActiveRepository; if (ActiveRepo == null) { @@ -86,7 +90,7 @@ void RefreshRepo() string path = vsGitServices?.GetActiveRepoPath() ?? String.Empty; try { - ActiveRepo = !String.IsNullOrEmpty(path) ? new LocalRepositoryModel(path, GitService.GitServiceHelper) : null; + ActiveRepo = !String.IsNullOrEmpty(path) ? GitService.GitServiceHelper.CreateLocalRepositoryModel(path) : null; } catch (Exception ex) { @@ -134,7 +138,7 @@ protected Task GenerateLink(LinkType linkType) var repo = GetRepositoryByPath(activeDocument.Name); - return repo.GenerateUrl(linkType, activeDocument.Name, activeDocument.StartLine, activeDocument.EndLine); + return GenerateUrl(lazyGitService.Value, repo, linkType, activeDocument.Name, activeDocument.StartLine, activeDocument.EndLine); } protected override void QueryStatus() @@ -160,5 +164,97 @@ public static bool IsFileDescendantOfDirectory(string file, string directory) } return false; } + + /// + /// Generates a http(s) url to the repository in the remote server, optionally + /// pointing to a specific file and specific line range in it. + /// + /// Type of link to generate + /// The file to generate an url to. Optional. + /// A specific line, or (if specifying the as well) the start of a range + /// The end of a line range on the specified file. + /// An UriString with the generated url, or null if the repository has no remote server configured or if it can't be found locally + public static async Task GenerateUrl(IGitService gitService, + LocalRepositoryModel repo, LinkType linkType, string path = null, int startLine = -1, int endLine = -1) + { + if (repo.CloneUrl == null) + return null; + + var sha = await gitService.GetLatestPushedSha(path ?? repo.LocalPath); + // this also incidentally checks whether the repo has a valid LocalPath + if (String.IsNullOrEmpty(sha)) + return repo.CloneUrl.ToRepositoryUrl().AbsoluteUri; + + if (path != null && Path.IsPathRooted(path)) + { + // if the path root doesn't match the repository local path, then ignore it + if (!path.StartsWith(repo.LocalPath, StringComparison.OrdinalIgnoreCase)) + { + System.Diagnostics.Debug.Assert(false, String.Format(CultureInfo.CurrentCulture, "GenerateUrl: path {0} doesn't match repository {1}", path, repo.LocalPath)); + path = null; + } + else + path = path.Substring(repo.LocalPath.Length + 1); + } + + if (startLine > 0 && endLine > 0 && startLine > endLine) + { + // if startLine is greater than endLine and both are set, swap them + var temp = startLine; + startLine = endLine; + endLine = temp; + } + + if (startLine == endLine) + { + // if startLine is the same as endLine don't generate a range link + endLine = -1; + } + + return new UriString(GenerateUrl(linkType, repo.CloneUrl.ToRepositoryUrl().AbsoluteUri, sha, path, startLine, endLine)); + } + + const string CommitFormat = "{0}/commit/{1}"; + const string BlobFormat = "{0}/blob/{1}/{2}"; + const string BlameFormat = "{0}/blame/{1}/{2}"; + const string StartLineFormat = "{0}#L{1}"; + const string EndLineFormat = "{0}-L{1}"; + static string GenerateUrl(LinkType linkType, string basePath, string sha, string path, int startLine = -1, int endLine = -1) + { + if (sha == null) + return basePath; + + if (String.IsNullOrEmpty(path)) + return String.Format(CultureInfo.InvariantCulture, CommitFormat, basePath, sha); + + var ret = String.Format(CultureInfo.InvariantCulture, GetLinkFormat(linkType), basePath, sha, path.Replace(@"\", "/")); + + if (startLine < 0) + return ret; + ret = String.Format(CultureInfo.InvariantCulture, StartLineFormat, ret, startLine); + if (endLine < 0) + return ret; + return String.Format(CultureInfo.InvariantCulture, EndLineFormat, ret, endLine); + } + + /// + /// Selects the proper format for the link type, defaults to the blob url when link type is not selected. + /// + /// Type of link to generate + /// The string format of the selected link type + static string GetLinkFormat(LinkType linkType) + { + switch (linkType) + { + case LinkType.Blame: + return BlameFormat; + + case LinkType.Blob: + return BlobFormat; + + default: + return BlobFormat; + } + } } } diff --git a/src/GitHub.VisualStudio/Commands/OpenFromClipboardCommand.cs b/src/GitHub.VisualStudio/Commands/OpenFromClipboardCommand.cs index 7476a6eb58..f0bb5358ff 100644 --- a/src/GitHub.VisualStudio/Commands/OpenFromClipboardCommand.cs +++ b/src/GitHub.VisualStudio/Commands/OpenFromClipboardCommand.cs @@ -4,7 +4,6 @@ using GitHub.Exports; using GitHub.Services; using GitHub.Services.Vssdk.Commands; -using GitHub.VisualStudio.UI; using Microsoft.VisualStudio.Shell; using Task = System.Threading.Tasks.Task; @@ -13,11 +12,12 @@ namespace GitHub.VisualStudio.Commands [Export(typeof(IOpenFromClipboardCommand))] public class OpenFromClipboardCommand : VsCommand, IOpenFromClipboardCommand { - + readonly Lazy gitHubContextService; readonly Lazy teamExplorerContext; readonly Lazy vsServices; + readonly Lazy gitService; readonly Lazy uiContext; /// @@ -34,12 +34,14 @@ public class OpenFromClipboardCommand : VsCommand, IOpenFromClipboardCom public OpenFromClipboardCommand( Lazy gitHubContextService, Lazy teamExplorerContext, - Lazy vsServices) + Lazy vsServices, + Lazy gitService) : base(CommandSet, CommandId) { this.gitHubContextService = gitHubContextService; this.teamExplorerContext = teamExplorerContext; this.vsServices = vsServices; + this.gitService = gitService; // See https://code.msdn.microsoft.com/windowsdesktop/AllowParams-2005-9442298f ParametersDescription = "u"; // accept a single url @@ -96,9 +98,11 @@ public override async Task Execute(string url) var hasChanges = gitHubContextService.Value.HasChangesInWorkingDirectory(repositoryDir, commitish, path); if (hasChanges) { - // AnnotateFile expects a branch name so we use the current branch - var branchName = activeRepository.CurrentBranch.Name; + // TODO: What if this returns null because we're not on a branch? + var currentBranch = gitService.Value.GetBranch(activeRepository); + var branchName = currentBranch.Name; + // AnnotateFile expects a branch name so we use the current branch if (await gitHubContextService.Value.TryAnnotateFile(repositoryDir, branchName, context)) { return; diff --git a/src/GitHub.VisualStudio/Commands/OpenLinkCommand.cs b/src/GitHub.VisualStudio/Commands/OpenLinkCommand.cs index 5d5ddc7142..3c00747ca2 100644 --- a/src/GitHub.VisualStudio/Commands/OpenLinkCommand.cs +++ b/src/GitHub.VisualStudio/Commands/OpenLinkCommand.cs @@ -14,8 +14,8 @@ namespace GitHub.VisualStudio.Commands public class OpenLinkCommand : LinkCommandBase, IOpenLinkCommand { [ImportingConstructor] - protected OpenLinkCommand(IGitHubServiceProvider serviceProvider) - : base(CommandSet, CommandId, serviceProvider) + protected OpenLinkCommand(IGitHubServiceProvider serviceProvider, Lazy gitService) + : base(CommandSet, CommandId, serviceProvider, gitService) { } diff --git a/src/GitHub.VisualStudio/GitHubPackage.cs b/src/GitHub.VisualStudio/GitHubPackage.cs index 741b9ccfe8..f8fff137dd 100644 --- a/src/GitHub.VisualStudio/GitHubPackage.cs +++ b/src/GitHub.VisualStudio/GitHubPackage.cs @@ -299,7 +299,7 @@ async Task CreateService(IAsyncServiceContainer container, CancellationT else if (serviceType == typeof(IVSGitExt)) { var vsVersion = ApplicationInfo.GetHostVersionInfo().FileMajorPart; - return new VSGitExtFactory(vsVersion, this).Create(); + return new VSGitExtFactory(vsVersion, this, GitService.GitServiceHelper).Create(); } else if (serviceType == typeof(IGitHubToolWindowManager)) { diff --git a/src/GitHub.VisualStudio/Services/LocalRepositories.cs b/src/GitHub.VisualStudio/Services/LocalRepositories.cs index d413b39422..b66e2e1e0b 100644 --- a/src/GitHub.VisualStudio/Services/LocalRepositories.cs +++ b/src/GitHub.VisualStudio/Services/LocalRepositories.cs @@ -37,10 +37,10 @@ public async Task Refresh() list.Except(repositories).ToList().ForEach(x => repositories.Add(x)); } - readonly ObservableCollectionEx repositories - = new ObservableCollectionEx(); + readonly ObservableCollectionEx repositories + = new ObservableCollectionEx(); /// - public IReadOnlyObservableCollection Repositories => repositories; + public IReadOnlyObservableCollection Repositories => repositories; } } diff --git a/src/GitHub.VisualStudio/Services/VSGitExtFactory.cs b/src/GitHub.VisualStudio/Services/VSGitExtFactory.cs index 928eb96d1d..1464d32c02 100644 --- a/src/GitHub.VisualStudio/Services/VSGitExtFactory.cs +++ b/src/GitHub.VisualStudio/Services/VSGitExtFactory.cs @@ -17,11 +17,13 @@ public class VSGitExtFactory readonly int vsVersion; readonly IServiceProvider serviceProvider; + readonly IGitService gitService; - public VSGitExtFactory(int vsVersion, IServiceProvider serviceProvider) + public VSGitExtFactory(int vsVersion, IServiceProvider serviceProvider, IGitService gitService) { this.vsVersion = vsVersion; this.serviceProvider = serviceProvider; + this.gitService = gitService; } public IVSGitExt Create() @@ -29,11 +31,11 @@ public IVSGitExt Create() switch (vsVersion) { case 14: - return Create(() => new VSGitExt14(serviceProvider)); + return Create(() => new VSGitExt14(serviceProvider, gitService)); case 15: - return Create(() => new VSGitExt15(serviceProvider)); + return Create(() => new VSGitExt15(serviceProvider, gitService)); case 16: - return Create(() => new VSGitExt16(serviceProvider)); + return Create(() => new VSGitExt16(serviceProvider, gitService)); default: log.Error("There is no IVSGitExt implementation for DTE version {Version}", vsVersion); return null; diff --git a/test/GitHub.Api.UnitTests/Args.cs b/test/GitHub.Api.UnitTests/Args.cs index 2a04705cde..171cbb9b0a 100644 --- a/test/GitHub.Api.UnitTests/Args.cs +++ b/test/GitHub.Api.UnitTests/Args.cs @@ -20,16 +20,17 @@ internal static class Args public static IApiClient ApiClient { get { return Arg.Any(); } } public static IServiceProvider ServiceProvider { get { return Arg.Any(); } } public static IAvatarProvider AvatarProvider { get { return Arg.Any(); } } - public static HostAddress HostAddress { get { return Arg.Any(); } } + public static HostAddress HostAddress { get { return Arg.Any(); } } public static Uri Uri { get { return Arg.Any(); } } public static LibGit2Sharp.IRepository LibGit2Repo { get { return Arg.Any(); } } public static LibGit2Sharp.Branch LibGit2Branch { get { return Arg.Any(); } } public static Remote LibgGit2Remote { get { return Arg.Any(); } } - public static ILocalRepositoryModel LocalRepositoryModel { get { return Arg.Any(); } } - public static IRemoteRepositoryModel RemoteRepositoryModel { get { return Arg.Any(); } } - public static IBranch Branch { get { return Arg.Any(); } } + public static LocalRepositoryModel LocalRepositoryModel { get { return Arg.Any(); } } + public static RemoteRepositoryModel RemoteRepositoryModel { get { return Arg.Any(); } } + public static BranchModel Branch { get { return Arg.Any(); } } public static IGitService GitService { get { return Arg.Any(); } } - public static Func> - TwoFactorChallengCallback - { get { return Arg.Any>> (); } } + public static Func> TwoFactorChallengCallback + { + get { return Arg.Any>>(); } + } } diff --git a/test/GitHub.App.UnitTests/Args.cs b/test/GitHub.App.UnitTests/Args.cs index 2a04705cde..35f32fc6e0 100644 --- a/test/GitHub.App.UnitTests/Args.cs +++ b/test/GitHub.App.UnitTests/Args.cs @@ -20,16 +20,16 @@ internal static class Args public static IApiClient ApiClient { get { return Arg.Any(); } } public static IServiceProvider ServiceProvider { get { return Arg.Any(); } } public static IAvatarProvider AvatarProvider { get { return Arg.Any(); } } - public static HostAddress HostAddress { get { return Arg.Any(); } } + public static HostAddress HostAddress { get { return Arg.Any(); } } public static Uri Uri { get { return Arg.Any(); } } public static LibGit2Sharp.IRepository LibGit2Repo { get { return Arg.Any(); } } public static LibGit2Sharp.Branch LibGit2Branch { get { return Arg.Any(); } } public static Remote LibgGit2Remote { get { return Arg.Any(); } } - public static ILocalRepositoryModel LocalRepositoryModel { get { return Arg.Any(); } } - public static IRemoteRepositoryModel RemoteRepositoryModel { get { return Arg.Any(); } } - public static IBranch Branch { get { return Arg.Any(); } } + public static LocalRepositoryModel LocalRepositoryModel { get { return Arg.Any(); } } + public static RemoteRepositoryModel RemoteRepositoryModel { get { return Arg.Any(); } } + public static BranchModel Branch { get { return Arg.Any(); } } public static IGitService GitService { get { return Arg.Any(); } } public static Func> TwoFactorChallengCallback - { get { return Arg.Any>> (); } } + { get { return Arg.Any>>(); } } } diff --git a/test/GitHub.App.UnitTests/Models/ModelServiceTests.cs b/test/GitHub.App.UnitTests/Models/ModelServiceTests.cs index f58cfb0255..5335ed080f 100644 --- a/test/GitHub.App.UnitTests/Models/ModelServiceTests.cs +++ b/test/GitHub.App.UnitTests/Models/ModelServiceTests.cs @@ -392,7 +392,7 @@ public async Task NonExpiredIndexReturnsCacheAsync() apiClient.GetOrganizations().Returns(Observable.Empty()); var act = modelService.GetAccounts().ToEnumerable().First().First(); - var repo = Substitute.For(); + var repo = Substitute.For(); repo.Name.Returns(reponame); repo.CloneUrl.Returns(new UriString("https://github.com/" + username + "/" + reponame)); @@ -444,7 +444,7 @@ public async Task ExpiredIndexReturnsLiveAsync() apiClient.GetOrganizations().Returns(Observable.Empty()); var act = modelService.GetAccounts().ToEnumerable().First().First(); - var repo = Substitute.For(); + var repo = Substitute.For(); repo.Name.Returns(reponame); repo.Owner.Returns(user.Login); repo.CloneUrl.Returns(new UriString("https://github.com/" + username + "/" + reponame)); @@ -513,7 +513,7 @@ public async Task ExpiredIndexClearsItemsAsync() apiClient.GetOrganizations().Returns(Observable.Empty()); var act = modelService.GetAccounts().ToEnumerable().First().First(); - var repo = Substitute.For(); + var repo = Substitute.For(); repo.Name.Returns(reponame); repo.Owner.Returns(user.Login); repo.CloneUrl.Returns(new UriString("https://github.com/" + username + "/" + reponame)); diff --git a/test/GitHub.App.UnitTests/Models/RepositoryModelTests.cs b/test/GitHub.App.UnitTests/Models/RepositoryModelTests.cs index fc048b5c95..fd2b9a574c 100644 --- a/test/GitHub.App.UnitTests/Models/RepositoryModelTests.cs +++ b/test/GitHub.App.UnitTests/Models/RepositoryModelTests.cs @@ -20,8 +20,8 @@ public class ComparisonTests : TestBaseClass public void SameContentEqualsTrue(string name1, string url1, string path1, string name2, string url2, string path2) { var gitService = Substitute.For(); - var a = new LocalRepositoryModel(name1, new UriString(url1), path1, gitService); - var b = new LocalRepositoryModel(name2, new UriString(url2), path2, gitService); + var a = new LocalRepositoryModel { Name = name1, CloneUrl = url1, LocalPath = path1 }; + var b = new LocalRepositoryModel { Name = name2, CloneUrl = url2, LocalPath = path2 }; Assert.That(a, Is.EqualTo(b)); Assert.False(a == b); Assert.That(a.GetHashCode(), Is.EqualTo(b.GetHashCode())); @@ -50,39 +50,6 @@ public void DifferentContentEqualsFalse(long id1, string name1, string url1, lon } } - //[Collection("PackageServiceProvider global data tests")] - public class PathConstructorTests : TestBaseClass - { - [Test] - public void NoRemoteUrl() - { - using (var temp = new TempDirectory()) - { - var gitService = Substitute.For(); - var repo = Substitute.For(); - var path = temp.Directory.CreateSubdirectory("repo-name"); - gitService.GetUri(path.FullName).Returns((UriString)null); - var model = new LocalRepositoryModel(path.FullName, gitService); - Assert.That("repo-name", Is.EqualTo(model.Name)); - } - } - - [Test] - public void WithRemoteUrl() - { - using (var temp = new TempDirectory()) - { - var gitService = Substitute.For(); - var repo = Substitute.For(); - var path = temp.Directory.CreateSubdirectory("repo-name"); - gitService.GetUri(path.FullName).Returns(new UriString("https://github.com/user/repo-name")); - var model = new LocalRepositoryModel(path.FullName, gitService); - Assert.That("repo-name", Is.EqualTo(model.Name)); - Assert.That("user", Is.EqualTo(model.Owner)); - } - } - } - public class HostAddressTests : TestBaseClass { [TestCase("https://github.com/owner/repo")] diff --git a/test/GitHub.App.UnitTests/Services/PullRequestServiceTests.cs b/test/GitHub.App.UnitTests/Services/PullRequestServiceTests.cs index c8e437cb8d..6fedcf1526 100644 --- a/test/GitHub.App.UnitTests/Services/PullRequestServiceTests.cs +++ b/test/GitHub.App.UnitTests/Services/PullRequestServiceTests.cs @@ -581,11 +581,14 @@ static PullRequestService CreatePullRequestService(Repository repo) return service; } - static ILocalRepositoryModel CreateLocalRepositoryModel(Repository repo) + static LocalRepositoryModel CreateLocalRepositoryModel(Repository repo) { var repoDir = repo.Info.WorkingDirectory; - var repositoryModel = Substitute.For(); - repositoryModel.LocalPath.Returns(repoDir); + var repositoryModel = new LocalRepositoryModel + { + LocalPath = repoDir + }; + return repositoryModel; } @@ -598,7 +601,8 @@ public async Task ExtractsExistingFile_Async() { var gitClient = MockGitClient(); var target = CreateTarget(gitClient); - var repository = Substitute.For(); + var repository = new LocalRepositoryModel { }; + var fileContent = "file content"; var pr = CreatePullRequest(); @@ -620,7 +624,7 @@ public async Task CreatesEmptyFileForNonExistentFileAsync() { var gitClient = MockGitClient(); var target = CreateTarget(gitClient); - var repository = Substitute.For(); + var repository = new LocalRepositoryModel { }; var pr = CreatePullRequest(); gitClient.ExtractFile(Arg.Any(), "123", "filename").Returns(GetFileTaskAsync(null)); @@ -647,7 +651,7 @@ public async Task CanChangeEncodingAsync(string encodingName) var fileContent = "file content"; var gitClient = MockGitClient(); var target = CreateTarget(gitClient); - var repository = Substitute.For(); + var repository = new LocalRepositoryModel { }; var pr = CreatePullRequest(); var expectedPath = Path.Combine(repoDir, fileName); @@ -689,22 +693,22 @@ public void CreatePullRequestAllArgsMandatory() Substitute.For()); IModelService ms = null; - ILocalRepositoryModel sourceRepo = null; - ILocalRepositoryModel targetRepo = null; + LocalRepositoryModel sourceRepo = null; + LocalRepositoryModel targetRepo = null; string title = null; string body = null; - IBranch source = null; - IBranch target = null; + BranchModel source = null; + BranchModel target = null; Assert.Throws(() => service.CreatePullRequest(ms, sourceRepo, targetRepo, source, target, title, body)); ms = Substitute.For(); Assert.Throws(() => service.CreatePullRequest(ms, sourceRepo, targetRepo, source, target, title, body)); - sourceRepo = new LocalRepositoryModel("name", new GitHub.Primitives.UriString("http://github.com/github/stuff"), "c:\\path", gitService); + sourceRepo = new LocalRepositoryModel { Name = "name", CloneUrl = "http://github.com/github/stuff", LocalPath = "c:\\path" }; Assert.Throws(() => service.CreatePullRequest(ms, sourceRepo, targetRepo, source, target, title, body)); - targetRepo = new LocalRepositoryModel("name", new GitHub.Primitives.UriString("http://github.com/github/stuff"), "c:\\path", gitService); + targetRepo = new LocalRepositoryModel { Name = "name", CloneUrl = "http://github.com/github/stuff", LocalPath = "c:\\path" }; Assert.Throws(() => service.CreatePullRequest(ms, sourceRepo, targetRepo, source, target, title, body)); title = "a title"; @@ -730,7 +734,7 @@ public async Task ShouldCheckoutExistingBranchAsync() var gitClient = MockGitClient(); var service = CreateTarget(gitClient, MockGitService()); - var localRepo = Substitute.For(); + var localRepo = new LocalRepositoryModel { }; var pr = new PullRequestDetailModel { @@ -753,9 +757,10 @@ public async Task ShouldCheckoutLocalBranchAsync() { var gitClient = MockGitClient(); var service = CreateTarget(gitClient, MockGitService()); - - var localRepo = Substitute.For(); - localRepo.CloneUrl.Returns(new UriString("https://foo.bar/owner/repo")); + var localRepo = new LocalRepositoryModel + { + CloneUrl = new UriString("https://foo.bar/owner/repo") + }; var pr = new PullRequestDetailModel { @@ -782,9 +787,10 @@ public async Task ShouldCheckoutLocalBranchOwnerCaseMismatchAsync() { var gitClient = MockGitClient(); var service = CreateTarget(gitClient, MockGitService()); - - var localRepo = Substitute.For(); - localRepo.CloneUrl.Returns(new UriString("https://foo.bar/Owner/repo")); + var localRepo = new LocalRepositoryModel + { + CloneUrl = new UriString("https://foo.bar/Owner/repo") + }; var pr = new PullRequestDetailModel { @@ -811,9 +817,10 @@ public async Task ShouldCheckoutBranchFromForkAsync() { var gitClient = MockGitClient(); var service = CreateTarget(gitClient, MockGitService()); - - var localRepo = Substitute.For(); - localRepo.CloneUrl.Returns(new UriString("https://foo.bar/owner/repo")); + var localRepo = new LocalRepositoryModel + { + CloneUrl = new UriString("https://foo.bar/owner/repo") + }; var pr = new PullRequestDetailModel { @@ -844,9 +851,10 @@ public async Task ShouldUseUniquelyNamedRemoteForForkAsync() var gitClient = MockGitClient(); var gitService = MockGitService(); var service = CreateTarget(gitClient, gitService); - - var localRepo = Substitute.For(); - localRepo.CloneUrl.Returns(new UriString("https://foo.bar/owner/repo")); + var localRepo = new LocalRepositoryModel + { + CloneUrl = new UriString("https://foo.bar/owner/repo") + }; using (var repo = gitService.GetRepository(localRepo.CloneUrl)) { @@ -881,7 +889,7 @@ public async Task ShouldReturnCorrectDefaultLocalBranchNameAsync() { var service = CreateTarget(MockGitClient(), MockGitService()); - var localRepo = Substitute.For(); + var localRepo = new LocalRepositoryModel { }; var result = await service.GetDefaultLocalBranchName(localRepo, 123, "Pull requests can be \"named\" all sorts of thing's (sic)"); Assert.That("pr/123-pull-requests-can-be-named-all-sorts-of-thing-s-sic", Is.EqualTo(result)); } @@ -897,7 +905,7 @@ public async Task ShouldReturnCorrectDefaultLocalBranchNameForPullRequestsWithNo Substitute.For(), Substitute.For()); - var localRepo = Substitute.For(); + var localRepo = new LocalRepositoryModel { }; var result = await service.GetDefaultLocalBranchName(localRepo, 123, "コードをレビューする準備ができたこと"); Assert.That("pr/123", Is.EqualTo(result)); } @@ -907,7 +915,7 @@ public async Task DefaultLocalBranchNameShouldNotClashWithExistingBranchNamesAsy { var service = CreateTarget(MockGitClient(), MockGitService()); - var localRepo = Substitute.For(); + var localRepo = new LocalRepositoryModel { }; var result = await service.GetDefaultLocalBranchName(localRepo, 123, "foo1"); Assert.That("pr/123-foo1-3", Is.EqualTo(result)); } @@ -919,9 +927,10 @@ public class TheGetLocalBranchesMethod public async Task ShouldReturnPullRequestBranchForPullRequestFromSameRepositoryAsync() { var service = CreateTarget(MockGitClient(), MockGitService()); - - var localRepo = Substitute.For(); - localRepo.CloneUrl.Returns(new UriString("https://github.com/foo/bar")); + var localRepo = new LocalRepositoryModel + { + CloneUrl = new UriString("https://github.com/foo/bar") + }; var result = await service.GetLocalBranches(localRepo, CreatePullRequest(fromFork: false)); @@ -932,9 +941,10 @@ public async Task ShouldReturnPullRequestBranchForPullRequestFromSameRepositoryA public async Task ShouldReturnPullRequestBranchForPullRequestFromSameRepositoryOwnerCaseMismatchAsync() { var service = CreateTarget(MockGitClient(), MockGitService()); - - var localRepo = Substitute.For(); - localRepo.CloneUrl.Returns(new UriString("https://github.com/Foo/bar")); + var localRepo = new LocalRepositoryModel + { + CloneUrl = new UriString("https://github.com/Foo/bar") + }; var result = await service.GetLocalBranches(localRepo, CreatePullRequest(fromFork: false)); @@ -964,8 +974,10 @@ public async Task ShouldReturnMarkedBranchForPullRequestFromForkAsync() var service = CreateTarget(MockGitClient(), MockGitService(repo)); - var localRepo = Substitute.For(); - localRepo.CloneUrl.Returns(new UriString("https://github.com/foo/bar.git")); + var localRepo = new LocalRepositoryModel + { + CloneUrl = new UriString("https://github.com/foo/bar.git") + }; var result = await service.GetLocalBranches(localRepo, CreatePullRequest(true)); @@ -1003,9 +1015,10 @@ public async Task ShouldRemoveUnusedRemoteAsync() var gitClient = MockGitClient(); var gitService = MockGitService(); var service = CreateTarget(gitClient, gitService); - - var localRepo = Substitute.For(); - localRepo.CloneUrl.Returns(new UriString("https://github.com/foo/bar")); + var localRepo = new LocalRepositoryModel + { + CloneUrl = new UriString("https://github.com/foo/bar") + }; using (var repo = gitService.GetRepository(localRepo.CloneUrl)) { diff --git a/test/GitHub.App.UnitTests/Services/TeamExplorerContextTests.cs b/test/GitHub.App.UnitTests/Services/TeamExplorerContextTests.cs index f539b589e0..3ccf012883 100644 --- a/test/GitHub.App.UnitTests/Services/TeamExplorerContextTests.cs +++ b/test/GitHub.App.UnitTests/Services/TeamExplorerContextTests.cs @@ -151,45 +151,21 @@ public void NoActiveRepositoryChange_SolutionChanges() public class TheStatusChangedEvent { - [TestCase(false, "name1", "sha1", "name1", "sha1", false)] - [TestCase(false, "name1", "sha1", "name2", "sha1", true)] - [TestCase(false, "name1", "sha1", "name1", "sha2", true)] - [TestCase(false, "name1", "sha1", "name2", "sha2", true)] - [TestCase(true, "name1", "sha1", "name1", "sha1", false)] - [TestCase(true, "name1", "sha1", "name2", "sha2", false)] - public void SameActiveRepository_ExpectWasRaised(bool changePath, string name1, string sha1, string name2, string sha2, bool expectWasRaised) + [TestCase("path", "path", true)] + [TestCase("path1", "path2", false)] + [TestCase(null, null, false)] + public void AlwaysFireWhenNoLocalPathChange(string path1, string path2, bool expectWasRaised) { var gitExt = CreateGitExt(); var repositoryPaths = new[] { Directory.GetCurrentDirectory(), Path.GetTempPath() }; - var path1 = Directory.GetCurrentDirectory(); - var path2 = changePath ? Path.GetTempPath() : path1; - var repoInfo1 = CreateRepositoryModel(path1, name1, sha1); - var repoInfo2 = CreateRepositoryModel(path2, name2, sha2); + var repoInfo1 = CreateRepositoryModel(path1); + var repoInfo2 = CreateRepositoryModel(path2); var target = CreateTeamExplorerContext(gitExt); - var eventWasRaised = false; - target.StatusChanged += (s, e) => eventWasRaised = true; - - SetActiveRepository(gitExt, repoInfo1); - SetActiveRepository(gitExt, repoInfo2); - - Assert.That(eventWasRaised, Is.EqualTo(expectWasRaised)); - } - [TestCase("trackedSha", "trackedSha", false)] - [TestCase("trackedSha1", "trackedSha2", true)] - public void TrackedShaChanges_CheckWasRaised(string trackedSha1, string trackedSha2, bool expectWasRaised) - { - var gitExt = CreateGitExt(); - var repositoryPaths = new[] { Directory.GetCurrentDirectory(), Path.GetTempPath() }; - var repoPath = Directory.GetCurrentDirectory(); - var repoInfo1 = CreateRepositoryModel(repoPath, "name", "sha", trackedSha1); - var repoInfo2 = CreateRepositoryModel(repoPath, "name", "sha", trackedSha2); - var target = CreateTeamExplorerContext(gitExt); SetActiveRepository(gitExt, repoInfo1); var eventWasRaised = false; target.StatusChanged += (s, e) => eventWasRaised = true; - SetActiveRepository(gitExt, repoInfo2); Assert.That(eventWasRaised, Is.EqualTo(expectWasRaised)); @@ -200,7 +176,7 @@ public void SolutionUnloadedAndReloaded_DontFireStatusChanged() { var gitExt = CreateGitExt(); var path = Directory.GetCurrentDirectory(); - var repoInfo1 = CreateRepositoryModel(path, "name", "sha"); + var repoInfo1 = CreateRepositoryModel(path); var repoInfo2 = CreateRepositoryModel(null); var target = CreateTeamExplorerContext(gitExt); SetActiveRepository(gitExt, repoInfo1); @@ -226,16 +202,12 @@ static TeamExplorerContext CreateTeamExplorerContext( return new TeamExplorerContext(gitExt, new AsyncLazy(() => Task.FromResult(dte)), pullRequestService, joinableTaskContext); } - static ILocalRepositoryModel CreateRepositoryModel(string path, string branchName = null, string headSha = null, string trackedSha = null) + static LocalRepositoryModel CreateRepositoryModel(string path) { - var repo = Substitute.For(); - repo.LocalPath.Returns(path); - var currentBranch = Substitute.For(); - currentBranch.Name.Returns(branchName); - currentBranch.Sha.Returns(headSha); - currentBranch.TrackedSha.Returns(trackedSha); - repo.CurrentBranch.Returns(currentBranch); - return repo; + return new LocalRepositoryModel + { + LocalPath = path + }; } static IVSGitExt CreateGitExt() @@ -243,9 +215,9 @@ static IVSGitExt CreateGitExt() return Substitute.For(); } - static void SetActiveRepository(IVSGitExt gitExt, ILocalRepositoryModel repo) + static void SetActiveRepository(IVSGitExt gitExt, LocalRepositoryModel repo) { - var repos = repo != null ? new[] { repo } : Array.Empty(); + var repos = repo != null ? new[] { repo } : Array.Empty(); gitExt.ActiveRepositories.Returns(repos); gitExt.ActiveRepositoriesChanged += Raise.Event(); } diff --git a/test/GitHub.App.UnitTests/ViewModels/Dialog/Clone/RepositoryCloneViewModelTests.cs b/test/GitHub.App.UnitTests/ViewModels/Dialog/Clone/RepositoryCloneViewModelTests.cs index 2fd7b6140c..a4a51b702b 100644 --- a/test/GitHub.App.UnitTests/ViewModels/Dialog/Clone/RepositoryCloneViewModelTests.cs +++ b/test/GitHub.App.UnitTests/ViewModels/Dialog/Clone/RepositoryCloneViewModelTests.cs @@ -343,7 +343,7 @@ public async Task Open_Is_Enabled_When_Path_DirectoryExists() Assert.That(target.Open.CanExecute(null), Is.True); } - static void SetRepository(IRepositoryCloneTabViewModel vm, IRepositoryModel repository) + static void SetRepository(IRepositoryCloneTabViewModel vm, RepositoryModel repository) { vm.Repository.Returns(repository); vm.PropertyChanged += Raise.Event( @@ -376,7 +376,7 @@ static IConnectionManager CreateConnectionManager(params string[] addresses) static IRepositorySelectViewModel CreateSelectViewModel() { var result = Substitute.For(); - result.Repository.Returns((IRepositoryModel)null); + result.Repository.Returns((RepositoryModel)null); result.WhenForAnyArgs(x => x.Initialize(null)).Do(_ => result.IsEnabled.Returns(true)); return result; } @@ -451,20 +451,17 @@ static IUsageService CreateUsageService(bool isGroupA = false) return usageService; } - static IRepositoryModel CreateRepositoryModel(string repo = "owner/repo") + static RepositoryModel CreateRepositoryModel(string repo = "owner/repo") { var split = repo.Split('/'); var (owner, name) = (split[0], split[1]); return CreateRepositoryModel(owner, name); } - static IRepositoryModel CreateRepositoryModel(string owner, string name) + static RepositoryModel CreateRepositoryModel(string owner, string name) { - var repository = Substitute.For(); - repository.Owner.Returns(owner); - repository.Name.Returns(name); - repository.CloneUrl.Returns(CreateGitHubUrl(owner, name)); - return repository; + var cloneUrl = CreateGitHubUrl(owner, name); + return new RepositoryModel(name, cloneUrl); } static UriString CreateGitHubUrl(string owner, string repo) @@ -475,7 +472,7 @@ static UriString CreateGitHubUrl(string owner, string repo) static IRepositoryUrlViewModel CreateRepositoryUrlViewModel() { var repositoryUrlViewModel = Substitute.For(); - repositoryUrlViewModel.Repository.Returns(null as IRepositoryModel); + repositoryUrlViewModel.Repository.Returns(null as RepositoryModel); repositoryUrlViewModel.Url.Returns(string.Empty); return repositoryUrlViewModel; } diff --git a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/GitHubPaneViewModelTests.cs b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/GitHubPaneViewModelTests.cs index c8cc19d815..7c8b86edeb 100644 --- a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/GitHubPaneViewModelTests.cs +++ b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/GitHubPaneViewModelTests.cs @@ -24,7 +24,7 @@ public class TheInitializeMethod public async Task NotAGitRepositoryShownWhenNoRepositoryAsync() { var te = Substitute.For(); - te.ActiveRepository.Returns(null as ILocalRepositoryModel); + te.ActiveRepository.Returns(null as LocalRepositoryModel); var target = CreateTarget(teamExplorerContext: te); await InitializeAsync(target); @@ -282,8 +282,10 @@ static INavigationViewModel CreateNavigator() static ITeamExplorerContext CreateTeamExplorerContext(string repositoryCloneUrl) { - var repository = Substitute.For(); - repository.CloneUrl.Returns(new UriString(repositoryCloneUrl)); + var repository = new LocalRepositoryModel + { + CloneUrl = new UriString(repositoryCloneUrl) + }; var result = Substitute.For(); result.ActiveRepository.Returns(repository); return result; diff --git a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/IssueListViewModelBaseTests.cs b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/IssueListViewModelBaseTests.cs index 6963c29fdd..2b274342c4 100644 --- a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/IssueListViewModelBaseTests.cs +++ b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/IssueListViewModelBaseTests.cs @@ -92,15 +92,15 @@ public async Task No_Items_With_Author_Filter_Returns_Message_NoOpenItems() Assert.That(target.Message, Is.EqualTo(IssueListMessage.NoItemsMatchCriteria)); } - protected static ILocalRepositoryModel CreateLocalRepository( + protected static LocalRepositoryModel CreateLocalRepository( string owner = "owner", string name = "name") { - var result = Substitute.For(); - result.CloneUrl.Returns(new UriString($"https://giuthub.com/{owner}/{name}")); - result.Owner.Returns(owner); - result.Name.Returns(name); - return result; + return new LocalRepositoryModel + { + CloneUrl = new UriString($"https://giuthub.com/{owner}/{name}"), + Name = name + }; } protected static IPullRequestSessionManager CreateSessionManager(PullRequestDetailModel pullRequest = null) @@ -146,7 +146,7 @@ static Target CreateTarget(IRepositoryService repositoryService = null, int item static async Task CreateTargetAndInitialize( IRepositoryService repositoryService = null, - ILocalRepositoryModel repository = null, + LocalRepositoryModel repository = null, IConnection connection = null, int itemCount = 1000) { diff --git a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestCreationViewModelTests.cs b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestCreationViewModelTests.cs index 5b21260e0a..dc7b82fa73 100644 --- a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestCreationViewModelTests.cs +++ b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestCreationViewModelTests.cs @@ -6,14 +6,12 @@ using GitHub.Models; using GitHub.Primitives; using GitHub.Services; -using GitHub.ViewModels; using GitHub.ViewModels.GitHubPane; using NSubstitute; using Octokit; using UnitTests; using NUnit.Framework; using IConnection = GitHub.Models.IConnection; -using ReactiveUI.Testing; using System.Reactive.Concurrency; using GitHub.Models.Drafts; @@ -45,12 +43,12 @@ static LibGit2Sharp.IRepository SetupLocalRepoMock(IGitClient gitClient, IGitSer struct TestData { public IServiceProvider ServiceProvider; - public ILocalRepositoryModel ActiveRepo; + public LocalRepositoryModel ActiveRepo; public LibGit2Sharp.IRepository L2Repo; - public IRepositoryModel SourceRepo; - public IRepositoryModel TargetRepo; - public IBranch SourceBranch; - public IBranch TargetBranch; + public RepositoryModel SourceRepo; + public RepositoryModel TargetRepo; + public BranchModel SourceBranch; + public BranchModel TargetBranch; public IGitClient GitClient; public IGitService GitService; public INotificationService NotificationService; @@ -83,26 +81,29 @@ static TestData PrepareTestData( connection.HostAddress.Returns(HostAddress.Create("https://github.com")); - // this is the local repo instance that is available via TeamExplorerServiceHolder and friends - var activeRepo = Substitute.For(); - activeRepo.LocalPath.Returns(""); - activeRepo.Name.Returns(repoName); - activeRepo.CloneUrl.Returns(new UriString("http://github.com/" + sourceRepoOwner + "/" + repoName)); - activeRepo.Owner.Returns(sourceRepoOwner); + var activeRepo = new LocalRepositoryModel + { + LocalPath = "", + Name = repoName, + CloneUrl = new UriString("http://github.com/" + sourceRepoOwner + "/" + repoName) + }; Repository githubRepoParent = null; if (repoIsFork) githubRepoParent = CreateRepository(targetRepoOwner, repoName, id: 1); var githubRepo = CreateRepository(sourceRepoOwner, repoName, id: 2, parent: githubRepoParent); var sourceBranch = new BranchModel(sourceBranchName, activeRepo); - var sourceRepo = new RemoteRepositoryModel(githubRepo); + var sourceRepo = CreateRemoteRepositoryModel(githubRepo); var targetRepo = targetRepoOwner == sourceRepoOwner ? sourceRepo : sourceRepo.Parent; var targetBranch = targetBranchName != targetRepo.DefaultBranch.Name ? new BranchModel(targetBranchName, targetRepo) : targetRepo.DefaultBranch; - activeRepo.CurrentBranch.Returns(sourceBranch); + gitService.GetBranch(activeRepo).Returns(sourceBranch); api.GetRepository(Args.String, Args.String).Returns(Observable.Return(githubRepo)); ms.ApiClient.Returns(api); + // Default to returning no branches + ms.GetBranches(null).ReturnsForAnyArgs(Observable.Empty()); + // sets up the libgit2sharp repo and branch objects var l2repo = SetupLocalRepoMock(gitClient, gitService, remote, sourceBranchName, sourceBranchIsTracking); @@ -124,13 +125,29 @@ static TestData PrepareTestData( }; } + static RemoteRepositoryModel CreateRemoteRepositoryModel(Repository repository) + { + var ownerAccount = new GitHub.Models.Account(repository.Owner); + var parent = repository.Parent != null ? CreateRemoteRepositoryModel(repository.Parent) : null; + var model = new RemoteRepositoryModel(repository.Id, repository.Name, repository.CloneUrl, + repository.Private, repository.Fork, ownerAccount, parent, repository.DefaultBranch); + + if (parent != null) + { + parent.DefaultBranch.DisplayName = parent.DefaultBranch.Id; + } + + return model; + } + [Test] public async Task TargetBranchDisplayNameIncludesRepoOwnerWhenForkAsync() { var data = PrepareTestData("octokit.net", "shana", "master", "octokit", "master", "origin", true, true); var prservice = new PullRequestService(data.GitClient, data.GitService, Substitute.For(), Substitute.For(), data.ServiceProvider.GetOperatingSystem(), Substitute.For()); prservice.GetPullRequestTemplate(data.ActiveRepo).Returns(Observable.Empty()); - var vm = new PullRequestCreationViewModel(data.GetModelServiceFactory(), prservice, data.NotificationService, Substitute.For()); + var vm = new PullRequestCreationViewModel(data.GetModelServiceFactory(), prservice, data.NotificationService, + Substitute.For(), data.GitService); await vm.InitializeAsync(data.ActiveRepo, data.Connection); Assert.That("octokit/master", Is.EqualTo(vm.TargetBranch.DisplayName)); } @@ -165,7 +182,8 @@ public async Task CreatingPRsAsync( var ms = data.ModelService; var prservice = new PullRequestService(data.GitClient, data.GitService, Substitute.For(), Substitute.For(), data.ServiceProvider.GetOperatingSystem(), Substitute.For()); - var vm = new PullRequestCreationViewModel(data.GetModelServiceFactory(), prservice, data.NotificationService, Substitute.For()); + var vm = new PullRequestCreationViewModel(data.GetModelServiceFactory(), prservice, data.NotificationService, + Substitute.For(), data.GitService); await vm.InitializeAsync(data.ActiveRepo, data.Connection); // the TargetBranch property gets set to whatever the repo default is (we assume master here), @@ -208,7 +226,7 @@ public async Task TemplateIsUsedIfPresentAsync() prservice.GetPullRequestTemplate(data.ActiveRepo).Returns(Observable.Return("Test PR template")); var vm = new PullRequestCreationViewModel(data.GetModelServiceFactory(), prservice, data.NotificationService, - Substitute.For()); + Substitute.For(), data.GitService); await vm.InitializeAsync(data.ActiveRepo, data.Connection); Assert.That("Test PR template", Is.EqualTo(vm.Description)); @@ -227,7 +245,8 @@ public async Task LoadsDraft() }); var prservice = Substitute.For(); - var vm = new PullRequestCreationViewModel(data.GetModelServiceFactory(), prservice, data.NotificationService, draftStore); + var vm = new PullRequestCreationViewModel(data.GetModelServiceFactory(), prservice, data.NotificationService, + draftStore, data.GitService); await vm.InitializeAsync(data.ActiveRepo, data.Connection); Assert.That(vm.PRTitle, Is.EqualTo("This is a Title.")); @@ -241,7 +260,8 @@ public async Task UpdatesDraftWhenDescriptionChanges() var scheduler = new HistoricalScheduler(); var draftStore = Substitute.For(); var prservice = Substitute.For(); - var vm = new PullRequestCreationViewModel(data.GetModelServiceFactory(), prservice, data.NotificationService, draftStore, scheduler); + var vm = new PullRequestCreationViewModel(data.GetModelServiceFactory(), prservice, data.NotificationService, + draftStore, data.GitService, scheduler); await vm.InitializeAsync(data.ActiveRepo, data.Connection); vm.Description = "Body changed."; @@ -263,7 +283,8 @@ public async Task UpdatesDraftWhenTitleChanges() var scheduler = new HistoricalScheduler(); var draftStore = Substitute.For(); var prservice = Substitute.For(); - var vm = new PullRequestCreationViewModel(data.GetModelServiceFactory(), prservice, data.NotificationService, draftStore, scheduler); + var vm = new PullRequestCreationViewModel(data.GetModelServiceFactory(), prservice, data.NotificationService, + draftStore, data.GitService, scheduler); await vm.InitializeAsync(data.ActiveRepo, data.Connection); vm.PRTitle = "Title changed."; @@ -285,7 +306,8 @@ public async Task DeletesDraftWhenPullRequestSubmitted() var scheduler = new HistoricalScheduler(); var draftStore = Substitute.For(); var prservice = Substitute.For(); - var vm = new PullRequestCreationViewModel(data.GetModelServiceFactory(), prservice, data.NotificationService, draftStore, scheduler); + var vm = new PullRequestCreationViewModel(data.GetModelServiceFactory(), prservice, data.NotificationService, draftStore, + data.GitService, scheduler); await vm.InitializeAsync(data.ActiveRepo, data.Connection); await vm.CreatePullRequest.Execute(); @@ -300,7 +322,8 @@ public async Task DeletesDraftWhenCanceled() var scheduler = new HistoricalScheduler(); var draftStore = Substitute.For(); var prservice = Substitute.For(); - var vm = new PullRequestCreationViewModel(data.GetModelServiceFactory(), prservice, data.NotificationService, draftStore, scheduler); + var vm = new PullRequestCreationViewModel(data.GetModelServiceFactory(), prservice, data.NotificationService, draftStore, + data.GitService, scheduler); await vm.InitializeAsync(data.ActiveRepo, data.Connection); await vm.Cancel.Execute(); diff --git a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestDetailViewModelTests.cs b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestDetailViewModelTests.cs index ac24f57097..d56dd7e272 100644 --- a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestDetailViewModelTests.cs +++ b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestDetailViewModelTests.cs @@ -550,12 +550,16 @@ static Tuple CreateTargetAndSer int behindBy = 0, IPullRequestSessionManager sessionManager = null) { - var repository = Substitute.For(); + var repository = new LocalRepositoryModel + { + CloneUrl = new UriString(Uri.ToString()), + LocalPath = @"C:\projects\ThisRepo", + Name = "repo" + }; + var currentBranchModel = new BranchModel(currentBranch, repository); - repository.CurrentBranch.Returns(currentBranchModel); - repository.CloneUrl.Returns(new UriString(Uri.ToString())); - repository.LocalPath.Returns(@"C:\projects\ThisRepo"); - repository.Name.Returns("repo"); + var gitService = Substitute.For(); + gitService.GetBranch(repository).Returns(currentBranchModel); var pullRequestService = Substitute.For(); @@ -568,7 +572,7 @@ static Tuple CreateTargetAndSer else { pullRequestService.GetLocalBranches(repository, Arg.Any()) - .Returns(Observable.Empty()); + .Returns(Observable.Empty()); } pullRequestService.Checkout(repository, Arg.Any(), Arg.Any()).Returns(x => Throws("Checkout threw")); @@ -607,7 +611,8 @@ static Tuple CreateTargetAndSer Substitute.For(), Substitute.For(), Substitute.For(), - Substitute.For()); + Substitute.For(), + gitService); vm.InitializeAsync(repository, Substitute.For(), "owner", "repo", 1).Wait(); return Tuple.Create(vm, pullRequestService); diff --git a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestFilesViewModelTests.cs b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestFilesViewModelTests.cs index c4fff09d4a..d043e97dda 100644 --- a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestFilesViewModelTests.cs +++ b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestFilesViewModelTests.cs @@ -100,8 +100,7 @@ static IPullRequestSession CreateSession() { var author = Substitute.For(); - var repository = Substitute.For(); - repository.LocalPath.Returns(@"C:\Foo"); + var repository = new LocalRepositoryModel { LocalPath = @"C:\Foo" }; var result = Substitute.For(); result.LocalRepository.Returns(repository); diff --git a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestListViewModelTests.cs b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestListViewModelTests.cs index adb8654359..0efab52f8f 100644 --- a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestListViewModelTests.cs +++ b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestListViewModelTests.cs @@ -45,7 +45,7 @@ static async Task CreateTargetAndInitialize( IPullRequestSessionManager sessionManager = null, IRepositoryService repositoryService = null, IPullRequestService service = null, - ILocalRepositoryModel repository = null, + LocalRepositoryModel repository = null, IConnection connection = null) { var result = CreateTarget(sessionManager, repositoryService, service); diff --git a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestReviewAuthoringViewModelTests.cs b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestReviewAuthoringViewModelTests.cs index 4036307ba2..42c876cb35 100644 --- a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestReviewAuthoringViewModelTests.cs +++ b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestReviewAuthoringViewModelTests.cs @@ -605,18 +605,19 @@ static IPullRequestSessionManager CreateSessionManager( return result; } - static ILocalRepositoryModel CreateLocalRepositoryModel() + static LocalRepositoryModel CreateLocalRepositoryModel() { - var result = Substitute.For(); - result.CloneUrl.Returns(new UriString("https://github.com/owner/repo")); - result.Owner.Returns("owner"); - result.Name.Returns("repo"); + var result = new LocalRepositoryModel + { + CloneUrl = new UriString("https://github.com/owner/repo"), + Name = "repo" + }; return result; } static async Task InitializeAsync( IPullRequestReviewAuthoringViewModel target, - ILocalRepositoryModel localRepository = null) + LocalRepositoryModel localRepository = null) { localRepository = localRepository ?? CreateLocalRepositoryModel(); diff --git a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestUserReviewsViewModelTests.cs b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestUserReviewsViewModelTests.cs index 061985d76d..6946d0e22e 100644 --- a/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestUserReviewsViewModelTests.cs +++ b/test/GitHub.App.UnitTests/ViewModels/GitHubPane/PullRequestUserReviewsViewModelTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using GitHub.Factories; using GitHub.Models; +using GitHub.Primitives; using GitHub.Services; using GitHub.ViewModels.GitHubPane; using NSubstitute; @@ -161,7 +162,7 @@ public async Task First_Review_Is_Expanded_Async() async Task InitializeAsync( PullRequestUserReviewsViewModel target, - ILocalRepositoryModel localRepository = null, + LocalRepositoryModel localRepository = null, IConnection connection = null, int pullRequestNumber = 5, string login = AuthorLogin) @@ -207,19 +208,13 @@ PullRequestUserReviewsViewModel CreateTarget( sessionManager); } - IModelServiceFactory CreateFactory(IModelService modelService) + LocalRepositoryModel CreateRepository(string owner = "owner", string name = "repo") { - var result = Substitute.For(); - result.CreateAsync(null).ReturnsForAnyArgs(modelService); - return result; - } - - ILocalRepositoryModel CreateRepository(string owner = "owner", string name = "repo") - { - var result = Substitute.For(); - result.Owner.Returns(owner); - result.Name.Returns(name); - return result; + return new LocalRepositoryModel + { + CloneUrl = new UriString($"https://github.com/{owner}/{name}"), + Name = name + }; } } } diff --git a/test/GitHub.App.UnitTests/ViewModels/PullRequestReviewCommentThreadViewModelTests.cs b/test/GitHub.App.UnitTests/ViewModels/PullRequestReviewCommentThreadViewModelTests.cs index 9ae63fd2ac..bb0cbd3ddb 100644 --- a/test/GitHub.App.UnitTests/ViewModels/PullRequestReviewCommentThreadViewModelTests.cs +++ b/test/GitHub.App.UnitTests/ViewModels/PullRequestReviewCommentThreadViewModelTests.cs @@ -184,9 +184,12 @@ static IPullRequestSession CreateSession() var result = Substitute.For(); result.User.Returns(new ActorModel { Login = "Viewer" }); result.RepositoryOwner.Returns("owner"); - result.LocalRepository.CloneUrl.Returns(new UriString("https://github.com/owner/repo")); - result.LocalRepository.Name.Returns("repo"); - result.LocalRepository.Owner.Returns("shouldnt-be-used"); + var localRepository = new LocalRepositoryModel + { + CloneUrl = new UriString("https://github.com/owner/repo"), + Name = "repo" + }; + result.LocalRepository.Returns(localRepository); result.PullRequest.Returns(new PullRequestDetailModel { Number = 47, diff --git a/test/GitHub.Exports.UnitTests/Args.cs b/test/GitHub.Exports.UnitTests/Args.cs index 03bc2d7fe3..63f0f74765 100644 --- a/test/GitHub.Exports.UnitTests/Args.cs +++ b/test/GitHub.Exports.UnitTests/Args.cs @@ -18,16 +18,16 @@ internal static class Args public static NewRepository NewRepository { get { return Arg.Any(); } } public static IAccount Account { get { return Arg.Any(); } } public static IServiceProvider ServiceProvider { get { return Arg.Any(); } } - public static HostAddress HostAddress { get { return Arg.Any(); } } + public static HostAddress HostAddress { get { return Arg.Any(); } } public static Uri Uri { get { return Arg.Any(); } } public static LibGit2Sharp.IRepository LibGit2Repo { get { return Arg.Any(); } } public static LibGit2Sharp.Branch LibGit2Branch { get { return Arg.Any(); } } public static Remote LibgGit2Remote { get { return Arg.Any(); } } - public static ILocalRepositoryModel LocalRepositoryModel { get { return Arg.Any(); } } - public static IRemoteRepositoryModel RemoteRepositoryModel { get { return Arg.Any(); } } - public static IBranch Branch { get { return Arg.Any(); } } + public static LocalRepositoryModel LocalRepositoryModel { get { return Arg.Any(); } } + public static RemoteRepositoryModel RemoteRepositoryModel { get { return Arg.Any(); } } + public static BranchModel Branch { get { return Arg.Any(); } } public static IGitService GitService { get { return Arg.Any(); } } public static Func> TwoFactorChallengCallback - { get { return Arg.Any>> (); } } + { get { return Arg.Any>>(); } } } diff --git a/test/GitHub.Exports.UnitTests/GitHub.Exports.UnitTests.csproj b/test/GitHub.Exports.UnitTests/GitHub.Exports.UnitTests.csproj index 4ae1485c51..c48202bd5e 100644 --- a/test/GitHub.Exports.UnitTests/GitHub.Exports.UnitTests.csproj +++ b/test/GitHub.Exports.UnitTests/GitHub.Exports.UnitTests.csproj @@ -9,6 +9,7 @@ + diff --git a/test/GitHub.Exports.UnitTests/GitServiceTests.cs b/test/GitHub.Exports.UnitTests/GitServiceTests.cs index 1b2ac321ba..c98db18f97 100644 --- a/test/GitHub.Exports.UnitTests/GitServiceTests.cs +++ b/test/GitHub.Exports.UnitTests/GitServiceTests.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading.Tasks; using GitHub.Services; using LibGit2Sharp; @@ -10,6 +8,41 @@ public class GitServiceTests { + public class CreateLocalRepositoryModelTests : TestBaseClass + { + [Test] + public void NoRemoteUrl() + { + using (var temp = new TempDirectory()) + { + var repositoryFacade = Substitute.For(); + var gitService = new GitService(repositoryFacade); + var path = temp.Directory.CreateSubdirectory("repo-name"); + + var model = gitService.CreateLocalRepositoryModel(path.FullName); + + Assert.That(model.Name, Is.EqualTo("repo-name")); + } + } + + [Test] + public void WithRemoteUrl() + { + using (var temp = new TempDirectory()) + { + var path = temp.Directory.CreateSubdirectory("repo-name"); + var repository = CreateRepositoryWithOrigin("https://github.com/user/repo-name"); + var repositoryFacade = CreateRepositoryFacade(path.FullName, repository); + var gitService = new GitService(repositoryFacade); + + var model = gitService.CreateLocalRepositoryModel(path.FullName); + + Assert.That(model.Name, Is.EqualTo("repo-name")); + Assert.That(model.Owner, Is.EqualTo("user")); + } + } + } + [TestCase("asdf", null)] [TestCase("", null)] [TestCase(null, null)] @@ -47,12 +80,20 @@ static IRepositoryFacade CreateRepositoryFacade(string path, IRepository repo) return repositoryFacade; } - static IRepository CreateRepository() + static IRepository CreateRepositoryWithOrigin(string originUrl) { - var repo = Substitute.For(); + var repo = CreateRepository(); + var origin = Substitute.For(); + origin.Url.Returns(originUrl); + repo.Network.Remotes["origin"].Returns(origin); return repo; } + static IRepository CreateRepository() + { + return Substitute.For(); + } + public class TheGetLatestPushedShaMethod : TestBaseClass { [Test] diff --git a/test/GitHub.InlineReviews.UnitTests/Services/PullRequestSessionManagerTests.cs b/test/GitHub.InlineReviews.UnitTests/Services/PullRequestSessionManagerTests.cs index 5b6dc68a57..9d19927856 100644 --- a/test/GitHub.InlineReviews.UnitTests/Services/PullRequestSessionManagerTests.cs +++ b/test/GitHub.InlineReviews.UnitTests/Services/PullRequestSessionManagerTests.cs @@ -41,7 +41,7 @@ public void ReadsPullRequestFromCorrectFork() var sessionService = CreateSessionService(); service.GetPullRequestForCurrentBranch(null).ReturnsForAnyArgs( - Observable.Return(Tuple.Create("fork", CurrentBranchPullRequestNumber))); + Observable.Return(("fork", CurrentBranchPullRequestNumber))); var connectionManager = CreateConnectionManager(); var target = CreateTarget( @@ -80,7 +80,7 @@ public void CreatesSessionForCurrentBranch() public void CurrentSessionIsNullIfNoPullRequestForCurrentBranch() { var service = CreatePullRequestService(); - service.GetPullRequestForCurrentBranch(null).ReturnsForAnyArgs(Observable.Empty>()); + service.GetPullRequestForCurrentBranch(null).ReturnsForAnyArgs(Observable.Empty<(string, int)>()); var target = CreateTarget(service: service); @@ -98,7 +98,7 @@ public void CurrentSessionChangesWhenBranchChanges() var session = target.CurrentSession; - service.GetPullRequestForCurrentBranch(null).ReturnsForAnyArgs(Observable.Return(Tuple.Create("foo", 22))); + service.GetPullRequestForCurrentBranch(null).ReturnsForAnyArgs(Observable.Return(("foo", 22))); teamExplorerContext.StatusChanged += Raise.Event(); Assert.That(session, Is.Not.SameAs(target.CurrentSession)); @@ -124,7 +124,7 @@ public void CurrentSessionChangesToNullIfNoPullRequestForCurrentBranch() teamExplorerContext: teamExplorerContext); Assert.That(target.CurrentSession, Is.Not.Null); - Tuple newPullRequest = null; + (string owner, int number) newPullRequest = default; service.GetPullRequestForCurrentBranch(null).ReturnsForAnyArgs(Observable.Return(newPullRequest)); teamExplorerContext.StatusChanged += Raise.Event(); @@ -237,7 +237,7 @@ public async Task DiffIsSet() sessionService.GetContents(textView.TextBuffer).Returns(contents); sessionService.GetPullRequestMergeBase(null, null).ReturnsForAnyArgs("MERGE_BASE"); sessionService.Diff( - Arg.Any(), + Arg.Any(), "MERGE_BASE", "HEADSHA", FilePath, @@ -690,7 +690,7 @@ IPullRequestSessionService CreateRealSessionService( Substitute.For()); result.CreateRebuildSignal().Returns(new Subject()); result.GetPullRequestMergeBase( - Arg.Any(), + Arg.Any(), Arg.Any()).Returns("MERGE_BASE"); result.ReadPullRequestDetail( Arg.Any(), @@ -813,14 +813,14 @@ public async Task GetSessionUpdatesCurrentSessionIfCurrentBranchIsPullRequestBut var model = CreatePullRequestModel(); var sessionService = CreateSessionService(model); - service.GetPullRequestForCurrentBranch(null).ReturnsForAnyArgs(Observable.Empty>()); + service.GetPullRequestForCurrentBranch(null).ReturnsForAnyArgs(Observable.Empty<(string, int)>()); var target = CreateTarget(service: service, sessionService: sessionService); Assert.That(target.CurrentSession, Is.Null); - service.EnsureLocalBranchesAreMarkedAsPullRequests(Arg.Any(), model).Returns(Observable.Return(true)); - service.GetPullRequestForCurrentBranch(null).ReturnsForAnyArgs(Observable.Return(Tuple.Create("owner", CurrentBranchPullRequestNumber))); + service.EnsureLocalBranchesAreMarkedAsPullRequests(Arg.Any(), model).Returns(Observable.Return(true)); + service.GetPullRequestForCurrentBranch(null).ReturnsForAnyArgs(Observable.Return(("owner", CurrentBranchPullRequestNumber))); var session = await target.GetSession("owner", "name", CurrentBranchPullRequestNumber); @@ -881,7 +881,7 @@ PullRequestDetailModel CreatePullRequestModel( IPullRequestService CreatePullRequestService(string owner = "owner") { var result = Substitute.For(); - result.GetPullRequestForCurrentBranch(null).ReturnsForAnyArgs(Observable.Return(Tuple.Create(owner, CurrentBranchPullRequestNumber))); + result.GetPullRequestForCurrentBranch(null).ReturnsForAnyArgs(Observable.Return((owner, CurrentBranchPullRequestNumber))); return result; } @@ -912,24 +912,24 @@ IPullRequestSessionService CreateSessionService( return sessionService; } - ILocalRepositoryModel CreateRepositoryModel(string cloneUrl = OwnerCloneUrl) + LocalRepositoryModel CreateRepositoryModel(string cloneUrl = OwnerCloneUrl) { - var result = Substitute.For(); - var uriString = new UriString(cloneUrl); - result.CloneUrl.Returns(uriString); - result.Name.Returns(uriString.RepositoryName); - result.Owner.Returns(uriString.Owner); - return result; + var cloneUrlString = new UriString(cloneUrl); + return new LocalRepositoryModel + { + CloneUrl = cloneUrlString, + Name = cloneUrlString.RepositoryName + }; } - static ITeamExplorerContext CreateTeamExplorerContext(ILocalRepositoryModel repo) + static ITeamExplorerContext CreateTeamExplorerContext(LocalRepositoryModel repo) { var teamExplorerContext = Substitute.For(); teamExplorerContext.ActiveRepository.Returns(repo); return teamExplorerContext; } - static void SetActiveRepository(ITeamExplorerContext teamExplorerContext, ILocalRepositoryModel localRepositoryModel) + static void SetActiveRepository(ITeamExplorerContext teamExplorerContext, LocalRepositoryModel localRepositoryModel) { teamExplorerContext.ActiveRepository.Returns(localRepositoryModel); var eventArgs = new PropertyChangedEventArgs(nameof(teamExplorerContext.ActiveRepository)); diff --git a/test/GitHub.InlineReviews.UnitTests/Services/PullRequestSessionTests.cs b/test/GitHub.InlineReviews.UnitTests/Services/PullRequestSessionTests.cs index 7389fc99a3..1f6d0b51fe 100644 --- a/test/GitHub.InlineReviews.UnitTests/Services/PullRequestSessionTests.cs +++ b/test/GitHub.InlineReviews.UnitTests/Services/PullRequestSessionTests.cs @@ -29,7 +29,7 @@ public void IsFalseWithNoPendingReview() CreateRealSessionService(), CreateActor(), CreatePullRequest(), - Substitute.For(), + Substitute.For(), "owner", true); @@ -48,7 +48,7 @@ public void IsFalseWithPendingReviewForOtherUser() CreateRealSessionService(), currentUser, pr, - Substitute.For(), + Substitute.For(), "owner", true); @@ -66,7 +66,7 @@ public void IsFalseWithNonPendingReviewForCurrentUser() CreateRealSessionService(), currentUser, pr, - Substitute.For(), + Substitute.For(), "owner", true); @@ -84,7 +84,7 @@ public void IsTrueWithPendingReviewForCurrentUser() CreateRealSessionService(), currentUser, pr, - Substitute.For(), + Substitute.For(), "owner", true); @@ -148,7 +148,7 @@ public async Task IsFalseWhenReviewCancelled() service, currentUser, pr, - Substitute.For(), + Substitute.For(), "owner", true); @@ -170,7 +170,7 @@ public async Task BaseShaIsSet() CreateRealSessionService(), CreateActor(), CreatePullRequest(), - Substitute.For(), + Substitute.For(), "owner", true); var file = await target.GetFile(FilePath); @@ -185,7 +185,7 @@ public async Task HeadCommitShaIsSet() CreateRealSessionService(), CreateActor(), CreatePullRequest(), - Substitute.For(), + Substitute.For(), "owner", true); var file = await target.GetFile(FilePath); @@ -201,7 +201,7 @@ public async Task PinnedCommitShaIsSet() CreateRealSessionService(), CreateActor(), CreatePullRequest(), - Substitute.For(), + Substitute.For(), "owner", true); var file = await target.GetFile(FilePath, "123"); @@ -217,7 +217,7 @@ public async Task DiffShaIsSet() var sessionService = CreateRealSessionService(); sessionService.Diff( - Arg.Any(), + Arg.Any(), "MERGE_BASE", "HEAD_SHA", FilePath).Returns(diff); @@ -226,7 +226,7 @@ public async Task DiffShaIsSet() sessionService, CreateActor(), CreatePullRequest(), - Substitute.For(), + Substitute.For(), "owner", true); var file = await target.GetFile(FilePath); @@ -264,7 +264,7 @@ Line 2 service, CreateActor(), pullRequest, - Substitute.For(), + Substitute.For(), "owner", true); @@ -281,7 +281,7 @@ public async Task SameNonHeadCommitShasReturnSameFiles() CreateRealSessionService(), CreateActor(), CreatePullRequest(), - Substitute.For(), + Substitute.For(), "owner", true); var file1 = await target.GetFile(FilePath, "123"); @@ -297,7 +297,7 @@ public async Task DifferentCommitShasReturnDifferentFiles() CreateRealSessionService(), CreateActor(), CreatePullRequest(), - Substitute.For(), + Substitute.For(), "owner", true); var file1 = await target.GetFile(FilePath, "123"); @@ -316,7 +316,7 @@ public void ThrowsWithNoPendingReview() CreateRealSessionService(), CreateActor(), CreatePullRequest(), - Substitute.For(), + Substitute.For(), "owner", true); @@ -334,7 +334,7 @@ public async Task CallsServiceWithNodeId() await target.CancelReview(); await service.Received(1).CancelPendingReview( - Arg.Any(), + Arg.Any(), "review1"); } @@ -365,7 +365,7 @@ public static PullRequestSession CreateTargetWithPendingReview( service, currentUser, pr, - Substitute.For(), + Substitute.For(), "owner", true); } @@ -747,11 +747,12 @@ static PullRequestDetailModel CreatePullRequest(params PullRequestReviewThreadMo }; } - static ILocalRepositoryModel CreateLocalRepository() + static LocalRepositoryModel CreateLocalRepository() { - var result = Substitute.For(); - result.CloneUrl.Returns(new UriString("https://github.com/owner/repo")); - return result; + return new LocalRepositoryModel + { + CloneUrl = new UriString("https://github.com/owner/repo") + }; } static IPullRequestSessionService CreateMockSessionService() @@ -770,8 +771,8 @@ static IPullRequestSessionService CreateRealSessionService(IDiffService diffServ Substitute.For(), Substitute.For()); - result.GetTipSha(Arg.Any()).Returns("BRANCH_TIP"); - result.GetPullRequestMergeBase(Arg.Any(), Arg.Any()) + result.GetTipSha(Arg.Any()).Returns("BRANCH_TIP"); + result.GetPullRequestMergeBase(Arg.Any(), Arg.Any()) .Returns("MERGE_BASE"); return result; } @@ -782,11 +783,11 @@ static PullRequestSession CreateTarget( string remoteRepositoryOwner, bool hasPendingReview) { - var repository = Substitute.For(); - - repository.CloneUrl.Returns(new UriString($"https://github.com/{localRepositoryOwner}/reop")); - repository.Owner.Returns(localRepositoryOwner); - repository.Name.Returns("repo"); + var repository = new LocalRepositoryModel + { + CloneUrl = $"https://github.com/{localRepositoryOwner}/repo", + Name = "repo" + }; var pr = CreatePullRequest(); var user = CreateActor(); diff --git a/test/GitHub.InlineReviews.UnitTests/ViewModels/InlineCommentPeekViewModelTests.cs b/test/GitHub.InlineReviews.UnitTests/ViewModels/InlineCommentPeekViewModelTests.cs index f1e5d96122..1b4945b842 100644 --- a/test/GitHub.InlineReviews.UnitTests/ViewModels/InlineCommentPeekViewModelTests.cs +++ b/test/GitHub.InlineReviews.UnitTests/ViewModels/InlineCommentPeekViewModelTests.cs @@ -314,7 +314,7 @@ static IPullRequestSession CreateSession() var result = Substitute.For(); result.PullRequest.Returns(new PullRequestDetailModel()); result.User.Returns(new ActorModel { Login = "CurrentUser" }); - result.LocalRepository.CloneUrl.Returns(new UriString("https://foo.bar")); + result.LocalRepository.Returns(new LocalRepositoryModel { CloneUrl = new UriString("https://foo.bar") }); return result; } diff --git a/test/GitHub.TeamFoundation.UnitTests/VSGitExtTests.cs b/test/GitHub.TeamFoundation.UnitTests/VSGitExtTests.cs index 7b9ee71c58..ddf11c119d 100644 --- a/test/GitHub.TeamFoundation.UnitTests/VSGitExtTests.cs +++ b/test/GitHub.TeamFoundation.UnitTests/VSGitExtTests.cs @@ -10,10 +10,12 @@ using NUnit.Framework; using NSubstitute; using Microsoft.VisualStudio.TeamFoundation.Git.Extensibility; -using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Threading; using Task = System.Threading.Tasks.Task; using static Microsoft.VisualStudio.VSConstants; +using GitHub.Primitives; +using LibGit2Sharp; +using System.Threading.Tasks; public class VSGitExtTests { @@ -149,32 +151,32 @@ public void RepositoryOpenContextNotActive_IsEmpty() public void RepositoryOpenIsActive_InitializeWithActiveRepositories() { var repoPath = "repoPath"; - var repoFactory = Substitute.For(); + var gitService = Substitute.For(); var context = CreateVSUIContext(true); var gitExt = CreateGitExt(new[] { repoPath }); - var target = CreateVSGitExt(context, gitExt, repoFactory: repoFactory); + var target = CreateVSGitExt(context, gitExt, gitService: gitService); target.JoinTillEmpty(); var activeRepositories = target.ActiveRepositories; Assert.That(activeRepositories.Count, Is.EqualTo(1)); - repoFactory.Received(1).Create(repoPath); + gitService.Received(1).CreateLocalRepositoryModel(repoPath); } [Test] public void ExceptionRefreshingRepositories_ReturnsEmptyList() { var repoPath = "repoPath"; - var repoFactory = Substitute.For(); - repoFactory.Create(repoPath).ReturnsForAnyArgs(x => { throw new Exception("Boom!"); }); + var gitService = Substitute.For(); + gitService.CreateLocalRepositoryModel(repoPath).ReturnsForAnyArgs(x => { throw new Exception("Boom!"); }); var context = CreateVSUIContext(true); var gitExt = CreateGitExt(new[] { repoPath }); - var target = CreateVSGitExt(context, gitExt, repoFactory: repoFactory); + var target = CreateVSGitExt(context, gitExt, gitService: gitService); target.JoinTillEmpty(); var activeRepositories = target.ActiveRepositories; - repoFactory.Received(1).Create(repoPath); + gitService.Received(1).CreateLocalRepositoryModel(repoPath); Assert.That(activeRepositories.Count, Is.EqualTo(0)); } @@ -182,8 +184,8 @@ public void ExceptionRefreshingRepositories_ReturnsEmptyList() public async Task ActiveRepositoriesChangedOrderingShouldBeCorrectAcrossThreads() { var gitExt = new MockGitExt(); - var repoFactory = new MockRepositoryFactory(); - var target = CreateVSGitExt(gitExt: gitExt, repoFactory: repoFactory); + var gitService = new MockGitService(); + var target = CreateVSGitExt(gitExt: gitExt, gitService: gitService); var activeRepositories1 = CreateActiveRepositories("repo1"); var activeRepositories2 = CreateActiveRepositories("repo2"); var task1 = Task.Run(() => gitExt.ActiveRepositories = activeRepositories1); @@ -211,18 +213,18 @@ static IReadOnlyList CreateActiveRepositories(params string[ } static VSGitExt CreateVSGitExt(IVSUIContext context = null, IGitExt gitExt = null, IServiceProvider sp = null, - ILocalRepositoryModelFactory repoFactory = null, JoinableTaskContext joinableTaskContext = null, string contextGuidString = null) + IGitService gitService = null, JoinableTaskContext joinableTaskContext = null, string contextGuidString = null) { context = context ?? CreateVSUIContext(true); gitExt = gitExt ?? CreateGitExt(); var contextGuid = new Guid(contextGuidString ?? Guids.GitSccProviderId); sp = sp ?? Substitute.For(); - repoFactory = repoFactory ?? Substitute.For(); + gitService = gitService ?? Substitute.For(); joinableTaskContext = joinableTaskContext ?? new JoinableTaskContext(); var factory = Substitute.For(); factory.GetUIContext(contextGuid).Returns(context); sp.GetService(typeof(IGitExt)).Returns(gitExt); - var vsGitExt = new VSGitExt(sp, factory, repoFactory, joinableTaskContext); + var vsGitExt = new VSGitExt(sp, factory, gitService, joinableTaskContext); vsGitExt.JoinTillEmpty(); return vsGitExt; } @@ -291,12 +293,14 @@ public IReadOnlyList ActiveRepositories public event PropertyChangedEventHandler PropertyChanged; } - class MockRepositoryFactory : ILocalRepositoryModelFactory + class MockGitService : IGitService { - public ILocalRepositoryModel Create(string localPath) + public LocalRepositoryModel CreateLocalRepositoryModel(string localPath) { - var result = Substitute.For(); - result.LocalPath.Returns(localPath); + var result = new LocalRepositoryModel + { + LocalPath = localPath + }; if (localPath == "repo1") { @@ -308,5 +312,12 @@ public ILocalRepositoryModel Create(string localPath) return result; } + + public BranchModel GetBranch(LocalRepositoryModel model) => throw new NotImplementedException(); + public Task GetLatestPushedSha(string path, string remote = "origin") => throw new NotImplementedException(); + public UriString GetRemoteUri(IRepository repo, string remote = "origin") => throw new NotImplementedException(); + public IRepository GetRepository(string path) => throw new NotImplementedException(); + public UriString GetUri(IRepository repository, string remote = "origin") => throw new NotImplementedException(); + public UriString GetUri(string path, string remote = "origin") => throw new NotImplementedException(); } } diff --git a/test/GitHub.VisualStudio.UnitTests/Args.cs b/test/GitHub.VisualStudio.UnitTests/Args.cs index 2a04705cde..35f32fc6e0 100644 --- a/test/GitHub.VisualStudio.UnitTests/Args.cs +++ b/test/GitHub.VisualStudio.UnitTests/Args.cs @@ -20,16 +20,16 @@ internal static class Args public static IApiClient ApiClient { get { return Arg.Any(); } } public static IServiceProvider ServiceProvider { get { return Arg.Any(); } } public static IAvatarProvider AvatarProvider { get { return Arg.Any(); } } - public static HostAddress HostAddress { get { return Arg.Any(); } } + public static HostAddress HostAddress { get { return Arg.Any(); } } public static Uri Uri { get { return Arg.Any(); } } public static LibGit2Sharp.IRepository LibGit2Repo { get { return Arg.Any(); } } public static LibGit2Sharp.Branch LibGit2Branch { get { return Arg.Any(); } } public static Remote LibgGit2Remote { get { return Arg.Any(); } } - public static ILocalRepositoryModel LocalRepositoryModel { get { return Arg.Any(); } } - public static IRemoteRepositoryModel RemoteRepositoryModel { get { return Arg.Any(); } } - public static IBranch Branch { get { return Arg.Any(); } } + public static LocalRepositoryModel LocalRepositoryModel { get { return Arg.Any(); } } + public static RemoteRepositoryModel RemoteRepositoryModel { get { return Arg.Any(); } } + public static BranchModel Branch { get { return Arg.Any(); } } public static IGitService GitService { get { return Arg.Any(); } } public static Func> TwoFactorChallengCallback - { get { return Arg.Any>> (); } } + { get { return Arg.Any>>(); } } } diff --git a/test/GitHub.Exports.UnitTests/LocalRepositoryModelTests.cs b/test/GitHub.VisualStudio.UnitTests/Commands/LinkCommandBaseTests.cs similarity index 81% rename from test/GitHub.Exports.UnitTests/LocalRepositoryModelTests.cs rename to test/GitHub.VisualStudio.UnitTests/Commands/LinkCommandBaseTests.cs index cb2c163b61..c993149b73 100644 --- a/test/GitHub.Exports.UnitTests/LocalRepositoryModelTests.cs +++ b/test/GitHub.VisualStudio.UnitTests/Commands/LinkCommandBaseTests.cs @@ -1,14 +1,13 @@ -using System.Threading.Tasks; -using System.Collections.Generic; +using System; +using System.Threading.Tasks; using GitHub.Models; using GitHub.Exports; using GitHub.Services; -using GitHub.Primitives; +using GitHub.VisualStudio.Commands; using NSubstitute; -using LibGit2Sharp; using NUnit.Framework; -public class LocalRepositoryModelTests : TestBaseClass +public class LinkCommandBaseTests : TestBaseClass { [TestCase(1, LinkType.Blob, false, "https://github.com/foo/bar", "123123", @"src\dir\file1.cs", -1, -1, "https://github.com/foo/bar/blob/123123/src/dir/file1.cs")] [TestCase(2, LinkType.Blob, false, "https://github.com/foo/bar", "123123", @"src\dir\file1.cs", 1, -1, "https://github.com/foo/bar/blob/123123/src/dir/file1.cs#L1")] @@ -47,33 +46,19 @@ public async Task GenerateUrl(int testid, LinkType linkType, bool createRootedPa var basePath = temp.Directory.CreateSubdirectory("generate-url-test1-" + testid); if (createRootedPath && path != null) path = System.IO.Path.Combine(basePath.FullName, path); - ILocalRepositoryModel model = null; - if (!string.IsNullOrEmpty(baseUrl)) - model = new LocalRepositoryModel("bar", new UriString(baseUrl), basePath.FullName, gitService); - else - model = new LocalRepositoryModel(basePath.FullName, gitService); - var result = await model.GenerateUrl(linkType, path, startLine, endLine); - Assert.That(expected, Is.EqualTo(result?.ToString())); + LocalRepositoryModel model = null; + model = new LocalRepositoryModel { Name = "bar", CloneUrl = baseUrl, LocalPath = basePath.FullName }; + + var result = await LinkCommandBase.GenerateUrl(gitService, model, linkType, path, startLine, endLine); + + Assert.That(result?.ToString(), Is.EqualTo(expected)); } } static IGitService CreateGitService(string sha) { var gitservice = Substitute.For(); - var repo = Substitute.For(); - gitservice.GetRepository(Args.String).Returns(repo); gitservice.GetLatestPushedSha(Args.String).Returns(Task.FromResult(sha)); - if (!string.IsNullOrEmpty(sha)) - { - var refs = Substitute.For(); - var refrence = Substitute.For(); - refs.ReachableFrom(Arg.Any>(), Arg.Any>()).Returns(new Reference[] { refrence }); - repo.Refs.Returns(refs); - var commit = Substitute.For(); - commit.Sha.Returns(sha); - repo.Commits.Returns(new FakeCommitLog { commit }); - } - return gitservice; } } diff --git a/test/GitHub.VisualStudio.UnitTests/Commands/OpenFromClipboardCommandTests.cs b/test/GitHub.VisualStudio.UnitTests/Commands/OpenFromClipboardCommandTests.cs index 0235482263..a29b9a8afe 100644 --- a/test/GitHub.VisualStudio.UnitTests/Commands/OpenFromClipboardCommandTests.cs +++ b/test/GitHub.VisualStudio.UnitTests/Commands/OpenFromClipboardCommandTests.cs @@ -3,10 +3,10 @@ using System.Threading.Tasks; using GitHub; using GitHub.Exports; +using GitHub.Models; using GitHub.Primitives; using GitHub.Services; using GitHub.VisualStudio.Commands; -using GitHub.VisualStudio.UI; using Microsoft.VisualStudio; using NSubstitute; using NUnit.Framework; @@ -146,7 +146,8 @@ public async Task HasChangesInWorkingDirectory(bool annotateFileSupported, strin var resolveBlobResult = (targetBranch, "foo.cs", ""); var vsServices = Substitute.For(); var target = CreateOpenFromClipboardCommand(gitHubContextService: gitHubContextService, vsServices: vsServices, - contextFromClipboard: context, repositoryDir: repositoryDir, repositoryName: repositoryName, currentBranch: currentBranch, resolveBlobResult: resolveBlobResult, hasChanges: true); + contextFromClipboard: context, repositoryDir: repositoryDir, repositoryName: repositoryName, + currentBranchName: currentBranch, resolveBlobResult: resolveBlobResult, hasChanges: true); await target.Execute(null); @@ -179,7 +180,7 @@ static OpenFromClipboardCommand CreateOpenFromClipboardCommand( string repositoryDir = null, string repositoryName = null, string repositoryOwner = null, - string currentBranch = null, + string currentBranchName = null, (string, string, string)? resolveBlobResult = null, bool? hasChanges = null) { @@ -189,10 +190,16 @@ static OpenFromClipboardCommand CreateOpenFromClipboardCommand( vsServices = vsServices ?? Substitute.For(); gitHubContextService.FindContextFromClipboard().Returns(contextFromClipboard); - teamExplorerContext.ActiveRepository.LocalPath.Returns(repositoryDir); - teamExplorerContext.ActiveRepository.Name.Returns(repositoryName); - teamExplorerContext.ActiveRepository.Owner.Returns(repositoryOwner); - teamExplorerContext.ActiveRepository.CurrentBranch.Name.Returns(currentBranch); + var activeRepository = new LocalRepositoryModel + { + LocalPath = repositoryDir, + Name = repositoryName, + CloneUrl = new UriString($"https://github.com/{repositoryOwner}/{repositoryName}") + }; + teamExplorerContext.ActiveRepository.Returns(activeRepository); + var gitService = Substitute.For(); + var currentBranch = currentBranchName != null ? new BranchModel(currentBranchName, activeRepository) : null; + gitService.GetBranch(activeRepository).Returns(currentBranch); if (resolveBlobResult != null) { gitHubContextService.ResolveBlob(repositoryDir, contextFromClipboard).Returns(resolveBlobResult.Value); @@ -206,7 +213,8 @@ static OpenFromClipboardCommand CreateOpenFromClipboardCommand( return new OpenFromClipboardCommand( new Lazy(() => gitHubContextService), new Lazy(() => teamExplorerContext), - new Lazy(() => vsServices)); + new Lazy(() => vsServices), + new Lazy(() => gitService)); } static string ResolveResources(string str) diff --git a/test/GitHub.VisualStudio.UnitTests/Services/LocalRepositoriesTests.cs b/test/GitHub.VisualStudio.UnitTests/Services/LocalRepositoriesTests.cs index 16d43caaf9..784d7b1041 100644 --- a/test/GitHub.VisualStudio.UnitTests/Services/LocalRepositoriesTests.cs +++ b/test/GitHub.VisualStudio.UnitTests/Services/LocalRepositoriesTests.cs @@ -32,7 +32,7 @@ public async Task RefreshShouldLoadRepositories() Assert.That( new[] { "repo1", "repo2" }, - Is.EqualTo(target.Repositories.Select(x => x.Name).ToList())); + Is.EqualTo(target.Repositories.Select(x => x.Name).ToList())); } [Test] @@ -53,7 +53,7 @@ public async Task RefreshShouldAddNewRepository() Assert.That( new[] { "repo1", "repo2", "new" }, - Is.EqualTo(target.Repositories.Select(x => x.Name).ToList())); + Is.EqualTo(target.Repositories.Select(x => x.Name).ToList())); } [Test] @@ -73,7 +73,7 @@ public async Task RefreshShouldRemoveRepository() Assert.That( new[] { "repo2" }, - Is.EqualTo(target.Repositories.Select(x => x.Name).ToList())); + Is.EqualTo(target.Repositories.Select(x => x.Name).ToList())); } [Test] @@ -105,7 +105,7 @@ public async Task GetRepositoriesForAddressShouldSortRepositories() Assert.That( new[] { "a", "b", "c" }, - Is.EqualTo(result.Select(x => x.Name).ToList())); + Is.EqualTo(result.Select(x => x.Name).ToList())); } static IVSGitServices CreateVSGitServices(params string[] names) @@ -116,21 +116,22 @@ static IVSGitServices CreateVSGitServices(params string[] names) static IVSGitServices CreateVSGitServices(params Tuple[] namesAndAddresses) { var result = Substitute.For(); - var repositories = new List(namesAndAddresses.Select(CreateRepository)); + var repositories = new List(namesAndAddresses.Select(CreateRepository)); result.GetKnownRepositories().Returns(repositories); return result; } - static ILocalRepositoryModel CreateRepository(string name) + static LocalRepositoryModel CreateRepository(string name) { return CreateRepository(Tuple.Create(name, "https://github.com")); } - static ILocalRepositoryModel CreateRepository(Tuple nameAndAddress) + static LocalRepositoryModel CreateRepository(Tuple nameAndAddress) { - var result = Substitute.For(); - result.Name.Returns(nameAndAddress.Item1); - result.CloneUrl.Returns(new UriString(nameAndAddress.Item2)); - return result; + return new LocalRepositoryModel + { + Name = nameAndAddress.Item1, + CloneUrl = new UriString(nameAndAddress.Item2) + }; } }