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

Commit d55bc99

Browse files
author
Meaghan Lewis
authored
Merge branch 'master' into task/auto-detect-tests
2 parents 668b94e + 9eb8dfb commit d55bc99

35 files changed

+1093
-449
lines changed

appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version: '2.3.7.{build}'
1+
version: '2.4.0.{build}'
22
skip_tags: true
33
install:
44
- ps: |

src/GitHub.App/GitHub.App.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@
5050
</PropertyGroup>
5151
<Import Project="$(SolutionDir)\src\common\signing.props" />
5252
<ItemGroup>
53+
<Reference Include="envdte, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
54+
<EmbedInteropTypes>False</EmbedInteropTypes>
55+
</Reference>
5356
<Reference Include="LibGit2Sharp, Version=0.23.1.0, Culture=neutral, PublicKeyToken=7cbde695407f0333, processorArchitecture=MSIL">
5457
<HintPath>..\..\packages\LibGit2Sharp.0.23.1\lib\net40\LibGit2Sharp.dll</HintPath>
5558
<Private>True</Private>
@@ -139,6 +142,7 @@
139142
<Compile Include="Models\PullRequestDetailArgument.cs" />
140143
<Compile Include="Services\EnterpriseCapabilitiesService.cs" />
141144
<Compile Include="Services\GlobalConnection.cs" />
145+
<Compile Include="Services\TeamExplorerContext.cs" />
142146
<Compile Include="Services\OAuthCallbackListener.cs" />
143147
<Compile Include="ViewModels\Dialog\GistCreationViewModel.cs" />
144148
<Compile Include="ViewModels\Dialog\GitHubDialogWindowViewModel.cs" />

src/GitHub.App/Services/PullRequestService.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
using System.Reactive;
1515
using System.Collections.Generic;
1616
using LibGit2Sharp;
17-
using System.Diagnostics;
1817
using GitHub.Logging;
1918

