From 103715c3cc6fbf3f7ea90a47058812ecf6d52ce8 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 24 Aug 2018 12:04:29 +0200 Subject: [PATCH] git: implement API --- extensions/git/src/api/api1.ts | 105 +++++++++++++++++++++++++- extensions/git/src/api/git.d.ts | 12 +-- extensions/git/src/contentProvider.ts | 6 +- extensions/git/src/git.ts | 62 ++++++++++++++- extensions/git/src/repository.ts | 70 ++++++++++++++++- 5 files changed, 238 insertions(+), 17 deletions(-) diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index ca79572b6bc..fdf0142fb45 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -6,8 +6,8 @@ 'use strict'; import { Model } from '../model'; -import { Repository as BaseRepository } from '../repository'; -import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit } from './git'; +import { Repository as BaseRepository, Resource } from '../repository'; +import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change } from './git'; import { Event, SourceControlInputBox, Uri } from 'vscode'; import { mapEvent } from '../util'; @@ -17,6 +17,11 @@ class ApiInputBox implements InputBox { constructor(private _inputBox: SourceControlInputBox) { } } +export class ApiChange implements Change { + + constructor(_resource: Resource) { } +} + export class ApiRepositoryState implements RepositoryState { get HEAD(): Branch | undefined { return this._repository.HEAD; } @@ -25,6 +30,10 @@ export class ApiRepositoryState implements RepositoryState { get submodules(): Submodule[] { return [...this._repository.submodules]; } get rebaseCommit(): Commit | undefined { return this._repository.rebaseCommit; } + get mergeChanges(): Change[] { return this._repository.mergeGroup.resourceStates.map(r => new ApiChange(r)); } + get indexChanges(): Change[] { return this._repository.indexGroup.resourceStates.map(r => new ApiChange(r)); } + get workingTreeChanges(): Change[] { return this._repository.workingTreeGroup.resourceStates.map(r => new ApiChange(r)); } + readonly onDidChange: Event = this._repository.onDidRunGitStatus; constructor(private _repository: BaseRepository) { } @@ -38,9 +47,101 @@ export class ApiRepository implements Repository { constructor(private _repository: BaseRepository) { } + getConfigs(): Promise<{ key: string; value: string; }[]> { + return this._repository.getConfigs(); + } + + getConfig(key: string): Promise { + return this._repository.getConfig(key); + } + + setConfig(key: string, value: string): Promise { + return this._repository.setConfig(key, value); + } + + show(ref: string, path: string): Promise { + return this._repository.show(ref, path); + } + + getCommit(ref: string): Promise { + return this._repository.getCommit(ref); + } + + getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number; }> { + return this._repository.getObjectDetails(treeish, path); + } + + diffWithHEAD(path: string): Promise { + return this._repository.diffWithHEAD(path); + } + + diffWith(ref: string, path: string): Promise { + return this._repository.diffWith(ref, path); + } + + diffIndexWithHEAD(path: string): Promise { + return this._repository.diffIndexWithHEAD(path); + } + + diffIndexWith(ref: string, path: string): Promise { + return this._repository.diffIndexWith(ref, path); + } + + diffBlobs(object1: string, object2: string): Promise { + return this._repository.diffBlobs(object1, object2); + } + + diffBetween(ref1: string, ref2: string, path: string): Promise { + return this._repository.diffBetween(ref1, ref2, path); + } + + hashObject(data: string): Promise { + return this._repository.hashObject(data); + } + + createBranch(name: string, checkout: boolean, ref?: string | undefined): Promise { + return this._repository.branch(name, checkout, ref); + } + + deleteBranch(name: string): Promise { + return this._repository.deleteBranch(name); + } + + getBranch(name: string): Promise { + return this._repository.getBranch(name); + } + + setBranchUpstream(name: string, upstream: string): Promise { + return this._repository.setBranchUpstream(name, upstream); + } + + getMergeBase(ref1: string, ref2: string): Promise { + throw new Error('Method not implemented.'); + } + status(): Promise { return this._repository.status(); } + + checkout(treeish: string): Promise { + return this._repository.checkout(treeish); + } + + addRemote(name: string, url: string): Promise { + return this._repository.addRemote(name, url); + } + + removeRemote(name: string): Promise { + return this._repository.removeRemote(name); + } + + fetch(remote?: string | undefined, ref?: string | undefined): Promise { + return this._repository.fetch(remote, ref); + } + + pull(): Promise { + return this._repository.pull(); + } } export class ApiGit implements Git { diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 7d6f07a963d..ca103c592e7 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -74,21 +74,15 @@ export interface RepositoryState { readonly onDidChange: Event; } -export const enum ConfigScope { - System, - Global, - Local -} - export interface Repository { readonly rootUri: Uri; readonly inputBox: InputBox; readonly state: RepositoryState; - getConfigs(scope: ConfigScope): Promise<{ key: string; value: string; }[]>; - getConfig(scope: ConfigScope, key: string): Promise; - setConfig(scope: ConfigScope, key: string, value: string): Promise; + getConfigs(): Promise<{ key: string; value: string; }[]>; + getConfig(key: string): Promise; + setConfig(key: string, value: string): Promise; show(ref: string, path: string): Promise; getCommit(ref: string): Promise; diff --git a/extensions/git/src/contentProvider.ts b/extensions/git/src/contentProvider.ts index 5c3dd22b1e4..e03a97bd29b 100644 --- a/extensions/git/src/contentProvider.ts +++ b/extensions/git/src/contentProvider.ts @@ -92,7 +92,11 @@ export class GitContentProvider { return ''; } - return await repository.diff(path, ref === 'index'); + if (ref === 'index') { + return await repository.diffIndexWithHEAD(path); + } else { + return await repository.diffWithHEAD(path); + } } const repository = this.model.getRepository(uri); diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index f861de0393a..93ee513cddf 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -682,7 +682,7 @@ export class Repository { return this.git.spawn(args, options); } - async config(scope: string, key: string, value: any, options: SpawnOptions): Promise { + async config(scope: string, key: string, value: any = null, options: SpawnOptions = {}): Promise { const args = ['config']; if (scope) { @@ -699,6 +699,24 @@ export class Repository { return result.stdout; } + async getConfigs(scope: string): Promise<{ key: string; value: string; }[]> { + const args = ['config']; + + if (scope) { + args.push('--' + scope); + } + + args.push('-l'); + + const result = await this.run(args); + const lines = result.stdout.trim().split(/\r|\r\n|\n/); + + return lines.map(entry => { + const equalsIndex = entry.indexOf('='); + return { key: entry.substr(0, equalsIndex), value: entry.substr(equalsIndex + 1) }; + }); + } + async bufferString(object: string, encoding: string = 'utf8', autoGuessEncoding = false): Promise { const stdout = await this.buffer(object); @@ -837,6 +855,36 @@ export class Repository { return result.stdout; } + async diffWithHEAD(path: string): Promise { + const args = ['diff', '--', path]; + const result = await this.run(args); + return result.stdout; + } + + async diffWith(ref: string, path: string): Promise { + const args = ['diff', ref, '--', path]; + const result = await this.run(args); + return result.stdout; + } + + async diffIndexWithHEAD(path: string): Promise { + const args = ['diff', '--cached', '--', path]; + const result = await this.run(args); + return result.stdout; + } + + async diffIndexWith(ref: string, path: string): Promise { + const args = ['diff', '--cached', ref, '--', path]; + const result = await this.run(args); + return result.stdout; + } + + async diffBlobs(object1: string, object2: string): Promise { + const args = ['diff', object1, object2]; + const result = await this.run(args); + return result.stdout; + } + async diffBetween(ref1: string, ref2: string, path: string): Promise { const args = ['diff', `${ref1}...${ref2}`, '--', path]; const result = await this.run(args); @@ -851,6 +899,13 @@ export class Repository { return result.stdout.trim(); } + async hashObject(data: string): Promise { + const args = ['hash-object', '-w', '--stdin']; + const result = await this.run(args, { input: data }); + + return result.stdout.trim(); + } + async add(paths: string[]): Promise { const args = ['add', '-A', '--']; @@ -1098,6 +1153,11 @@ export class Repository { await this.run(args); } + async removeRemote(name: string): Promise { + const args = ['remote', 'rm', name]; + await this.run(args); + } + async fetch(remote?: string, ref?: string): Promise { const args = ['fetch']; diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index d9caa7787d2..055af72527c 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -6,7 +6,7 @@ 'use strict'; import { commands, Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData, Memento, SourceControlInputBoxValidationType } from 'vscode'; -import { Repository as BaseRepository, Commit, GitErrorCodes, Stash, GitError, Submodule, DiffOptions } from './git'; +import { Repository as BaseRepository, Commit, GitErrorCodes, Stash, GitError, Submodule } from './git'; import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent } from './util'; import { memoize, throttle, debounce } from './decorators'; import { toGitUri } from './uri'; @@ -277,14 +277,20 @@ export class Resource implements SourceControlResourceState { export enum Operation { Status = 'Status', + Config = 'Config', Diff = 'Diff', + MergeBase = 'MergeBase', Add = 'Add', RevertFiles = 'RevertFiles', Commit = 'Commit', Clean = 'Clean', Branch = 'Branch', + GetBranch = 'GetBranch', + SetBranchUpstream = 'SetBranchUpstream', + HashObject = 'HashObject', Checkout = 'Checkout', Reset = 'Reset', + Remote = 'Remote', Fetch = 'Fetch', Pull = 'Pull', Push = 'Push', @@ -650,13 +656,53 @@ export class Repository implements Disposable { } } + getConfigs(): Promise<{ key: string; value: string; }[]> { + return this.run(Operation.Config, () => this.repository.getConfigs('local')); + } + + getConfig(key: string): Promise { + return this.run(Operation.Config, () => this.repository.config('local', key)); + } + + setConfig(key: string, value: string): Promise { + return this.run(Operation.Config, () => this.repository.config('local', key, value)); + } + @throttle async status(): Promise { await this.run(Operation.Status); } - diff(path: string, cached = false): Promise { - return this.run(Operation.Diff, () => this.repository.diff(path, cached)); + diffWithHEAD(path: string): Promise { + return this.run(Operation.Diff, () => this.repository.diffWithHEAD(path)); + } + + diffWith(ref: string, path: string): Promise { + return this.run(Operation.Diff, () => this.repository.diffWith(ref, path)); + } + + diffIndexWithHEAD(path: string): Promise { + return this.run(Operation.Diff, () => this.repository.diffIndexWithHEAD(path)); + } + + diffIndexWith(ref: string, path: string): Promise { + return this.run(Operation.Diff, () => this.repository.diffIndexWith(ref, path)); + } + + diffBlobs(object1: string, object2: string): Promise { + return this.run(Operation.Diff, () => this.repository.diffBlobs(object1, object2)); + } + + diffBetween(ref1: string, ref2: string, path: string): Promise { + return this.run(Operation.Diff, () => this.repository.diffBetween(ref1, ref2, path)); + } + + getMergeBase(ref1: string, ref2: string): Promise { + return this.run(Operation.MergeBase, () => this.repository.getMergeBase(ref1, ref2)); + } + + async hashObject(data: string): Promise { + return this.run(Operation.HashObject, () => this.repository.hashObject(data)); } async add(resources: Uri[]): Promise { @@ -758,6 +804,14 @@ export class Repository implements Disposable { await this.run(Operation.RenameBranch, () => this.repository.renameBranch(name)); } + async getBranch(name: string): Promise { + return await this.run(Operation.GetBranch, () => this.repository.getBranch(name)); + } + + async setBranchUpstream(name: string, upstream: string): Promise { + await this.run(Operation.SetBranchUpstream, () => this.repository.setBranchUpstream(name, upstream)); + } + async merge(ref: string): Promise { await this.run(Operation.Merge, () => this.repository.merge(ref)); } @@ -782,6 +836,14 @@ export class Repository implements Disposable { await this.run(Operation.DeleteRef, () => this.repository.deleteRef(ref)); } + async addRemote(name: string, url: string): Promise { + await this.run(Operation.Remote, () => this.repository.addRemote(name, url)); + } + + async removeRemote(name: string): Promise { + await this.run(Operation.Remote, () => this.repository.removeRemote(name)); + } + @throttle async fetchDefault(): Promise { await this.run(Operation.Fetch, () => this.repository.fetch()); @@ -805,7 +867,7 @@ export class Repository implements Disposable { } @throttle - async pull(head: Branch | undefined): Promise { + async pull(head?: Branch): Promise { let remote: string | undefined; let branch: string | undefined;