electronGitService.ts 8.3 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
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';
11
import { ITextFileService } from 'vs/workbench/parts/files/common/files';
E
Erich Gamma 已提交
12 13 14 15
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';
16
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
E
Erich Gamma 已提交
17 18 19
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 已提交
20
import { getDelayedChannel, getNextTickChannel } from 'vs/base/parts/ipc/common/ipc';
J
Joao Moreno 已提交
21
import { Client } from 'vs/base/parts/ipc/node/ipc.cp';
J
Joao Moreno 已提交
22
import { GitChannelClient, UnavailableGitChannel } from 'vs/workbench/parts/git/common/gitIpc';
J
Joao Moreno 已提交
23
import { RawGitService, DelayedRawGitService } from 'vs/workbench/parts/git/node/rawGitService';
E
Erich Gamma 已提交
24 25 26
import URI from 'vs/base/common/uri';
import { spawn, exec } from 'child_process';
import { join } from 'path';
J
Joao Moreno 已提交
27
import { IStorageService } from 'vs/platform/storage/common/storage';
J
Joao Moreno 已提交
28
import { readdir } from 'vs/base/node/pfs';
E
Erich Gamma 已提交
29

J
Joao Moreno 已提交
30 31 32 33 34 35 36 37 38 39 40 41 42 43
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 已提交
44
		child.on('error', e);
J
Joao Moreno 已提交
45
		child.on('exit', code => code ? e(new Error('Not found')) : c({ path, version: parseVersion(Buffer.concat(buffers).toString('utf8').trim()) }));
E
Erich Gamma 已提交
46 47 48
	});
}

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

J
Joao Moreno 已提交
56 57 58 59 60 61 62 63 64 65 66 67
			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 已提交
68

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

			// 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 已提交
82
				getVersion(path);
E
Erich Gamma 已提交
83 84 85 86 87
			});
		});
	});
}

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

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

J
Joao Moreno 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109
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 已提交
110
function findGitWin32(): TPromise<IGit> {
E
Erich Gamma 已提交
111 112 113
	return findSystemGitWin32(process.env['ProgramW6432'])
		.then(null, () => findSystemGitWin32(process.env['ProgramFiles(x86)']))
		.then(null, () => findSystemGitWin32(process.env['ProgramFiles']))
J
Joao Moreno 已提交
114 115
		.then(null, () => findSpecificGit('git'))
		.then(null, () => findGitHubGitWin32());
E
Erich Gamma 已提交
116 117
}

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

	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 已提交
134 135 136 137

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

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

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

150
function createRemoteRawGitService(gitPath: string, execPath: string, workspaceRoot: string, encoding: string, verbose: boolean): IRawGitService {
J
Joao Moreno 已提交
151 152 153 154 155 156 157 158
	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,
159
					args: [path, workspaceRoot, encoding, execPath, version],
J
Joao Moreno 已提交
160
					env: {
161
						ATOM_SHELL_INTERNAL_RUN_AS_NODE: 1,
J
Joao Moreno 已提交
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
						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>;
}

181
function createRawGitService(gitPath: string, execPath: string, workspaceRoot: string, encoding: string, verbose: boolean): IRawGitService {
J
Joao Moreno 已提交
182 183 184
	const promise = new TPromise<IRawGitService>((c, e) => {
		require(['vs/workbench/parts/git/node/rawGitServiceBootstrap'], ({ createRawGitService }: IRawGitServiceBootstrap) => {
			findGit(gitPath)
185
				.then(({ path, version }) => createRawGitService(path, workspaceRoot, encoding, execPath, version))
J
Joao Moreno 已提交
186 187 188 189 190 191 192
				.done(c, e);
		}, e);
	});

	return new DelayedRawGitService(promise);
}

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

	private static USE_REMOTE_PROCESS_SERVICE = true;

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

		let raw: IRawGitService;

J
Joao Moreno 已提交
216
		if (!conf.enabled) {
J
Joao Moreno 已提交
217 218 219 220
			raw = new DisabledRawGitService();
		} else if (!workspace) {
			raw = new NoOpGitService();
		} else {
J
Joao Moreno 已提交
221
			const gitPath = conf.path || null;
B
Benjamin Pasero 已提交
222
			const encoding = (filesConf && filesConf.encoding) || 'utf8';
J
Joao Moreno 已提交
223
			const workspaceRoot = workspace.resource.fsPath;
224
			const verbose = !environmentService.isBuilt || environmentService.verbose;
J
Joao Moreno 已提交
225

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

233
		super(raw, instantiationService, eventService, messageService, editorService, outputService, textFileService, contextService, lifecycleService, storageService, configurationService);
E
Erich Gamma 已提交
234 235
	}
}