2019
namespace GitHub.Services
@@ -287,7 +286,7 @@ public bool IsPullRequestFromRepository(ILocalRepositoryModel repository, IPullR
287286
{
288287
if (pullRequest.Head?.RepositoryCloneUrl != null)
289288
{
290-
return repository.CloneUrl.ToRepositoryUrl() == pullRequest.Head.RepositoryCloneUrl.ToRepositoryUrl();
289+
return repository.CloneUrl?.ToRepositoryUrl() == pullRequest.Head.RepositoryCloneUrl.ToRepositoryUrl();
291290
}
292291

293292
return false;
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
using System;
2+
using System.Linq;
3+
using System.ComponentModel;
4+
using System.ComponentModel.Composition;
5+
using GitHub.Models;
6+
using GitHub.Logging;
7+
using Serilog;
8+
using EnvDTE;
9+
10+
namespace GitHub.Services
11+
{
12+
/// <summary>
13+
/// This implementation listenes to IGitExt for ActiveRepositories property change events and fires
14+
/// <see cref="PropertyChanged"/> and <see cref="StatusChanged"/> events when appropriate.
15+
/// </summary>
16+
/// <remarks>
17+
/// A <see cref="PropertyChanged"/> is fired when a solution is opened in a new repository (or not in a repository).
18+
/// A <see cref="StatusChanged"/> event is only fired when the current branch, head SHA or tracked SHA changes (not
19+
/// on every IGitExt property change event). <see cref="ActiveRepository"/> contains the active repository or null
20+
/// if a solution is opened that isn't in a repository. No events are fired when the same solution is unloaded then
21+
/// reloaded (e.g. when a .sln file is touched).
22+
/// </remarks>
23+
[Export(typeof(ITeamExplorerContext))]
24+
[PartCreationPolicy(CreationPolicy.Shared)]
25+
public class TeamExplorerContext : ITeamExplorerContext
26+
{
27+
static ILogger log = LogManager.ForContext<TeamExplorerContext>();
28+
29+
readonly DTE dte;
30+
readonly IVSGitExt gitExt;
31+
32+
string solutionPath;
33+
string repositoryPath;
34+
string branchName;
35+
string headSha;
36+
string trackedSha;
37+
38+
ILocalRepositoryModel repositoryModel;
39+
40+
[ImportingConstructor]
41+
public TeamExplorerContext(IGitHubServiceProvider serviceProvider, IVSGitExt gitExt)
42+
: this(gitExt, serviceProvider)
43+
{
44+
}
45+
46+
public TeamExplorerContext(IVSGitExt gitExt, IGitHubServiceProvider serviceProvider)
47+
{
48+
this.gitExt = gitExt;
49+
50+
// This is a standard service which should always be available.
51+
dte = serviceProvider.GetService<DTE>();
52+
53+
Refresh();
54+
gitExt.ActiveRepositoriesChanged += Refresh;
55+
}
56+
57+
void Refresh()
58+
{
59+
try
60+
{
61+
var repo = gitExt.ActiveRepositories?.FirstOrDefault();
62+
var newSolutionPath = dte.Solution?.FullName;
63+
64+
if (repo == null && newSolutionPath == solutionPath)
65+
{
66+
// Ignore when ActiveRepositories is empty and solution hasn't changed.
67+
// https://github.com/github/VisualStudio/issues/1421
68+
log.Information("Ignoring no ActiveRepository when solution hasn't changed");
69+
}
70+
else
71+
{
72+
var newRepositoryPath = repo?.LocalPath;
73+
var newBranchName = repo?.CurrentBranch?.Name;
74+
var newHeadSha = repo?.CurrentBranch?.Sha;
75+
var newTrackedSha = repo?.CurrentBranch?.TrackedSha;
76+
77+
if (newRepositoryPath != repositoryPath)
78+
{
79+
log.Information("Fire PropertyChanged event for ActiveRepository");
80+
ActiveRepository = repo;
81+
}
82+
else if (newBranchName != branchName)
83+
{
84+
log.Information("Fire StatusChanged event when BranchName changes for ActiveRepository");
85+
StatusChanged?.Invoke(this, EventArgs.Empty);
86+
}
87+
else if (newHeadSha != headSha)
88+
{
89+
log.Information("Fire StatusChanged event when HeadSha changes for ActiveRepository");
90+
StatusChanged?.Invoke(this, EventArgs.Empty);
91+
}
92+
else if (newTrackedSha != trackedSha)
93+
{
94+
log.Information("Fire StatusChanged event when TrackedSha changes for ActiveRepository");
95+
StatusChanged?.Invoke(this, EventArgs.Empty);
96+
}
97+
98+
repositoryPath = newRepositoryPath;
99+
branchName = newBranchName;
100+
headSha = newHeadSha;
101+
solutionPath = newSolutionPath;
102+
trackedSha = newTrackedSha;
103+
}
104+
}
105+
catch (Exception e)
106+
{
107+
log.Error(e, "Refreshing active repository");
108+
}
109+
}
110+
111+
/// <summary>
112+
/// The active repository or null if not in a repository.
113+
/// </summary>
114+
public ILocalRepositoryModel ActiveRepository
115+
{
116+
get
117+
{
118+
return repositoryModel;
119+
}
120+
121+
private set
122+
{
123+
if (value != repositoryModel)
124+
{
125+
repositoryModel = value;
126+
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ActiveRepository)));
127+
}
128+
}
129+
}
130+
131+
/// <summary>
132+
/// Fired when a solution is opened in a new repository (or that isn't in a repository).
133+
/// </summary>
134+
public event PropertyChangedEventHandler PropertyChanged;
135+
136+
/// <summary>
137+
/// Fired when the current branch, head SHA or tracked SHA changes.
138+
/// </summary>
139+
public event EventHandler StatusChanged;
140+
}
141+
}

