electronGitService.ts 8.0 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

J
Joao Moreno 已提交
6
import { TPromise } from 'vs/base/common/winjs.base';
J
Joao Moreno 已提交
7
import { IRawGitService, RawServiceState, IGitConfiguration } from 'vs/workbench/parts/git/common/git';
E
Erich Gamma 已提交
8 9 10 11 12 13 14 15 16 17
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';
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 { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService } from 'vs/platform/message/common/message';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
J
Joao Moreno 已提交
18
import { getDelayedChannel, getNextTickChannel } from 'vs/base/parts/ipc/common/ipc';
J
Joao Moreno 已提交
19
import { Client } from 'vs/base/parts/ipc/node/ipc.cp';
J
Joao Moreno 已提交
20
import { GitChannelClient, UnavailableGitChannel } from 'vs/workbench/parts/git/common/gitIpc';
J
Joao Moreno 已提交
21
import { RawGitService, DelayedRawGitService } from 'vs/workbench/parts/git/node/rawGitService';
E
Erich Gamma 已提交
22 23 24
import URI from 'vs/base/common/uri';
import { spawn, exec } from 'child_process';
import { join } from 'path';
B
Benjamin Pasero 已提交
25
import { remote } from 'electron';
J
Joao Moreno 已提交
26
import { IStorageService } from 'vs/platform/storage/common/storage';
J
Joao Moreno 已提交
27
import { readdir } from 'vs/base/node/pfs';
E
Erich Gamma 已提交
28

J
Joao Moreno 已提交
29 30 31 32 33 34 35 36 37 38 39 40 41 42
interface IGit {
	path: string;
	version: string;
}

function parseVersion(raw: string): string {
	return raw.replace(/^git version /, '');
}

function findSpecificGit(path: string): TPromise<IGit> {
	return new TPromise<IGit>((c, e) => {
		const buffers: Buffer[] = [];
		const child = spawn(path, ['--version']);
		child.stdout.on('data', b => buffers.push(b));
E
Erich Gamma 已提交
43
		child.on('error', e);
J
Joao Moreno 已提交
44
		child.on('exit', code => code ? e(new Error('Not found')) : c({ path, version: parseVersion(Buffer.concat(buffers).toString('utf8').trim()) }));
E
Erich Gamma 已提交
45 46 47
	});
}

J
Joao Moreno 已提交
48 49
function findGitDarwin(): TPromise<IGit> {
	return new TPromise<IGit>((c, e) => {
E
Erich Gamma 已提交
50 51 52 53 54
		exec('which git', (err, gitPathBuffer) => {
			if (err) {
				return e('git not found');
			}

J
Joao Moreno 已提交
55 56 57 58 59 60 61 62 63 64 65 66
			const path = gitPathBuffer.toString().replace(/^\s+|\s+$/g, '');

			function getVersion(path: string) {
				// make sure git executes
				exec('git --version', (err, stdout) => {
					if (err) {
						return e('git not found');
					}

					return c({ path, version: parseVersion(stdout.toString('utf8').trim()) });
				});
			}
E
Erich Gamma 已提交
67

J
Joao Moreno 已提交
68 69
			if (path !== '/usr/bin/git')	{
				return getVersion(path);
E
Erich Gamma 已提交
70 71 72 73 74 75 76 77 78 79 80
			}

			// must check if XCode is installed
			exec('xcode-select -p', (err: any) => {
				if (err && err.code === 2) {
					// git is not installed, and launching /usr/bin/git
					// will prompt the user to install it

					return e('git not found');
				}

J
Joao Moreno 已提交
81
				getVersion(path);
E
Erich Gamma 已提交
82 83 84 85 86
			});
		});
	});
}

J
Joao Moreno 已提交
87
function findSystemGitWin32(base: string): TPromise<IGit> {
E
Erich Gamma 已提交
88
	if (!base) {
J
Joao Moreno 已提交
89
		return TPromise.wrapError('Not found');
E
Erich Gamma 已提交
90 91 92 93 94
	}

	return findSpecificGit(join(base, 'Git', 'cmd', 'git.exe'));
}

J
Joao Moreno 已提交
95 96 97 98 99 100 101 102 103 104 105 106 107 108
function findGitHubGitWin32(): TPromise<IGit> {
	const github = join(process.env['LOCALAPPDATA'], 'GitHub');

	return readdir(github).then(children => {
		const git = children.filter(child => /^PortableGit/.test(child))[0];

		if (!git) {
			return TPromise.wrapError('Not found');
		}

		return findSpecificGit(join(github, git, 'cmd', 'git.exe'));
	});
}

J
Joao Moreno 已提交
109
function findGitWin32(): TPromise<IGit> {
E
Erich Gamma 已提交
110 111 112
	return findSystemGitWin32(process.env['ProgramW6432'])
		.then(null, () => findSystemGitWin32(process.env['ProgramFiles(x86)']))
		.then(null, () => findSystemGitWin32(process.env['ProgramFiles']))
J
Joao Moreno 已提交
113 114
		.then(null, () => findSpecificGit('git'))
		.then(null, () => findGitHubGitWin32());
E
Erich Gamma 已提交
115 116
}

