提交 3ff3b840 编写于 作者: J João Moreno

Merge pull request #718 from joaomoreno/scoped-git

Scoped git services

fixes #57 
......@@ -33,17 +33,17 @@ import wbar = require('vs/workbench/browser/actionRegistry');
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { OpenChangeAction, SyncAction } from './gitActions';
import Severity from 'vs/base/common/severity';
import paths = require('vs/base/common/paths');
import URI from 'vs/base/common/uri';
function getStatus(gitService: IGitService, contextService: IWorkspaceContextService, input: WorkbenchEditorCommon.IFileEditorInput): IFileStatus {
var statusModel = gitService.getModel().getStatus();
const model = gitService.getModel();
const repositoryRoot = model.getRepositoryRoot();
const statusModel = model.getStatus();
const repositoryRelativePath = paths.normalize(paths.relative(repositoryRoot, input.getResource().fsPath));
var workspaceRelativePath = contextService.toWorkspaceRelativePath(input.getResource());
if (!workspaceRelativePath) {
return null; // out of workspace not yet supported
}
return statusModel.getWorkingTreeStatus().find(workspaceRelativePath) ||
statusModel.getIndexStatus().find(workspaceRelativePath);
return statusModel.getWorkingTreeStatus().find(repositoryRelativePath) ||
statusModel.getIndexStatus().find(repositoryRelativePath);
}
class OpenInDiffAction extends baseeditor.EditorInputAction {
......@@ -78,6 +78,10 @@ class OpenInDiffAction extends baseeditor.EditorInputAction {
return false;
}
if (!(typeof this.gitService.getModel().getRepositoryRoot() === 'string')) {
return false;
}
var status = this.getStatus();
return status && (
......@@ -164,6 +168,10 @@ class OpenInEditorAction extends baseeditor.EditorInputAction {
return false;
}
if (!(typeof this.gitService.getModel().getRepositoryRoot() === 'string')) {
return false;
}
var status:IFileStatus = (<any>this.input).getFileStatus();
if (OpenInEditorAction.DELETED_STATES.indexOf(status.getStatus()) > -1) {
return false;
......@@ -173,18 +181,19 @@ class OpenInEditorAction extends baseeditor.EditorInputAction {
}
public run(event?: any): Promise {
var sideBySide = !!(event && (event.ctrlKey || event.metaKey));
var modifiedViewState = this.saveTextViewState();
var path = this.getPath();
const model = this.gitService.getModel();
const resource = URI.file(paths.join(model.getRepositoryRoot(), this.getRepositoryRelativePath()));
const sideBySide = !!(event && (event.ctrlKey || event.metaKey));
const modifiedViewState = this.saveTextViewState();
return this.fileService.resolveFile(this.contextService.toResource(path)).then((stat: IFileStat) => {
return this.fileService.resolveFile(resource).then(stat => {
return this.editorService.openEditor({
resource: stat.resource,
mime: stat.mime,
options: {
forceOpen: true
}
}, sideBySide).then((editor)=> {
}, sideBySide).then(editor => {
this.restoreTextViewState(modifiedViewState);
if (this.partService.isVisible(Parts.SIDEBAR_PART)) {
......@@ -222,7 +231,7 @@ class OpenInEditorAction extends baseeditor.EditorInputAction {
return null;
}
private getPath():string {
private getRepositoryRelativePath():string {
var status: IFileStatus = (<any> this.input).getFileStatus();
if (status.getStatus() === Status.INDEX_RENAMED) {
......
......@@ -32,6 +32,7 @@ import {IInstantiationService} from 'vs/platform/instantiation/common/instantiat
import {IMessageService} from 'vs/platform/message/common/message';
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
import {ILifecycleService} from 'vs/platform/lifecycle/common/lifecycle';
import URI from 'vs/base/common/uri';
function toReadablePath(path: string): string {
if (!platform.isWindows) {
......@@ -158,9 +159,9 @@ class EditorInputCache
}
private createRightInput(status: git.IFileStatus): winjs.Promise {
var path = status.getPath();
var resource = this.contextService.toResource(path);
var model = this.gitService.getModel();
const model = this.gitService.getModel();
const path = status.getPath();
let resource = URI.file(paths.join(model.getRepositoryRoot(), path));
switch (status.getStatus()) {
case git.Status.INDEX_MODIFIED:
......@@ -181,13 +182,13 @@ class EditorInputCache
var indexStatus = model.getStatus().find(path, git.StatusType.INDEX);
if (indexStatus && indexStatus.getStatus() === git.Status.INDEX_RENAMED) {
return this.editorService.inputToType({ resource: this.contextService.toResource(indexStatus.getRename()) });
resource = URI.file(paths.join(model.getRepositoryRoot(), indexStatus.getRename()));
}
return this.editorService.inputToType({ resource: resource });
return this.editorService.inputToType({ resource });
case git.Status.BOTH_MODIFIED:
return this.editorService.inputToType({ resource: resource });
return this.editorService.inputToType({ resource });
default:
return winjs.Promise.as(null);
......@@ -391,7 +392,16 @@ export class GitService extends ee.EventEmitter
private refreshDelayer: async.ThrottledDelayer;
private autoFetcher: AutoFetcher;
constructor(raw: git.IRawGitService, @IInstantiationService instantiationService: IInstantiationService, @IEventService eventService: IEventService, @IMessageService messageService: IMessageService, @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IOutputService outputService: IOutputService, @IWorkspaceContextService contextService: IWorkspaceContextService, @ILifecycleService lifecycleService: ILifecycleService) {
constructor(
raw: git.IRawGitService,
@IInstantiationService instantiationService: IInstantiationService,
@IEventService eventService: IEventService,
@IMessageService messageService: IMessageService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
@IOutputService outputService: IOutputService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@ILifecycleService lifecycleService: ILifecycleService
) {
super();
this.instantiationService = instantiationService;
......
......@@ -9,6 +9,7 @@ import 'vs/css!./media/git.contribution';
import nls = require('vs/nls');
import async = require('vs/base/common/async');
import errors = require('vs/base/common/errors');
import paths = require('vs/base/common/paths');
import actions = require('vs/base/common/actions');
import lifecycle = require('vs/base/common/lifecycle');
import Severity from 'vs/base/common/severity';
......@@ -335,7 +336,19 @@ export class DirtyDiffDecorator implements ext.IWorkbenchContribution {
// HACK: This is the best current way of figuring out whether to draw these decorations
// or not. Needs context from the editor, to know whether it is a diff editor, in place editor
// etc.
var models = this.editorService.getVisibleEditors()
const repositoryRoot = this.gitService.getModel().getRepositoryRoot();
// If there is no repository root, just wait until that changes
if (typeof repositoryRoot !== 'string') {
this.gitService.addOneTimeListener(git.ServiceEvents.STATE_CHANGED, () => this.onEditorInputChange());
this.models.forEach(m => this.onModelInvisible(m));
this.models = [];
return;
}
const models = this.editorService.getVisibleEditors()
// map to the editor controls
.map(e => e.getControl())
......@@ -354,12 +367,12 @@ export class DirtyDiffDecorator implements ext.IWorkbenchContribution {
// remove nulls
.filter(p => !!p.resource &&
// and ivalid resources
(p.resource.scheme === 'file' && !!this.contextService.isInsideWorkspace(p.resource))
// and invalid resources
(p.resource.scheme === 'file' && paths.isEqualOrParent(p.resource.fsPath, repositoryRoot))
)
// get paths
.map(p => ({ model: p.model, path: this.contextService.toWorkspaceRelativePath(p.resource) }))
.map(p => ({ model: p.model, path: paths.normalize(paths.relative(repositoryRoot, p.resource.fsPath)) }))
// remove nulls and inside .git files
.filter(p => !!p.path && p.path.indexOf('.git/') === -1);
......
......@@ -60,7 +60,7 @@
.git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .status-group {
font-size: 11px;
font-weight: bold;
font-weight: bold;
text-transform: uppercase;
cursor: default;
}
......@@ -84,6 +84,10 @@
color: inherit;
}
.git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .file-status.out-of-workspace {
opacity: 0.5;
}
.git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .file-status .status {
position: absolute;
top: 4px;
......@@ -99,7 +103,7 @@
}
.git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .file-status .name {
margin-left: 20px;
margin-left: 20px;
}
.git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .file-status.modified .status { background-color: #007ACC; }
......@@ -123,7 +127,7 @@
.git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .file-status.both-deleted .name,
.git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .file-status.deleted-by-them .name,
.git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .file-status.deleted-by-us .name {
text-decoration: line-through;
text-decoration: line-through;
}
.git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .file-status:not(.renamed) > .rename {
......@@ -159,8 +163,8 @@
.hc-black .git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .file-status.untracked .status,
.hc-black .git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .file-status.ignored .status,
.hc-black .git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .file-status.conflict .status,
.hc-black .git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row.selected .file-status .status {
background-color: #000;
.hc-black .git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row.selected .file-status .status {
background-color: #000;
color: #fff;
border: 1px solid #6FC3DF;
}
\ No newline at end of file
......@@ -12,6 +12,7 @@ import Lifecycle = require('vs/base/common/lifecycle');
import EventEmitter = require('vs/base/common/eventEmitter');
import Strings = require('vs/base/common/strings');
import Errors = require('vs/base/common/errors');
import * as paths from 'vs/base/common/paths';
import WinJS = require('vs/base/common/winjs.base');
import Builder = require('vs/base/browser/builder');
import Keyboard = require('vs/base/browser/keyboardEvent');
......@@ -400,19 +401,27 @@ export class ChangesView extends EventEmitter.EventEmitter implements GitView.IV
}
if (input instanceof Files.FileEditorInput) {
var fileInput = <Files.FileEditorInput> input;
const fileInput = <Files.FileEditorInput> input;
const resource = fileInput.getResource();
var workspaceRelativePath = this.contextService.toWorkspaceRelativePath(fileInput.getResource());
if (!workspaceRelativePath) {
const workspaceRoot = this.contextService.getWorkspace().resource.fsPath;
if (!paths.isEqualOrParent(resource.fsPath, workspaceRoot)) {
return null; // out of workspace not yet supported
}
var status = this.gitService.getModel().getStatus().getWorkingTreeStatus().find(workspaceRelativePath);
const repositoryRoot = this.gitService.getModel().getRepositoryRoot();
if (!paths.isEqualOrParent(resource.fsPath, repositoryRoot)) {
return null; // out of repository not supported
}
const repositoryRelativePath = paths.normalize(paths.relative(repositoryRoot, resource.fsPath));
var status = this.gitService.getModel().getStatus().getWorkingTreeStatus().find(repositoryRelativePath);
if (status && (status.getStatus() === git.Status.UNTRACKED || status.getStatus() === git.Status.IGNORED)) {
return status;
}
status = this.gitService.getModel().getStatus().getMergeStatus().find(workspaceRelativePath);
status = this.gitService.getModel().getStatus().getMergeStatus().find(repositoryRelativePath);
if (status) {
return status;
}
......
......@@ -8,6 +8,7 @@ import winjs = require('vs/base/common/winjs.base');
import nls = require('vs/nls');
import platform = require('vs/base/common/platform');
import errors = require('vs/base/common/errors');
import paths = require('vs/base/common/paths');
import severity from 'vs/base/common/severity';
import lifecycle = require('vs/base/common/lifecycle');
import dom = require('vs/base/browser/dom');
......@@ -24,10 +25,12 @@ import actionsrenderer = require('vs/base/parts/tree/browser/actionsRenderer');
import git = require('vs/workbench/parts/git/common/git');
import gitmodel = require('vs/workbench/parts/git/common/gitModel');
import gitactions = require('vs/workbench/parts/git/browser/gitActions');
import {IContextMenuService} from 'vs/platform/contextview/browser/contextView';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {IMessageService} from 'vs/platform/message/common/message';
import {CommonKeybindings} from 'vs/base/common/keyCodes';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService } from 'vs/platform/message/common/message';
import { CommonKeybindings } from 'vs/base/common/keyCodes';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import URI from 'vs/base/common/uri';
import IGitService = git.IGitService
......@@ -218,10 +221,14 @@ interface IStatusGroupTemplateData {
export class Renderer implements tree.IRenderer {
private messageService: IMessageService;
constructor(private actionProvider:ActionProvider, private actionRunner: actions.IActionRunner, @IMessageService messageService: IMessageService) {
this.messageService = messageService;
constructor(
private actionProvider:ActionProvider,
private actionRunner: actions.IActionRunner,
@IMessageService private messageService: IMessageService,
@IGitService private gitService: IGitService,
@IWorkspaceContextService private contextService: IWorkspaceContextService
) {
// noop
}
public getHeight(tree:tree.ITree, element:any): number {
......@@ -298,7 +305,7 @@ export class Renderer implements tree.IRenderer {
public renderElement(tree: tree.ITree, element: any, templateId: string, templateData: any): void {
if (/^file:/.test(templateId)) {
Renderer.renderFileStatus(tree, <git.IFileStatus> element, templateData);
this.renderFileStatus(tree, <git.IFileStatus> element, templateData);
} else {
Renderer.renderStatusGroup(<git.IStatusGroup> element, templateData);
}
......@@ -309,30 +316,49 @@ export class Renderer implements tree.IRenderer {
data.count.setCount(statusGroup.all().length);
}
private static renderFileStatus(tree: tree.ITree, fileStatus: git.IFileStatus, data: IFileStatusTemplateData): void {
private renderFileStatus(tree: tree.ITree, fileStatus: git.IFileStatus, data: IFileStatusTemplateData): void {
data.actionBar.context = {
tree: tree,
fileStatus: fileStatus
};
var status = fileStatus.getStatus();
var renamePath = fileStatus.getRename();
var path = fileStatus.getPath();
var lastSlashIndex = path.lastIndexOf('/');
var name = lastSlashIndex === -1 ? path : path.substr(lastSlashIndex + 1, path.length);
var folder = (lastSlashIndex === -1 ? '' : path.substr(0, lastSlashIndex));
const repositoryRoot = this.gitService.getModel().getRepositoryRoot();
const workspaceRoot = this.contextService.getWorkspace().resource.fsPath;
const status = fileStatus.getStatus();
const renamePath = fileStatus.getRename();
const path = fileStatus.getPath();
const lastSlashIndex = path.lastIndexOf('/');
const name = lastSlashIndex === -1 ? path : path.substr(lastSlashIndex + 1, path.length);
const folder = (lastSlashIndex === -1 ? '' : path.substr(0, lastSlashIndex));
data.root.className = 'file-status ' + Renderer.statusToClass(status);
data.status.textContent = Renderer.statusToChar(status);
data.status.title = Renderer.statusToTitle(status);
const resource = URI.file(paths.normalize(paths.join(repositoryRoot, path)));
let isInWorkspace = paths.isEqualOrParent(resource.fsPath, workspaceRoot);
let rename = '';
let renameFolder = '';
if (renamePath) {
var renameLastSlashIndex = renamePath.lastIndexOf('/');
var rename = renameLastSlashIndex === -1 ? renamePath : renamePath.substr(renameLastSlashIndex + 1, renamePath.length);
var renameFolder = (renameLastSlashIndex === -1 ? '' : renamePath.substr(0, renameLastSlashIndex));
const renameLastSlashIndex = renamePath.lastIndexOf('/');
rename = renameLastSlashIndex === -1 ? renamePath : renamePath.substr(renameLastSlashIndex + 1, renamePath.length);
renameFolder = (renameLastSlashIndex === -1 ? '' : renamePath.substr(0, renameLastSlashIndex));
data.renameName.textContent = name;
data.renameFolder.textContent = folder;
const resource = URI.file(paths.normalize(paths.join(repositoryRoot, renamePath)));
isInWorkspace = paths.isEqualOrParent(resource.fsPath, workspaceRoot)
}
if (isInWorkspace) {
data.root.title = '';
} else {
data.root.title = nls.localize('outsideOfWorkspace', "This file is located outside the current workspace.");
data.root.className += ' out-of-workspace';
}
data.name.textContent = rename || name;
......
......@@ -38,6 +38,7 @@ export interface ITag {
}
export interface IRawStatus {
repositoryRoot: string;
state?: ServiceState;
status: IRawFileStatus[];
HEAD: IBranch;
......@@ -113,7 +114,7 @@ export interface IStatusSummary {
export interface IStatusModel extends EventEmitter.IEventEmitter {
getSummary(): IStatusSummary;
update(rawStatuses: IRawFileStatus[]): void;
update(status: IRawFileStatus[]): void;
getIndexStatus(): IStatusGroup;
getWorkingTreeStatus(): IStatusGroup;
getMergeStatus(): IStatusGroup;
......@@ -122,6 +123,7 @@ export interface IStatusModel extends EventEmitter.IEventEmitter {
}
export interface IModel extends EventEmitter.IEventEmitter {
getRepositoryRoot(): string;
getStatus(): IStatusModel;
getHEAD(): IBranch;
getHeads(): IBranch[];
......
......@@ -12,19 +12,17 @@ import Git = require('vs/workbench/parts/git/common/git');
export class FileStatus implements Git.IFileStatus {
private id: string;
private path: string;
private pathComponents: string[];
private mimetype: string;
private status: Git.Status;
private rename: string;
constructor(path: string, mimetype: string, status: Git.Status, rename?: string, isModifiedInIndex?: boolean) {
constructor(
private path: string,
private mimetype: string,
private status: Git.Status,
private rename?: string,
isModifiedInIndex?: boolean
) {
this.id = FileStatus.typeOf(status) + ':' + path + (rename ? ':' + rename : '') + (isModifiedInIndex ? '$' : '');
this.path = path;
this.pathComponents = path.split('/');
this.mimetype = mimetype;
this.rename = rename;
this.status = status;
}
public getPath(): string {
......@@ -211,27 +209,25 @@ export class StatusModel extends EventEmitter.EventEmitter implements Git.IStatu
};
}
public update(rawStatuses: Git.IRawFileStatus[]): void {
public update(status: Git.IRawFileStatus[]): void {
var index: FileStatus[] = [];
var workingTree: FileStatus[] = [];
var merge: FileStatus[] = [];
for (var i = 0; i < rawStatuses.length; i++) {
var raw = rawStatuses[i];
status.forEach(raw => {
switch(raw.x + raw.y) {
case '??': workingTree.push(new FileStatus(raw.path, raw.mimetype, Git.Status.UNTRACKED)); continue;
case '!!': workingTree.push(new FileStatus(raw.path, raw.mimetype, Git.Status.IGNORED)); continue;
case 'DD': merge.push(new FileStatus(raw.path, raw.mimetype, Git.Status.BOTH_DELETED)); continue;
case 'AU': merge.push(new FileStatus(raw.path, raw.mimetype, Git.Status.ADDED_BY_US)); continue;
case 'UD': merge.push(new FileStatus(raw.path, raw.mimetype, Git.Status.DELETED_BY_THEM)); continue;
case 'UA': merge.push(new FileStatus(raw.path, raw.mimetype, Git.Status.ADDED_BY_THEM)); continue;
case 'DU': merge.push(new FileStatus(raw.path, raw.mimetype, Git.Status.DELETED_BY_US)); continue;
case 'AA': merge.push(new FileStatus(raw.path, raw.mimetype, Git.Status.BOTH_ADDED)); continue;
case 'UU': merge.push(new FileStatus(raw.path, raw.mimetype, Git.Status.BOTH_MODIFIED)); continue;
case '??': return workingTree.push(new FileStatus(raw.path, raw.mimetype, Git.Status.UNTRACKED));
case '!!': return workingTree.push(new FileStatus(raw.path, raw.mimetype, Git.Status.IGNORED));
case 'DD': return merge.push(new FileStatus(raw.path, raw.mimetype, Git.Status.BOTH_DELETED));
case 'AU': return merge.push(new FileStatus(raw.path, raw.mimetype, Git.Status.ADDED_BY_US));
case 'UD': return merge.push(new FileStatus(raw.path, raw.mimetype, Git.Status.DELETED_BY_THEM));
case 'UA': return merge.push(new FileStatus(raw.path, raw.mimetype, Git.Status.ADDED_BY_THEM));
case 'DU': return merge.push(new FileStatus(raw.path, raw.mimetype, Git.Status.DELETED_BY_US));
case 'AA': return merge.push(new FileStatus(raw.path, raw.mimetype, Git.Status.BOTH_ADDED));
case 'UU': return merge.push(new FileStatus(raw.path, raw.mimetype, Git.Status.BOTH_MODIFIED));
}
var isModifiedInIndex = false;
let isModifiedInIndex = false;
switch (raw.x) {
case 'M': index.push(new FileStatus(raw.path, raw.mimetype, Git.Status.INDEX_MODIFIED)); isModifiedInIndex = true; break;
......@@ -245,7 +241,7 @@ export class StatusModel extends EventEmitter.EventEmitter implements Git.IStatu
case 'M': workingTree.push(new FileStatus(raw.path, raw.mimetype, Git.Status.MODIFIED, raw.rename, isModifiedInIndex)); break;
case 'D': workingTree.push(new FileStatus(raw.path, raw.mimetype, Git.Status.DELETED, raw.rename)); break;
}
}
});
this.indexStatus.update(index);
this.workingTreeStatus.update(workingTree);
......@@ -311,6 +307,7 @@ export class StatusModel extends EventEmitter.EventEmitter implements Git.IStatu
export class Model extends EventEmitter.EventEmitter implements Git.IModel {
private repositoryRoot: string;
private status: Git.IStatusModel;
private HEAD: Git.IBranch;
private heads: Git.IBranch[];
......@@ -322,6 +319,7 @@ export class Model extends EventEmitter.EventEmitter implements Git.IModel {
this.toDispose = [];
this.repositoryRoot = null;
this.status = new StatusModel();
this.toDispose.push(this.addEmitter2(this.status));
......@@ -330,6 +328,10 @@ export class Model extends EventEmitter.EventEmitter implements Git.IModel {
this.tags = [];
}
public getRepositoryRoot(): string {
return this.repositoryRoot;
}
public getStatus(): Git.IStatusModel {
return this.status;
}
......@@ -349,6 +351,7 @@ export class Model extends EventEmitter.EventEmitter implements Git.IModel {
public update(status: Git.IRawStatus): void {
if (!status) {
status = {
repositoryRoot: null,
status: [],
HEAD: null,
heads: [],
......@@ -356,6 +359,7 @@ export class Model extends EventEmitter.EventEmitter implements Git.IModel {
};
}
this.repositoryRoot = status.repositoryRoot;
this.status.update(status.status);
this.HEAD = status.HEAD;
......
......@@ -9,6 +9,7 @@ import winjs = require('vs/base/common/winjs.base');
export class NoOpGitService implements git.IRawGitService {
private static STATUS:git.IRawStatus = {
repositoryRoot: null,
state: git.ServiceState.NotAWorkspace,
status: [],
HEAD: null,
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Promise, TPromise } from 'vs/base/common/winjs.base';
import { IRawGitService, IRawStatus, RawServiceState } from 'vs/workbench/parts/git/common/git';
import { RawServiceState } from 'vs/workbench/parts/git/common/git';
import { NoOpGitService } from 'vs/workbench/parts/git/common/noopGitService';
import { GitService } from 'vs/workbench/parts/git/browser/gitServices';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
......@@ -12,12 +12,11 @@ import { IOutputService } from 'vs/workbench/parts/output/common/output';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEventService } from 'vs/platform/event/common/event';
import { IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService } from 'vs/platform/message/common/message';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { Client } from 'vs/base/node/service.cp';
import { RawGitService } from 'vs/workbench/parts/git/node/rawGitService';
import { RawGitService, DelayedRawGitService } from 'vs/workbench/parts/git/node/rawGitService';
import URI from 'vs/base/common/uri';
import { spawn, exec } from 'child_process';
import { join } from 'path';
......@@ -102,17 +101,16 @@ class DisabledRawGitService extends RawGitService {
}
}
export function createNativeRawGitService(basePath: string, gitPath: string, defaultEncoding: string): Promise {
export function createNativeRawGitService(workspaceRoot: string, gitPath: string, defaultEncoding: string): Promise {
return findGit(gitPath).then(gitPath => {
const client = new Client(
URI.parse(require.toUrl('bootstrap')).fsPath,
{
serverName: 'Git',
timeout: 1000 * 60,
args: [gitPath, basePath, defaultEncoding, remote.process.execPath],
args: [gitPath, workspaceRoot, defaultEncoding, remote.process.execPath],
env: {
ATOM_SHELL_INTERNAL_RUN_AS_NODE: 1,
PIPE_LOGGING: 'true',
AMD_ENTRYPOINT: 'vs/workbench/parts/git/electron-browser/gitApp'
}
}
......@@ -122,12 +120,9 @@ export function createNativeRawGitService(basePath: string, gitPath: string, def
}, () => new UnavailableRawGitService());
}
class ElectronRawGitService implements IRawGitService {
private raw: TPromise<IRawGitService>;
constructor(basePath: string, @IConfigurationService configurationService: IConfigurationService) {
this.raw = configurationService.loadConfiguration().then(conf => {
class ElectronRawGitService extends DelayedRawGitService {
constructor(workspaceRoot: string, @IConfigurationService configurationService: IConfigurationService) {
super(configurationService.loadConfiguration().then(conf => {
var enabled = conf.git ? conf.git.enabled : true;
if (!enabled) {
......@@ -137,89 +132,12 @@ class ElectronRawGitService implements IRawGitService {
var gitPath = (conf.git && conf.git.path) || null;
var encoding = (conf.files && conf.files.encoding) || 'utf8';
return createNativeRawGitService(basePath, gitPath, encoding);
});
}
public serviceState(): TPromise<RawServiceState> {
return this.raw.then(raw => raw.serviceState());
}
public status(): TPromise<IRawStatus> {
return this.raw.then(raw => raw.status());
}
public init(): TPromise<IRawStatus> {
return this.raw.then(raw => raw.init());
}
public add(filesPaths?: string[]): TPromise<IRawStatus> {
return this.raw.then(raw => raw.add(filesPaths));
}
public stage(filePath: string, content: string): TPromise<IRawStatus> {
return this.raw.then(raw => raw.stage(filePath, content));
}
public branch(name: string, checkout?: boolean): TPromise<IRawStatus> {
return this.raw.then(raw => raw.branch(name, checkout));
}
public checkout(treeish?: string, filePaths?: string[]): TPromise<IRawStatus> {
return this.raw.then(raw => raw.checkout(treeish, filePaths));
}
public clean(filePaths: string[]): TPromise<IRawStatus> {
return this.raw.then(raw => raw.clean(filePaths));
}
public undo(): TPromise<IRawStatus> {
return this.raw.then(raw => raw.undo());
}
public reset(treeish: string, hard?: boolean): TPromise<IRawStatus> {
return this.raw.then(raw => raw.reset(treeish, hard));
}
public revertFiles(treeish: string, filePaths?: string[]): TPromise<IRawStatus> {
return this.raw.then(raw => raw.revertFiles(treeish, filePaths));
}
public fetch(): TPromise<IRawStatus> {
return this.raw.then(raw => raw.fetch());
}
public pull(): TPromise<IRawStatus> {
return this.raw.then(raw => raw.pull());
}
public push(): TPromise<IRawStatus> {
return this.raw.then(raw => raw.push());
}
public sync(): TPromise<IRawStatus> {
return this.raw.then(raw => raw.sync());
}
public commit(message: string, amend?: boolean, stage?: boolean): TPromise<IRawStatus> {
return this.raw.then(raw => raw.commit(message, amend, stage));
}
public detectMimetypes(path: string, treeish?: string): TPromise<string[]> {
return this.raw.then(raw => raw.detectMimetypes(path, treeish));
}
public show(path: string, treeish?: string): TPromise<string> {
return this.raw.then(raw => raw.show(path, treeish));
}
public onOutput(): Promise {
return this.raw.then(raw => raw.onOutput());
return createNativeRawGitService(workspaceRoot, gitPath, encoding);
}));
}
}
export class ElectronGitService extends GitService {
constructor(
@IInstantiationService instantiationService: IInstantiationService,
@IEventService eventService: IEventService,
......
......@@ -4,60 +4,50 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { Server } from 'vs/base/node/service.cp';
import objects = require('vs/base/common/objects');
import uri from 'vs/base/common/uri';
import { IRawGitService } from 'vs/workbench/parts/git/common/git';
import gitlib = require('vs/workbench/parts/git/node/git.lib');
import rawgitservice = require('vs/workbench/parts/git/node/rawGitService');
import { RawGitService, DelayedRawGitService } from 'vs/workbench/parts/git/node/rawGitService';
import { join, dirname, normalize } from 'path';
import { tmpdir } from 'os';
import { realpath } from 'vs/base/node/pfs';
import path = require('path');
import fs = require('fs');
class IPCRawGitService extends DelayedRawGitService {
class NativeRawGitService extends rawgitservice.RawGitService {
constructor(gitPath: string, basePath: string, defaultEncoding: string, exePath: string) {
constructor(gitPath: string, workspaceRoot: string, defaultEncoding: string, exePath: string) {
if (!gitPath) {
super(null);
return;
}
var gitRootPath = uri.parse(require.toUrl('vs/workbench/parts/git/electron-main')).fsPath;
var env = objects.assign(objects.assign({}, process.env), {
GIT_ASKPASS: path.join(gitRootPath, 'askpass.sh'),
VSCODE_GIT_ASKPASS_BOOTSTRAP: path.join(path.dirname(path.dirname(path.dirname(path.dirname(path.dirname(gitRootPath))))), 'bootstrap.js'),
VSCODE_GIT_ASKPASS_NODE: exePath,
VSCODE_GIT_ASKPASS_MODULE_ID: 'vs/workbench/parts/git/electron-main/askpass'
});
var git = new gitlib.Git({
gitPath: gitPath,
tmpPath: tmpdirSync(), // TODO@Joao os.tmpdir()???
defaultEncoding: defaultEncoding,
env: env
});
super(git.open(path.normalize(basePath)));
}
}
function tmpdirSync(): string {
var path: string;
var paths = /^win/i.test(process.platform) ? [process.env.TMP, process.env.TEMP] : ['/tmp', '/var/tmp', '/private/tmp', '/private/var/tmp'];
for (var i = 0; i < paths.length; i++) {
path = paths[i];
try {
if (fs.statSync(path).isDirectory()) {
return path;
}
} catch (e) {
// Ignore
super(TPromise.as(new RawGitService(null)));
} else {
const gitRootPath = uri.parse(require.toUrl('vs/workbench/parts/git/electron-main')).fsPath;
const bootstrapPath = `${ uri.parse(require.toUrl('bootstrap')).fsPath }.js`;
const env = objects.assign({}, process.env, {
GIT_ASKPASS: join(gitRootPath, 'askpass.sh'),
VSCODE_GIT_ASKPASS_BOOTSTRAP: bootstrapPath,
VSCODE_GIT_ASKPASS_NODE: exePath,
VSCODE_GIT_ASKPASS_MODULE_ID: 'vs/workbench/parts/git/electron-main/askpass'
});
const git = new gitlib.Git({
gitPath: gitPath,
tmpPath: tmpdir(),
defaultEncoding: defaultEncoding,
env: env
});
const repo = git.open(normalize(workspaceRoot));
const promise = repo.getRoot()
.then(root => realpath(root))
.then(root => git.open(root))
.then(repo => new RawGitService(repo));
super(promise);
}
}
throw new Error('Temp dir not found');
}
const server = new Server();
server.registerService('GitService', new NativeRawGitService(process.argv[2], process.argv[3], process.argv[4], process.argv[5]));
\ No newline at end of file
server.registerService('GitService', new IPCRawGitService(process.argv[2], process.argv[3], process.argv[4], process.argv[5]));
\ No newline at end of file
......@@ -5,7 +5,7 @@
'use strict';
import path = require('path');
import winjs = require('vs/base/common/winjs.base');
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';
......@@ -23,23 +23,25 @@ function pathsAreEqual(p1: string, p2: string): boolean {
export class RawGitService implements IRawGitService {
private repo: Repository;
private repoRealRootPath: winjs.TPromise<string>;
private _repositoryRoot: TPromise<string>;
constructor(repo: Repository) {
this.repo = repo;
this.repoRealRootPath = null;
}
public serviceState(): winjs.TPromise<RawServiceState> {
return winjs.TPromise.as<RawServiceState>(this.repo
private getRepositoryRoot(): TPromise<string> {
return this._repositoryRoot || (this._repositoryRoot = pfs.realpath(this.repo.path));
}
public serviceState(): TPromise<RawServiceState> {
return TPromise.as<RawServiceState>(this.repo
? RawServiceState.OK
: RawServiceState.GitNotFound
);
}
public status(): winjs.TPromise<IRawStatus> {
return this.checkRoot()
.then(() => this.repo.getStatus())
public status(): TPromise<IRawStatus> {
return this.repo.getStatus()
.then(status => this.repo.getHEAD()
.then(HEAD => {
if (HEAD.name) {
......@@ -48,85 +50,86 @@ export class RawGitService implements IRawGitService {
return HEAD;
}
}, (): IHead => null)
.then(HEAD => winjs.Promise.join([this.repo.getHeads(), this.repo.getTags()]).then(r => {
.then(HEAD => Promise.join([this.getRepositoryRoot(), this.repo.getHeads(), this.repo.getTags()]).then(r => {
return {
repositoryRoot: r[0],
status: status,
HEAD: HEAD,
heads: r[0],
tags: r[1]
heads: r[1],
tags: r[2]
};
})))
.then(null, (err) => {
if (err.gitErrorCode === GitErrorCodes.BadConfigFile) {
return winjs.Promise.wrapError(err);
return Promise.wrapError(err);
} else if (err.gitErrorCode === GitErrorCodes.NotAtRepositoryRoot) {
return winjs.Promise.wrapError(err);
return Promise.wrapError(err);
}
return null;
});
}
public init(): winjs.TPromise<IRawStatus> {
public init(): TPromise<IRawStatus> {
return this.repo.init().then(() => this.status());
}
public add(filePaths?: string[]): winjs.TPromise<IRawStatus> {
public add(filePaths?: string[]): TPromise<IRawStatus> {
return this.repo.add(filePaths).then(() => this.status());
}
public stage(filePath: string, content: string): winjs.TPromise<IRawStatus> {
public stage(filePath: string, content: string): TPromise<IRawStatus> {
return this.repo.stage(filePath, content).then(() => this.status());
}
public branch(name: string, checkout?: boolean): winjs.TPromise<IRawStatus> {
public branch(name: string, checkout?: boolean): TPromise<IRawStatus> {
return this.repo.branch(name, checkout).then(() => this.status());
}
public checkout(treeish?: string, filePaths?: string[]): winjs.TPromise<IRawStatus> {
public checkout(treeish?: string, filePaths?: string[]): TPromise<IRawStatus> {
return this.repo.checkout(treeish, filePaths).then(() => this.status());
}
public clean(filePaths: string[]): winjs.TPromise<IRawStatus> {
public clean(filePaths: string[]): TPromise<IRawStatus> {
return this.repo.clean(filePaths).then(() => this.status());
}
public undo(): winjs.TPromise<IRawStatus> {
public undo(): TPromise<IRawStatus> {
return this.repo.undo().then(() => this.status());
}
public reset(treeish: string, hard?: boolean): winjs.TPromise<IRawStatus> {
public reset(treeish: string, hard?: boolean): TPromise<IRawStatus> {
return this.repo.reset(treeish, hard).then(() => this.status());
}
public revertFiles(treeish: string, filePaths?: string[]): winjs.TPromise<IRawStatus> {
public revertFiles(treeish: string, filePaths?: string[]): TPromise<IRawStatus> {
return this.repo.revertFiles(treeish, filePaths).then(() => this.status());
}
public fetch(): winjs.TPromise<IRawStatus> {
public fetch(): TPromise<IRawStatus> {
return this.repo.fetch().then(null, (err) => {
if (err.gitErrorCode === GitErrorCodes.NoRemoteRepositorySpecified) {
return winjs.Promise.as(null);
return Promise.as(null);
}
return winjs.Promise.wrapError(err);
return Promise.wrapError(err);
}).then(() => this.status());
}
public pull(): winjs.TPromise<IRawStatus> {
public pull(): TPromise<IRawStatus> {
return this.repo.pull().then(() => this.status());
}
public push(): winjs.TPromise<IRawStatus> {
public push(): TPromise<IRawStatus> {
return this.repo.push().then(() => this.status());
}
public sync(): winjs.TPromise<IRawStatus> {
public sync(): TPromise<IRawStatus> {
return this.repo.sync().then(() => this.status());
}
public commit(message:string, amend?: boolean, stage?: boolean): winjs.TPromise<IRawStatus> {
var promise: winjs.Promise = winjs.Promise.as(null);
public commit(message:string, amend?: boolean, stage?: boolean): TPromise<IRawStatus> {
var promise: Promise = Promise.as(null);
if (stage) {
promise = this.repo.add(null);
......@@ -137,10 +140,10 @@ export class RawGitService implements IRawGitService {
.then(() => this.status());
}
public detectMimetypes(filePath: string, treeish?: string): winjs.TPromise<string[]> {
public detectMimetypes(filePath: string, treeish?: string): TPromise<string[]> {
return pfs.exists(path.join(this.repo.path, filePath)).then((exists) => {
if (exists) {
return new winjs.TPromise<string[]>((c, e) => {
return new TPromise<string[]>((c, e) => {
mime.detectMimesFromFile(path.join(this.repo.path, filePath), (err, result) => {
if (err) { e(err); }
else { c(result.mimes); }
......@@ -150,7 +153,7 @@ export class RawGitService implements IRawGitService {
var child = this.repo.show(treeish + ':' + filePath);
return new winjs.TPromise<string[]>((c, e) => {
return new TPromise<string[]>((c, e) => {
mime.detectMimesFromStream(child.stdout, filePath, (err, result) => {
if (err) { e(err); }
else { c(result.mimes); }
......@@ -160,42 +163,103 @@ export class RawGitService implements IRawGitService {
}
// careful, this buffers the whole object into memory
public show(filePath: string, treeish?: string): winjs.TPromise<string> {
public show(filePath: string, treeish?: string): TPromise<string> {
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 winjs.TPromise.wrapError<string>(e);
return TPromise.wrapError<string>(e);
});
}
public onOutput(): winjs.Promise {
public onOutput(): Promise {
var cancel: () => void;
return new winjs.Promise((c, e, p) => {
return new Promise((c, e, p) => {
cancel = this.repo.onOutput(p);
}, () => cancel());
}
}
private checkRoot(): winjs.Promise {
if (!this.repoRealRootPath) {
this.repoRealRootPath = pfs.realpath(this.repo.path);
}
export class DelayedRawGitService implements IRawGitService {
return this.repo.getRoot().then(root => {
return winjs.Promise.join([
this.repoRealRootPath,
pfs.realpath(root)
]).then(paths => {
if (!pathsAreEqual(paths[0], paths[1])) {
return winjs.Promise.wrapError(new GitError({
message: 'Not at the repository root',
gitErrorCode: GitErrorCodes.NotAtRepositoryRoot
}));
}
});
});
constructor(private raw: TPromise<IRawGitService>) { }
public serviceState(): TPromise<RawServiceState> {
return this.raw.then(raw => raw.serviceState());
}
}
public status(): TPromise<IRawStatus> {
return this.raw.then(raw => raw.status());
}
public init(): TPromise<IRawStatus> {
return this.raw.then(raw => raw.init());
}
public add(filesPaths?: string[]): TPromise<IRawStatus> {
return this.raw.then(raw => raw.add(filesPaths));
}
public stage(filePath: string, content: string): TPromise<IRawStatus> {
return this.raw.then(raw => raw.stage(filePath, content));
}
public branch(name: string, checkout?: boolean): TPromise<IRawStatus> {
return this.raw.then(raw => raw.branch(name, checkout));
}
public checkout(treeish?: string, filePaths?: string[]): TPromise<IRawStatus> {
return this.raw.then(raw => raw.checkout(treeish, filePaths));
}
public clean(filePaths: string[]): TPromise<IRawStatus> {
return this.raw.then(raw => raw.clean(filePaths));
}
public undo(): TPromise<IRawStatus> {
return this.raw.then(raw => raw.undo());
}
public reset(treeish: string, hard?: boolean): TPromise<IRawStatus> {
return this.raw.then(raw => raw.reset(treeish, hard));
}
public revertFiles(treeish: string, filePaths?: string[]): TPromise<IRawStatus> {
return this.raw.then(raw => raw.revertFiles(treeish, filePaths));
}
public fetch(): TPromise<IRawStatus> {
return this.raw.then(raw => raw.fetch());
}
public pull(): TPromise<IRawStatus> {
return this.raw.then(raw => raw.pull());
}
public push(): TPromise<IRawStatus> {
return this.raw.then(raw => raw.push());
}
public sync(): TPromise<IRawStatus> {
return this.raw.then(raw => raw.sync());
}
public commit(message: string, amend?: boolean, stage?: boolean): TPromise<IRawStatus> {
return this.raw.then(raw => raw.commit(message, amend, stage));
}
public detectMimetypes(path: string, treeish?: string): TPromise<string[]> {
return this.raw.then(raw => raw.detectMimetypes(path, treeish));
}
public show(path: string, treeish?: string): TPromise<string> {
return this.raw.then(raw => raw.show(path, treeish));
}
public onOutput(): Promise {
return this.raw.then(raw => raw.onOutput());
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册