From 9623aa1b22cb37f4790abb70193c17da76757bb4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 7 Sep 2026 10:40:17 +0000 Subject: [PATCH 1/4] Initial plan From 4e6372447d4b1c82e47a123d63a3c3767112d829 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 7 Sep 2026 10:58:19 +0000 Subject: [PATCH 2/4] Open PR links without a workspace Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> --- src/extension.ts | 7 +- src/github/externalUriOpener.ts | 53 ++------------- src/github/folderRepositoryManagerProvider.ts | 58 ++++++++++++++++ src/github/overviewRestorer.ts | 6 +- src/test/github/externalUriOpener.test.ts | 9 ++- src/test/uriHandler.test.ts | 66 +++++++++++++++++++ src/uriHandler.ts | 8 ++- 7 files changed, 151 insertions(+), 56 deletions(-) create mode 100644 src/github/folderRepositoryManagerProvider.ts create mode 100644 src/test/uriHandler.test.ts diff --git a/src/extension.ts b/src/extension.ts index cbf9ebdcc7..9a1a89366c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -26,6 +26,7 @@ import { createExperimentationService, ExperimentationTelemetry } from './experi import { CopilotRemoteAgentManager } from './github/copilotRemoteAgent'; import { CredentialStore } from './github/credentials'; import { FolderRepositoryManager } from './github/folderRepositoryManager'; +import { FolderRepositoryManagerProvider } from './github/folderRepositoryManagerProvider'; import { OverviewRestorer } from './github/overviewRestorer'; import { RepositoriesManager } from './github/repositoriesManager'; import { registerBuiltinGitProvider, registerLiveShareGitProvider } from './gitProviders/api'; @@ -292,7 +293,9 @@ async function init( context.subscriptions.push(new GitLensIntegration()); - context.subscriptions.push(new OverviewRestorer(reposManager, telemetry, context, credentialStore)); + const folderRepositoryManagerProvider = new FolderRepositoryManagerProvider(context, reposManager, telemetry, git); + context.subscriptions.push(folderRepositoryManagerProvider); + context.subscriptions.push(new OverviewRestorer(reposManager, telemetry, context, credentialStore, folderRepositoryManagerProvider)); await vscode.commands.executeCommand('setContext', 'github:initialized', true); @@ -302,7 +305,7 @@ async function init( await resumePendingCheckout(reviewsManager, context, reposManager); initChat(context, credentialStore, reposManager); - context.subscriptions.push(vscode.window.registerUriHandler(new UriHandler(reposManager, reviewsManager, telemetry, context, git))); + context.subscriptions.push(vscode.window.registerUriHandler(new UriHandler(reposManager, reviewsManager, telemetry, context, git, folderRepositoryManagerProvider))); // Make sure any compare changes tabs, which come from the create flow, are closed. CompareChanges.closeTabs(); diff --git a/src/github/externalUriOpener.ts b/src/github/externalUriOpener.ts index a04fb38c69..8a772c8739 100644 --- a/src/github/externalUriOpener.ts +++ b/src/github/externalUriOpener.ts @@ -4,28 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { RemoteOnlyRepository } from '../api/remoteOnlyRepository'; -import { Disposable } from '../common/lifecycle'; -import { IThemeWatcher } from '../themeWatcher'; -import { CredentialStore } from './credentials'; -import { FolderRepositoryManager } from './folderRepositoryManager'; +import { FolderRepositoryManagerProvider } from './folderRepositoryManagerProvider'; import { IssueOverviewPanel } from './issueOverview'; import { PullRequestOverviewPanel } from './pullRequestOverview'; -import { RepositoriesManager } from './repositoriesManager'; -import { GitApiImpl } from '../api/api1'; import { getGitHubIssueOrPullRequestUriOpenerPriority, parseGitHubIssueOrPullRequestUri } from '../common/externalUri'; +import { Disposable } from '../common/lifecycle'; import { ITelemetry } from '../common/telemetry'; import { EXTENSION_ID } from '../constants'; -import { CreatePullRequestHelper } from '../view/createPullRequestHelper'; -import { ThemeData } from '../view/theme'; class GitHubIssueOrPullRequestExternalUriOpener extends Disposable implements vscode.ExternalUriOpener { - private _remoteFolderRepositoryManager: FolderRepositoryManager | undefined; - constructor( private readonly _context: vscode.ExtensionContext, - private readonly _repositoriesManager: RepositoriesManager, - private readonly _credentialStore: CredentialStore, + private readonly _folderRepositoryManagerProvider: FolderRepositoryManagerProvider, private readonly _telemetry: ITelemetry, ) { super(); @@ -45,7 +35,7 @@ class GitHubIssueOrPullRequestExternalUriOpener extends Disposable implements vs return; } - const folderRepositoryManager = this.getFolderRepositoryManager(identity.owner, identity.repo); + const folderRepositoryManager = this._folderRepositoryManagerProvider.getManagerForRepository(identity.owner, identity.repo); if (identity.kind === 'pullRequest') { const pullRequest = await folderRepositoryManager.resolvePullRequest(identity.owner, identity.repo, identity.number, true); if (token.isCancellationRequested) { @@ -81,43 +71,12 @@ class GitHubIssueOrPullRequestExternalUriOpener extends Disposable implements vs } } - private getFolderRepositoryManager(owner: string, repo: string): FolderRepositoryManager { - const existingManager = this._repositoriesManager.getManagerForRepository(owner, repo) - ?? this._repositoriesManager.folderManagers[0]; - if (existingManager) { - return existingManager; - } - if (this._remoteFolderRepositoryManager) { - return this._remoteFolderRepositoryManager; - } - - const repository = this._register(new RemoteOnlyRepository()); - const git = this._register(new GitApiImpl(this._repositoriesManager)); - const createPullRequestHelper = this._register(new CreatePullRequestHelper()); - const onDidChangeTheme = this._register(new vscode.EventEmitter()); - const themeWatcher: IThemeWatcher = { - onDidChangeTheme: onDidChangeTheme.event, - themeData: undefined, - }; - this._remoteFolderRepositoryManager = this._register(new FolderRepositoryManager( - -1, - this._context, - repository, - this._telemetry, - git, - this._credentialStore, - createPullRequestHelper, - themeWatcher, - )); - return this._remoteFolderRepositoryManager; - } } export function registerGitHubIssueOrPullRequestExternalUriOpener( context: vscode.ExtensionContext, - repositoriesManager: RepositoriesManager, - credentialStore: CredentialStore, + folderRepositoryManagerProvider: FolderRepositoryManagerProvider, telemetry: ITelemetry, ): vscode.Disposable { - return new GitHubIssueOrPullRequestExternalUriOpener(context, repositoriesManager, credentialStore, telemetry); + return new GitHubIssueOrPullRequestExternalUriOpener(context, folderRepositoryManagerProvider, telemetry); } diff --git a/src/github/folderRepositoryManagerProvider.ts b/src/github/folderRepositoryManagerProvider.ts new file mode 100644 index 0000000000..c824f04df7 --- /dev/null +++ b/src/github/folderRepositoryManagerProvider.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { FolderRepositoryManager } from './folderRepositoryManager'; +import { RepositoriesManager } from './repositoriesManager'; +import { GitApiImpl } from '../api/api1'; +import { RemoteOnlyRepository } from '../api/remoteOnlyRepository'; +import { Disposable } from '../common/lifecycle'; +import { ITelemetry } from '../common/telemetry'; +import { IThemeWatcher } from '../themeWatcher'; +import { CreatePullRequestHelper } from '../view/createPullRequestHelper'; +import { ThemeData } from '../view/theme'; + +export class FolderRepositoryManagerProvider extends Disposable { + private _remoteFolderRepositoryManager: FolderRepositoryManager | undefined; + + constructor( + private readonly _context: vscode.ExtensionContext, + private readonly _repositoriesManager: RepositoriesManager, + private readonly _telemetry: ITelemetry, + private readonly _git: GitApiImpl, + ) { + super(); + } + + getManagerForRepository(owner: string, repo: string): FolderRepositoryManager { + const existingManager = this._repositoriesManager.getManagerForRepository(owner, repo) + ?? this._repositoriesManager.folderManagers[0]; + if (existingManager) { + return existingManager; + } + if (this._remoteFolderRepositoryManager) { + return this._remoteFolderRepositoryManager; + } + + const repository = this._register(new RemoteOnlyRepository()); + const createPullRequestHelper = this._register(new CreatePullRequestHelper()); + const onDidChangeTheme = this._register(new vscode.EventEmitter()); + const themeWatcher: IThemeWatcher = { + onDidChangeTheme: onDidChangeTheme.event, + themeData: undefined, + }; + this._remoteFolderRepositoryManager = this._register(new FolderRepositoryManager( + -1, + this._context, + repository, + this._telemetry, + this._git, + this._repositoriesManager.credentialStore, + createPullRequestHelper, + themeWatcher, + )); + return this._remoteFolderRepositoryManager; + } +} diff --git a/src/github/overviewRestorer.ts b/src/github/overviewRestorer.ts index 2ad97d6dc5..2bb8f70101 100644 --- a/src/github/overviewRestorer.ts +++ b/src/github/overviewRestorer.ts @@ -7,6 +7,7 @@ import * as vscode from 'vscode'; import { CredentialStore } from './credentials'; import { registerGitHubIssueOrPullRequestExternalUriOpener } from './externalUriOpener'; import { FolderRepositoryManager } from './folderRepositoryManager'; +import { FolderRepositoryManagerProvider } from './folderRepositoryManagerProvider'; import { GitHubRepository } from './githubRepository'; import { IssueOverviewPanel } from './issueOverview'; import { PullRequestOverviewPanel } from './pullRequestOverview'; @@ -22,12 +23,13 @@ export class OverviewRestorer extends Disposable implements vscode.WebviewPanelS constructor(private readonly _repositoriesManager: RepositoriesManager, private readonly _telemetry: ITelemetry, private readonly _context: vscode.ExtensionContext, - private readonly _credentialStore: CredentialStore + private readonly _credentialStore: CredentialStore, + folderRepositoryManagerProvider: FolderRepositoryManagerProvider, ) { super(); this._register(vscode.window.registerWebviewPanelSerializer(IssueOverviewPanel.viewType, this)); this._register(vscode.window.registerWebviewPanelSerializer(PullRequestOverviewPanel.viewType, this)); - this._register(registerGitHubIssueOrPullRequestExternalUriOpener(_context, _repositoriesManager, _credentialStore, _telemetry)); + this._register(registerGitHubIssueOrPullRequestExternalUriOpener(_context, folderRepositoryManagerProvider, _telemetry)); } async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: PullRequest): Promise { diff --git a/src/test/github/externalUriOpener.test.ts b/src/test/github/externalUriOpener.test.ts index 7cd2f6496a..7a25e46ec8 100644 --- a/src/test/github/externalUriOpener.test.ts +++ b/src/test/github/externalUriOpener.test.ts @@ -6,10 +6,12 @@ import { default as assert } from 'assert'; import { createSandbox, SinonSandbox } from 'sinon'; import * as vscode from 'vscode'; +import { GitApiImpl } from '../../api/api1'; import { RemoteOnlyRepository } from '../../api/remoteOnlyRepository'; import { CredentialStore } from '../../github/credentials'; import { registerGitHubIssueOrPullRequestExternalUriOpener } from '../../github/externalUriOpener'; import { FolderRepositoryManager } from '../../github/folderRepositoryManager'; +import { FolderRepositoryManagerProvider } from '../../github/folderRepositoryManagerProvider'; import { RepositoriesManager } from '../../github/repositoriesManager'; import { MockExtensionContext } from '../mocks/mockExtensionContext'; import { MockTelemetry } from '../mocks/mockTelemetry'; @@ -30,6 +32,8 @@ describe('GitHubIssueOrPullRequestExternalUriOpener', () => { const telemetry = new MockTelemetry(); const credentialStore = new CredentialStore(telemetry, context); const repositoriesManager = new RepositoriesManager(credentialStore, telemetry); + const git = new GitApiImpl(repositoriesManager); + const folderRepositoryManagerProvider = new FolderRepositoryManagerProvider(context, repositoriesManager, telemetry, git); let opener: vscode.ExternalUriOpener | undefined; sandbox.stub(vscode.window, 'registerExternalUriOpener').callsFake((_id, value) => { opener = value; @@ -43,8 +47,7 @@ describe('GitHubIssueOrPullRequestExternalUriOpener', () => { const registration = registerGitHubIssueOrPullRequestExternalUriOpener( context, - repositoriesManager, - credentialStore, + folderRepositoryManagerProvider, telemetry, ); const uri = vscode.Uri.parse('https://github.com/microsoft/vscode/issues/1'); @@ -56,6 +59,8 @@ describe('GitHubIssueOrPullRequestExternalUriOpener', () => { assert.strictEqual(repositoriesManager.folderManagers.length, 0); assert.strictEqual(resolveIssue.callCount, 1); registration.dispose(); + folderRepositoryManagerProvider.dispose(); + git.dispose(); repositoriesManager.dispose(); credentialStore.dispose(); }); diff --git a/src/test/uriHandler.test.ts b/src/test/uriHandler.test.ts new file mode 100644 index 0000000000..b3886f834a --- /dev/null +++ b/src/test/uriHandler.test.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { default as assert } from 'assert'; +import { createSandbox, SinonSandbox } from 'sinon'; +import * as vscode from 'vscode'; +import { GitApiImpl } from '../api/api1'; +import { RemoteOnlyRepository } from '../api/remoteOnlyRepository'; +import { CredentialStore } from '../github/credentials'; +import { FolderRepositoryManagerProvider } from '../github/folderRepositoryManagerProvider'; +import { PullRequestOverviewPanel } from '../github/pullRequestOverview'; +import { RepositoriesManager } from '../github/repositoriesManager'; +import { UriHandler } from '../uriHandler'; +import { ReviewsManager } from '../view/reviewsManager'; +import { MockExtensionContext } from './mocks/mockExtensionContext'; +import { MockTelemetry } from './mocks/mockTelemetry'; + +describe('UriHandler', function () { + let context: MockExtensionContext; + let credentialStore: CredentialStore; + let folderRepositoryManagerProvider: FolderRepositoryManagerProvider; + let git: GitApiImpl; + let repositoriesManager: RepositoriesManager; + let sandbox: SinonSandbox; + let telemetry: MockTelemetry; + + beforeEach(function () { + sandbox = createSandbox(); + context = new MockExtensionContext(); + telemetry = new MockTelemetry(); + credentialStore = new CredentialStore(telemetry, context); + repositoriesManager = new RepositoriesManager(credentialStore, telemetry); + git = new GitApiImpl(repositoriesManager); + folderRepositoryManagerProvider = new FolderRepositoryManagerProvider(context, repositoriesManager, telemetry, git); + }); + + afterEach(function () { + folderRepositoryManagerProvider.dispose(); + git.dispose(); + repositoriesManager.dispose(); + credentialStore.dispose(); + context.dispose(); + sandbox.restore(); + }); + + it('opens a pull request with a remote-only manager when no workspace is open', async function () { + const createOrShow = sandbox.stub(PullRequestOverviewPanel, 'createOrShow').resolves(); + const handler = new UriHandler( + repositoriesManager, + {} as ReviewsManager, + telemetry, + context, + git, + folderRepositoryManagerProvider, + ); + const uri = vscode.Uri.parse('vscode://github.vscode-pull-request-github/open-pull-request-webview?uri=https://github.com/microsoft/vscode/pull/1'); + + await handler.handleUri(uri); + + assert.strictEqual(createOrShow.callCount, 1); + assert.ok(createOrShow.firstCall.args[2].repository instanceof RemoteOnlyRepository); + assert.strictEqual(repositoriesManager.folderManagers.length, 0); + }); +}); diff --git a/src/uriHandler.ts b/src/uriHandler.ts index f884f7c39c..6ab677d19b 100644 --- a/src/uriHandler.ts +++ b/src/uriHandler.ts @@ -10,6 +10,7 @@ import Logger from './common/logger'; import { ITelemetry } from './common/telemetry'; import { fromOpenIssueWebviewUri, fromOpenOrCheckoutPullRequestWebviewUri, UriHandlerPaths } from './common/uri'; import { FolderRepositoryManager } from './github/folderRepositoryManager'; +import { FolderRepositoryManagerProvider } from './github/folderRepositoryManagerProvider'; import { IssueOverviewPanel } from './github/issueOverview'; import { PullRequestModel } from './github/pullRequestModel'; import { PullRequestOverviewPanel } from './github/pullRequestOverview'; @@ -106,7 +107,8 @@ export class UriHandler implements vscode.UriHandler { private readonly _reviewsManagers: ReviewsManager, private readonly _telemetry: ITelemetry, private readonly _context: vscode.ExtensionContext, - private readonly _git: GitApiImpl + private readonly _git: GitApiImpl, + private readonly _folderRepositoryManagerProvider: FolderRepositoryManagerProvider, ) { } async handleUri(uri: vscode.Uri): Promise { @@ -129,7 +131,7 @@ export class UriHandler implements vscode.UriHandler { if (!params) { return; } - const folderManager = this._reposManagers.getManagerForRepository(params.owner, params.repo) ?? this._reposManagers.folderManagers[0]; + const folderManager = this._folderRepositoryManagerProvider.getManagerForRepository(params.owner, params.repo); const identity = { owner: params.owner, repo: params.repo, number: params.issueNumber }; return IssueOverviewPanel.createOrShow(this._telemetry, this._context.extensionUri, folderManager, identity); } @@ -141,7 +143,7 @@ export class UriHandler implements vscode.UriHandler { Logger.error('Failed to parse pull request URI.', UriHandler.ID); return; } - const folderManager = this._reposManagers.getManagerForRepository(params.owner, params.repo) ?? this._reposManagers.folderManagers[0]; + const folderManager = this._folderRepositoryManagerProvider.getManagerForRepository(params.owner, params.repo); return { folderManager, identity: { owner: params.owner, repo: params.repo, number: params.pullRequestNumber } }; } From 1d9ed839e4fd64d3427e4acce5c80fe4a518f158 Mon Sep 17 00:00:00 2001 From: Alex Ross <38270282+alexr00@users.noreply.github.com> Date: Mon, 7 Sep 2026 16:59:33 +0200 Subject: [PATCH 3/4] Rename --- src/extension.ts | 10 +++++----- src/github/externalUriOpener.ts | 10 +++++----- ...rProvider.ts => folderRepositoryManagerResolver.ts} | 2 +- src/github/overviewRestorer.ts | 6 +++--- src/test/github/externalUriOpener.test.ts | 8 ++++---- src/test/uriHandler.test.ts | 10 +++++----- src/uriHandler.ts | 8 ++++---- 7 files changed, 27 insertions(+), 27 deletions(-) rename src/github/{folderRepositoryManagerProvider.ts => folderRepositoryManagerResolver.ts} (97%) diff --git a/src/extension.ts b/src/extension.ts index 9a1a89366c..e76879847c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -26,7 +26,7 @@ import { createExperimentationService, ExperimentationTelemetry } from './experi import { CopilotRemoteAgentManager } from './github/copilotRemoteAgent'; import { CredentialStore } from './github/credentials'; import { FolderRepositoryManager } from './github/folderRepositoryManager'; -import { FolderRepositoryManagerProvider } from './github/folderRepositoryManagerProvider'; +import { FolderRepositoryManagerResolver } from './github/folderRepositoryManagerResolver'; import { OverviewRestorer } from './github/overviewRestorer'; import { RepositoriesManager } from './github/repositoriesManager'; import { registerBuiltinGitProvider, registerLiveShareGitProvider } from './gitProviders/api'; @@ -293,9 +293,9 @@ async function init( context.subscriptions.push(new GitLensIntegration()); - const folderRepositoryManagerProvider = new FolderRepositoryManagerProvider(context, reposManager, telemetry, git); - context.subscriptions.push(folderRepositoryManagerProvider); - context.subscriptions.push(new OverviewRestorer(reposManager, telemetry, context, credentialStore, folderRepositoryManagerProvider)); + const folderRepositoryManagerResolver = new FolderRepositoryManagerResolver(context, reposManager, telemetry, git); + context.subscriptions.push(folderRepositoryManagerResolver); + context.subscriptions.push(new OverviewRestorer(reposManager, telemetry, context, credentialStore, folderRepositoryManagerResolver)); await vscode.commands.executeCommand('setContext', 'github:initialized', true); @@ -305,7 +305,7 @@ async function init( await resumePendingCheckout(reviewsManager, context, reposManager); initChat(context, credentialStore, reposManager); - context.subscriptions.push(vscode.window.registerUriHandler(new UriHandler(reposManager, reviewsManager, telemetry, context, git, folderRepositoryManagerProvider))); + context.subscriptions.push(vscode.window.registerUriHandler(new UriHandler(reposManager, reviewsManager, telemetry, context, git, folderRepositoryManagerResolver))); // Make sure any compare changes tabs, which come from the create flow, are closed. CompareChanges.closeTabs(); diff --git a/src/github/externalUriOpener.ts b/src/github/externalUriOpener.ts index 8a772c8739..581a1f5109 100644 --- a/src/github/externalUriOpener.ts +++ b/src/github/externalUriOpener.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { FolderRepositoryManagerProvider } from './folderRepositoryManagerProvider'; +import { FolderRepositoryManagerResolver } from './folderRepositoryManagerResolver'; import { IssueOverviewPanel } from './issueOverview'; import { PullRequestOverviewPanel } from './pullRequestOverview'; import { getGitHubIssueOrPullRequestUriOpenerPriority, parseGitHubIssueOrPullRequestUri } from '../common/externalUri'; @@ -15,7 +15,7 @@ import { EXTENSION_ID } from '../constants'; class GitHubIssueOrPullRequestExternalUriOpener extends Disposable implements vscode.ExternalUriOpener { constructor( private readonly _context: vscode.ExtensionContext, - private readonly _folderRepositoryManagerProvider: FolderRepositoryManagerProvider, + private readonly _folderRepositoryManagerResolver: FolderRepositoryManagerResolver, private readonly _telemetry: ITelemetry, ) { super(); @@ -35,7 +35,7 @@ class GitHubIssueOrPullRequestExternalUriOpener extends Disposable implements vs return; } - const folderRepositoryManager = this._folderRepositoryManagerProvider.getManagerForRepository(identity.owner, identity.repo); + const folderRepositoryManager = this._folderRepositoryManagerResolver.getManagerForRepository(identity.owner, identity.repo); if (identity.kind === 'pullRequest') { const pullRequest = await folderRepositoryManager.resolvePullRequest(identity.owner, identity.repo, identity.number, true); if (token.isCancellationRequested) { @@ -75,8 +75,8 @@ class GitHubIssueOrPullRequestExternalUriOpener extends Disposable implements vs export function registerGitHubIssueOrPullRequestExternalUriOpener( context: vscode.ExtensionContext, - folderRepositoryManagerProvider: FolderRepositoryManagerProvider, + folderRepositoryManagerResolver: FolderRepositoryManagerResolver, telemetry: ITelemetry, ): vscode.Disposable { - return new GitHubIssueOrPullRequestExternalUriOpener(context, folderRepositoryManagerProvider, telemetry); + return new GitHubIssueOrPullRequestExternalUriOpener(context, folderRepositoryManagerResolver, telemetry); } diff --git a/src/github/folderRepositoryManagerProvider.ts b/src/github/folderRepositoryManagerResolver.ts similarity index 97% rename from src/github/folderRepositoryManagerProvider.ts rename to src/github/folderRepositoryManagerResolver.ts index c824f04df7..d4d5ce8ab2 100644 --- a/src/github/folderRepositoryManagerProvider.ts +++ b/src/github/folderRepositoryManagerResolver.ts @@ -14,7 +14,7 @@ import { IThemeWatcher } from '../themeWatcher'; import { CreatePullRequestHelper } from '../view/createPullRequestHelper'; import { ThemeData } from '../view/theme'; -export class FolderRepositoryManagerProvider extends Disposable { +export class FolderRepositoryManagerResolver extends Disposable { private _remoteFolderRepositoryManager: FolderRepositoryManager | undefined; constructor( diff --git a/src/github/overviewRestorer.ts b/src/github/overviewRestorer.ts index 2bb8f70101..a9df4941ee 100644 --- a/src/github/overviewRestorer.ts +++ b/src/github/overviewRestorer.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import { CredentialStore } from './credentials'; import { registerGitHubIssueOrPullRequestExternalUriOpener } from './externalUriOpener'; import { FolderRepositoryManager } from './folderRepositoryManager'; -import { FolderRepositoryManagerProvider } from './folderRepositoryManagerProvider'; +import { FolderRepositoryManagerResolver } from './folderRepositoryManagerResolver'; import { GitHubRepository } from './githubRepository'; import { IssueOverviewPanel } from './issueOverview'; import { PullRequestOverviewPanel } from './pullRequestOverview'; @@ -24,12 +24,12 @@ export class OverviewRestorer extends Disposable implements vscode.WebviewPanelS private readonly _telemetry: ITelemetry, private readonly _context: vscode.ExtensionContext, private readonly _credentialStore: CredentialStore, - folderRepositoryManagerProvider: FolderRepositoryManagerProvider, + folderRepositoryManagerResolver: FolderRepositoryManagerResolver, ) { super(); this._register(vscode.window.registerWebviewPanelSerializer(IssueOverviewPanel.viewType, this)); this._register(vscode.window.registerWebviewPanelSerializer(PullRequestOverviewPanel.viewType, this)); - this._register(registerGitHubIssueOrPullRequestExternalUriOpener(_context, folderRepositoryManagerProvider, _telemetry)); + this._register(registerGitHubIssueOrPullRequestExternalUriOpener(_context, folderRepositoryManagerResolver, _telemetry)); } async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: PullRequest): Promise { diff --git a/src/test/github/externalUriOpener.test.ts b/src/test/github/externalUriOpener.test.ts index 7a25e46ec8..d9b4807608 100644 --- a/src/test/github/externalUriOpener.test.ts +++ b/src/test/github/externalUriOpener.test.ts @@ -11,7 +11,7 @@ import { RemoteOnlyRepository } from '../../api/remoteOnlyRepository'; import { CredentialStore } from '../../github/credentials'; import { registerGitHubIssueOrPullRequestExternalUriOpener } from '../../github/externalUriOpener'; import { FolderRepositoryManager } from '../../github/folderRepositoryManager'; -import { FolderRepositoryManagerProvider } from '../../github/folderRepositoryManagerProvider'; +import { FolderRepositoryManagerResolver } from '../../github/folderRepositoryManagerResolver'; import { RepositoriesManager } from '../../github/repositoriesManager'; import { MockExtensionContext } from '../mocks/mockExtensionContext'; import { MockTelemetry } from '../mocks/mockTelemetry'; @@ -33,7 +33,7 @@ describe('GitHubIssueOrPullRequestExternalUriOpener', () => { const credentialStore = new CredentialStore(telemetry, context); const repositoriesManager = new RepositoriesManager(credentialStore, telemetry); const git = new GitApiImpl(repositoriesManager); - const folderRepositoryManagerProvider = new FolderRepositoryManagerProvider(context, repositoriesManager, telemetry, git); + const folderRepositoryManagerResolver = new FolderRepositoryManagerResolver(context, repositoriesManager, telemetry, git); let opener: vscode.ExternalUriOpener | undefined; sandbox.stub(vscode.window, 'registerExternalUriOpener').callsFake((_id, value) => { opener = value; @@ -47,7 +47,7 @@ describe('GitHubIssueOrPullRequestExternalUriOpener', () => { const registration = registerGitHubIssueOrPullRequestExternalUriOpener( context, - folderRepositoryManagerProvider, + folderRepositoryManagerResolver, telemetry, ); const uri = vscode.Uri.parse('https://github.com/microsoft/vscode/issues/1'); @@ -59,7 +59,7 @@ describe('GitHubIssueOrPullRequestExternalUriOpener', () => { assert.strictEqual(repositoriesManager.folderManagers.length, 0); assert.strictEqual(resolveIssue.callCount, 1); registration.dispose(); - folderRepositoryManagerProvider.dispose(); + folderRepositoryManagerResolver.dispose(); git.dispose(); repositoriesManager.dispose(); credentialStore.dispose(); diff --git a/src/test/uriHandler.test.ts b/src/test/uriHandler.test.ts index b3886f834a..a7c2ee6499 100644 --- a/src/test/uriHandler.test.ts +++ b/src/test/uriHandler.test.ts @@ -9,7 +9,7 @@ import * as vscode from 'vscode'; import { GitApiImpl } from '../api/api1'; import { RemoteOnlyRepository } from '../api/remoteOnlyRepository'; import { CredentialStore } from '../github/credentials'; -import { FolderRepositoryManagerProvider } from '../github/folderRepositoryManagerProvider'; +import { FolderRepositoryManagerResolver } from '../github/folderRepositoryManagerResolver'; import { PullRequestOverviewPanel } from '../github/pullRequestOverview'; import { RepositoriesManager } from '../github/repositoriesManager'; import { UriHandler } from '../uriHandler'; @@ -20,7 +20,7 @@ import { MockTelemetry } from './mocks/mockTelemetry'; describe('UriHandler', function () { let context: MockExtensionContext; let credentialStore: CredentialStore; - let folderRepositoryManagerProvider: FolderRepositoryManagerProvider; + let folderRepositoryManagerResolver: FolderRepositoryManagerResolver; let git: GitApiImpl; let repositoriesManager: RepositoriesManager; let sandbox: SinonSandbox; @@ -33,11 +33,11 @@ describe('UriHandler', function () { credentialStore = new CredentialStore(telemetry, context); repositoriesManager = new RepositoriesManager(credentialStore, telemetry); git = new GitApiImpl(repositoriesManager); - folderRepositoryManagerProvider = new FolderRepositoryManagerProvider(context, repositoriesManager, telemetry, git); + folderRepositoryManagerResolver = new FolderRepositoryManagerResolver(context, repositoriesManager, telemetry, git); }); afterEach(function () { - folderRepositoryManagerProvider.dispose(); + folderRepositoryManagerResolver.dispose(); git.dispose(); repositoriesManager.dispose(); credentialStore.dispose(); @@ -53,7 +53,7 @@ describe('UriHandler', function () { telemetry, context, git, - folderRepositoryManagerProvider, + folderRepositoryManagerResolver, ); const uri = vscode.Uri.parse('vscode://github.vscode-pull-request-github/open-pull-request-webview?uri=https://github.com/microsoft/vscode/pull/1'); diff --git a/src/uriHandler.ts b/src/uriHandler.ts index 6ab677d19b..11d70805f2 100644 --- a/src/uriHandler.ts +++ b/src/uriHandler.ts @@ -10,7 +10,7 @@ import Logger from './common/logger'; import { ITelemetry } from './common/telemetry'; import { fromOpenIssueWebviewUri, fromOpenOrCheckoutPullRequestWebviewUri, UriHandlerPaths } from './common/uri'; import { FolderRepositoryManager } from './github/folderRepositoryManager'; -import { FolderRepositoryManagerProvider } from './github/folderRepositoryManagerProvider'; +import { FolderRepositoryManagerResolver } from './github/folderRepositoryManagerResolver'; import { IssueOverviewPanel } from './github/issueOverview'; import { PullRequestModel } from './github/pullRequestModel'; import { PullRequestOverviewPanel } from './github/pullRequestOverview'; @@ -108,7 +108,7 @@ export class UriHandler implements vscode.UriHandler { private readonly _telemetry: ITelemetry, private readonly _context: vscode.ExtensionContext, private readonly _git: GitApiImpl, - private readonly _folderRepositoryManagerProvider: FolderRepositoryManagerProvider, + private readonly _folderRepositoryManagerResolver: FolderRepositoryManagerResolver, ) { } async handleUri(uri: vscode.Uri): Promise { @@ -131,7 +131,7 @@ export class UriHandler implements vscode.UriHandler { if (!params) { return; } - const folderManager = this._folderRepositoryManagerProvider.getManagerForRepository(params.owner, params.repo); + const folderManager = this._folderRepositoryManagerResolver.getManagerForRepository(params.owner, params.repo); const identity = { owner: params.owner, repo: params.repo, number: params.issueNumber }; return IssueOverviewPanel.createOrShow(this._telemetry, this._context.extensionUri, folderManager, identity); } @@ -143,7 +143,7 @@ export class UriHandler implements vscode.UriHandler { Logger.error('Failed to parse pull request URI.', UriHandler.ID); return; } - const folderManager = this._folderRepositoryManagerProvider.getManagerForRepository(params.owner, params.repo); + const folderManager = this._folderRepositoryManagerResolver.getManagerForRepository(params.owner, params.repo); return { folderManager, identity: { owner: params.owner, repo: params.repo, number: params.pullRequestNumber } }; } From cb1814d478447f308ef5c0fbf6ef9224c163a451 Mon Sep 17 00:00:00 2001 From: Alex Ross <38270282+alexr00@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:13:25 +0200 Subject: [PATCH 4/4] Fix merge loss --- src/extension.ts | 2 +- src/github/folderRepositoryManagerResolver.ts | 4 ++-- src/test/github/externalUriOpener.test.ts | 5 +---- src/test/uriHandler.test.ts | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 576aa0df22..24396f5d0b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -293,7 +293,7 @@ async function init( context.subscriptions.push(new GitLensIntegration()); - const folderRepositoryManagerResolver = new FolderRepositoryManagerResolver(context, reposManager, telemetry, git); + const folderRepositoryManagerResolver = new FolderRepositoryManagerResolver(context, reposManager, telemetry); context.subscriptions.push(folderRepositoryManagerResolver); context.subscriptions.push(new OverviewRestorer(reposManager, telemetry, context, credentialStore, folderRepositoryManagerResolver)); diff --git a/src/github/folderRepositoryManagerResolver.ts b/src/github/folderRepositoryManagerResolver.ts index d4d5ce8ab2..c89a44fae2 100644 --- a/src/github/folderRepositoryManagerResolver.ts +++ b/src/github/folderRepositoryManagerResolver.ts @@ -21,7 +21,6 @@ export class FolderRepositoryManagerResolver extends Disposable { private readonly _context: vscode.ExtensionContext, private readonly _repositoriesManager: RepositoriesManager, private readonly _telemetry: ITelemetry, - private readonly _git: GitApiImpl, ) { super(); } @@ -37,6 +36,7 @@ export class FolderRepositoryManagerResolver extends Disposable { } const repository = this._register(new RemoteOnlyRepository()); + const git = this._register(new GitApiImpl(this._repositoriesManager)); const createPullRequestHelper = this._register(new CreatePullRequestHelper()); const onDidChangeTheme = this._register(new vscode.EventEmitter()); const themeWatcher: IThemeWatcher = { @@ -48,7 +48,7 @@ export class FolderRepositoryManagerResolver extends Disposable { this._context, repository, this._telemetry, - this._git, + git, this._repositoriesManager.credentialStore, createPullRequestHelper, themeWatcher, diff --git a/src/test/github/externalUriOpener.test.ts b/src/test/github/externalUriOpener.test.ts index d9b4807608..8620234300 100644 --- a/src/test/github/externalUriOpener.test.ts +++ b/src/test/github/externalUriOpener.test.ts @@ -6,7 +6,6 @@ import { default as assert } from 'assert'; import { createSandbox, SinonSandbox } from 'sinon'; import * as vscode from 'vscode'; -import { GitApiImpl } from '../../api/api1'; import { RemoteOnlyRepository } from '../../api/remoteOnlyRepository'; import { CredentialStore } from '../../github/credentials'; import { registerGitHubIssueOrPullRequestExternalUriOpener } from '../../github/externalUriOpener'; @@ -32,8 +31,7 @@ describe('GitHubIssueOrPullRequestExternalUriOpener', () => { const telemetry = new MockTelemetry(); const credentialStore = new CredentialStore(telemetry, context); const repositoriesManager = new RepositoriesManager(credentialStore, telemetry); - const git = new GitApiImpl(repositoriesManager); - const folderRepositoryManagerResolver = new FolderRepositoryManagerResolver(context, repositoriesManager, telemetry, git); + const folderRepositoryManagerResolver = new FolderRepositoryManagerResolver(context, repositoriesManager, telemetry); let opener: vscode.ExternalUriOpener | undefined; sandbox.stub(vscode.window, 'registerExternalUriOpener').callsFake((_id, value) => { opener = value; @@ -60,7 +58,6 @@ describe('GitHubIssueOrPullRequestExternalUriOpener', () => { assert.strictEqual(resolveIssue.callCount, 1); registration.dispose(); folderRepositoryManagerResolver.dispose(); - git.dispose(); repositoriesManager.dispose(); credentialStore.dispose(); }); diff --git a/src/test/uriHandler.test.ts b/src/test/uriHandler.test.ts index a7c2ee6499..39c88ad11b 100644 --- a/src/test/uriHandler.test.ts +++ b/src/test/uriHandler.test.ts @@ -33,7 +33,7 @@ describe('UriHandler', function () { credentialStore = new CredentialStore(telemetry, context); repositoriesManager = new RepositoriesManager(credentialStore, telemetry); git = new GitApiImpl(repositoriesManager); - folderRepositoryManagerResolver = new FolderRepositoryManagerResolver(context, repositoriesManager, telemetry, git); + folderRepositoryManagerResolver = new FolderRepositoryManagerResolver(context, repositoriesManager, telemetry); }); afterEach(function () {