/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ 'use strict'; import path = require('path'); import { TPromise, Promise } from 'vs/base/common/winjs.base'; import mime = require('vs/base/node/mime'); import pfs = require('vs/base/node/pfs'); import { Repository, GitError } from 'vs/workbench/parts/git/node/git.lib'; import { IRawGitService, RawServiceState, IRawStatus, IHead, GitErrorCodes, IPushOptions } from 'vs/workbench/parts/git/common/git'; function pathsAreEqual(p1: string, p2: string): boolean { if (/^(win32|darwin)$/.test(process.platform)) { p1 = p1.toLowerCase(); p2 = p2.toLowerCase(); } return p1 === p2; } export class RawGitService implements IRawGitService { private repo: Repository; private _repositoryRoot: TPromise; constructor(repo: Repository) { this.repo = repo; } private getRepositoryRoot(): TPromise { return this._repositoryRoot || (this._repositoryRoot = pfs.realpath(this.repo.path)); } public serviceState(): TPromise { return TPromise.as(this.repo ? RawServiceState.OK : RawServiceState.GitNotFound ); } public status(): TPromise { return this.repo.getStatus() .then(status => this.repo.getHEAD() .then(HEAD => { if (HEAD.name) { return this.repo.getBranch(HEAD.name).then(null, () => HEAD); } else { return HEAD; } }, (): IHead => null) .then(HEAD => Promise.join([this.getRepositoryRoot(), this.repo.getHeads(), this.repo.getTags(), this.repo.getRemotes()]).then(r => { return { repositoryRoot: r[0], status: status, HEAD: HEAD, heads: r[1], tags: r[2], remotes: r[3] }; }))) .then(null, (err) => { if (err.gitErrorCode === GitErrorCodes.BadConfigFile) { return Promise.wrapError(err); } else if (err.gitErrorCode === GitErrorCodes.NotAtRepositoryRoot) { return Promise.wrapError(err); } return null; }); } public init(): TPromise { return this.repo.init().then(() => this.status()); } public add(filePaths?: string[]): TPromise { return this.repo.add(filePaths).then(() => this.status()); } public stage(filePath: string, content: string): TPromise { return this.repo.stage(filePath, content).then(() => this.status()); } public branch(name: string, checkout?: boolean): TPromise { return this.repo.branch(name, checkout).then(() => this.status()); } public checkout(treeish?: string, filePaths?: string[]): TPromise { return this.repo.checkout(treeish, filePaths).then(() => this.status()); } public clean(filePaths: string[]): TPromise { return this.repo.clean(filePaths).then(() => this.status()); } public undo(): TPromise { return this.repo.undo().then(() => this.status()); } public reset(treeish: string, hard?: boolean): TPromise { return this.repo.reset(treeish, hard).then(() => this.status()); } public revertFiles(treeish: string, filePaths?: string[]): TPromise { return this.repo.revertFiles(treeish, filePaths).then(() => this.status()); } public fetch(): TPromise { return this.repo.fetch().then(null, (err) => { if (err.gitErrorCode === GitErrorCodes.NoRemoteRepositorySpecified) { return Promise.as(null); } return Promise.wrapError(err); }).then(() => this.status()); } public pull(rebase?: boolean): TPromise { return this.repo.pull(rebase).then(() => this.status()); } public push(remote?: string, name?: string, options?:IPushOptions): TPromise { return this.repo.push(remote, name, options).then(() => this.status()); } public sync(): TPromise { return this.repo.sync().then(() => this.status()); } public commit(message:string, amend?: boolean, stage?: boolean): TPromise { var promise: Promise = Promise.as(null); if (stage) { promise = this.repo.add(null); } return promise .then(() => this.repo.commit(message, stage, amend)) .then(() => this.status()); } public detectMimetypes(filePath: string, treeish?: string): TPromise { return pfs.exists(path.join(this.repo.path, filePath)).then((exists) => { if (exists) { return new TPromise((c, e) => { mime.detectMimesFromFile(path.join(this.repo.path, filePath), (err, result) => { if (err) { e(err); } else { c(result.mimes); } }); }); } var child = this.repo.show(treeish + ':' + filePath); return new TPromise((c, e) => { mime.detectMimesFromStream(child.stdout, filePath, (err, result) => { if (err) { e(err); } else { c(result.mimes); } }); }); }); } // careful, this buffers the whole object into memory public show(filePath: string, treeish?: string): TPromise { treeish = treeish === '~' ? '' : treeish; return this.repo.buffer(treeish + ':' + filePath).then(null, e => { if (e instanceof GitError) { return ''; // mostly untracked files end up in a git error } return TPromise.wrapError(e); }); } public onOutput(): Promise { var cancel: () => void; return new Promise((c, e, p) => { cancel = this.repo.onOutput(p); }, () => cancel()); } } export class DelayedRawGitService implements IRawGitService { constructor(private raw: TPromise) { } public serviceState(): TPromise { return this.raw.then(raw => raw.serviceState()); } public status(): TPromise { return this.raw.then(raw => raw.status()); } public init(): TPromise { return this.raw.then(raw => raw.init()); } public add(filesPaths?: string[]): TPromise { return this.raw.then(raw => raw.add(filesPaths)); } public stage(filePath: string, content: string): TPromise { return this.raw.then(raw => raw.stage(filePath, content)); } public branch(name: string, checkout?: boolean): TPromise { return this.raw.then(raw => raw.branch(name, checkout)); } public checkout(treeish?: string, filePaths?: string[]): TPromise { return this.raw.then(raw => raw.checkout(treeish, filePaths)); } public clean(filePaths: string[]): TPromise { return this.raw.then(raw => raw.clean(filePaths)); } public undo(): TPromise { return this.raw.then(raw => raw.undo()); } public reset(treeish: string, hard?: boolean): TPromise { return this.raw.then(raw => raw.reset(treeish, hard)); } public revertFiles(treeish: string, filePaths?: string[]): TPromise { return this.raw.then(raw => raw.revertFiles(treeish, filePaths)); } public fetch(): TPromise { return this.raw.then(raw => raw.fetch()); } public pull(rebase?: boolean): TPromise { return this.raw.then(raw => raw.pull(rebase)); } public push(origin?: string, name?: string, options?:IPushOptions): TPromise { return this.raw.then(raw => raw.push(origin, name, options)); } public sync(): TPromise { return this.raw.then(raw => raw.sync()); } public commit(message: string, amend?: boolean, stage?: boolean): TPromise { return this.raw.then(raw => raw.commit(message, amend, stage)); } public detectMimetypes(path: string, treeish?: string): TPromise { return this.raw.then(raw => raw.detectMimetypes(path, treeish)); } public show(path: string, treeish?: string): TPromise { return this.raw.then(raw => raw.show(path, treeish)); } public onOutput(): Promise { return this.raw.then(raw => raw.onOutput()); } }