Merge from vscode bead496a613e475819f89f08e9e882b841bc1fe8 (#14883)

* Merge from vscode bead496a613e475819f89f08e9e882b841bc1fe8

* Bump distro

* Upgrade GCC to 4.9 due to yarn install errors

* Update build image

* Fix bootstrap base url

* Bump distro

* Fix build errors

* Update source map file

* Disable checkbox for blocking migration issues (#15131)

* disable checkbox for blocking issues

* wip

* disable checkbox fixes

* fix strings

* Remove duplicate tsec command

* Default to off for tab color if settings not present

* re-skip failing tests

* Fix mocha error

* Bump sqlite version & fix notebooks search view

* Turn off esbuild warnings

* Update esbuild log level

* Fix overflowactionbar tests

* Fix ts-ignore in dropdown tests

* cleanup/fixes

* Fix hygiene

* Bundle in entire zone.js module

* Remove extra constructor param

* bump distro for web compile break

* bump distro for web compile break v2

* Undo log level change

* New distro

* Fix integration test scripts

* remove the "no yarn.lock changes" workflow

* fix scripts v2

* Update unit test scripts

* Ensure ads-kerberos2 updates in .vscodeignore

* Try fix unit tests

* Upload crash reports

* remove nogpu

* always upload crashes

* Use bash script

* Consolidate data/ext dir names

* Create in tmp directory

Co-authored-by: chlafreniere <hichise@gmail.com>
Co-authored-by: Christopher Suh <chsuh@microsoft.com>
Co-authored-by: chgagnon <chgagnon@microsoft.com>
This commit is contained in:
Karl Burtram
2021-04-27 14:01:59 -07:00
committed by GitHub
parent 7e1c0076ba
commit 867a963882
1817 changed files with 81812 additions and 50843 deletions

View File

@@ -5,7 +5,7 @@
import { Model } from '../model';
import { Repository as BaseRepository, Resource } from '../repository';
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, RemoteSourceProvider, CredentialsProvider, BranchQuery, PushErrorHandler } from './git';
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, RemoteSourceProvider, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent } from './git';
import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands } from 'vscode';
import { mapEvent } from '../util';
import { toGitUri } from '../uri';
@@ -201,8 +201,8 @@ export class ApiRepository implements Repository {
return this._repository.pull(undefined, unshallow);
}
push(remoteName?: string, branchName?: string, setUpstream: boolean = false): Promise<void> {
return this._repository.pushTo(remoteName, branchName, setUpstream);
push(remoteName?: string, branchName?: string, setUpstream: boolean = false, force?: ForcePushMode): Promise<void> {
return this._repository.pushTo(remoteName, branchName, setUpstream, force);
}
blame(path: string): Promise<string> {
@@ -237,6 +237,10 @@ export class ApiImpl implements API {
return this._model.onDidChangeState;
}
get onDidPublish(): Event<PublishEvent> {
return this._model.onDidPublish;
}
get onDidOpenRepository(): Event<Repository> {
return mapEvent(this._model.onDidOpenRepository, r => new ApiRepository(r));
}
@@ -265,6 +269,11 @@ export class ApiImpl implements API {
return this.getRepository(root) || null;
}
async openRepository(root: Uri): Promise<Repository | null> {
await this._model.openRepository(root.fsPath);
return this.getRepository(root) || null;
}
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable {
return this._model.registerRemoteSourceProvider(provider);
}

View File

@@ -14,6 +14,11 @@ export interface InputBox {
value: string;
}
export const enum ForcePushMode {
Force,
ForceWithLease
}
export const enum RefType {
Head,
RemoteHead,
@@ -131,6 +136,7 @@ export interface CommitOptions {
signCommit?: boolean;
empty?: boolean;
noVerify?: boolean;
requireUserConfig?: boolean;
}
export interface BranchQuery {
@@ -193,7 +199,7 @@ export interface Repository {
fetch(remote?: string, ref?: string, depth?: number): Promise<void>;
pull(unshallow?: boolean): Promise<void>;
push(remoteName?: string, branchName?: string, setUpstream?: boolean): Promise<void>;
push(remoteName?: string, branchName?: string, setUpstream?: boolean, force?: ForcePushMode): Promise<void>;
blame(path: string): Promise<string>;
log(options?: LogOptions): Promise<Commit[]>;
@@ -231,9 +237,15 @@ export interface PushErrorHandler {
export type APIState = 'uninitialized' | 'initialized';
export interface PublishEvent {
repository: Repository;
branch?: string;
}
export interface API {
readonly state: APIState;
readonly onDidChangeState: Event<APIState>;
readonly onDidPublish: Event<PublishEvent>;
readonly git: Git;
readonly repositories: Repository[];
readonly onDidOpenRepository: Event<Repository>;
@@ -242,6 +254,7 @@ export interface API {
toGitUri(uri: Uri, ref: string): Uri;
getRepository(uri: Uri): Repository | null;
init(root: Uri): Promise<Repository | null>;
openRepository(root: Uri): Promise<Repository | null>
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
registerCredentialsProvider(provider: CredentialsProvider): Disposable;

View File

@@ -30,7 +30,7 @@ function main(argv: string[]): void {
const output = process.env['VSCODE_GIT_ASKPASS_PIPE'] as string;
const request = argv[2];
const host = argv[4].substring(1, argv[4].length - 2);
const host = argv[4].replace(/^["']+|["']+$/g, '');
const ipcClient = new IPCClient('askpass');
ipcClient.call({ request, host }).then(res => {

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { workspace, Disposable, EventEmitter, Memento, window, MessageItem, ConfigurationTarget, Uri } from 'vscode';
import { workspace, Disposable, EventEmitter, Memento, window, MessageItem, ConfigurationTarget, Uri, ConfigurationChangeEvent } from 'vscode';
import { Repository, Operation } from './repository';
import { eventToPromise, filterEvent, onceEvent } from './util';
import * as nls from 'vscode-nls';
@@ -23,6 +23,7 @@ export class AutoFetcher {
private onDidChange = this._onDidChange.event;
private _enabled: boolean = false;
private _fetchAll: boolean = false;
get enabled(): boolean { return this._enabled; }
set enabled(enabled: boolean) { this._enabled = enabled; this._onDidChange.fire(enabled); }
@@ -68,13 +69,26 @@ export class AutoFetcher {
this.globalState.update(AutoFetcher.DidInformUser, true);
}
private onConfiguration(): void {
const gitConfig = workspace.getConfiguration('git', Uri.file(this.repository.root));
private onConfiguration(e?: ConfigurationChangeEvent): void {
if (e !== undefined && !e.affectsConfiguration('git.autofetch')) {
return;
}
if (gitConfig.get<boolean>('autofetch') === false) {
this.disable();
} else {
this.enable();
const gitConfig = workspace.getConfiguration('git', Uri.file(this.repository.root));
switch (gitConfig.get<boolean | 'all'>('autofetch')) {
case true:
this._fetchAll = false;
this.enable();
break;
case 'all':
this._fetchAll = true;
this.enable();
break;
case false:
default:
this._fetchAll = false;
this.disable();
break;
}
}
@@ -100,7 +114,11 @@ export class AutoFetcher {
}
try {
await this.repository.fetchDefault({ silent: true });
if (this._fetchAll) {
await this.repository.fetchAll();
} else {
await this.repository.fetchDefault({ silent: true });
}
} catch (err) {
if (err.gitErrorCode === GitErrorCodes.AuthenticationFailed) {
this.disable();

View File

@@ -8,8 +8,8 @@ import * as path from 'path';
import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider } from 'vscode';
import TelemetryReporter from 'vscode-extension-telemetry';
import * as nls from 'vscode-nls';
import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourceProvider } from './api/git';
import { ForcePushMode, Git, Stash } from './git';
import { Branch, ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourceProvider } from './api/git';
import { Git, Stash } from './git';
import { Model } from './model';
import { Repository, Resource, ResourceGroupType } from './repository';
import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging';
@@ -54,7 +54,7 @@ class CheckoutRemoteHeadItem extends CheckoutItem {
return localize('remote branch at', "Remote branch at {0}", this.shortCommit);
}
async run(repository: Repository): Promise<void> {
async run(repository: Repository, opts?: { detached?: boolean }): Promise<void> {
if (!this.ref.name) {
return;
}
@@ -62,9 +62,9 @@ class CheckoutRemoteHeadItem extends CheckoutItem {
const branches = await repository.findTrackingBranches(this.ref.name);
if (branches.length > 0) {
await repository.checkout(branches[0].name!);
await repository.checkout(branches[0].name!, opts);
} else {
await repository.checkoutTracking(this.ref.name);
await repository.checkoutTracking(this.ref.name, opts);
}
}
}
@@ -277,6 +277,12 @@ interface PushOptions {
pushType: PushType;
forcePush?: boolean;
silent?: boolean;
pushTo?: {
remote?: string;
refspec?: string;
setUpstream?: boolean;
}
}
class CommandErrorOutputTextDocumentContentProvider implements TextDocumentContentProvider {
@@ -366,6 +372,24 @@ export class CommandCenter {
await resource.open();
}
@command('git.openAllChanges', { repository: true })
async openChanges(repository: Repository): Promise<void> {
for (const resource of [...repository.workingTreeGroup.resourceStates, ...repository.untrackedGroup.resourceStates]) {
if (
resource.type === Status.DELETED || resource.type === Status.DELETED_BY_THEM ||
resource.type === Status.DELETED_BY_US || resource.type === Status.BOTH_DELETED
) {
continue;
}
void commands.executeCommand(
'vscode.open',
resource.resourceUri,
{ background: true, preview: false, }
);
}
}
async cloneRepository(url?: string, parentPath?: string, options: { recursive?: boolean } = {}): Promise<void> {
if (!url || typeof url !== 'string') {
url = await pickRemoteSource(this.model, {
@@ -500,12 +524,12 @@ export class CommandCenter {
@command('git.clone')
async clone(url?: string, parentPath?: string): Promise<void> {
this.cloneRepository(url, parentPath);
await this.cloneRepository(url, parentPath);
}
@command('git.cloneRecursive')
async cloneRecursive(url?: string, parentPath?: string): Promise<void> {
this.cloneRepository(url, parentPath, { recursive: true });
await this.cloneRepository(url, parentPath, { recursive: true });
}
@command('git.init')
@@ -1323,8 +1347,8 @@ export class CommandCenter {
const enableSmartCommit = config.get<boolean>('enableSmartCommit') === true;
const enableCommitSigning = config.get<boolean>('enableCommitSigning') === true;
const noStagedChanges = repository.indexGroup.resourceStates.length === 0;
const noUnstagedChanges = repository.workingTreeGroup.resourceStates.length === 0;
let noStagedChanges = repository.indexGroup.resourceStates.length === 0;
let noUnstagedChanges = repository.workingTreeGroup.resourceStates.length === 0;
if (promptToSaveFilesBeforeCommit !== 'never') {
let documents = workspace.textDocuments
@@ -1346,6 +1370,9 @@ export class CommandCenter {
if (pick === saveAndCommit) {
await Promise.all(documents.map(d => d.save()));
await repository.add(documents.map(d => d.uri));
noStagedChanges = repository.indexGroup.resourceStates.length === 0;
noUnstagedChanges = repository.workingTreeGroup.resourceStates.length === 0;
} else if (pick !== commit) {
return false; // do not commit on cancel
}
@@ -2113,23 +2140,27 @@ export class CommandCenter {
}
} else {
const branchName = repository.HEAD.name;
const addRemote = new AddRemoteItem(this);
const picks = [...remotes.filter(r => r.pushUrl !== undefined).map(r => ({ label: r.name, description: r.pushUrl })), addRemote];
const placeHolder = localize('pick remote', "Pick a remote to publish the branch '{0}' to:", branchName);
const choice = await window.showQuickPick(picks, { placeHolder });
if (!pushOptions.pushTo?.remote) {
const addRemote = new AddRemoteItem(this);
const picks = [...remotes.filter(r => r.pushUrl !== undefined).map(r => ({ label: r.name, description: r.pushUrl })), addRemote];
const placeHolder = localize('pick remote', "Pick a remote to publish the branch '{0}' to:", branchName);
const choice = await window.showQuickPick(picks, { placeHolder });
if (!choice) {
return;
}
if (!choice) {
return;
}
if (choice === addRemote) {
const newRemote = await this.addRemote(repository);
if (choice === addRemote) {
const newRemote = await this.addRemote(repository);
if (newRemote) {
await repository.pushTo(newRemote, branchName, undefined, forcePushMode);
if (newRemote) {
await repository.pushTo(newRemote, branchName, undefined, forcePushMode);
}
} else {
await repository.pushTo(choice.label, branchName, undefined, forcePushMode);
}
} else {
await repository.pushTo(choice.label, branchName, undefined, forcePushMode);
await repository.pushTo(pushOptions.pushTo.remote, pushOptions.pushTo.refspec || branchName, pushOptions.pushTo.setUpstream, forcePushMode);
}
}
}
@@ -2170,13 +2201,13 @@ export class CommandCenter {
}
@command('git.pushTo', { repository: true })
async pushTo(repository: Repository): Promise<void> {
await this._push(repository, { pushType: PushType.PushTo });
async pushTo(repository: Repository, remote?: string, refspec?: string, setUpstream?: boolean): Promise<void> {
await this._push(repository, { pushType: PushType.PushTo, pushTo: { remote: remote, refspec: refspec, setUpstream: setUpstream } });
}
@command('git.pushToForce', { repository: true })
async pushToForce(repository: Repository): Promise<void> {
await this._push(repository, { pushType: PushType.PushTo, forcePush: true });
async pushToForce(repository: Repository, remote?: string, refspec?: string, setUpstream?: boolean): Promise<void> {
await this._push(repository, { pushType: PushType.PushTo, pushTo: { remote: remote, refspec: refspec, setUpstream: setUpstream }, forcePush: true });
}
@command('git.pushTags', { repository: true })
@@ -2356,11 +2387,16 @@ export class CommandCenter {
}
await provider.publishRepository!(new ApiRepository(repository));
this.model.firePublishEvent(repository, branchName);
return;
}
if (remotes.length === 1) {
return await repository.pushTo(remotes[0].name, branchName, true);
await repository.pushTo(remotes[0].name, branchName, true);
this.model.firePublishEvent(repository, branchName);
return;
}
const addRemote = new AddRemoteItem(this);
@@ -2377,9 +2413,13 @@ export class CommandCenter {
if (newRemote) {
await repository.pushTo(newRemote, branchName, true);
this.model.firePublishEvent(repository, branchName);
}
} else {
await repository.pushTo(choice.label, branchName, true);
this.model.firePublishEvent(repository, branchName);
}
}
@@ -2649,7 +2689,7 @@ export class CommandCenter {
return Promise.resolve();
}
return Promise.resolve(method.apply(this, [repository, ...args]));
return Promise.resolve(method.apply(this, [repository, ...args.slice(1)]));
});
}

View File

@@ -14,7 +14,7 @@ import * as filetype from 'file-type';
import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter } from './util';
import { CancellationToken, Progress, Uri } from 'vscode';
import { detectEncoding } from './encoding';
import { Ref, RefType, Branch, Remote, GitErrorCodes, LogOptions, Change, Status, CommitOptions, BranchQuery } from './api/git';
import { Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, BranchQuery } from './api/git';
import * as byline from 'byline';
import { StringDecoder } from 'string_decoder';
@@ -311,6 +311,7 @@ export class GitError {
export interface IGitOptions {
gitPath: string;
userAgent: string;
version: string;
env?: any;
}
@@ -362,6 +363,8 @@ export interface ICloneOptions {
export class Git {
readonly path: string;
readonly userAgent: string;
readonly version: string;
private env: any;
private _onOutput = new EventEmitter();
@@ -369,6 +372,8 @@ export class Git {
constructor(options: IGitOptions) {
this.path = options.gitPath;
this.version = options.version;
this.userAgent = options.userAgent;
this.env = options.env || {};
}
@@ -427,7 +432,11 @@ export class Git {
if (options.recursive) {
command.push('--recursive');
}
await this.exec(options.parentPath, command, { cancellationToken, onSpawn });
await this.exec(options.parentPath, command, {
cancellationToken,
env: { 'GIT_HTTP_USER_AGENT': this.userAgent },
onSpawn,
});
} catch (err) {
if (err.stderr) {
err.stderr = err.stderr.replace(/^Cloning.+$/m, '').trim();
@@ -458,7 +467,7 @@ export class Git {
try {
const networkPath = await new Promise<string | undefined>(resolve =>
realpath.native(`${letter}:`, { encoding: 'utf8' }, (err, resolvedPath) =>
realpath.native(`${letter}:\\`, { encoding: 'utf8' }, (err, resolvedPath) =>
resolve(err !== null ? undefined : resolvedPath),
),
);
@@ -798,11 +807,6 @@ export interface PullOptions {
readonly cancellationToken?: CancellationToken;
}
export enum ForcePushMode {
Force,
ForceWithLease
}
export class Repository {
constructor(
@@ -819,8 +823,7 @@ export class Repository {
return this.repositoryRoot;
}
// TODO@Joao: rename to exec
async run(args: string[], options: SpawnOptions = {}): Promise<IExecutionResult<string>> {
async exec(args: string[], options: SpawnOptions = {}): Promise<IExecutionResult<string>> {
return await this.git.exec(this.repositoryRoot, args, options);
}
@@ -845,7 +848,7 @@ export class Repository {
args.push(value);
}
const result = await this.run(args, options);
const result = await this.exec(args, options);
return result.stdout.trim();
}
@@ -858,7 +861,7 @@ export class Repository {
args.push('-l');
const result = await this.run(args);
const result = await this.exec(args);
const lines = result.stdout.trim().split(/\r|\r\n|\n/);
return lines.map(entry => {
@@ -874,7 +877,7 @@ export class Repository {
args.push(options.path);
}
const result = await this.run(args);
const result = await this.exec(args);
if (result.exitCode) {
// An empty repo
return [];
@@ -905,7 +908,7 @@ export class Repository {
args.push('--', uri.fsPath);
const result = await this.run(args);
const result = await this.exec(args);
if (result.exitCode) {
// No file history, e.g. a new file or untracked
return [];
@@ -960,7 +963,7 @@ export class Repository {
}
const { mode, object } = elements[0];
const catFile = await this.run(['cat-file', '-s', object]);
const catFile = await this.exec(['cat-file', '-s', object]);
const size = parseInt(catFile.stdout);
return { mode, object, size };
@@ -977,12 +980,12 @@ export class Repository {
}
async lstree(treeish: string, path: string): Promise<LsTreeElement[]> {
const { stdout } = await this.run(['ls-tree', '-l', treeish, '--', sanitizePath(path)]);
const { stdout } = await this.exec(['ls-tree', '-l', treeish, '--', sanitizePath(path)]);
return parseLsTree(stdout);
}
async lsfiles(path: string): Promise<LsFilesElement[]> {
const { stdout } = await this.run(['ls-files', '--stage', '--', sanitizePath(path)]);
const { stdout } = await this.exec(['ls-files', '--stage', '--', sanitizePath(path)]);
return parseLsFiles(stdout);
}
@@ -1047,7 +1050,7 @@ export class Repository {
}
try {
await this.run(args);
await this.exec(args);
} catch (err) {
if (/patch does not apply/.test(err.stderr)) {
err.gitErrorCode = GitErrorCodes.PatchDoesNotApply;
@@ -1064,7 +1067,7 @@ export class Repository {
args.push('--cached');
}
const result = await this.run(args);
const result = await this.exec(args);
return result.stdout;
}
@@ -1077,7 +1080,7 @@ export class Repository {
}
const args = ['diff', '--', sanitizePath(path)];
const result = await this.run(args);
const result = await this.exec(args);
return result.stdout;
}
@@ -1090,7 +1093,7 @@ export class Repository {
}
const args = ['diff', ref, '--', sanitizePath(path)];
const result = await this.run(args);
const result = await this.exec(args);
return result.stdout;
}
@@ -1103,7 +1106,7 @@ export class Repository {
}
const args = ['diff', '--cached', '--', sanitizePath(path)];
const result = await this.run(args);
const result = await this.exec(args);
return result.stdout;
}
@@ -1116,13 +1119,13 @@ export class Repository {
}
const args = ['diff', '--cached', ref, '--', sanitizePath(path)];
const result = await this.run(args);
const result = await this.exec(args);
return result.stdout;
}
async diffBlobs(object1: string, object2: string): Promise<string> {
const args = ['diff', object1, object2];
const result = await this.run(args);
const result = await this.exec(args);
return result.stdout;
}
@@ -1136,7 +1139,7 @@ export class Repository {
}
const args = ['diff', range, '--', sanitizePath(path)];
const result = await this.run(args);
const result = await this.exec(args);
return result.stdout.trim();
}
@@ -1151,7 +1154,7 @@ export class Repository {
args.push(ref);
}
const gitResult = await this.run(args);
const gitResult = await this.exec(args);
if (gitResult.exitCode) {
return [];
}
@@ -1224,14 +1227,14 @@ export class Repository {
async getMergeBase(ref1: string, ref2: string): Promise<string> {
const args = ['merge-base', ref1, ref2];
const result = await this.run(args);
const result = await this.exec(args);
return result.stdout.trim();
}
async hashObject(data: string): Promise<string> {
const args = ['hash-object', '-w', '--stdin'];
const result = await this.run(args, { input: data });
const result = await this.exec(args, { input: data });
return result.stdout.trim();
}
@@ -1247,10 +1250,10 @@ export class Repository {
if (paths && paths.length) {
for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) {
await this.run([...args, '--', ...chunk]);
await this.exec([...args, '--', ...chunk]);
}
} else {
await this.run([...args, '--', '.']);
await this.exec([...args, '--', '.']);
}
}
@@ -1263,7 +1266,7 @@ export class Repository {
args.push(...paths.map(sanitizePath));
await this.run(args);
await this.exec(args);
}
async stage(path: string, data: string): Promise<void> {
@@ -1296,7 +1299,7 @@ export class Repository {
add = '--add';
}
await this.run(['update-index', add, '--cacheinfo', mode, hash, path]);
await this.exec(['update-index', add, '--cacheinfo', mode, hash, path]);
}
async checkout(treeish: string, paths: string[], opts: { track?: boolean, detached?: boolean } = Object.create(null)): Promise<void> {
@@ -1317,10 +1320,10 @@ export class Repository {
try {
if (paths && paths.length > 0) {
for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) {
await this.run([...args, '--', ...chunk]);
await this.exec([...args, '--', ...chunk]);
}
} else {
await this.run(args);
await this.exec(args);
}
} catch (err) {
if (/Please,? commit your changes or stash them/.test(err.stderr || '')) {
@@ -1365,25 +1368,27 @@ export class Repository {
args.push('--no-verify');
}
// Stops git from guessing at user/email
args.splice(0, 0, '-c', 'user.useConfigOnly=true');
if (opts.requireUserConfig ?? true) {
// Stops git from guessing at user/email
args.splice(0, 0, '-c', 'user.useConfigOnly=true');
}
try {
await this.run(args, !opts.amend || message ? { input: message || '' } : {});
await this.exec(args, !opts.amend || message ? { input: message || '' } : {});
} catch (commitErr) {
await this.handleCommitError(commitErr);
}
}
async rebaseAbort(): Promise<void> {
await this.run(['rebase', '--abort']);
await this.exec(['rebase', '--abort']);
}
async rebaseContinue(): Promise<void> {
const args = ['rebase', '--continue'];
try {
await this.run(args);
await this.exec(args);
} catch (commitErr) {
await this.handleCommitError(commitErr);
}
@@ -1396,14 +1401,14 @@ export class Repository {
}
try {
await this.run(['config', '--get-all', 'user.name']);
await this.exec(['config', '--get-all', 'user.name']);
} catch (err) {
err.gitErrorCode = GitErrorCodes.NoUserNameConfigured;
throw err;
}
try {
await this.run(['config', '--get-all', 'user.email']);
await this.exec(['config', '--get-all', 'user.email']);
} catch (err) {
err.gitErrorCode = GitErrorCodes.NoUserEmailConfigured;
throw err;
@@ -1419,39 +1424,39 @@ export class Repository {
args.push(ref);
}
await this.run(args);
await this.exec(args);
}
async deleteBranch(name: string, force?: boolean): Promise<void> {
const args = ['branch', force ? '-D' : '-d', name];
await this.run(args);
await this.exec(args);
}
async renameBranch(name: string): Promise<void> {
const args = ['branch', '-m', name];
await this.run(args);
await this.exec(args);
}
async move(from: string, to: string): Promise<void> {
const args = ['mv', from, to];
await this.run(args);
await this.exec(args);
}
async setBranchUpstream(name: string, upstream: string): Promise<void> {
const args = ['branch', '--set-upstream-to', upstream, name];
await this.run(args);
await this.exec(args);
}
async deleteRef(ref: string): Promise<void> {
const args = ['update-ref', '-d', ref];
await this.run(args);
await this.exec(args);
}
async merge(ref: string): Promise<void> {
const args = ['merge', ref];
try {
await this.run(args);
await this.exec(args);
} catch (err) {
if (/^CONFLICT /m.test(err.stdout || '')) {
err.gitErrorCode = GitErrorCodes.Conflict;
@@ -1470,12 +1475,12 @@ export class Repository {
args = [...args, name];
}
await this.run(args);
await this.exec(args);
}
async deleteTag(name: string): Promise<void> {
let args = ['tag', '-d', name];
await this.run(args);
await this.exec(args);
}
async clean(paths: string[]): Promise<void> {
@@ -1488,7 +1493,7 @@ export class Repository {
for (const paths of groups) {
for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) {
promises.push(limiter.queue(() => this.run([...args, '--', ...chunk])));
promises.push(limiter.queue(() => this.exec([...args, '--', ...chunk])));
}
}
@@ -1496,10 +1501,10 @@ export class Repository {
}
async undo(): Promise<void> {
await this.run(['clean', '-fd']);
await this.exec(['clean', '-fd']);
try {
await this.run(['checkout', '--', '.']);
await this.exec(['checkout', '--', '.']);
} catch (err) {
if (/did not match any file\(s\) known to git\./.test(err.stderr || '')) {
return;
@@ -1511,11 +1516,11 @@ export class Repository {
async reset(treeish: string, hard: boolean = false): Promise<void> {
const args = ['reset', hard ? '--hard' : '--soft', treeish];
await this.run(args);
await this.exec(args);
}
async revert(treeish: string, paths: string[]): Promise<void> {
const result = await this.run(['branch']);
const result = await this.exec(['branch']);
let args: string[];
// In case there are no branches, we must use rm --cached
@@ -1528,10 +1533,10 @@ export class Repository {
try {
if (paths && paths.length > 0) {
for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) {
await this.run([...args, '--', ...chunk]);
await this.exec([...args, '--', ...chunk]);
}
} else {
await this.run([...args, '--', '.']);
await this.exec([...args, '--', '.']);
}
} catch (err) {
// In case there are merge conflicts to be resolved, git reset will output
@@ -1546,23 +1551,24 @@ export class Repository {
async addRemote(name: string, url: string): Promise<void> {
const args = ['remote', 'add', name, url];
await this.run(args);
await this.exec(args);
}
async removeRemote(name: string): Promise<void> {
const args = ['remote', 'remove', name];
await this.run(args);
await this.exec(args);
}
async renameRemote(name: string, newName: string): Promise<void> {
const args = ['remote', 'rename', name, newName];
await this.run(args);
await this.exec(args);
}
async fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean, readonly cancellationToken?: CancellationToken } = {}): Promise<void> {
const args = ['fetch'];
const spawnOptions: SpawnOptions = {
cancellationToken: options.cancellationToken,
env: { 'GIT_HTTP_USER_AGENT': this.git.userAgent }
};
if (options.remote) {
@@ -1584,11 +1590,11 @@ export class Repository {
}
if (options.silent) {
spawnOptions.env = { 'VSCODE_GIT_FETCH_SILENT': 'true' };
spawnOptions.env!['VSCODE_GIT_FETCH_SILENT'] = 'true';
}
try {
await this.run(args, spawnOptions);
await this.exec(args, spawnOptions);
} catch (err) {
if (/No remote repository specified\./.test(err.stderr || '')) {
err.gitErrorCode = GitErrorCodes.NoRemoteRepositorySpecified;
@@ -1621,7 +1627,10 @@ export class Repository {
}
try {
await this.run(args, options);
await this.exec(args, {
cancellationToken: options.cancellationToken,
env: { 'GIT_HTTP_USER_AGENT': this.git.userAgent }
});
} catch (err) {
if (/^CONFLICT \([^)]+\): \b/m.test(err.stdout || '')) {
err.gitErrorCode = GitErrorCodes.Conflict;
@@ -1648,7 +1657,7 @@ export class Repository {
args.push(branch);
try {
await this.run(args, options);
await this.exec(args, options);
} catch (err) {
if (/^CONFLICT \([^)]+\): \b/m.test(err.stdout || '')) {
err.gitErrorCode = GitErrorCodes.Conflict;
@@ -1690,7 +1699,7 @@ export class Repository {
}
try {
await this.run(args);
await this.exec(args, { env: { 'GIT_HTTP_USER_AGENT': this.git.userAgent } });
} catch (err) {
if (/^error: failed to push some refs to\b/m.test(err.stderr || '')) {
err.gitErrorCode = GitErrorCodes.PushRejected;
@@ -1708,13 +1717,13 @@ export class Repository {
async cherryPick(commitHash: string): Promise<void> {
const args = ['cherry-pick', commitHash];
await this.run(args);
await this.exec(args);
}
async blame(path: string): Promise<string> {
try {
const args = ['blame', sanitizePath(path)];
const result = await this.run(args);
const result = await this.exec(args);
return result.stdout.trim();
} catch (err) {
if (/^fatal: no such path/.test(err.stderr || '')) {
@@ -1737,7 +1746,7 @@ export class Repository {
args.push('-m', message);
}
await this.run(args);
await this.exec(args);
} catch (err) {
if (/No local changes to save/.test(err.stderr || '')) {
err.gitErrorCode = GitErrorCodes.NoLocalChanges;
@@ -1763,7 +1772,7 @@ export class Repository {
args.push(`stash@{${index}}`);
}
await this.run(args);
await this.exec(args);
} catch (err) {
if (/No stash found/.test(err.stderr || '')) {
err.gitErrorCode = GitErrorCodes.NoStashFound;
@@ -1785,7 +1794,7 @@ export class Repository {
}
try {
await this.run(args);
await this.exec(args);
} catch (err) {
if (/No stash found/.test(err.stderr || '')) {
err.gitErrorCode = GitErrorCodes.NoStashFound;
@@ -1850,7 +1859,7 @@ export class Repository {
async getHEAD(): Promise<Ref> {
try {
const result = await this.run(['symbolic-ref', '--short', 'HEAD']);
const result = await this.exec(['symbolic-ref', '--short', 'HEAD']);
if (!result.stdout) {
throw new Error('Not in a branch');
@@ -1858,7 +1867,7 @@ export class Repository {
return { name: result.stdout.trim(), commit: undefined, type: RefType.Head };
} catch (err) {
const result = await this.run(['rev-parse', 'HEAD']);
const result = await this.exec(['rev-parse', 'HEAD']);
if (!result.stdout) {
throw new Error('Error parsing HEAD');
@@ -1869,7 +1878,7 @@ export class Repository {
}
async findTrackingBranches(upstreamBranch: string): Promise<Branch[]> {
const result = await this.run(['for-each-ref', '--format', '%(refname:short)%00%(upstream:short)', 'refs/heads']);
const result = await this.exec(['for-each-ref', '--format', '%(refname:short)%00%(upstream:short)', 'refs/heads']);
return result.stdout.trim().split('\n')
.map(line => line.trim().split('\0'))
.filter(([_, upstream]) => upstream === upstreamBranch)
@@ -1897,7 +1906,7 @@ export class Repository {
args.push('--contains', opts.contains);
}
const result = await this.run(args);
const result = await this.exec(args);
const fn = (line: string): Ref | null => {
let match: RegExpExecArray | null;
@@ -1913,14 +1922,14 @@ export class Repository {
return null;
};
return result.stdout.trim().split('\n')
return result.stdout.split('\n')
.filter(line => !!line)
.map(fn)
.filter(ref => !!ref) as Ref[];
}
async getStashes(): Promise<Stash[]> {
const result = await this.run(['stash', 'list']);
const result = await this.exec(['stash', 'list']);
const regex = /^stash@{(\d+)}:(.+)$/;
const rawStashes = result.stdout.trim().split('\n')
.filter(b => !!b)
@@ -1932,7 +1941,7 @@ export class Repository {
}
async getRemotes(): Promise<Remote[]> {
const result = await this.run(['remote', '--verbose']);
const result = await this.exec(['remote', '--verbose']);
const lines = result.stdout.trim().split('\n').filter(l => !!l);
const remotes: MutableRemote[] = [];
@@ -1968,50 +1977,59 @@ export class Repository {
return this.getHEAD();
}
let result = await this.run(['rev-parse', name]);
if (!result.stdout && /^@/.test(name)) {
const symbolicFullNameResult = await this.run(['rev-parse', '--symbolic-full-name', name]);
name = symbolicFullNameResult.stdout.trim();
result = await this.run(['rev-parse', name]);
const args = ['for-each-ref', '--format=%(refname)%00%(upstream:short)%00%(upstream:track)%00%(objectname)'];
if (/^refs\/(head|remotes)\//i.test(name)) {
args.push(name);
} else {
args.push(`refs/heads/${name}`, `refs/remotes/${name}`);
}
if (!result.stdout) {
return Promise.reject<Branch>(new Error('No such branch'));
}
const result = await this.exec(args);
const branches: Branch[] = result.stdout.trim().split('\n').map<Branch | undefined>(line => {
let [branchName, upstream, status, ref] = line.trim().split('\0');
const commit = result.stdout.trim();
if (branchName.startsWith('refs/heads/')) {
branchName = branchName.substring(11);
const index = upstream.indexOf('/');
try {
const res2 = await this.run(['rev-parse', '--symbolic-full-name', name + '@{u}']);
const fullUpstream = res2.stdout.trim();
const match = /^refs\/remotes\/([^/]+)\/(.+)$/.exec(fullUpstream);
if (!match) {
throw new Error(`Could not parse upstream branch: ${fullUpstream}`);
}
const upstream = { remote: match[1], name: match[2] };
const res3 = await this.run(['rev-list', '--left-right', name + '...' + fullUpstream]);
let ahead = 0, behind = 0;
let i = 0;
while (i < res3.stdout.length) {
switch (res3.stdout.charAt(i)) {
case '<': ahead++; break;
case '>': behind++; break;
default: i++; break;
let ahead;
let behind;
const match = /\[(?:ahead ([0-9]+))?[,\s]*(?:behind ([0-9]+))?]|\[gone]/.exec(status);
if (match) {
[, ahead, behind] = match;
}
while (res3.stdout.charAt(i++) !== '\n') { /* no-op */ }
}
return {
type: RefType.Head,
name: branchName,
upstream: upstream ? {
name: upstream.substring(index + 1),
remote: upstream.substring(0, index)
} : undefined,
commit: ref || undefined,
ahead: Number(ahead) || 0,
behind: Number(behind) || 0,
};
} else if (branchName.startsWith('refs/remotes/')) {
branchName = branchName.substring(13);
const index = branchName.indexOf('/');
return { name, type: RefType.Head, commit, upstream, ahead, behind };
} catch (err) {
return { name, type: RefType.Head, commit };
return {
type: RefType.RemoteHead,
name: branchName.substring(index + 1),
remote: branchName.substring(0, index),
commit: ref,
};
} else {
return undefined;
}
}).filter((b?: Branch): b is Branch => !!b);
if (branches.length) {
return branches[0];
}
return Promise.reject<Branch>(new Error('No such branch'));
}
async getBranches(query: BranchQuery): Promise<Ref[]> {
@@ -2048,7 +2066,7 @@ export class Repository {
async getCommitTemplate(): Promise<string> {
try {
const result = await this.run(['config', '--get', 'commit.template']);
const result = await this.exec(['config', '--get', 'commit.template']);
if (!result.stdout) {
return '';
@@ -2071,7 +2089,7 @@ export class Repository {
}
async getCommit(ref: string): Promise<Commit> {
const result = await this.run(['show', '-s', `--format=${COMMIT_FORMAT}`, '-z', ref]);
const result = await this.exec(['show', '-s', `--format=${COMMIT_FORMAT}`, '-z', ref]);
const commits = parseGitCommits(result.stdout);
if (commits.length === 0) {
return Promise.reject<Commit>('bad commit format');
@@ -2083,7 +2101,7 @@ export class Repository {
const args = ['submodule', 'update'];
for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) {
await this.run([...args, '--', ...chunk]);
await this.exec([...args, '--', ...chunk]);
}
}

View File

@@ -6,7 +6,7 @@
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
import { ExtensionContext, workspace, window, Disposable, commands, OutputChannel, Uri } from 'vscode';
import { ExtensionContext, workspace, window, Disposable, commands, Uri, OutputChannel } from 'vscode';
import { findGit, Git, IGit } from './git';
import { Model } from './model';
import { CommandCenter } from './commands';
@@ -20,6 +20,7 @@ import { GitProtocolHandler } from './protocolHandler';
import { GitExtensionImpl } from './api/extension';
// import * as path from 'path';
// import * as fs from 'fs';
import * as os from 'os';
import { GitTimelineProvider } from './timelineProvider';
import { registerAPICommands } from './api/api1';
import { TerminalEnvironmentManager } from './terminal';
@@ -39,11 +40,17 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann
const askpass = await Askpass.create(outputChannel, context.storagePath);
disposables.push(askpass);
const env = askpass.getEnv();
const terminalEnvironmentManager = new TerminalEnvironmentManager(context, env);
const environment = askpass.getEnv();
const terminalEnvironmentManager = new TerminalEnvironmentManager(context, environment);
disposables.push(terminalEnvironmentManager);
const git = new Git({ gitPath: info.path, version: info.version, env });
const git = new Git({
gitPath: info.path,
userAgent: `git/${info.version} (${(os as any).version?.() ?? os.type()} ${os.release()}; ${os.platform()} ${os.arch()}) azuredatudio`,
version: info.version,
env: environment,
});
const model = new Model(git, askpass, context.globalState, outputChannel);
disposables.push(model);

View File

@@ -12,10 +12,11 @@ import * as path from 'path';
import * as fs from 'fs';
import * as nls from 'vscode-nls';
import { fromGitUri } from './uri';
import { APIState as State, RemoteSourceProvider, CredentialsProvider, PushErrorHandler } from './api/git';
import { APIState as State, RemoteSourceProvider, CredentialsProvider, PushErrorHandler, PublishEvent } from './api/git';
import { Askpass } from './askpass';
import { IRemoteSourceProviderRegistry } from './remoteProvider';
import { IPushErrorHandlerRegistry } from './pushError';
import { ApiRepository } from './api/api1';
const localize = nls.loadMessageBundle();
@@ -69,6 +70,13 @@ export class Model implements IRemoteSourceProviderRegistry, IPushErrorHandlerRe
private _onDidChangeState = new EventEmitter<State>();
readonly onDidChangeState = this._onDidChangeState.event;
private _onDidPublish = new EventEmitter<PublishEvent>();
readonly onDidPublish = this._onDidPublish.event;
firePublishEvent(repository: Repository, branch?: string) {
this._onDidPublish.fire({ repository: new ApiRepository(repository), branch: branch });
}
private _state: State = 'uninitialized';
get state(): State { return this._state; }

View File

@@ -7,10 +7,10 @@ import * as fs from 'fs';
import * as path from 'path';
import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands } from 'vscode';
import * as nls from 'vscode-nls';
import { Branch, Change, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, CommitOptions, BranchQuery } from './api/git';
import { Branch, Change, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, CommitOptions, BranchQuery } from './api/git';
import { AutoFetcher } from './autofetch';
import { debounce, memoize, throttle } from './decorators';
import { Commit, ForcePushMode, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions } from './git';
import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions } from './git';
import { StatusBarCommands } from './statusbar';
import { toGitUri } from './uri';
import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent } from './util';
@@ -79,7 +79,7 @@ export class Resource implements SourceControlResourceState {
return this.resources[0];
}
get rightUri(): Uri {
get rightUri(): Uri | undefined {
return this.resources[1];
}
@@ -88,7 +88,7 @@ export class Resource implements SourceControlResourceState {
}
@memoize
private get resources(): [Uri | undefined, Uri] {
private get resources(): [Uri | undefined, Uri | undefined] {
return this._commandResolver.getResources(this);
}
@@ -613,7 +613,7 @@ class ResourceCommandResolver {
}
}
getResources(resource: Resource): [Uri | undefined, Uri] {
getResources(resource: Resource): [Uri | undefined, Uri | undefined] {
for (const submodule of this.repository.submodules) {
if (path.join(this.repository.root, submodule.path) === resource.resourceUri.fsPath) {
return [undefined, toGitUri(resource.resourceUri, resource.resourceGroupType === ResourceGroupType.Index ? 'index' : 'wt', { submoduleOf: this.repository.root })];
@@ -641,7 +641,7 @@ class ResourceCommandResolver {
return undefined;
}
private getRightResource(resource: Resource): Uri {
private getRightResource(resource: Resource): Uri | undefined {
switch (resource.type) {
case Status.INDEX_MODIFIED:
case Status.INDEX_ADDED:
@@ -677,7 +677,7 @@ class ResourceCommandResolver {
return resource.resourceUri;
}
throw new Error('Should never happen');
return undefined;
}
private getTitle(resource: Resource): string {
@@ -1129,7 +1129,7 @@ export class Repository implements Disposable {
return this.run(Operation.HashObject, () => this.repository.hashObject(data));
}
async add(resources: Uri[], opts?: { update?: boolean }): Promise<void> {
async add(resources: Uri[], opts?: { update?: boolean; }): Promise<void> {
await this.run(Operation.Add, () => this.repository.add(resources.map(r => r.fsPath), opts));
}
@@ -1165,6 +1165,12 @@ export class Repository implements Disposable {
}
delete opts.all;
if (opts.requireUserConfig === undefined || opts.requireUserConfig === null) {
const config = workspace.getConfiguration('git', Uri.file(this.root));
opts.requireUserConfig = config.get<boolean>('requireGitUserConfig');
}
await this.repository.commit(message, opts);
});
}
@@ -1260,12 +1266,12 @@ export class Repository implements Disposable {
await this.run(Operation.DeleteTag, () => this.repository.deleteTag(name));
}
async checkout(treeish: string, opts?: { detached?: boolean }): Promise<void> {
async checkout(treeish: string, opts?: { detached?: boolean; }): Promise<void> {
await this.run(Operation.Checkout, () => this.repository.checkout(treeish, [], opts));
}
async checkoutTracking(treeish: string): Promise<void> {
await this.run(Operation.CheckoutTracking, () => this.repository.checkout(treeish, [], { track: true }));
async checkoutTracking(treeish: string, opts: { detached?: boolean; } = {}): Promise<void> {
await this.run(Operation.CheckoutTracking, () => this.repository.checkout(treeish, [], { ...opts, track: true }));
}
async findTrackingBranches(upstreamRef: string): Promise<Branch[]> {
@@ -1297,7 +1303,7 @@ export class Repository implements Disposable {
}
@throttle
async fetchDefault(options: { silent?: boolean } = {}): Promise<void> {
async fetchDefault(options: { silent?: boolean; } = {}): Promise<void> {
await this._fetch({ silent: options.silent });
}
@@ -1315,7 +1321,7 @@ export class Repository implements Disposable {
await this._fetch({ remote, ref, depth });
}
private async _fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean } = {}): Promise<void> {
private async _fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean; } = {}): Promise<void> {
if (!options.prune) {
const config = workspace.getConfiguration('git', Uri.file(this.root));
const prune = config.get<boolean>('pruneOnFetch');
@@ -1363,7 +1369,9 @@ export class Repository implements Disposable {
await this.repository.fetch({ all: true });
}
await this.repository.pull(rebase, remote, branch, { unshallow, tags });
if (await this.checkIfMaybeRebased(this.HEAD?.name)) {
await this.repository.pull(rebase, remote, branch, { unshallow, tags });
}
});
});
}
@@ -1432,10 +1440,11 @@ export class Repository implements Disposable {
await this.repository.fetch({ all: true, cancellationToken });
}
await this.repository.pull(rebase, remoteName, pullBranch, { tags, cancellationToken });
if (await this.checkIfMaybeRebased(this.HEAD?.name)) {
await this.repository.pull(rebase, remoteName, pullBranch, { tags, cancellationToken });
}
};
if (supportCancellation) {
const opts: ProgressOptions = {
location: ProgressLocation.Notification,
@@ -1463,6 +1472,54 @@ export class Repository implements Disposable {
});
}
private async checkIfMaybeRebased(currentBranch?: string) {
const config = workspace.getConfiguration('git');
const shouldIgnore = config.get<boolean>('ignoreRebaseWarning') === true;
if (shouldIgnore) {
return true;
}
const maybeRebased = await this.run(Operation.Log, async () => {
try {
const result = await this.repository.exec(['log', '--oneline', '--cherry', `${currentBranch ?? ''}...${currentBranch ?? ''}@{upstream}`, '--']);
if (result.exitCode) {
return false;
}
return /^=/.test(result.stdout);
} catch {
return false;
}
});
if (!maybeRebased) {
return true;
}
const always = { title: localize('always pull', "Always Pull") };
const pull = { title: localize('pull', "Pull") };
const cancel = { title: localize('dont pull', "Don't Pull") };
const result = await window.showWarningMessage(
currentBranch
? localize('pull branch maybe rebased', "It looks like the current branch \'{0}\' might have been rebased. Are you sure you still want to pull into it?", currentBranch)
: localize('pull maybe rebased', "It looks like the current branch might have been rebased. Are you sure you still want to pull into it?"),
always, pull, cancel
);
if (result === pull) {
return true;
}
if (result === always) {
await config.update('ignoreRebaseWarning', true, true);
return true;
}
return false;
}
async show(ref: string, filePath: string): Promise<string> {
return await this.run(Operation.Show, async () => {
const relativePath = path.relative(this.repository.root, filePath).replace(/\\/g, '/');
@@ -1490,11 +1547,11 @@ export class Repository implements Disposable {
});
}
getObjectDetails(ref: string, filePath: string): Promise<{ mode: string, object: string, size: number }> {
getObjectDetails(ref: string, filePath: string): Promise<{ mode: string, object: string, size: number; }> {
return this.run(Operation.GetObjectDetails, () => this.repository.getObjectDetails(ref, filePath));
}
detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }> {
detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string; }> {
return this.run(Operation.Show, () => this.repository.detectObjectType(object));
}

View File

@@ -124,4 +124,31 @@ suite('git smoke test', function () {
assert.equal(repository.state.workingTreeChanges.length, 0);
assert.equal(repository.state.indexChanges.length, 0);
});
test('rename/delete conflict', async function () {
cp.execSync('git branch test', { cwd });
cp.execSync('git checkout test', { cwd });
fs.unlinkSync(file('app.js'));
cp.execSync('git add .', { cwd });
await repository.commit('commit on test');
cp.execSync('git checkout master', { cwd });
fs.renameSync(file('app.js'), file('rename.js'));
cp.execSync('git add .', { cwd });
await repository.commit('commit on master');
try {
cp.execSync('git merge test', { cwd });
} catch (e) { }
setTimeout(() => {
commands.executeCommand('workbench.scm.focus');
}, 2e3);
await new Promise(resolve => {
setTimeout(resolve, 5e3);
});
});
});