J
Joao Moreno 已提交
117 118
function findGit(hint: string): TPromise<IGit> {
	var first = hint ? findSpecificGit(hint) : TPromise.wrapError(null);
E
Erich Gamma 已提交
119 120 121 122 123 124 125 126 127 128 129 130 131 132

	return first.then(null, () => {
		switch (process.platform) {
			case 'darwin': return findGitDarwin();
			case 'win32': return findGitWin32();
			default: return findSpecificGit('git');
		}
	});
}

class UnavailableRawGitService extends RawGitService {
	constructor() {
		super(null);
	}
J
Joao Moreno 已提交
133 134 135 136

	serviceState(): TPromise<RawServiceState> {
		return TPromise.as(RawServiceState.GitNotFound);
	}
E
Erich Gamma 已提交
137 138 139 140 141 142 143
}

class DisabledRawGitService extends RawGitService {
	constructor() {
		super(null);
	}

J
Joao Moreno 已提交
144 145
	serviceState(): TPromise<RawServiceState> {
		return TPromise.as(RawServiceState.Disabled);
E
Erich Gamma 已提交
146 147 148
	}
}

J
Joao Moreno 已提交
149 150 151 152 153 154 155 156 157 158 159
function createRemoteRawGitService(gitPath: string, workspaceRoot: string, encoding: string, verbose: boolean): IRawGitService {
	const promise = TPromise.timeout(0) // free event loop cos finding git costs
		.then(() => findGit(gitPath))
		.then(({ path, version }) => {
			const client = new Client(
				URI.parse(require.toUrl('bootstrap')).fsPath,
				{
					serverName: 'Git',
					timeout: 1000 * 60,
					args: [path, workspaceRoot, encoding, remote.process.execPath, version],
					env: {
160
						ATOM_SHELL_INTERNAL_RUN_AS_NODE: 1,
J
Joao Moreno 已提交
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
						PIPE_LOGGING: 'true',
						AMD_ENTRYPOINT: 'vs/workbench/parts/git/node/gitApp',
						VERBOSE_LOGGING: String(verbose)
					}
				}
			);

			return client.getChannel('git');
		})
		.then(null, () => new UnavailableGitChannel());

	const channel = getNextTickChannel(getDelayedChannel(promise));
	return new GitChannelClient(channel);
}

interface IRawGitServiceBootstrap {
	createRawGitService(gitPath: string, workspaceRoot: string, defaultEncoding: string, exePath: string, version: string): TPromise<IRawGitService>;
}

function createRawGitService(gitPath: string, workspaceRoot: string, encoding: string, verbose: boolean): IRawGitService {
	const promise = new TPromise<IRawGitService>((c, e) => {
		require(['vs/workbench/parts/git/node/rawGitServiceBootstrap'], ({ createRawGitService }: IRawGitServiceBootstrap) => {
			findGit(gitPath)
				.then(({ path, version }) => createRawGitService(path, workspaceRoot, encoding, remote.process.execPath, version))
				.done(c, e);
		}, e);
	});

	return new DelayedRawGitService(promise);
}

E
Erich Gamma 已提交
192
export class ElectronGitService extends GitService {
J
Joao Moreno 已提交
193 194 195

	private static USE_REMOTE_PROCESS_SERVICE = true;

E
Erich Gamma 已提交
196 197 198 199 200 201 202
	constructor(
		@IInstantiationService instantiationService: IInstantiationService,
		@IEventService eventService: IEventService,
		@IMessageService messageService: IMessageService,
		@IWorkbenchEditorService editorService: IWorkbenchEditorService,
		@IOutputService outputService: IOutputService,
		@IWorkspaceContextService contextService: IWorkspaceContextService,
J
Joao Moreno 已提交
203
		@ILifecycleService lifecycleService: ILifecycleService,
J
Joao Moreno 已提交
204 205
		@IStorageService storageService: IStorageService,
		@IConfigurationService configurationService: IConfigurationService
E
Erich Gamma 已提交
206
	) {
J
Joao Moreno 已提交
207 208
		const conf = configurationService.getConfiguration<IGitConfiguration>('git');
		const filesConf = configurationService.getConfiguration<any>('files');
J
Joao Moreno 已提交
209 210 211 212
		const workspace = contextService.getWorkspace();

		let raw: IRawGitService;

J
Joao Moreno 已提交
213
		if (!conf.enabled) {
J
Joao Moreno 已提交
214 215 216 217
			raw = new DisabledRawGitService();
		} else if (!workspace) {
			raw = new NoOpGitService();
		} else {
J
Joao Moreno 已提交
218 219
			const gitPath = conf.path || null;
			const encoding = filesConf.encoding || 'utf8';
J
Joao Moreno 已提交
220
			const workspaceRoot = workspace.resource.fsPath;
J
Joao Moreno 已提交
221
			const verbose = !contextService.getConfiguration().env.isBuilt || contextService.getConfiguration().env.verboseLogging;
J
Joao Moreno 已提交
222

J
Joao Moreno 已提交
223 224 225 226 227
			if (ElectronGitService.USE_REMOTE_PROCESS_SERVICE) {
				raw = createRemoteRawGitService(gitPath, workspaceRoot, encoding, verbose);
			} else {
				raw = createRawGitService(gitPath, workspaceRoot, encoding, verbose);
			}
J
Joao Moreno 已提交
228
		}
E
Erich Gamma 已提交
229

230
		super(raw, instantiationService, eventService, messageService, editorService, outputService, contextService, lifecycleService, storageService, configurationService);
E
Erich Gamma 已提交
231 232
	}
}