From e9cb0dd0dcfe0a63fe633b5bcc335fdbc245d47d Mon Sep 17 00:00:00 2001 From: Matt Rothenberg Date: Thu, 1 Sep 2022 09:42:44 +0100 Subject: [PATCH] fix: use updated Git API --- git.d.ts | 364 ++++++++++++++++++++++++++++++++++++++++ package.json | 3 + src/flatConfigEditor.ts | 6 +- src/git.ts | 47 ++---- src/types.ts | 70 -------- 5 files changed, 378 insertions(+), 112 deletions(-) create mode 100644 git.d.ts diff --git a/git.d.ts b/git.d.ts new file mode 100644 index 0000000..6b00ae3 --- /dev/null +++ b/git.d.ts @@ -0,0 +1,364 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Uri, Event, Disposable, ProviderResult, Command } from 'vscode' +export { ProviderResult } from 'vscode' + +export interface Git { + readonly path: string +} + +export interface InputBox { + value: string +} + +export const enum ForcePushMode { + Force, + ForceWithLease, +} + +export const enum RefType { + Head, + RemoteHead, + Tag, +} + +export interface Ref { + readonly type: RefType + readonly name?: string + readonly commit?: string + readonly remote?: string +} + +export interface UpstreamRef { + readonly remote: string + readonly name: string +} + +export interface Branch extends Ref { + readonly upstream?: UpstreamRef + readonly ahead?: number + readonly behind?: number +} + +export interface Commit { + readonly hash: string + readonly message: string + readonly parents: string[] + readonly authorDate?: Date + readonly authorName?: string + readonly authorEmail?: string + readonly commitDate?: Date +} + +export interface Submodule { + readonly name: string + readonly path: string + readonly url: string +} + +export interface Remote { + readonly name: string + readonly fetchUrl?: string + readonly pushUrl?: string + readonly isReadOnly: boolean +} + +export const enum Status { + INDEX_MODIFIED, + INDEX_ADDED, + INDEX_DELETED, + INDEX_RENAMED, + INDEX_COPIED, + + MODIFIED, + DELETED, + UNTRACKED, + IGNORED, + INTENT_TO_ADD, + + ADDED_BY_US, + ADDED_BY_THEM, + DELETED_BY_US, + DELETED_BY_THEM, + BOTH_ADDED, + BOTH_DELETED, + BOTH_MODIFIED, +} + +export interface Change { + /** + * Returns either `originalUri` or `renameUri`, depending + * on whether this change is a rename change. When + * in doubt always use `uri` over the other two alternatives. + */ + readonly uri: Uri + readonly originalUri: Uri + readonly renameUri: Uri | undefined + readonly status: Status +} + +export interface RepositoryState { + readonly HEAD: Branch | undefined + readonly refs: Ref[] + readonly remotes: Remote[] + readonly submodules: Submodule[] + readonly rebaseCommit: Commit | undefined + + readonly mergeChanges: Change[] + readonly indexChanges: Change[] + readonly workingTreeChanges: Change[] + + readonly onDidChange: Event +} + +export interface RepositoryUIState { + readonly selected: boolean + readonly onDidChange: Event +} + +/** + * Log options. + */ +export interface LogOptions { + /** Max number of log entries to retrieve. If not specified, the default is 32. */ + readonly maxEntries?: number + readonly path?: string +} + +export interface CommitOptions { + all?: boolean | 'tracked' + amend?: boolean + signoff?: boolean + signCommit?: boolean + empty?: boolean + noVerify?: boolean + requireUserConfig?: boolean + useEditor?: boolean + verbose?: boolean + postCommitCommand?: string +} + +export interface FetchOptions { + remote?: string + ref?: string + all?: boolean + prune?: boolean + depth?: number +} + +export interface BranchQuery { + readonly remote?: boolean + readonly pattern?: string + readonly count?: number + readonly contains?: string +} + +export interface Repository { + readonly rootUri: Uri + readonly inputBox: InputBox + readonly state: RepositoryState + readonly ui: RepositoryUIState + + getConfigs(): Promise<{ key: string; value: string }[]> + getConfig(key: string): Promise + setConfig(key: string, value: string): Promise + getGlobalConfig(key: string): Promise + + getObjectDetails( + treeish: string, + path: string + ): Promise<{ mode: string; object: string; size: number }> + detectObjectType( + object: string + ): Promise<{ mimetype: string; encoding?: string }> + buffer(ref: string, path: string): Promise + show(ref: string, path: string): Promise + getCommit(ref: string): Promise + + add(paths: string[]): Promise + revert(paths: string[]): Promise + clean(paths: string[]): Promise + + apply(patch: string, reverse?: boolean): Promise + diff(cached?: boolean): Promise + diffWithHEAD(): Promise + diffWithHEAD(path: string): Promise + diffWith(ref: string): Promise + diffWith(ref: string, path: string): Promise + diffIndexWithHEAD(): Promise + diffIndexWithHEAD(path: string): Promise + diffIndexWith(ref: string): Promise + diffIndexWith(ref: string, path: string): Promise + diffBlobs(object1: string, object2: string): Promise + diffBetween(ref1: string, ref2: string): Promise + diffBetween(ref1: string, ref2: string, path: string): Promise + + hashObject(data: string): Promise + + createBranch(name: string, checkout: boolean, ref?: string): Promise + deleteBranch(name: string, force?: boolean): Promise + getBranch(name: string): Promise + getBranches(query: BranchQuery): Promise + setBranchUpstream(name: string, upstream: string): Promise + + getMergeBase(ref1: string, ref2: string): Promise + + tag(name: string, upstream: string): Promise + deleteTag(name: string): Promise + + status(): Promise + checkout(treeish: string): Promise + + addRemote(name: string, url: string): Promise + removeRemote(name: string): Promise + renameRemote(name: string, newName: string): Promise + + fetch(options?: FetchOptions): Promise + fetch(remote?: string, ref?: string, depth?: number): Promise + pull(unshallow?: boolean): Promise + push( + remoteName?: string, + branchName?: string, + setUpstream?: boolean, + force?: ForcePushMode + ): Promise + + blame(path: string): Promise + log(options?: LogOptions): Promise + + commit(message: string, opts?: CommitOptions): Promise +} + +export interface RemoteSource { + readonly name: string + readonly description?: string + readonly url: string | string[] +} + +export interface RemoteSourceProvider { + readonly name: string + readonly icon?: string // codicon name + readonly supportsQuery?: boolean + getRemoteSources(query?: string): ProviderResult + getBranches?(url: string): ProviderResult + publishRepository?(repository: Repository): Promise +} + +export interface RemoteSourcePublisher { + readonly name: string + readonly icon?: string // codicon name + publishRepository(repository: Repository): Promise +} + +export interface Credentials { + readonly username: string + readonly password: string +} + +export interface CredentialsProvider { + getCredentials(host: Uri): ProviderResult +} + +export type CommitCommand = Command & { description?: string } + +export interface PostCommitCommandsProvider { + getCommands(repository: Repository): CommitCommand[] +} + +export interface PushErrorHandler { + handlePushError( + repository: Repository, + remote: Remote, + refspec: string, + error: Error & { gitErrorCode: GitErrorCodes } + ): Promise +} + +export type APIState = 'uninitialized' | 'initialized' + +export interface PublishEvent { + repository: Repository + branch?: string +} + +export interface API { + readonly state: APIState + readonly onDidChangeState: Event + readonly onDidPublish: Event + readonly git: Git + readonly repositories: Repository[] + readonly onDidOpenRepository: Event + readonly onDidCloseRepository: Event + + toGitUri(uri: Uri, ref: string): Uri + getRepository(uri: Uri): Repository | null + init(root: Uri): Promise + openRepository(root: Uri): Promise + + registerRemoteSourcePublisher(publisher: RemoteSourcePublisher): Disposable + registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable + registerCredentialsProvider(provider: CredentialsProvider): Disposable + registerPostCommitCommandsProvider( + provider: PostCommitCommandsProvider + ): Disposable + registerPushErrorHandler(handler: PushErrorHandler): Disposable +} + +export interface GitExtension { + readonly enabled: boolean + readonly onDidChangeEnablement: Event + + /** + * Returns a specific API version. + * + * Throws error if git extension is disabled. You can listen to the + * [GitExtension.onDidChangeEnablement](#GitExtension.onDidChangeEnablement) event + * to know when the extension becomes enabled/disabled. + * + * @param version Version number. + * @returns API instance + */ + getAPI(version: 1): API +} + +export const enum GitErrorCodes { + BadConfigFile = 'BadConfigFile', + AuthenticationFailed = 'AuthenticationFailed', + NoUserNameConfigured = 'NoUserNameConfigured', + NoUserEmailConfigured = 'NoUserEmailConfigured', + NoRemoteRepositorySpecified = 'NoRemoteRepositorySpecified', + NotAGitRepository = 'NotAGitRepository', + NotAtRepositoryRoot = 'NotAtRepositoryRoot', + Conflict = 'Conflict', + StashConflict = 'StashConflict', + UnmergedChanges = 'UnmergedChanges', + PushRejected = 'PushRejected', + RemoteConnectionError = 'RemoteConnectionError', + DirtyWorkTree = 'DirtyWorkTree', + CantOpenResource = 'CantOpenResource', + GitNotFound = 'GitNotFound', + CantCreatePipe = 'CantCreatePipe', + PermissionDenied = 'PermissionDenied', + CantAccessRemote = 'CantAccessRemote', + RepositoryNotFound = 'RepositoryNotFound', + RepositoryIsLocked = 'RepositoryIsLocked', + BranchNotFullyMerged = 'BranchNotFullyMerged', + NoRemoteReference = 'NoRemoteReference', + InvalidBranchName = 'InvalidBranchName', + BranchAlreadyExists = 'BranchAlreadyExists', + NoLocalChanges = 'NoLocalChanges', + NoStashFound = 'NoStashFound', + LocalChangesOverwritten = 'LocalChangesOverwritten', + NoUpstreamBranch = 'NoUpstreamBranch', + IsInSubmodule = 'IsInSubmodule', + WrongCase = 'WrongCase', + CantLockRef = 'CantLockRef', + CantRebaseMultipleBranches = 'CantRebaseMultipleBranches', + PatchDoesNotApply = 'PatchDoesNotApply', + NoPathFound = 'NoPathFound', + UnknownPath = 'UnknownPath', + EmptyCommitMessage = 'EmptyCommitMessage', +} diff --git a/package.json b/package.json index 37c11ce..1adb905 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,9 @@ "@babel/plugin-transform-runtime" ] }, + "extensionDependencies": [ + "vscode.git" + ], "main": "./out/extension.js", "contributes": { "customEditors": [ diff --git a/src/flatConfigEditor.ts b/src/flatConfigEditor.ts index e6c683b..b1e0e05 100644 --- a/src/flatConfigEditor.ts +++ b/src/flatConfigEditor.ts @@ -422,12 +422,8 @@ export class FlatConfigEditor implements vscode.CustomTextEditorProvider { return new Promise(async (resolve, reject) => { try { const gitClient = new VSCodeGit() - await gitClient.activateExtension() - await gitClient.waitForRepo(3) - - // Next, let's grab the repo name. + await gitClient.waitForRepo() const { name, owner } = gitClient.repoDetails - resolve({ name, owner }) } catch (e) { reject('Couldnt activate git') diff --git a/src/git.ts b/src/git.ts index c59c88c..1757679 100644 --- a/src/git.ts +++ b/src/git.ts @@ -1,47 +1,36 @@ import * as vscode from 'vscode' -import { GitAPI } from './types' +import { API, GitExtension } from '../git' const GitUrlParse = require('git-url-parse') -interface GitExtension { - getAPI(version: number): GitAPI -} - export class VSCodeGit { - extension: vscode.Extension + extension: GitExtension + api: API constructor() { - const gitExtension = vscode.extensions.getExtension('vscode.git') + const gitExtension = vscode.extensions.getExtension('vscode.git')?.exports if (!gitExtension) { throw new Error('Git extension not found') } this.extension = gitExtension + this.api = gitExtension.getAPI(1) } - async activateExtension() { - await this.extension.activate() - } - - get rawGit() { - // Unsure about this magic number, but it works. - return this.extension.exports.getAPI(1) - } - - waitForRepo(times: number): Promise<{ name: string; owner: string }> { + waitForRepo(): Promise<{ name: string; owner: string }> { let count = 0 return new Promise((resolve, reject) => { let interval = setInterval(() => { try { - const remotes = this.repository._repository.remotes + const remotes = this.repository.state.remotes if (remotes.length > 0) { const remote = remotes[0] const parsed = GitUrlParse(remote.pushUrl) resolve({ name: parsed.name, owner: parsed.owner }) } else { - if (count === times) { + if (count === 3) { clearInterval(interval) reject(new Error("Couldn't get repo details")) } @@ -55,7 +44,7 @@ export class VSCodeGit { } get repoDetails() { - const remotes = this.repository._repository.remotes + const remotes = this.repository.state.remotes if (remotes.length === 0) { throw new Error( "No remotes found. Are you sure you've created an upstream repo?" @@ -71,7 +60,7 @@ export class VSCodeGit { } get repository() { - return this.rawGit.repositories[0] + return this.api.repositories[0] } get workingTreeChanges() { @@ -81,20 +70,4 @@ export class VSCodeGit { return this.repository.state.workingTreeChanges } - - add(resources: vscode.Uri[]) { - if (!this.repository) { - throw new Error("No repository found. Are you sure you're in a repo?") - } - - this.repository._repository.add(resources) - } - - commit(message: string) { - if (!this.repository) { - throw new Error("No repository found. Are you sure you're in a repo?") - } - - this.repository._repository.commit(message) - } } diff --git a/src/types.ts b/src/types.ts index a2ab30a..676ec80 100644 --- a/src/types.ts +++ b/src/types.ts @@ -118,73 +118,3 @@ export type FlatYamlDoc = { scheduled: FlatYamlJob } } - -export interface CommitOptions { - all?: boolean | 'tracked' -} - -export interface Branch { - readonly name: string -} - -export interface RepositoryState { - HEAD: Branch | undefined | null - refs: Branch[] - workingTreeChanges: Change[] - indexChanges: Change[] - mergeChanges: Change[] - onDidChange: vscode.Event -} - -export interface Change { - readonly uri: vscode.Uri -} - -export interface Remote { - fetchUrl: string - isReadOnly: boolean - name: string - pushUrl: string -} - -export interface RawRepository { - remotes: Remote[] - add(resources: vscode.Uri[]): Promise - commit(message: string): Promise - createBranch(name: string, checkout: boolean, ref?: string): Promise - deleteBranch(name: string, force?: boolean): Promise - - checkout(treeish: string): Promise - - push( - remoteName?: string, - branchName?: string, - setUpstream?: boolean - ): Promise -} - -export interface Repository { - state: RepositoryState - - createBranch(name: string, checkout: boolean, ref?: string): Promise - deleteBranch(name: string, force?: boolean): Promise - - checkout(treeish: string): Promise - - push( - remoteName?: string, - branchName?: string, - setUpstream?: boolean - ): Promise - - commit(message: string, opts?: CommitOptions): Promise - - _repository: RawRepository -} - -export interface GitAPI { - repositories: Repository[] - getRepository(uri: vscode.Uri): Repository | null - onDidOpenRepository: vscode.Event - onDidCloseRepository: vscode.Event -}