Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion browser/src/components/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,13 @@ export class Header extends React.Component<HeaderProps, HeaderState> {
<a
className="hint--right hint--rounded hint--bounce"
aria-label="Open repository on Github"
href={selectedBranch.remote.replace(/\.git$/, '') + '/tree/' + encodeURI(selectedBranch.name)}
href={
selectedBranch.remote
.replace(/.*?github.com(\/|:)/, 'https://github.com/')
.replace(/.git?$/, '') +
'/tree/' +
encodeURI(selectedBranch.name)
}
>
<GoMarkGithub />
</a>
Expand Down
82 changes: 64 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
"*"
],
"main": "./dist/src/extension",
"capabilities": {
"virtualWorkspaces": false,
"untrustedWorkspaces": {
"supported": false
}
},
"contributes": {
"views": {
"explorer": [
Expand Down Expand Up @@ -513,7 +519,6 @@
"fs-extra": "^8.1.0",
"gravatar": "^1.8.1",
"hash.js": "^1.1.7",
"iconv-lite": "^0.5.1",
"inversify": "^5.0.1",
"query-string": "^6.13.7",
"reflect-metadata": "^0.1.12",
Expand Down
51 changes: 14 additions & 37 deletions src/adapter/exec/gitCommandExec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { spawn } from 'child_process';
import * as iconv from 'iconv-lite';
import { injectable, multiInject } from 'inversify';
import { Writable } from 'stream';
import { extensions } from 'vscode';
import { IGitCommandExecutor } from './types';
import { GitExtension, API } from '../repository/git.d';
import { StopWatch } from '../../common/stopWatch';
import { ILogService } from '../../common/types';

const DEFAULT_ENCODING = 'utf8';
const isWindows = /^win/.test(process.platform);

@injectable()
Expand All @@ -35,37 +32,23 @@ export class GitCommandExecutor implements IGitCommandExecutor {
});
this.gitExecutablePath = this.gitApi.then(api => api.git.path);
}
public exec(cwd: string, ...args: string[]): Promise<string>;
// tslint:disable-next-line:unified-signatures
public exec(options: { cwd: string; shell?: boolean }, ...args: string[]): Promise<string>;
public exec(options: { cwd: string; encoding: 'binary' }, destination: Writable, ...args: string[]): Promise<void>;

// tslint:disable-next-line:no-any
public async exec(options: any, ...args: any[]): Promise<any> {
public async exec(cwd: any, ...args: any[]): Promise<any> {
let gitPath = await this.gitExecutablePath;
gitPath = isWindows ? gitPath.replace(/\\/g, '/') : gitPath;
const childProcOptions = typeof options === 'string' ? { cwd: options, encoding: DEFAULT_ENCODING } : options;
if (typeof childProcOptions.encoding !== 'string' || childProcOptions.encoding.length === 0) {
childProcOptions.encoding = DEFAULT_ENCODING;
}
const binaryOuput = childProcOptions.encoding === 'binary';
const destination: Writable = binaryOuput ? args.shift() : undefined;
const gitPathCommand = childProcOptions.shell && gitPath.indexOf(' ') > 0 ? `"${gitPath}"` : gitPath;
const stopWatch = new StopWatch();
const gitShow = spawn(gitPathCommand, args, childProcOptions);

let stdout: Buffer = new Buffer('');
let stderr: Buffer = new Buffer('');
const stopWatch = new StopWatch();
const gitShow = spawn(gitPath, args, { cwd: cwd });

if (binaryOuput) {
gitShow.stdout.pipe(destination);
} else {
gitShow.stdout.on('data', data => {
stdout = Buffer.concat([stdout, data as Buffer]);
});
}
let stdout = '';
let stderr = '';

gitShow.stdout.on('data', data => {
stdout += data;
});
gitShow.stderr.on('data', data => {
stderr = Buffer.concat([stderr, data as Buffer]);
stderr += data;
});

return new Promise<any>((resolve, reject) => {
Expand All @@ -76,23 +59,17 @@ export class GitCommandExecutor implements IGitCommandExecutor {
});

if (code === 0) {
const stdOut = binaryOuput ? undefined : decode(stdout, childProcOptions.encoding);
this.loggers.forEach(logger => {
logger.trace(binaryOuput ? '<binary>' : stdout);
logger.trace(stdout);
});
resolve(stdOut);
resolve(stdout);
} else {
const stdErr = binaryOuput ? undefined : decode(stderr, childProcOptions.encoding);
this.loggers.forEach(logger => {
logger.error(stdErr);
logger.error(stderr);
});
reject({ code, error: stdErr });
reject({ code, error: stderr });
}
});
});
}
}