src/GitHub.App/ViewModels/GitHubPane/GitHubPaneViewModel.cs

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public sealed class GitHubPaneViewModel : ViewModelBase, IGitHubPaneViewModel, I
3232
readonly IViewViewModelFactory viewModelFactory;
3333
readonly ISimpleApiClientFactory apiClientFactory;
3434
readonly IConnectionManager connectionManager;
35-
readonly ITeamExplorerServiceHolder teServiceHolder;
35+
readonly ITeamExplorerContext teamExplorerContext;
3636
readonly IVisualStudioBrowser browser;
3737
readonly IUsageTracker usageTracker;
3838
readonly INavigationViewModel navigator;
@@ -46,7 +46,6 @@ public sealed class GitHubPaneViewModel : ViewModelBase, IGitHubPaneViewModel, I
4646
readonly ReactiveCommand<Unit> refresh;
4747
readonly ReactiveCommand<Unit> showPullRequests;
4848
readonly ReactiveCommand<object> openInBrowser;
49-
bool initialized;
5049
IViewModel content;
5150
ILocalRepositoryModel localRepository;
5251
string searchQuery;
@@ -56,7 +55,7 @@ public GitHubPaneViewModel(
5655
IViewViewModelFactory viewModelFactory,
5756
ISimpleApiClientFactory apiClientFactory,
5857
IConnectionManager connectionManager,
59-
ITeamExplorerServiceHolder teServiceHolder,
58+
ITeamExplorerContext teamExplorerContext,
6059
IVisualStudioBrowser browser,
6160
IUsageTracker usageTracker,
6261
INavigationViewModel navigator,
@@ -67,7 +66,7 @@ public GitHubPaneViewModel(
6766
Guard.ArgumentNotNull(viewModelFactory, nameof(viewModelFactory));
6867
Guard.ArgumentNotNull(apiClientFactory, nameof(apiClientFactory));
6968
Guard.ArgumentNotNull(connectionManager, nameof(connectionManager));
70-
Guard.ArgumentNotNull(teServiceHolder, nameof(teServiceHolder));
69+
Guard.ArgumentNotNull(teamExplorerContext, nameof(teamExplorerContext));
7170
Guard.ArgumentNotNull(browser, nameof(browser));
7271
Guard.ArgumentNotNull(usageTracker, nameof(usageTracker));
7372
Guard.ArgumentNotNull(navigator, nameof(navigator));
@@ -78,7 +77,7 @@ public GitHubPaneViewModel(
7877
this.viewModelFactory = viewModelFactory;
7978
this.apiClientFactory = apiClientFactory;
8079
this.connectionManager = connectionManager;
81-
this.teServiceHolder = teServiceHolder;
80+
this.teamExplorerContext = teamExplorerContext;
8281
this.browser = browser;
8382
this.usageTracker = usageTracker;
8483
this.navigator = navigator;
@@ -199,8 +198,12 @@ public void Dispose()
199198
/// <inheritdoc/>
200199
public async Task InitializeAsync(IServiceProvider paneServiceProvider)
201200
{
202-
await UpdateContent(teServiceHolder.ActiveRepo);
203-
teServiceHolder.Subscribe(this, x => UpdateContentIfRepositoryChanged(x).Forget());
201+
await UpdateContent(teamExplorerContext.ActiveRepository);
202+
teamExplorerContext.WhenAnyValue(x => x.ActiveRepository)
203+
.Skip(1)
204+
.ObserveOn(RxApp.MainThreadScheduler)
205+
.Subscribe(x => UpdateContent(x).Forget());
206+
204207
connectionManager.Connections.CollectionChanged += (_, __) => UpdateContent(LocalRepository).Forget();
205208

206209
BindNavigatorCommand(paneServiceProvider, PkgCmdIDList.pullRequestCommand, showPullRequests);
@@ -274,7 +277,7 @@ public Task ShowCreatePullRequest()
274277
/// <inheritdoc/>
275278
public Task ShowPullRequests()
276279
{
277-
return NavigateTo<IPullRequestListViewModel>(x => x.InitializeAsync(LocalRepository, Connection));
280+
return NavigateTo<IPullRequestListViewModel>(x => x.InitializeAsync(LocalRepository, Connection));
278281
}
279282

280283
/// <inheritdoc/>
@@ -344,7 +347,6 @@ async Task NavigateTo<TViewModel>(Func<TViewModel, Task> initialize, Func<TViewM
344347

345348
async Task UpdateContent(ILocalRepositoryModel repository)
346349
{
347-
initialized = true;
348350
LocalRepository = repository;
349351
Connection = null;
350352
Content = null;
@@ -388,14 +390,6 @@ async Task UpdateContent(ILocalRepositoryModel repository)
388390
}
389391
}
390392

391-
async Task UpdateContentIfRepositoryChanged(ILocalRepositoryModel repository)
392-
{
393-
if (!initialized || !Equals(repository, LocalRepository))
394-
{
395-
await UpdateContent(repository);
396-
}
397-
}
398-
399393
static async Task<bool> IsValidRepository(ISimpleApiClient client)
400394
{
401395
try

0 commit comments

Comments
 (0)