Path: blob/main/src/vs/workbench/api/browser/mainThreadGitExtensionService.ts
13397 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Sequencer } from '../../../base/common/async.js'; import { CancellationToken } from '../../../base/common/cancellation.js';6import { Disposable } from '../../../base/common/lifecycle.js';7import { ResourceMap } from '../../../base/common/map.js';8import { waitForState } from '../../../base/common/observable.js';9import { URI } from '../../../base/common/uri.js';10import { GitRepository } from '../../contrib/git/browser/gitService.js';11import { IGitExtensionDelegate, IGitService, GitRef, GitRefQuery, GitRefType, GitRepositoryState, GitBranch, GitChange, GitDiffChange, IGitRepository } from '../../contrib/git/common/gitService.js';12import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js';13import { ExtHostContext, ExtHostGitExtensionShape, GitDiffChangeDto, GitRefTypeDto, GitRepositoryStateDto, MainContext, MainThreadGitExtensionShape } from '../common/extHost.protocol.js';1415function toGitRefType(type: GitRefTypeDto): GitRefType {16switch (type) {17case GitRefTypeDto.Head: return GitRefType.Head;18case GitRefTypeDto.RemoteHead: return GitRefType.RemoteHead;19case GitRefTypeDto.Tag: return GitRefType.Tag;20default: throw new Error(`Unknown GitRefType: ${type}`);21}22}2324function toGitDiffChange(dto: GitDiffChangeDto): GitDiffChange {25return {26uri: URI.revive(dto.uri),27originalUri: dto.originalUri ? URI.revive(dto.originalUri) : undefined,28modifiedUri: dto.modifiedUri ? URI.revive(dto.modifiedUri) : undefined,29insertions: dto.insertions,30deletions: dto.deletions,31};32}3334function toGitRepositoryState(dto: GitRepositoryStateDto | undefined): GitRepositoryState {35return {36HEAD: dto?.HEAD ? {37type: toGitRefType(dto.HEAD.type),38name: dto.HEAD.name,39commit: dto.HEAD.commit,40remote: dto.HEAD.remote,41upstream: dto.HEAD.upstream,42ahead: dto.HEAD.ahead,43behind: dto.HEAD.behind,44} satisfies GitBranch : undefined,45remotes: dto?.remotes ?? [],46mergeChanges: dto?.mergeChanges?.map(c => ({47uri: URI.revive(c.uri),48originalUri: c.originalUri ? URI.revive(c.originalUri) : undefined,49modifiedUri: c.modifiedUri ? URI.revive(c.modifiedUri) : undefined,50} satisfies GitChange)) ?? [],51indexChanges: dto?.indexChanges?.map(c => ({52uri: URI.revive(c.uri),53originalUri: c.originalUri ? URI.revive(c.originalUri) : undefined,54modifiedUri: c.modifiedUri ? URI.revive(c.modifiedUri) : undefined,55} satisfies GitChange)) ?? [],56workingTreeChanges: dto?.workingTreeChanges?.map(c => ({57uri: URI.revive(c.uri),58originalUri: c.originalUri ? URI.revive(c.originalUri) : undefined,59modifiedUri: c.modifiedUri ? URI.revive(c.modifiedUri) : undefined,60} satisfies GitChange)) ?? [],61untrackedChanges: dto?.untrackedChanges?.map(c => ({62uri: URI.revive(c.uri),63originalUri: c.originalUri ? URI.revive(c.originalUri) : undefined,64modifiedUri: c.modifiedUri ? URI.revive(c.modifiedUri) : undefined,65} satisfies GitChange)) ?? [],66};67}6869@extHostNamedCustomer(MainContext.MainThreadGitExtension)70export class MainThreadGitExtensionService extends Disposable implements MainThreadGitExtensionShape, IGitExtensionDelegate {71private readonly _proxy: ExtHostGitExtensionShape;72private readonly _openRepositorySequencer = new Sequencer();7374private _repositoryHandles = new ResourceMap<number>();75private _repositories = new Map<number, IGitRepository>();7677get repositories(): Iterable<IGitRepository> {78return this._repositories.values();79}8081constructor(82extHostContext: IExtHostContext,83@IGitService private readonly gitService: IGitService84) {85super();8687this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostGitExtension);88this._initializeDelegate();89}9091private async _initializeDelegate(): Promise<void> {92// Check whether the vscode.git extension is available in the extension host93// process before setting the delegate. The delegate should only be set once,94// for the extension host process that runs the vscode.git extension95const isExtensionAvailable = await this._proxy.$isGitExtensionAvailable();9697if (isExtensionAvailable && !this._store.isDisposed) {98this._register(this.gitService.setDelegate(this));99}100}101102async openRepository(uri: URI): Promise<IGitRepository | undefined> {103return this._openRepositorySequencer.queue(async () => {104// Open the repository105const result = await this._proxy.$openRepository(uri);106if (!result) {107return undefined;108}109110const repositoryRootUri = URI.revive(result.rootUri);111112// Create a new repository and store it in the maps113const state = toGitRepositoryState(result.state);114const repository = new GitRepository(repositoryRootUri, state, this);115116this._repositories.set(result.handle, repository);117this._repositoryHandles.set(repositoryRootUri, result.handle);118119// Wait for the repository to be fully initialized before returning it120await waitForState(repository.state, state => state.HEAD !== undefined);121122return repository;123});124}125126async getRefs(root: URI, query: GitRefQuery, token?: CancellationToken): Promise<GitRef[]> {127const handle = this._repositoryHandles.get(root);128if (handle === undefined) {129return [];130}131132const result = await this._proxy.$getRefs(handle, query, token);133134if (token?.isCancellationRequested) {135return [];136}137138return result.map(ref => ({139...ref,140type: toGitRefType(ref.type)141} satisfies GitRef));142}143144async diffBetweenWithStats(root: URI, ref1: string, ref2: string, path?: string): Promise<GitDiffChange[]> {145const handle = this._repositoryHandles.get(root);146if (handle === undefined) {147return [];148}149150const result = await this._proxy.$diffBetweenWithStats(handle, ref1, ref2, path);151return result.map(toGitDiffChange);152}153154async diffBetweenWithStats2(root: URI, ref: string, path?: string): Promise<GitDiffChange[]> {155const handle = this._repositoryHandles.get(root);156if (handle === undefined) {157return [];158}159160const result = await this._proxy.$diffBetweenWithStats2(handle, ref, path);161return result.map(toGitDiffChange);162}163164async $onDidChangeRepository(handle: number): Promise<void> {165const repository = this._repositories.get(handle);166if (!repository) {167return;168}169170const state = await this._proxy.$getRepositoryState(handle);171if (!state) {172return;173}174175// Update the repository state176repository.updateState(toGitRepositoryState(state));177}178}179180181