function decode(buffers: Buffer, encoding: string): string {
return iconv.decode(buffers, encoding);
}
9 changes: 1 addition & 8 deletions src/adapter/exec/types.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import { Writable } from 'stream';
import { API } from '../repository/git.d';

export const IGitCommandExecutor = Symbol.for('IGitCommandExecutor');

export interface IGitCommandExecutor {
readonly gitApi: Promise<API>;
exec(cwd: string, ...args: string[]): Promise<string>;
exec(options: { cwd: string; shell?: boolean }, ...args: string[]): Promise<string>;
exec(
options: { cwd: string; shell?: boolean; encoding: 'binary' },
destination: Writable,
...args: string[]
): Promise<void>;
exec(cwd: string, ...args: string[]): Promise<any>;
}
2 changes: 1 addition & 1 deletion src/adapter/repository/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class Git implements IGitService {
@inject(ILogParser) private logParser: ILogParser,
@inject(IGitArgsService) private gitArgsService: IGitArgsService,
) {
this.remotesService = new GitRemoteService(repo, this.gitCmdExecutor);
this.remotesService = new GitRemoteService(repo);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/adapter/repository/gitArgsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ export class GitArgsService implements IGitArgsService {
return ['shortlog', '-e', '-s', '-n', 'HEAD'];
}
public getDiffCommitWithNumStatArgs(hash1: string, hash2: string): string[] {
return ['diff', '--numstat', hash1, hash2];
return ['-c', 'core.quotePath=false', 'diff', '--numstat', hash1, hash2];
}
public getDiffCommitNameStatusArgs(hash1: string, hash2: string): string[] {
return ['diff', '--name-status', hash1, hash2];
return ['-c', 'core.quotePath=false', 'diff', '--name-status', hash1, hash2];
}
public getPreviousCommitHashForFileArgs(hash: string, file: string): string[] {
return ['log', '--format=%H-%h', `${hash}^1`, '-n', '1', '--', file];
Expand Down
30 changes: 1 addition & 29 deletions src/adapter/repository/gitRemoteService.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,12 @@
import { Repository } from './git.d';
import { IGitCommandExecutor } from '..';
import { GitOriginType } from '.';
import { captureTelemetry } from '../../common/telemetry';

export class GitRemoteService {
constructor(private readonly repo: Repository, private readonly gitCmdExecutor: IGitCommandExecutor) {}
constructor(private readonly repo: Repository) {}
private get currentBranch(): string {
return this.repo.state.HEAD!.name || '';
}

public async getBranchesConfiguredForPullForRemote(remoteName: string): Promise<string[]> {
const gitShowRemoteOutput = await this.gitCmdExecutor.exec(
this.repo.rootUri.fsPath,
...['remote', 'show', remoteName, '-n'],
);

const lines = gitShowRemoteOutput
.split(/\r?\n/g)
.map(line => line.trim())
.filter(line => line.length > 0);

const startLineIndex = lines.findIndex(line => line.startsWith('Local branches configured for'));
const endLineIndex = lines.findIndex(line => line.startsWith('Local ref configured for'));

if (startLineIndex === -1 || endLineIndex == -1) {
// TODO: Capture telemetry, something is wrong.
return [];
}
if (startLineIndex > endLineIndex) {
// TODO: Capture telemetry, something is wrong.
return [];
}

// Branch name is first word in the line
return lines.slice(startLineIndex + 1, endLineIndex).map(line => line.split(' ')[0]);
}
public async getOriginType(url?: string): Promise<GitOriginType | undefined> {
if (!url) {
url = await this.getOriginUrl();
Expand Down