1
1
mirror of https://github.com/kahole/edamagit.git synced 2024-09-11 07:15:31 +03:00

move away from private vscode apis by absorbing their code (fixes #237)

This commit is contained in:
kahole 2022-12-07 17:22:26 +01:00
parent 2df2c6e9cd
commit 02841c5264
11 changed files with 590 additions and 66 deletions

79
package-lock.json generated
View File

@ -6,17 +6,20 @@
"packages": {
"": {
"name": "magit",
"version": "0.6.34",
"version": "0.6.35",
"license": "MIT",
"dependencies": {
"@vscode/iconv-lite-umd": "^0.7.0",
"date-fns": "^2.16.1",
"jsonc-parser": "^3.0.0"
"jsonc-parser": "^3.0.0",
"which": "^3.0.0"
},
"devDependencies": {
"@types/glob": "^7.1.3",
"@types/mocha": "^8.2.0",
"@types/node": "^14.14.14",
"@types/vscode": "^1.50.0",
"@types/which": "^2.0.1",
"@typescript-eslint/eslint-plugin": "^4.10.0",
"@typescript-eslint/parser": "^4.10.0",
"eslint": "^7.16.0",
@ -248,6 +251,12 @@
"integrity": "sha512-Kt3bvWzAvvF/WH9YEcrCICDp0Z7aHhJGhLJ1BxeyNP6yRjonWqWnAIh35/pXAjswAnWOABrYlF7SwXR9+1nnLA==",
"dev": true
},
"node_modules/@types/which": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.1.tgz",
"integrity": "sha512-Jjakcv8Roqtio6w1gr0D7y6twbhx6gGgFGF5BLwajPpnOIOxFkakFhCq+LmyyeAz7BX6ULrjBOxdKaCDy+4+dQ==",
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "4.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.10.0.tgz",
@ -435,6 +444,11 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@vscode/iconv-lite-umd": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz",
"integrity": "sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg=="
},
"node_modules/@webassemblyjs/ast": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.1.tgz",
@ -1065,6 +1079,21 @@
"node": ">= 8"
}
},
"node_modules/cross-spawn/node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/date-fns": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz",
@ -2224,8 +2253,7 @@
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"dev": true
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
},
"node_modules/jest-worker": {
"version": "26.6.2",
@ -3887,18 +3915,17 @@
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/which/-/which-3.0.0.tgz",
"integrity": "sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ==",
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
"node-which": "bin/which.js"
},
"engines": {
"node": ">= 8"
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/word-wrap": {
@ -4261,6 +4288,12 @@
"integrity": "sha512-Kt3bvWzAvvF/WH9YEcrCICDp0Z7aHhJGhLJ1BxeyNP6yRjonWqWnAIh35/pXAjswAnWOABrYlF7SwXR9+1nnLA==",
"dev": true
},
"@types/which": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.1.tgz",
"integrity": "sha512-Jjakcv8Roqtio6w1gr0D7y6twbhx6gGgFGF5BLwajPpnOIOxFkakFhCq+LmyyeAz7BX6ULrjBOxdKaCDy+4+dQ==",
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "4.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.10.0.tgz",
@ -4366,6 +4399,11 @@
"eslint-visitor-keys": "^2.0.0"
}
},
"@vscode/iconv-lite-umd": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz",
"integrity": "sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg=="
},
"@webassemblyjs/ast": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.1.tgz",
@ -4892,6 +4930,17 @@
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
},
"dependencies": {
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
}
}
},
"date-fns": {
@ -5751,8 +5800,7 @@
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"dev": true
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
},
"jest-worker": {
"version": "26.6.2",
@ -6979,10 +7027,9 @@
}
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/which/-/which-3.0.0.tgz",
"integrity": "sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ==",
"requires": {
"isexe": "^2.0.0"
}

View File

@ -739,6 +739,7 @@
"@types/mocha": "^8.2.0",
"@types/node": "^14.14.14",
"@types/vscode": "^1.50.0",
"@types/which": "^2.0.1",
"@typescript-eslint/eslint-plugin": "^4.10.0",
"@typescript-eslint/parser": "^4.10.0",
"eslint": "^7.16.0",
@ -752,7 +753,9 @@
"webpack-cli": "^4.2.0"
},
"dependencies": {
"@vscode/iconv-lite-umd": "^0.7.0",
"date-fns": "^2.16.1",
"jsonc-parser": "^3.0.0"
"jsonc-parser": "^3.0.0",
"which": "^3.0.0"
}
}

