Merge from vscode 2cfc8172e533e50c90e6a3152f6bfb1f82f963f3 (#6516)

* Merge from vscode 2cfc8172e533e50c90e6a3152f6bfb1f82f963f3

* fix tests
This commit is contained in:
Anthony Dresser
2019-07-28 15:15:24 -07:00
committed by GitHub
parent aacf1e7f1c
commit 1d56a17f32
292 changed files with 19784 additions and 1873 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 } from './git';
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState } from './git';
import { Event, SourceControlInputBox, Uri, SourceControl } from 'vscode';
import { mapEvent } from '../util';
@@ -214,6 +214,14 @@ export class ApiImpl implements API {
readonly git = new ApiGit(this._model);
get state(): APIState {
return this._model.state;
}
get onDidChangeState(): Event<APIState> {
return this._model.onDidChangeState;
}
get onDidOpenRepository(): Event<Repository> {
return mapEvent(this._model.onDidOpenRepository, r => new ApiRepository(r));
}

View File

@@ -176,7 +176,11 @@ export interface Repository {
log(options?: LogOptions): Promise<Commit[]>;
}
export type APIState = 'uninitialized' | 'initialized';
export interface API {
readonly state: APIState;
readonly onDidChangeState: Event<APIState>;
readonly git: Git;
readonly repositories: Repository[];
readonly onDidOpenRepository: Event<Repository>;

View File

@@ -1115,7 +1115,7 @@ export class CommandCenter {
if (scmResources.length === 1) {
if (untrackedCount > 0) {
message = localize('confirm delete', "Are you sure you want to DELETE {0}?", path.basename(scmResources[0].resourceUri.fsPath));
message = localize('confirm delete', "Are you sure you want to DELETE {0}?\nThis is IRREVERSIBLE!\nThis file will be FOREVER LOST.", path.basename(scmResources[0].resourceUri.fsPath));
yes = localize('delete file', "Delete file");
} else {
if (scmResources[0].type === Status.DELETED) {
@@ -1134,7 +1134,7 @@ export class CommandCenter {
}
if (untrackedCount > 0) {
message = `${message}\n\n${localize('warn untracked', "This will DELETE {0} untracked files!", untrackedCount)}`;
message = `${message}\n\n${localize('warn untracked', "This will DELETE {0} untracked files!\nThis is IRREVERSIBLE!\nThese files will be FOREVER LOST.", untrackedCount)}`;
}
}
@@ -1175,7 +1175,7 @@ export class CommandCenter {
await repository.clean(resources.map(r => r.resourceUri));
return;
} else if (resources.length === 1) {
const message = localize('confirm delete', "Are you sure you want to DELETE {0}?", path.basename(resources[0].resourceUri.fsPath));
const message = localize('confirm delete', "Are you sure you want to DELETE {0}?\nThis is IRREVERSIBLE!\nThis file will be FOREVER LOST.", path.basename(resources[0].resourceUri.fsPath));
const yes = localize('delete file', "Delete file");
const pick = await window.showWarningMessage(message, { modal: true }, yes);

View File

@@ -12,7 +12,7 @@ import * as path from 'path';
import * as fs from 'fs';
import * as nls from 'vscode-nls';
import { fromGitUri } from './uri';
import { GitErrorCodes } from './api/git';
import { GitErrorCodes, APIState as State } from './api/git';
const localize = nls.loadMessageBundle();
@@ -63,15 +63,22 @@ export class Model {
private possibleGitRepositoryPaths = new Set<string>();
private _onDidChangeState = new EventEmitter<State>();
readonly onDidChangeState = this._onDidChangeState.event;
private _state: State = 'uninitialized';
get state(): State { return this._state; }
setState(state: State): void {
this._state = state;
this._onDidChangeState.fire(state);
}
private disposables: Disposable[] = [];
constructor(readonly git: Git, private globalState: Memento, private outputChannel: OutputChannel) {
workspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables);
this.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] });
window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables);
this.onDidChangeVisibleTextEditors(window.visibleTextEditors);
workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables);
const fsWatcher = workspace.createFileSystemWatcher('**');
@@ -82,7 +89,15 @@ export class Model {
const onPossibleGitRepositoryChange = filterEvent(onGitRepositoryChange, uri => !this.getRepository(uri));
onPossibleGitRepositoryChange(this.onPossibleGitRepositoryChange, this, this.disposables);
this.scanWorkspaceFolders();
this.doInitialScan().finally(() => this.setState('initialized'));
}
private async doInitialScan(): Promise<void> {
await Promise.all([
this.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] }),
this.onDidChangeVisibleTextEditors(window.visibleTextEditors),
this.scanWorkspaceFolders()
]);
}
/**
@@ -157,8 +172,8 @@ export class Model {
.filter(r => !activeRepositories.has(r!.repository))
.filter(r => !(workspace.workspaceFolders || []).some(f => isDescendant(f.uri.fsPath, r!.repository.root))) as OpenRepository[];
possibleRepositoryFolders.forEach(p => this.openRepository(p.uri.fsPath));
openRepositoriesToDispose.forEach(r => r.dispose());
await Promise.all(possibleRepositoryFolders.map(p => this.openRepository(p.uri.fsPath)));
}
private onDidChangeConfiguration(): void {
@@ -175,7 +190,7 @@ export class Model {
openRepositoriesToDispose.forEach(r => r.dispose());
}
private onDidChangeVisibleTextEditors(editors: TextEditor[]): void {
private async onDidChangeVisibleTextEditors(editors: TextEditor[]): Promise<void> {
const config = workspace.getConfiguration('git');
const autoRepositoryDetection = config.get<boolean | 'subFolders' | 'openEditors'>('autoRepositoryDetection');
@@ -183,7 +198,7 @@ export class Model {
return;
}
editors.forEach(editor => {
await Promise.all(editors.map(async editor => {
const uri = editor.document.uri;
if (uri.scheme !== 'file') {
@@ -196,8 +211,8 @@ export class Model {
return;
}
this.openRepository(path.dirname(uri.fsPath));
});
await this.openRepository(path.dirname(uri.fsPath));
}));
}
@sequentialize
@@ -236,6 +251,7 @@ export class Model {
const repository = new Repository(this.git.open(repositoryRoot, dotGit), this.globalState, this.outputChannel);
this.open(repository);
await repository.status();
} catch (err) {
if (err.gitErrorCode === GitErrorCodes.NotAGitRepository) {
return;
@@ -421,4 +437,4 @@ export class Model {
this.possibleGitRepositoryPaths.clear();
this.disposables = dispose(this.disposables);
}
}
}

View File

@@ -519,8 +519,8 @@ class DotGitWatcher implements IFileWatcher {
this.transientDisposables.push(upstreamWatcher);
upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables);
} catch (err) {
if (env.logLevel <= LogLevel.Info) {
this.outputChannel.appendLine(`Failed to watch ref '${upstreamPath}'. Ref is most likely packed.`);
if (env.logLevel <= LogLevel.Error) {
this.outputChannel.appendLine(`Failed to watch ref '${upstreamPath}', is most likely packed.\n${err.stack || err}`);
}
}
}
@@ -651,19 +651,30 @@ export class Repository implements Disposable {
const onWorkspaceRepositoryFileChange = filterEvent(onWorkspaceFileChange, uri => isDescendant(repository.root, uri.fsPath));
const onWorkspaceWorkingTreeFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => !/\/\.git($|\/)/.test(uri.path));
const dotGitFileWatcher = new DotGitWatcher(this, outputChannel);
this.disposables.push(dotGitFileWatcher);
let onDotGitFileChange: Event<Uri>;
try {
const dotGitFileWatcher = new DotGitWatcher(this, outputChannel);
onDotGitFileChange = dotGitFileWatcher.event;
this.disposables.push(dotGitFileWatcher);
} catch (err) {
if (env.logLevel <= LogLevel.Error) {
outputChannel.appendLine(`Failed to watch '${this.dotGit}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`);
}
onDotGitFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => /\/\.git($|\/)/.test(uri.path));
}
// FS changes should trigger `git status`:
// - any change inside the repository working tree
// - any change whithin the first level of the `.git` folder, except the folder itself and `index.lock`
const onFileChange = anyEvent(onWorkspaceWorkingTreeFileChange, dotGitFileWatcher.event);
const onFileChange = anyEvent(onWorkspaceWorkingTreeFileChange, onDotGitFileChange);
onFileChange(this.onFileChange, this, this.disposables);
// Relevate repository changes should trigger virtual document change events
dotGitFileWatcher.event(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables);
onDotGitFileChange(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables);
this.disposables.push(new FileEventLogger(onWorkspaceWorkingTreeFileChange, dotGitFileWatcher.event, outputChannel));
this.disposables.push(new FileEventLogger(onWorkspaceWorkingTreeFileChange, onDotGitFileChange, outputChannel));
const root = Uri.file(repository.root);
this._sourceControl = scm.createSourceControl('git', 'Git', root);
@@ -713,7 +724,6 @@ export class Repository implements Disposable {
this.disposables.push(progressManager);
this.updateCommitTemplate();
this.status();
}
validateInput(text: string, position: number): SourceControlInputBoxValidation | undefined {