diff --git a/extensions/git/package.json b/extensions/git/package.json index 0301d7f471d..16fc5f3d3b1 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -21,6 +21,15 @@ }, "contributes": { "commands": [ + { + "command": "git.init", + "title": "%command.init%", + "category": "Git", + "icon": { + "light": "resources/icons/light/refresh.svg", + "dark": "resources/icons/dark/refresh.svg" + } + }, { "command": "git.refresh", "title": "%command.refresh%", @@ -192,188 +201,193 @@ ], "menus": { "scm/title": [ + { + "command": "git.init", + "group": "navigation", + "when": "scmProvider == git && scmProviderState == norepo" + }, { "command": "git.commit", "group": "navigation", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" }, { "command": "git.refresh", "group": "navigation", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" }, { "command": "git.sync", "group": "1_sync", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" }, { "command": "git.pull", "group": "1_sync", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" }, { "command": "git.pullRebase", "group": "1_sync", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" }, { "command": "git.push", "group": "1_sync", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" }, { "command": "git.pushTo", "group": "1_sync", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" }, { "command": "git.publish", "group": "2_publish", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" }, { "command": "git.commitStaged", "group": "3_commit", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" }, { "command": "git.commitStagedSigned", "group": "3_commit", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" }, { "command": "git.commitAll", "group": "3_commit", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" }, { "command": "git.commitAllSigned", "group": "3_commit", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" }, { "command": "git.undoCommit", "group": "3_commit", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" }, { "command": "git.unstageAll", "group": "4_stage", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" }, { "command": "git.cleanAll", "group": "4_stage", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" }, { "command": "git.showOutput", "group": "5_output", - "when": "scmProvider == git" + "when": "scmProvider == git && scmProviderState == idle" } ], "scm/resourceGroup/context": [ { "command": "git.stageAll", - "when": "scmProvider == git && scmResourceGroup == merge", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == merge", "group": "1_modification" }, { "command": "git.stageAll", - "when": "scmProvider == git && scmResourceGroup == merge", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == merge", "group": "inline" }, { "command": "git.unstageAll", - "when": "scmProvider == git && scmResourceGroup == index", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == index", "group": "1_modification" }, { "command": "git.unstageAll", - "when": "scmProvider == git && scmResourceGroup == index", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == index", "group": "inline" }, { "command": "git.cleanAll", - "when": "scmProvider == git && scmResourceGroup == workingTree", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree", "group": "1_modification" }, { "command": "git.stageAll", - "when": "scmProvider == git && scmResourceGroup == workingTree", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree", "group": "1_modification" }, { "command": "git.cleanAll", - "when": "scmProvider == git && scmResourceGroup == workingTree", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree", "group": "inline" }, { "command": "git.stageAll", - "when": "scmProvider == git && scmResourceGroup == workingTree", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree", "group": "inline" } ], "scm/resource/context": [ { "command": "git.stage", - "when": "scmProvider == git && scmResourceGroup == merge", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == merge", "group": "1_modification" }, { "command": "git.stage", - "when": "scmProvider == git && scmResourceGroup == merge", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == merge", "group": "inline" }, { "command": "git.openChange", - "when": "scmProvider == git && scmResourceGroup == index", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == index", "group": "navigation" }, { "command": "git.openFile", - "when": "scmProvider == git && scmResourceGroup == index", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == index", "group": "navigation" }, { "command": "git.unstage", - "when": "scmProvider == git && scmResourceGroup == index", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == index", "group": "1_modification" }, { "command": "git.unstage", - "when": "scmProvider == git && scmResourceGroup == index", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == index", "group": "inline" }, { "command": "git.openChange", - "when": "scmProvider == git && scmResourceGroup == workingTree", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree", "group": "navigation" }, { "command": "git.openFile", - "when": "scmProvider == git && scmResourceGroup == workingTree", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree", "group": "navigation" }, { "command": "git.stage", - "when": "scmProvider == git && scmResourceGroup == workingTree", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree", "group": "1_modification" }, { "command": "git.clean", - "when": "scmProvider == git && scmResourceGroup == workingTree", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree", "group": "1_modification" }, { "command": "git.clean", - "when": "scmProvider == git && scmResourceGroup == workingTree", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree", "group": "inline" }, { "command": "git.stage", - "when": "scmProvider == git && scmResourceGroup == workingTree", + "when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree", "group": "inline" } ], diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 97bad65ecde..f73fa49e1e3 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -1,4 +1,5 @@ { + "command.init": "Initialize Repository", "command.refresh": "Refresh", "command.openChange": "Open Change", "command.openFile": "Open File", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 3746ab9b9f1..b2657bb8b9d 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -190,6 +190,11 @@ export class CommandCenter { return ''; } + @command('git.init') + async init(): Promise { + await this.model.init(); + } + @command('git.openFile') async openFile(uri: Uri): Promise { const scmResource = resolveGitResource(uri); diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index a282582e529..5c9a5a98254 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -42,13 +42,12 @@ async function init(disposables: Disposable[]): Promise { const info = await findGit(pathHint); const git = new Git({ gitPath: info.path, version: info.version }); const repository = git.open(rootPath); - const repositoryRoot = await repository.getRoot(); - const model = new Model(repositoryRoot, repository, onWorkspaceChange); + const model = new Model(repository, onWorkspaceChange); outputChannel.appendLine(localize('using git', "Using git {0} from {1}", info.version, info.path)); git.onOutput(str => outputChannel.append(str), null, disposables); - const commitHandler = new CommitController(model); + const commitHandler = new CommitController(); const commandCenter = new CommandCenter(model, outputChannel); const provider = new GitSCMProvider(model, commandCenter); const contentProvider = new GitContentProvider(git, rootPath, onGitChange); diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 80fd9d7191d..10ae49695ae 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -6,7 +6,7 @@ 'use strict'; import { Uri, EventEmitter, Event, SCMResource, SCMResourceDecorations, SCMResourceGroup, Disposable, window, workspace } from 'vscode'; -import { Repository, Ref, Branch, Remote, PushOptions, Commit } from './git'; +import { Repository, Ref, Branch, Remote, PushOptions, Commit, GitErrorCodes } from './git'; import { anyEvent, eventToPromise, filterEvent, mapEvent } from './util'; import { memoize, throttle, debounce } from './decorators'; import { watch } from './watch'; @@ -175,6 +175,7 @@ export enum Operation { Pull = 1 << 9, Push = 1 << 10, Sync = 1 << 11, + Init = 1 << 12, } export interface Operations { @@ -211,6 +212,12 @@ export interface CommitOptions { signoff?: boolean; } +export enum State { + Uninitialized, + Idle, + NotAGitRepository +} + export class Model { private _onDidChange = new EventEmitter(); @@ -222,6 +229,9 @@ export class Model { private _onDidRunOperation = new EventEmitter(); readonly onDidRunOperation: Event = this._onDidRunOperation.event; + private _onDidChangeState = new EventEmitter(); + readonly onDidChangeState: Event = this._onDidChangeState.event; + @memoize get onDidChangeOperations(): Event { return anyEvent(this.onRunOperation as Event, this.onDidRunOperation as Event); @@ -255,33 +265,52 @@ export class Model { private _operations = new OperationsImpl(); get operations(): Operations { return this._operations; } + private repositoryRoot: string; + + private _state = State.Uninitialized; + get state(): State { return this._state; } + set state(state: State) { + this._state = state; + this._onDidChangeState.fire(state); + } + private disposables: Disposable[] = []; constructor( - private _repositoryRoot: string, private repository: Repository, - onWorkspaceChange: Event + private onWorkspaceChange: Event ) { - /* We use the native Node `watch` for faster, non debounced events. - * That way we hopefully get the events during the operations we're - * performing, thus sparing useless `git status` calls to refresh - * the model's state. - */ - const gitPath = path.join(_repositoryRoot, '.git'); - const { event, disposable } = watch(gitPath); - const onGitChange = mapEvent(event, ({ filename }) => Uri.file(path.join(gitPath, filename))); - const onRelevantGitChange = filterEvent(onGitChange, uri => !/\/\.git\/index\.lock$/.test(uri.fsPath)); - onRelevantGitChange(this.onFSChange, this, this.disposables); - this.disposables.push(disposable); - - const onNonGitChange = filterEvent(onWorkspaceChange, uri => !/\/\.git\//.test(uri.fsPath)); - onNonGitChange(this.onFSChange, this, this.disposables); - - this.status(); + this.initialize().catch(err => console.error(err)); } - get repositoryRoot(): string { - return this._repositoryRoot; + private async initialize(): Promise { + try { + this.repositoryRoot = await this.repository.getRoot(); + this.state = State.Idle; + + /* We use the native Node `watch` for faster, non debounced events. + * That way we hopefully get the events during the operations we're + * performing, thus sparing useless `git status` calls to refresh + * the model's state. + */ + const gitPath = path.join(this.repositoryRoot, '.git'); + const { event, disposable } = watch(gitPath); + const onGitChange = mapEvent(event, ({ filename }) => Uri.file(path.join(gitPath, filename))); + const onRelevantGitChange = filterEvent(onGitChange, uri => !/\/\.git\/index\.lock$/.test(uri.fsPath)); + onRelevantGitChange(this.onFSChange, this, this.disposables); + this.disposables.push(disposable); + + const onNonGitChange = filterEvent(this.onWorkspaceChange, uri => !/\/\.git\//.test(uri.fsPath)); + onNonGitChange(this.onFSChange, this, this.disposables); + + this.status(); + } catch (err) { + if (err.gitErrorCode === GitErrorCodes.NotAGitRepository) { + this.state = State.NotAGitRepository; + } else { + throw err; + } + } } private _HEAD: Branch | undefined; @@ -299,6 +328,11 @@ export class Model { return this._remotes; } + @throttle + async init(): Promise { + await this.run(Operation.Init, () => this.repository.init()); + } + @throttle async status(): Promise { await this.run(Operation.Status); diff --git a/extensions/git/src/scmProvider.ts b/extensions/git/src/scmProvider.ts index 3a764d6259c..61ccb32ab21 100644 --- a/extensions/git/src/scmProvider.ts +++ b/extensions/git/src/scmProvider.ts @@ -6,17 +6,31 @@ 'use strict'; import { scm, Uri, Disposable, SCMProvider, SCMResourceGroup, Event, ProviderResult, workspace } from 'vscode'; -import { Model, Resource, ResourceGroup } from './model'; +import { Model, Resource, ResourceGroup, State } from './model'; import { CommandCenter } from './commands'; +import { anyEvent, mapEvent } from './util'; export class GitSCMProvider implements SCMProvider { private disposables: Disposable[] = []; get resources(): SCMResourceGroup[] { return this.model.resources; } - get onDidChange(): Event { return this.model.onDidChange; } + + get onDidChange(): Event { + return mapEvent(anyEvent(this.model.onDidChange, this.model.onDidChangeState), () => this.model.resources); + } + get label(): string { return 'Git'; } + get state(): string { + switch (this.model.state) { + case State.Uninitialized: return 'uninitialized'; + case State.Idle: return 'idle'; + case State.NotAGitRepository: return 'norepo'; + default: return ''; + } + } + get count(): number { const countBadge = workspace.getConfiguration('git').get('countBadge'); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index dc4f1b3f2a6..03dc91cd19e 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -134,6 +134,7 @@ declare module 'vscode' { readonly resources: SCMResourceGroup[]; readonly onDidChange: Event; readonly count?: number | undefined; + readonly state?: string; getOriginalResource?(uri: Uri, token: CancellationToken): ProviderResult; open?(resource: SCMResource, token: CancellationToken): ProviderResult; diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 3e227a1150a..fe2c9212892 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -258,7 +258,7 @@ export type SCMRawResourceGroup = [string /*id*/, string /*label*/, SCMRawResour export abstract class MainThreadSCMShape { $register(id: string, features: SCMProviderFeatures): void { throw ni(); } $unregister(id: string): void { throw ni(); } - $onChange(id: string, resources: SCMRawResourceGroup[], count: number | undefined): void { throw ni(); } + $onChange(id: string, resources: SCMRawResourceGroup[], count: number | undefined, state: string | undefined): void { throw ni(); } $setInputBoxValue(value: string): void { throw ni(); } } diff --git a/src/vs/workbench/api/node/extHostSCM.ts b/src/vs/workbench/api/node/extHostSCM.ts index c874314608e..978ca281f8e 100644 --- a/src/vs/workbench/api/node/extHostSCM.ts +++ b/src/vs/workbench/api/node/extHostSCM.ts @@ -189,7 +189,7 @@ export class ExtHostSCM { return [g.id, g.label, rawResources] as SCMRawResourceGroup; }); - this._proxy.$onChange(providerId, rawResourceGroups, provider.count); + this._proxy.$onChange(providerId, rawResourceGroups, provider.count, provider.state); }); return new Disposable(() => { diff --git a/src/vs/workbench/api/node/mainThreadSCM.ts b/src/vs/workbench/api/node/mainThreadSCM.ts index 0962c9453c7..5726da0a8e1 100644 --- a/src/vs/workbench/api/node/mainThreadSCM.ts +++ b/src/vs/workbench/api/node/mainThreadSCM.ts @@ -30,6 +30,9 @@ class MainThreadSCMProvider implements ISCMProvider { private _count: number | undefined = undefined; get count(): number | undefined { return this._count; } + private _state: string | undefined = undefined; + get state(): string | undefined { return this._state; } + constructor( private _id: string, private proxy: ExtHostSCMShape, @@ -71,7 +74,7 @@ class MainThreadSCMProvider implements ISCMProvider { // } } - $onChange(rawResourceGroups: SCMRawResourceGroup[], count: number | undefined): void { + $onChange(rawResourceGroups: SCMRawResourceGroup[], count: number | undefined, state: string | undefined): void { this._resources = rawResourceGroups.map(rawGroup => { const [id, label, rawResources] = rawGroup; @@ -96,7 +99,9 @@ class MainThreadSCMProvider implements ISCMProvider { return { id, label, resources }; }); + this._count = count; + this._state = state; this._onDidChange.fire(this.resources); } @@ -140,14 +145,14 @@ export class MainThreadSCM extends MainThreadSCMShape { delete this.providers[id]; } - $onChange(id: string, rawResourceGroups: SCMRawResourceGroup[], count: number | undefined): void { + $onChange(id: string, rawResourceGroups: SCMRawResourceGroup[], count: number | undefined, state: string | undefined): void { const provider = this.providers[id]; if (!provider) { return; } - provider.$onChange(rawResourceGroups, count); + provider.$onChange(rawResourceGroups, count, state); } $setInputBoxValue(value: string): void { diff --git a/src/vs/workbench/parts/scm/electron-browser/scmMenus.ts b/src/vs/workbench/parts/scm/electron-browser/scmMenus.ts index eec09210e13..6446431418c 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmMenus.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmMenus.ts @@ -6,8 +6,9 @@ 'use strict'; import 'vs/css!./media/scmViewlet'; +import Event, { Emitter } from 'vs/base/common/event'; import { IDisposable, dispose, empty as EmptyDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IAction } from 'vs/base/common/actions'; import URI from 'vs/base/common/uri'; @@ -18,18 +19,19 @@ export class SCMMenus implements IDisposable { private disposables: IDisposable[] = []; + private activeProviderId: string; private titleDisposable: IDisposable = EmptyDisposable; private titleActions: IAction[] = []; private titleSecondaryActions: IAction[] = []; - private activeProviderContextKey: IContextKey; + + private _onDidChangeTitle = new Emitter(); + get onDidChangeTitle(): Event { return this._onDidChangeTitle.event; } constructor( @IContextKeyService private contextKeyService: IContextKeyService, @ISCMService private scmService: ISCMService, @IMenuService private menuService: IMenuService ) { - this.activeProviderContextKey = contextKeyService.createKey('scmProvider', ''); - this.setActiveProvider(this.scmService.activeProvider); this.scmService.onDidChangeProvider(this.setActiveProvider, this, this.disposables); } @@ -40,17 +42,18 @@ export class SCMMenus implements IDisposable { this.titleDisposable = EmptyDisposable; } - this.activeProviderContextKey.set(activeProvider ? activeProvider.id : ''); - if (!activeProvider) { return; } + this.activeProviderId = activeProvider.id; + const titleMenu = this.menuService.createMenu(MenuId.SCMTitle, this.contextKeyService); const updateActions = () => { this.titleActions = []; this.titleSecondaryActions = []; fillInActions(titleMenu, null, { primary: this.titleActions, secondary: this.titleSecondaryActions }); + this._onDidChangeTitle.fire(); }; const listener = titleMenu.onDidChange(updateActions); @@ -91,7 +94,7 @@ export class SCMMenus implements IDisposable { private getSCMResourceGroupURI(resourceGroup: ISCMResourceGroup): URI { return URI.from({ scheme: 'scm', - authority: this.activeProviderContextKey.get(), + authority: this.activeProviderId, path: `/${resourceGroup.id}` }); } @@ -99,7 +102,7 @@ export class SCMMenus implements IDisposable { private getSCMResourceURI(resource: ISCMResource): URI { return URI.from({ scheme: 'scm', - authority: this.activeProviderContextKey.get(), + authority: this.activeProviderId, path: `/${resource.resourceGroupId}/${JSON.stringify(resource.uri)}` }); } diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 96ec709587b..9810b078de0 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -178,6 +178,7 @@ export class SCMViewlet extends Viewlet { super(VIEWLET_ID, telemetryService); this.menus = this.instantiationService.createInstance(SCMMenus); + this.menus.onDidChangeTitle(this.updateTitleArea, this, this.disposables); this.disposables.push(this.menus); } diff --git a/src/vs/workbench/services/scm/common/scm.ts b/src/vs/workbench/services/scm/common/scm.ts index 12dc2b0e22c..42f1bc2272f 100644 --- a/src/vs/workbench/services/scm/common/scm.ts +++ b/src/vs/workbench/services/scm/common/scm.ts @@ -41,7 +41,8 @@ export interface ISCMProvider extends IDisposable { readonly label: string; readonly resources: ISCMResourceGroup[]; readonly onDidChange: Event; - readonly count?: number | undefined; + readonly count?: number; + readonly state?: string; open(uri: ISCMResource): TPromise; drag(from: ISCMResource, to: ISCMResourceGroup): TPromise; diff --git a/src/vs/workbench/services/scm/common/scmService.ts b/src/vs/workbench/services/scm/common/scmService.ts index 957003bb734..204b5dcb95d 100644 --- a/src/vs/workbench/services/scm/common/scmService.ts +++ b/src/vs/workbench/services/scm/common/scmService.ts @@ -5,7 +5,7 @@ 'use strict'; -import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable, empty as EmptyDisposable } from 'vs/base/common/lifecycle'; import Event, { Emitter } from 'vs/base/common/event'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IModel } from 'vs/editor/common/editorCommon'; @@ -20,7 +20,9 @@ export class SCMService implements ISCMService { _serviceBrand; + private providerChangeDisposable: IDisposable = EmptyDisposable; private activeProviderContextKey: IContextKey; + private activeProviderStateContextKey: IContextKey; private _activeProvider: ISCMProvider | undefined; @@ -39,6 +41,11 @@ export class SCMService implements ISCMService { this._activeProvider = provider; this.activeProviderContextKey.set(provider ? provider.id : void 0); + + this.providerChangeDisposable.dispose(); + this.providerChangeDisposable = provider.onDidChange(this.onDidChangeProviderState, this); + this.onDidChangeProviderState(); + this._onDidChangeProvider.fire(provider); } @@ -56,7 +63,8 @@ export class SCMService implements ISCMService { @IModeService modeService: IModeService, @IModelService modelService: IModelService ) { - this.activeProviderContextKey = contextKeyService.createKey('scm.provider', void 0); + this.activeProviderContextKey = contextKeyService.createKey('scmProvider', void 0); + this.activeProviderStateContextKey = contextKeyService.createKey('scmProviderState', void 0); const options = modelService.getCreationOptions('git-commit'); const rawText = RawText.fromString('', options); @@ -88,4 +96,8 @@ export class SCMService implements ISCMService { } }); } + + private onDidChangeProviderState(): void { + this.activeProviderStateContextKey.set(this.activeProvider.state); + } } \ No newline at end of file