View File

@ -3,7 +3,7 @@ import { MenuUtil, MenuState } from '../menu/menu';
import { window, workspace, Uri } from 'vscode';
import { gitRun, LogLevel } from '../utils/gitRawRunner';
import * as ProcessCommands from './processCommands';
import { SpawnOptions } from 'child_process';
import { SpawnOptions } from '../utils/commandRunner/command';
const runningMenu = {
title: 'Running',

View File

@ -1,41 +0,0 @@
import * as cp from 'child_process';
// This refers to Repository in
// vscode/extension/git/src/git.ts
interface BaseGitRepository {
exec?(args: string[], options?: SpawnOptions): Promise<IExecutionResult<string>>;
}
// This refers to the Repository in
// vscode/extension/git/src/repository.ts
interface BaseRepository {
repository: BaseGitRepository;
}
// This refers to ApiRepository from
// vscode/extension/git/src/api/api1.ts
declare module '../typings/git' {
export interface Repository {
// Breaking change in VSCode: https://github.com/microsoft/vscode/pull/154555/files#diff-b7c16e46aefbf6182f8be03b099e5c407da09bd345ff2908abddd6bfe90c34aaL65-R65
// Going from this
readonly _repository: BaseRepository;
// to this:
readonly repository: BaseRepository;
}
}
// types from /extensions/git/src/git.ts
export interface IExecutionResult<T extends string | Buffer> {
exitCode: number;
stdout: T;
stderr: string;
}
export interface SpawnOptions extends cp.SpawnOptions {
input?: string;
encoding?: string;
log?: boolean;
// cancellationToken?: CancellationToken;
// onSpawn?: (childProcess: cp.ChildProcess) => void;
}

View File

@ -0,0 +1,11 @@
In this folder the code from https://github.com/microsoft/vscode/tree/main/extensions/git has been used. Some of it has been refactored.
For the code in this folder, i.e. `commandRunnder`, with the following files:
- `command.ts`
- `disposable.ts`
- `findGit.ts`
the follwing applies:
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

View File

@ -0,0 +1,373 @@
import * as cp from 'child_process';
import { CancellationToken, Disposable, Event, Uri } from 'vscode';
import { findGit } from './findGit';
import * as iconv from '@vscode/iconv-lite-umd';
import { dispose, IDisposable, toDisposable } from './disposable';
const canceledName = 'Canceled';
class CancellationError extends Error {
constructor() {
super(canceledName);
this.name = this.message;
}
}
export interface SpawnOptions extends Omit<cp.SpawnOptions, 'cwd'> {
input?: string;
encoding?: string;
log?: boolean;
cancellationToken?: CancellationToken;
onSpawn?: (childProcess: cp.ChildProcess) => void;
cwd?: string | Uri;
}
interface IGitErrorData {
error?: Error;
message?: string;
stdout?: string;
stderr?: string;
exitCode?: number;
gitErrorCode?: string;
gitCommand?: string;
gitArgs?: string[];
}
class GitError {
error?: Error;
message: string;
stdout?: string;
stderr?: string;
exitCode?: number;
gitErrorCode?: string;
gitCommand?: string;
gitArgs?: string[];
constructor(data: IGitErrorData) {
if (data.error) {
this.error = data.error;
this.message = data.error.message;
} else {
this.error = undefined;
this.message = '';
}
this.message = this.message || data.message || 'Git error';
this.stdout = data.stdout;
this.stderr = data.stderr;
this.exitCode = data.exitCode;
this.gitErrorCode = data.gitErrorCode;
this.gitCommand = data.gitCommand;
this.gitArgs = data.gitArgs;
}
toString(): string {
let result = this.message + ' ' + JSON.stringify({
exitCode: this.exitCode,
gitErrorCode: this.gitErrorCode,
gitCommand: this.gitCommand,
stdout: this.stdout,
stderr: this.stderr
}, null, 2);
if (this.error) {
result += (<any>this.error).stack;
}
return result;
}
}
export interface IExecutionResult<T extends string | Buffer> {
exitCode: number;
stdout: T;
stderr: string;
}
export function cpErrorHandler(cb: (reason?: any) => void): (reason?: any) => void {
return err => {
if (/ENOENT/.test(err.message)) {
err = new GitError({
error: err,
message: 'Failed to execute git (ENOENT)',
gitErrorCode: GitErrorCodes.NotAGitRepository
});
}
cb(err);
};
}
export async function run(args: string[], options: SpawnOptions = {}): Promise<IExecutionResult<string>> {
const res = await _exec(args, options);
return {
...res,
stdout: res.stdout.toString()
};
}
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.' });
}
if (cancellationToken && cancellationToken.isCancellationRequested) {
throw new CancellationError();
}
const disposables: IDisposable[] = [];
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: (...args: any[]) => void) => {
ee.on(name, fn);
disposables.push(toDisposable(() => ee.removeListener(name, fn)));
};
let result = Promise.all<any>([
new Promise<number>((c, e) => {
once(child, 'error', cpErrorHandler(e));
once(child, 'exit', c);
}),
new Promise<Buffer>(c => {
const buffers: Buffer[] = [];
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: Buffer) => buffers.push(b));
once(child.stderr!, 'close', () => c(Buffer.concat(buffers).toString('utf8')));
})
]) as Promise<[number, Buffer, string]>;
if (cancellationToken) {
const cancellationPromise = new Promise<[number, Buffer, string]>((_, e) => {
onceEvent(cancellationToken.onCancellationRequested)(() => {
try {
child.kill();
} catch (err) {
// noop
}
e(new CancellationError());
});
});
result = Promise.race([result, cancellationPromise]);
}
try {
const [exitCode, stdout, stderr] = await result;
return { exitCode, stdout, stderr };
} finally {
dispose(disposables);
}
}
async function _exec(args: string[], options: SpawnOptions = {}): Promise<IExecutionResult<string>> {
const git = await findGit([], () => true);
const child = spawn(git.path, args, options);
// options.onSpawn?.(child);
if (options.input) {
child.stdin!.end(options.input, 'utf8');
}
const startExec = Date.now();
let bufferResult: IExecutionResult<Buffer>;
try {
bufferResult = await exec(child, options.cancellationToken);
} catch (ex) {
if (ex instanceof CancellationError) {
// this.log(`> git ${args.join(' ')} [${Date.now() - startExec}ms] (cancelled)\n`);
}
throw ex;
}
if (options.log !== false) {
// command
// this.log(`> git ${args.join(' ')} [${Date.now() - startExec}ms]\n`);
// stdout
// if (bufferResult.stdout.length > 0 && args.find(a => this.commandsToLog.includes(a))) {
// this.log(`${bufferResult.stdout}\n`);
// }
// stderr
if (bufferResult.stderr.length > 0) {
// this.log(`${bufferResult.stderr}\n`);
}
}
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,
exitCode: result.exitCode,
gitErrorCode: getGitErrorCode(result.stderr),
gitCommand: args[0],
gitArgs: args
}));
}
return result;
}
// https://github.com/microsoft/vscode/issues/89373
// https://github.com/git-for-windows/git/issues/2478
function sanitizePath(path: string): string {
return path.replace(/^([a-z]):\\/i, (_, letter) => `${letter.toUpperCase()}:\\`);
}
function spawn(path: string, args: string[], options: SpawnOptions = {}): cp.ChildProcess {
if (!options) {
options = {};
}
if (!options.stdio && !options.input) {
options.stdio = ['ignore', null, null]; // Unless provided, ignore stdin and leave default streams for stdout and stderr
}
options.env = Object.assign({}, process.env, options.env || {}, {
VSCODE_GIT_COMMAND: args[0],
LC_ALL: 'en_US.UTF-8',
LANG: 'en_US.UTF-8',
GIT_PAGER: 'cat',
// TODO: add ask pass functionality
// GIT_ASKPASS: path.join(__dirname, 'askpass.sh'),
// VSCODE_GIT_ASKPASS_NODE: process.execPath,
// VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js')
});
const santizedOptions = sanitizeOptions(options);
return cp.spawn(path, args, santizedOptions);
}
function sanitizeOptions(options: SpawnOptions): cp.SpawnOptions {
let cwd = getCwd(options);
if (cwd) {
cwd = sanitizePath(cwd);
}
return {
...options,
cwd: cwd
};
}
function getCwd(options: SpawnOptions): string | undefined {
const cwd = options.cwd;
if (typeof cwd === 'undefined' || typeof cwd === 'string') {
return cwd;
}
if (cwd.scheme) {
return cwd.fsPath;
}
return undefined;
}
function onceEvent<T>(event: Event<T>): Event<T> {
return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => {
const result = event(e => {
result.dispose();
return listener.call(thisArgs, e);
}, null, disposables);
return result;
};
}
const enum GitErrorCodes {
BadConfigFile = 'BadConfigFile',
AuthenticationFailed = 'AuthenticationFailed',
NoUserNameConfigured = 'NoUserNameConfigured',
NoUserEmailConfigured = 'NoUserEmailConfigured',
NoRemoteRepositorySpecified = 'NoRemoteRepositorySpecified',
NotAGitRepository = 'NotAGitRepository',
NotAtRepositoryRoot = 'NotAtRepositoryRoot',
Conflict = 'Conflict',
StashConflict = 'StashConflict',
UnmergedChanges = 'UnmergedChanges',
PushRejected = 'PushRejected',
RemoteConnectionError = 'RemoteConnectionError',
DirtyWorkTree = 'DirtyWorkTree',
CantOpenResource = 'CantOpenResource',
GitNotFound = 'GitNotFound',
CantCreatePipe = 'CantCreatePipe',
PermissionDenied = 'PermissionDenied',
CantAccessRemote = 'CantAccessRemote',
RepositoryNotFound = 'RepositoryNotFound',
RepositoryIsLocked = 'RepositoryIsLocked',
BranchNotFullyMerged = 'BranchNotFullyMerged',
NoRemoteReference = 'NoRemoteReference',
InvalidBranchName = 'InvalidBranchName',
BranchAlreadyExists = 'BranchAlreadyExists',
NoLocalChanges = 'NoLocalChanges',
NoStashFound = 'NoStashFound',
LocalChangesOverwritten = 'LocalChangesOverwritten',
NoUpstreamBranch = 'NoUpstreamBranch',
IsInSubmodule = 'IsInSubmodule',
WrongCase = 'WrongCase',
CantLockRef = 'CantLockRef',
CantRebaseMultipleBranches = 'CantRebaseMultipleBranches',
PatchDoesNotApply = 'PatchDoesNotApply',
NoPathFound = 'NoPathFound',
UnknownPath = 'UnknownPath',
EmptyCommitMessage = 'EmptyCommitMessage',
BranchFastForwardRejected = 'BranchFastForwardRejected',
TagConflict = 'TagConflict'
}
function getGitErrorCode(stderr: string): string | undefined {
if (/Another git process seems to be running in this repository|If no other git process is currently running/.test(stderr)) {
return GitErrorCodes.RepositoryIsLocked;
} else if (/Authentication failed/i.test(stderr)) {
return GitErrorCodes.AuthenticationFailed;
} else if (/Not a git repository/i.test(stderr)) {
return GitErrorCodes.NotAGitRepository;
} else if (/bad config file/.test(stderr)) {
return GitErrorCodes.BadConfigFile;
} else if (/cannot make pipe for command substitution|cannot create standard input pipe/.test(stderr)) {
return GitErrorCodes.CantCreatePipe;
} else if (/Repository not found/.test(stderr)) {
return GitErrorCodes.RepositoryNotFound;
} else if (/unable to access/.test(stderr)) {
return GitErrorCodes.CantAccessRemote;
} else if (/branch '.+' is not fully merged/.test(stderr)) {
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;
} else if (/Please,? commit your changes or stash them/.test(stderr)) {
return GitErrorCodes.DirtyWorkTree;
}
return undefined;
}

