mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-23 01:25:38 -05:00
Initial VS Code 1.19 source merge (#571)
* Initial 1.19 xcopy * Fix yarn build * Fix numerous build breaks * Next batch of build break fixes * More build break fixes * Runtime breaks * Additional post merge fixes * Fix windows setup file * Fix test failures. * Update license header blocks to refer to source eula
This commit is contained in:
@@ -9,9 +9,12 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import * as cp from 'child_process';
|
||||
import * as which from 'which';
|
||||
import { EventEmitter } from 'events';
|
||||
import iconv = require('iconv-lite');
|
||||
import { assign, uniqBy, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp } from './util';
|
||||
import * as filetype from 'file-type';
|
||||
import { assign, uniqBy, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent } from './util';
|
||||
import { CancellationToken } from 'vscode';
|
||||
|
||||
const readfile = denodeify<string>(fs.readFile);
|
||||
|
||||
@@ -60,8 +63,10 @@ function parseVersion(raw: string): string {
|
||||
return raw.replace(/^git version /, '');
|
||||
}
|
||||
|
||||
function findSpecificGit(path: string): Promise<IGit> {
|
||||
function findSpecificGit(path: string, onLookup: (path: string) => void): Promise<IGit> {
|
||||
return new Promise<IGit>((c, e) => {
|
||||
onLookup(path);
|
||||
|
||||
const buffers: Buffer[] = [];
|
||||
const child = cp.spawn(path, ['--version']);
|
||||
child.stdout.on('data', (b: Buffer) => buffers.push(b));
|
||||
@@ -70,7 +75,7 @@ function findSpecificGit(path: string): Promise<IGit> {
|
||||
});
|
||||
}
|
||||
|
||||
function findGitDarwin(): Promise<IGit> {
|
||||
function findGitDarwin(onLookup: (path: string) => void): Promise<IGit> {
|
||||
return new Promise<IGit>((c, e) => {
|
||||
cp.exec('which git', (err, gitPathBuffer) => {
|
||||
if (err) {
|
||||
@@ -80,8 +85,11 @@ function findGitDarwin(): Promise<IGit> {
|
||||
const path = gitPathBuffer.toString().replace(/^\s+|\s+$/g, '');
|
||||
|
||||
function getVersion(path: string) {
|
||||
onLookup(path);
|
||||
|
||||
// make sure git executes
|
||||
cp.exec('git --version', (err, stdout) => {
|
||||
|
||||
if (err) {
|
||||
return e('git not found');
|
||||
}
|
||||
@@ -109,38 +117,44 @@ function findGitDarwin(): Promise<IGit> {
|
||||
});
|
||||
}
|
||||
|
||||
function findSystemGitWin32(base: string): Promise<IGit> {
|
||||
function findSystemGitWin32(base: string, onLookup: (path: string) => void): Promise<IGit> {
|
||||
if (!base) {
|
||||
return Promise.reject<IGit>('Not found');
|
||||
}
|
||||
|
||||
return findSpecificGit(path.join(base, 'Git', 'cmd', 'git.exe'));
|
||||
return findSpecificGit(path.join(base, 'Git', 'cmd', 'git.exe'), onLookup);
|
||||
}
|
||||
|
||||
function findGitWin32(): Promise<IGit> {
|
||||
return findSystemGitWin32(process.env['ProgramW6432'])
|
||||
.then(void 0, () => findSystemGitWin32(process.env['ProgramFiles(x86)']))
|
||||
.then(void 0, () => findSystemGitWin32(process.env['ProgramFiles']))
|
||||
.then(void 0, () => findSpecificGit('git'));
|
||||
function findGitWin32InPath(onLookup: (path: string) => void): Promise<IGit> {
|
||||
const whichPromise = new Promise<string>((c, e) => which('git.exe', (err, path) => err ? e(err) : c(path)));
|
||||
return whichPromise.then(path => findSpecificGit(path, onLookup));
|
||||
}
|
||||
|
||||
export function findGit(hint: string | undefined): Promise<IGit> {
|
||||
var first = hint ? findSpecificGit(hint) : Promise.reject<IGit>(null);
|
||||
function findGitWin32(onLookup: (path: string) => void): Promise<IGit> {
|
||||
return findSystemGitWin32(process.env['ProgramW6432'] as string, onLookup)
|
||||
.then(void 0, () => findSystemGitWin32(process.env['ProgramFiles(x86)'] as string, onLookup))
|
||||
.then(void 0, () => findSystemGitWin32(process.env['ProgramFiles'] as string, onLookup))
|
||||
.then(void 0, () => findSystemGitWin32(path.join(process.env['LocalAppData'] as string, 'Programs'), onLookup))
|
||||
.then(void 0, () => findGitWin32InPath(onLookup));
|
||||
}
|
||||
|
||||
export function findGit(hint: string | undefined, onLookup: (path: string) => void): Promise<IGit> {
|
||||
const first = hint ? findSpecificGit(hint, onLookup) : Promise.reject<IGit>(null);
|
||||
|
||||
return first
|
||||
.then(void 0, () => {
|
||||
switch (process.platform) {
|
||||
case 'darwin': return findGitDarwin();
|
||||
case 'win32': return findGitWin32();
|
||||
default: return findSpecificGit('git');
|
||||
case 'darwin': return findGitDarwin(onLookup);
|
||||
case 'win32': return findGitWin32(onLookup);
|
||||
default: return findSpecificGit('git', onLookup);
|
||||
}
|
||||
})
|
||||
.then(null, () => Promise.reject(new Error('Git installation not found.')));
|
||||
}
|
||||
|
||||
export interface IExecutionResult {
|
||||
export interface IExecutionResult<T extends string | Buffer> {
|
||||
exitCode: number;
|
||||
stdout: string;
|
||||
stdout: T;
|
||||
stderr: string;
|
||||
}
|
||||
|
||||
@@ -162,50 +176,69 @@ export interface SpawnOptions extends cp.SpawnOptions {
|
||||
input?: string;
|
||||
encoding?: string;
|
||||
log?: boolean;
|
||||
cancellationToken?: CancellationToken;
|
||||
}
|
||||
|
||||
async function exec(child: cp.ChildProcess, options: SpawnOptions = {}): Promise<IExecutionResult> {
|
||||
async function exec(child: cp.ChildProcess, cancellationToken?: CancellationToken): Promise<IExecutionResult<Buffer>> {
|
||||
if (!child.stdout || !child.stderr) {
|
||||
throw new GitError({
|
||||
message: 'Failed to get stdout or stderr from git process.'
|
||||
});
|
||||
throw new GitError({ message: 'Failed to get stdout or stderr from git process.' });
|
||||
}
|
||||
|
||||
if (cancellationToken && cancellationToken.isCancellationRequested) {
|
||||
throw new GitError({ message: 'Cancelled' });
|
||||
}
|
||||
|
||||
const disposables: IDisposable[] = [];
|
||||
|
||||
const once = (ee: NodeJS.EventEmitter, name: string, fn: Function) => {
|
||||
const once = (ee: NodeJS.EventEmitter, name: string, fn: (...args: any[]) => void) => {
|
||||
ee.once(name, fn);
|
||||
disposables.push(toDisposable(() => ee.removeListener(name, fn)));
|
||||
};
|
||||
|
||||
const on = (ee: NodeJS.EventEmitter, name: string, fn: Function) => {
|
||||
const on = (ee: NodeJS.EventEmitter, name: string, fn: (...args: any[]) => void) => {
|
||||
ee.on(name, fn);
|
||||
disposables.push(toDisposable(() => ee.removeListener(name, fn)));
|
||||
};
|
||||
|
||||
let encoding = options.encoding || 'utf8';
|
||||
encoding = iconv.encodingExists(encoding) ? encoding : 'utf8';
|
||||
|
||||
const [exitCode, stdout, stderr] = await Promise.all<any>([
|
||||
let result = Promise.all<any>([
|
||||
new Promise<number>((c, e) => {
|
||||
once(child, 'error', cpErrorHandler(e));
|
||||
once(child, 'exit', c);
|
||||
}),
|
||||
new Promise<string>(c => {
|
||||
new Promise<Buffer>(c => {
|
||||
const buffers: Buffer[] = [];
|
||||
on(child.stdout, 'data', b => buffers.push(b));
|
||||
once(child.stdout, 'close', () => c(iconv.decode(Buffer.concat(buffers), encoding)));
|
||||
on(child.stdout, 'data', (b: Buffer) => buffers.push(b));
|
||||
once(child.stdout, 'close', () => c(Buffer.concat(buffers)));
|
||||
}),
|
||||
new Promise<string>(c => {
|
||||
const buffers: Buffer[] = [];
|
||||
on(child.stderr, 'data', b => buffers.push(b));
|
||||
on(child.stderr, 'data', (b: Buffer) => buffers.push(b));
|
||||
once(child.stderr, 'close', () => c(Buffer.concat(buffers).toString('utf8')));
|
||||
})
|
||||
]);
|
||||
]) as Promise<[number, Buffer, string]>;
|
||||
|
||||
dispose(disposables);
|
||||
if (cancellationToken) {
|
||||
const cancellationPromise = new Promise<[number, Buffer, string]>((_, e) => {
|
||||
onceEvent(cancellationToken.onCancellationRequested)(() => {
|
||||
try {
|
||||
child.kill();
|
||||
} catch (err) {
|
||||
// noop
|
||||
}
|
||||
|
||||
return { exitCode, stdout, stderr };
|
||||
e(new GitError({ message: 'Cancelled' }));
|
||||
});
|
||||
});
|
||||
|
||||
result = Promise.race([result, cancellationPromise]);
|
||||
}
|
||||
|
||||
try {
|
||||
const [exitCode, stdout, stderr] = await result;
|
||||
return { exitCode, stdout, stderr };
|
||||
} finally {
|
||||
dispose(disposables);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IGitErrorData {
|
||||
@@ -288,6 +321,8 @@ export const GitErrorCodes = {
|
||||
RepositoryIsLocked: 'RepositoryIsLocked',
|
||||
BranchNotFullyMerged: 'BranchNotFullyMerged',
|
||||
NoRemoteReference: 'NoRemoteReference',
|
||||
InvalidBranchName: 'InvalidBranchName',
|
||||
BranchAlreadyExists: 'BranchAlreadyExists',
|
||||
NoLocalChanges: 'NoLocalChanges',
|
||||
NoStashFound: 'NoStashFound',
|
||||
LocalChangesOverwritten: 'LocalChangesOverwritten'
|
||||
@@ -312,6 +347,10 @@ function getGitErrorCode(stderr: string): string | undefined {
|
||||
return GitErrorCodes.BranchNotFullyMerged;
|
||||
} else if (/Couldn\'t find remote ref/.test(stderr)) {
|
||||
return GitErrorCodes.NoRemoteReference;
|
||||
} else if (/A branch named '.+' already exists/.test(stderr)) {
|
||||
return GitErrorCodes.BranchAlreadyExists;
|
||||
} else if (/'.+' is not a valid branch name/.test(stderr)) {
|
||||
return GitErrorCodes.InvalidBranchName;
|
||||
}
|
||||
|
||||
return void 0;
|
||||
@@ -341,12 +380,12 @@ export class Git {
|
||||
return;
|
||||
}
|
||||
|
||||
async clone(url: string, parentPath: string): Promise<string> {
|
||||
async clone(url: string, parentPath: string, cancellationToken?: CancellationToken): Promise<string> {
|
||||
const folderName = decodeURI(url).replace(/^.*\//, '').replace(/\.git$/, '') || 'repository';
|
||||
const folderPath = path.join(parentPath, folderName);
|
||||
|
||||
await mkdirp(parentPath);
|
||||
await this.exec(parentPath, ['clone', url, folderPath]);
|
||||
await this.exec(parentPath, ['clone', url, folderPath], { cancellationToken });
|
||||
return folderPath;
|
||||
}
|
||||
|
||||
@@ -355,7 +394,7 @@ export class Git {
|
||||
return path.normalize(result.stdout.trim());
|
||||
}
|
||||
|
||||
async exec(cwd: string, args: string[], options: SpawnOptions = {}): Promise<IExecutionResult> {
|
||||
async exec(cwd: string, args: string[], options: SpawnOptions = {}): Promise<IExecutionResult<string>> {
|
||||
options = assign({ cwd }, options || {});
|
||||
return await this._exec(args, options);
|
||||
}
|
||||
@@ -365,21 +404,30 @@ export class Git {
|
||||
return this.spawn(args, options);
|
||||
}
|
||||
|
||||
private async _exec(args: string[], options: SpawnOptions = {}): Promise<IExecutionResult> {
|
||||
private async _exec(args: string[], options: SpawnOptions = {}): Promise<IExecutionResult<string>> {
|
||||
const child = this.spawn(args, options);
|
||||
|
||||
if (options.input) {
|
||||
child.stdin.end(options.input, 'utf8');
|
||||
}
|
||||
|
||||
const result = await exec(child, options);
|
||||
const bufferResult = await exec(child, options.cancellationToken);
|
||||
|
||||
if (options.log !== false && result.stderr.length > 0) {
|
||||
this.log(`${result.stderr}\n`);
|
||||
if (options.log !== false && bufferResult.stderr.length > 0) {
|
||||
this.log(`${bufferResult.stderr}\n`);
|
||||
}
|
||||
|
||||
if (result.exitCode) {
|
||||
return Promise.reject<IExecutionResult>(new GitError({
|
||||
let encoding = options.encoding || 'utf8';
|
||||
encoding = iconv.encodingExists(encoding) ? encoding : 'utf8';
|
||||
|
||||
const result: IExecutionResult<string> = {
|
||||
exitCode: bufferResult.exitCode,
|
||||
stdout: iconv.decode(bufferResult.stdout, encoding),
|
||||
stderr: bufferResult.stderr
|
||||
};
|
||||
|
||||
if (bufferResult.exitCode) {
|
||||
return Promise.reject<IExecutionResult<string>>(new GitError({
|
||||
message: 'Failed to execute git',
|
||||
stdout: result.stdout,
|
||||
stderr: result.stderr,
|
||||
@@ -510,7 +558,7 @@ export class Repository {
|
||||
}
|
||||
|
||||
// TODO@Joao: rename to exec
|
||||
async run(args: string[], options: SpawnOptions = {}): Promise<IExecutionResult> {
|
||||
async run(args: string[], options: SpawnOptions = {}): Promise<IExecutionResult<string>> {
|
||||
return await this.git.exec(this.repositoryRoot, args, options);
|
||||
}
|
||||
|
||||
@@ -539,39 +587,97 @@ export class Repository {
|
||||
return result.stdout;
|
||||
}
|
||||
|
||||
async buffer(object: string, encoding: string = 'utf8'): Promise<string> {
|
||||
async bufferString(object: string, encoding: string = 'utf8'): Promise<string> {
|
||||
const stdout = await this.buffer(object);
|
||||
return iconv.decode(stdout, iconv.encodingExists(encoding) ? encoding : 'utf8');
|
||||
}
|
||||
|
||||
async buffer(object: string): Promise<Buffer> {
|
||||
const child = this.stream(['show', object]);
|
||||
|
||||
if (!child.stdout) {
|
||||
return Promise.reject<string>('Can\'t open file from git');
|
||||
return Promise.reject<Buffer>('Can\'t open file from git');
|
||||
}
|
||||
|
||||
const { exitCode, stdout } = await exec(child, { encoding });
|
||||
const { exitCode, stdout } = await exec(child);
|
||||
|
||||
if (exitCode) {
|
||||
return Promise.reject<string>(new GitError({
|
||||
return Promise.reject<Buffer>(new GitError({
|
||||
message: 'Could not show object.',
|
||||
exitCode
|
||||
}));
|
||||
}
|
||||
|
||||
return stdout;
|
||||
}
|
||||
|
||||
// TODO@joao
|
||||
// return new Promise((c, e) => {
|
||||
// detectMimesFromStream(child.stdout, null, (err, result) => {
|
||||
// if (err) {
|
||||
// e(err);
|
||||
// } else if (isBinaryMime(result.mimes)) {
|
||||
// e(<IFileOperationResult>{
|
||||
// message: localize('fileBinaryError', "File seems to be binary and cannot be opened as text"),
|
||||
// fileOperationResult: FileOperationResult.FILE_IS_BINARY
|
||||
// });
|
||||
// } else {
|
||||
// c(this.doBuffer(object));
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
async lstree(treeish: string, path: string): Promise<{ mode: number, object: string, size: number }> {
|
||||
if (!treeish) { // index
|
||||
const { stdout } = await this.run(['ls-files', '--stage', '--', path]);
|
||||
|
||||
const match = /^(\d+)\s+([0-9a-f]{40})\s+(\d+)/.exec(stdout);
|
||||
|
||||
if (!match) {
|
||||
throw new GitError({ message: 'Error running ls-files' });
|
||||
}
|
||||
|
||||
const [, mode, object] = match;
|
||||
const catFile = await this.run(['cat-file', '-s', object]);
|
||||
const size = parseInt(catFile.stdout);
|
||||
|
||||
return { mode: parseInt(mode), object, size };
|
||||
}
|
||||
|
||||
const { stdout } = await this.run(['ls-tree', '-l', treeish, '--', path]);
|
||||
|
||||
const match = /^(\d+)\s+(\w+)\s+([0-9a-f]{40})\s+(\d+)/.exec(stdout);
|
||||
|
||||
if (!match) {
|
||||
throw new GitError({ message: 'Error running ls-tree' });
|
||||
}
|
||||
|
||||
const [, mode, , object, size] = match;
|
||||
return { mode: parseInt(mode), object, size: parseInt(size) };
|
||||
}
|
||||
|
||||
async detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }> {
|
||||
const child = await this.stream(['show', object]);
|
||||
const buffer = await readBytes(child.stdout, 4100);
|
||||
|
||||
try {
|
||||
child.kill();
|
||||
} catch (err) {
|
||||
// noop
|
||||
}
|
||||
|
||||
const encoding = detectUnicodeEncoding(buffer);
|
||||
let isText = true;
|
||||
|
||||
if (encoding !== Encoding.UTF16be && encoding !== Encoding.UTF16le) {
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
if (buffer.readInt8(i) === 0) {
|
||||
isText = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isText) {
|
||||
const result = filetype(buffer);
|
||||
|
||||
if (!result) {
|
||||
return { mimetype: 'application/octet-stream' };
|
||||
} else {
|
||||
return { mimetype: result.mime };
|
||||
}
|
||||
}
|
||||
|
||||
if (encoding) {
|
||||
return { mimetype: 'text/plain', encoding };
|
||||
} else {
|
||||
// TODO@JOAO: read the setting OUTSIDE!
|
||||
return { mimetype: 'text/plain' };
|
||||
}
|
||||
}
|
||||
|
||||
async add(paths: string[]): Promise<void> {
|
||||
@@ -591,6 +697,7 @@ export class Repository {
|
||||
child.stdin.end(data, 'utf8');
|
||||
|
||||
const { exitCode, stdout } = await exec(child);
|
||||
const hash = stdout.toString('utf8');
|
||||
|
||||
if (exitCode) {
|
||||
throw new GitError({
|
||||
@@ -599,7 +706,7 @@ export class Repository {
|
||||
});
|
||||
}
|
||||
|
||||
await this.run(['update-index', '--cacheinfo', '100644', stdout, path]);
|
||||
await this.run(['update-index', '--cacheinfo', '100644', hash, path]);
|
||||
}
|
||||
|
||||
async checkout(treeish: string, paths: string[]): Promise<void> {
|
||||
@@ -680,6 +787,11 @@ export class Repository {
|
||||
await this.run(args);
|
||||
}
|
||||
|
||||
async renameBranch(name: string): Promise<void> {
|
||||
const args = ['branch', '-m', name];
|
||||
await this.run(args);
|
||||
}
|
||||
|
||||
async merge(ref: string): Promise<void> {
|
||||
const args = ['merge', ref];
|
||||
|
||||
@@ -847,10 +959,14 @@ export class Repository {
|
||||
}
|
||||
}
|
||||
|
||||
async createStash(message?: string): Promise<void> {
|
||||
async createStash(message?: string, includeUntracked?: boolean): Promise<void> {
|
||||
try {
|
||||
const args = ['stash', 'save'];
|
||||
|
||||
if (includeUntracked) {
|
||||
args.push('-u');
|
||||
}
|
||||
|
||||
if (message) {
|
||||
args.push('--', message);
|
||||
}
|
||||
@@ -869,7 +985,7 @@ export class Repository {
|
||||
try {
|
||||
const args = ['stash', 'pop'];
|
||||
|
||||
if (typeof index === 'string') {
|
||||
if (typeof index === 'number') {
|
||||
args.push(`stash@{${index}}`);
|
||||
}
|
||||
|
||||
@@ -891,7 +1007,7 @@ export class Repository {
|
||||
const env = { GIT_OPTIONAL_LOCKS: '0' };
|
||||
const child = this.stream(['status', '-z', '-u'], { env });
|
||||
|
||||
const onExit = exitCode => {
|
||||
const onExit = (exitCode: number) => {
|
||||
if (exitCode !== 0) {
|
||||
const stderr = stderrData.join('');
|
||||
return e(new GitError({
|
||||
@@ -909,12 +1025,12 @@ export class Repository {
|
||||
const onStdoutData = (raw: string) => {
|
||||
parser.update(raw);
|
||||
|
||||
if (parser.status.length > 5000) {
|
||||
if (parser.status.length > limit) {
|
||||
child.removeListener('exit', onExit);
|
||||
child.stdout.removeListener('data', onStdoutData);
|
||||
child.kill();
|
||||
|
||||
c({ status: parser.status.slice(0, 5000), didHitLimit: true });
|
||||
c({ status: parser.status.slice(0, limit), didHitLimit: true });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -953,7 +1069,7 @@ export class Repository {
|
||||
async getRefs(): Promise<Ref[]> {
|
||||
const result = await this.run(['for-each-ref', '--format', '%(refname) %(objectname)']);
|
||||
|
||||
const fn = (line): Ref | null => {
|
||||
const fn = (line: string): Ref | null => {
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
if (match = /^refs\/heads\/([^ ]+) ([0-9a-f]{40})$/.exec(line)) {
|
||||
@@ -978,7 +1094,7 @@ export class Repository {
|
||||
const regex = /^stash@{(\d+)}:(.+)$/;
|
||||
const rawStashes = result.stdout.trim().split('\n')
|
||||
.filter(b => !!b)
|
||||
.map(line => regex.exec(line))
|
||||
.map(line => regex.exec(line) as RegExpExecArray)
|
||||
.filter(g => !!g)
|
||||
.map(([, index, description]: RegExpExecArray) => ({ index: parseInt(index), description }));
|
||||
|
||||
@@ -990,7 +1106,7 @@ export class Repository {
|
||||
const regex = /^([^\s]+)\s+([^\s]+)\s/;
|
||||
const rawRemotes = result.stdout.trim().split('\n')
|
||||
.filter(b => !!b)
|
||||
.map(line => regex.exec(line))
|
||||
.map(line => regex.exec(line) as RegExpExecArray)
|
||||
.filter(g => !!g)
|
||||
.map((groups: RegExpExecArray) => ({ name: groups[1], url: groups[2] }));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user