/*--------------------------------------------------------------------------------------------- * 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 } 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 repoRealRootPath: TPromise; constructor(repo: Repository) { this.repo = repo; this.repoRealRootPath = null; } public serviceState(): TPromise { return TPromise.as(this.repo ? RawServiceState.OK : RawServiceState.GitNotFound ); } public status(): TPromise { return this.checkRoot() .then(() => 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.repo.getHeads(), this.repo.getTags()]).then(r => { return { status: status, HEAD: HEAD, heads: r[0], tags: r[1] }; }))) .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(): TPromise { return this.repo.pull().then(() => this.status()); } public push(): TPromise { return this.repo.push().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()); } private checkRoot(): Promise { if (!this.repoRealRootPath) { this.repoRealRootPath = pfs.realpath(this.repo.path); } return this.repo.getRoot().then(root => { return Promise.join([ this.repoRealRootPath, pfs.realpath(root) ]).then(paths => { if (!pathsAreEqual(paths[0], paths[1])) { return Promise.wrapError(new GitError({ message: 'Not at the repository root', gitErrorCode: GitErrorCodes.NotAtRepositoryRoot })); } }); }); } } export class RawGitServiceWrapper 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(): TPromise { return this.raw.then(raw => raw.pull()); } public push(): TPromise { return this.raw.then(raw => raw.push()); } 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()); } }