View File

@ -0,0 +1,15 @@
export interface IDisposable {
dispose(): void;
}
export function dispose<T extends IDisposable>(disposables: T[]): T[] {
disposables.forEach(d => d.dispose());
return [];
}
export function toDisposable(dispose: () => void): IDisposable {
return { dispose };
}

View File

@ -0,0 +1,114 @@
import * as cp from 'child_process';
import { cpErrorHandler } from './command';
import path = require('path');
import * as which from 'which';
export interface IGit {
path: string;
version: string;
}
function parseVersion(raw: string): string {
return raw.replace(/^git version /, '');
}
function findSpecificGit(path: string, onValidate: (path: string) => boolean): Promise<IGit> {
return new Promise<IGit>((c, e) => {
if (!onValidate(path)) {
return e('git not found');
}
const buffers: Buffer[] = [];
const child = cp.spawn(path, ['--version']);
child.stdout.on('data', (b: Buffer) => buffers.push(b));
child.on('error', cpErrorHandler(e));
child.on('exit', code => code ? e(new Error('Not found')) : c({ path, version: parseVersion(Buffer.concat(buffers).toString('utf8').trim()) }));
});
}
function findGitDarwin(onValidate: (path: string) => boolean): Promise<IGit> {
return new Promise<IGit>((c, e) => {
cp.exec('which git', (err, gitPathBuffer) => {
if (err) {
return e('git not found');
}
const path = gitPathBuffer.toString().trim();
function getVersion(path: string) {
if (!onValidate(path)) {
return e('git not found');
}
// make sure git executes
cp.exec('git --version', (err, stdout) => {
if (err) {
return e('git not found');
}
return c({ path, version: parseVersion(stdout.trim()) });
});
}
if (path !== '/usr/bin/git') {
return getVersion(path);
}
// must check if XCode is installed
cp.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');
}
getVersion(path);
});
});
});
}
function findSystemGitWin32(base: string, onValidate: (path: string) => boolean): Promise<IGit> {
if (!base) {
return Promise.reject<IGit>('Not found');
}
return findSpecificGit(path.join(base, 'Git', 'cmd', 'git.exe'), onValidate);
}
function findGitWin32InPath(onValidate: (path: string) => boolean): Promise<IGit> {
const whichPromise = new Promise<string>((c, e) => which('git.exe', (err, path) => err ? e(err) : (path ? c(path) : e(new Error('not found')))));
return whichPromise.then(path => findSpecificGit(path, onValidate));
}
function findGitWin32(onValidate: (path: string) => boolean): Promise<IGit> {
return findSystemGitWin32(process.env['ProgramW6432'] as string, onValidate)
.then(undefined, () => findSystemGitWin32(process.env['ProgramFiles(x86)'] as string, onValidate))
.then(undefined, () => findSystemGitWin32(process.env['ProgramFiles'] as string, onValidate))
.then(undefined, () => findSystemGitWin32(path.join(process.env['LocalAppData'] as string, 'Programs'), onValidate))
.then(undefined, () => findGitWin32InPath(onValidate));
}
export async function findGit(hints: string[], onValidate: (path: string) => boolean): Promise<IGit> {
for (const hint of hints) {
try {
return await findSpecificGit(hint, onValidate);
} catch {
// noop
}
}
try {
switch (process.platform) {
case 'darwin': return await findGitDarwin(onValidate);
case 'win32': return await findGitWin32(onValidate);
default: return await findSpecificGit('git', onValidate);
}
} catch {
// noop
}
throw new Error('Git installation not found.');
}

View File

@ -1,6 +1,6 @@
import { processLog } from '../extension';
import { MagitProcessLogEntry } from '../models/magitProcessLogEntry';
import { IExecutionResult } from '../common/gitApiExtensions';
import { IExecutionResult } from './commandRunner/command';
export default class GitProcessLogger {

View File

@ -1,5 +1,5 @@
import { SpawnOptions } from '../common/gitApiExtensions';
import { Repository } from '../typings/git';
import { run, SpawnOptions } from './commandRunner/command';
import GitProcessLogger from './gitProcessLogger';
export enum LogLevel {
@ -16,9 +16,11 @@ export async function gitRun(repository: Repository, args: string[], spawnOption
}
try {
// Protect against coming breaking change in VSCode: https://github.com/microsoft/vscode/pull/154555/files#diff-b7c16e46aefbf6182f8be03b099e5c407da09bd345ff2908abddd6bfe90c34aaL65-R65
const baseRepository = repository._repository ?? repository.repository;
let result = await baseRepository.repository.exec!(args, spawnOptions);
let spawnOptionsWCwd = { ...spawnOptions };
if (!spawnOptionsWCwd.cwd) {
spawnOptionsWCwd.cwd = repository.rootUri;
}
let result = await run(args, spawnOptionsWCwd);
if (logLevel === LogLevel.Detailed && logEntry) {
GitProcessLogger.logGitResult(result, logEntry);
@ -31,4 +33,4 @@ export async function gitRun(repository: Repository, args: string[], spawnOption
}
throw error;
}
}
}

View File

@ -1,6 +1,6 @@
import { MagitRepository } from '../models/magitRepository';
import { IExecutionResult } from './commandRunner/command';
import { gitRun } from './gitRawRunner';
import { IExecutionResult } from '../common/gitApiExtensions';
export default class GitUtils {