提交 303dd117 编写于 作者: J Joao Moreno

git multirepo stage

上级 16e81896
......@@ -8,7 +8,7 @@
import { Uri, commands, scm, Disposable, window, workspace, QuickPickItem, OutputChannel, Range, WorkspaceEdit, Position, LineChange, SourceControlResourceState, TextDocumentShowOptions, ViewColumn } from 'vscode';
import { Ref, RefType, Git, GitErrorCodes, Branch } from './git';
import { Repository, Resource, Status, CommitOptions, WorkingTreeGroup, IndexGroup, MergeGroup } from './repository';
import { ModelRegistry } from './modelRegistry';
import { Model } from './model';
import { toGitUri, fromGitUri } from './uri';
import { applyLineChanges, intersectDiffWithRange, toLineRanges, invertLineChange } from './staging';
import * as path from 'path';
......@@ -130,7 +130,7 @@ export class CommandCenter {
constructor(
private git: Git,
private modelRegistry: ModelRegistry,
private model: Model,
private outputChannel: OutputChannel,
private telemetryReporter: TelemetryReporter
) {
......@@ -145,21 +145,6 @@ export class CommandCenter {
});
}
private groupByModel(resources: Uri[]): [Repository | undefined, Uri[]][] {
return resources.reduce((result, resource) => {
const model = this.modelRegistry.getModel(resource);
const pair = result.filter(p => p[0] === model)[0];
if (pair) {
pair[1].push(resource);
} else {
result.push([model, [resource]]);
}
return result;
}, [] as [Repository | undefined, Uri[]][]);
}
@command('git.refresh', { model: true })
async refresh(model: Repository): Promise<void> {
await model.status();
......@@ -450,15 +435,7 @@ export class CommandCenter {
}
const resources = scmResources.map(r => r.resourceUri);
const resourcesByModel = this.groupByModel(resources);
await Promise.all(resourcesByModel.map(async ([model, resources]) => {
if (!model) {
return; // TODO@joao
}
await model.add(...resources);
}));
await this.model.add(...resources);
}
@command('git.stageAll', { model: true })
......@@ -1237,12 +1214,12 @@ export class CommandCenter {
if (!options.model) {
result = Promise.resolve(method.apply(this, args));
} else {
result = this.modelRegistry.pickModel().then(model => {
if (!model) {
result = this.model.pickRepository().then(repository => {
if (!repository) {
return Promise.reject(localize('modelnotfound', "Git model not found"));
}
return Promise.resolve(method.apply(this, [model, ...args]));
return Promise.resolve(method.apply(this, [repository, ...args]));
});
}
......@@ -1309,14 +1286,14 @@ export class CommandCenter {
if (uri.scheme === 'file') {
const uriString = uri.toString();
const model = this.modelRegistry.getModel(uri);
const repository = this.model.getRepository(uri);
if (!model) {
if (!repository) {
return undefined;
}
return model.workingTreeGroup.resources.filter(r => r.resourceUri.toString() === uriString)[0]
|| model.indexGroup.resources.filter(r => r.resourceUri.toString() === uriString)[0];
return repository.workingTreeGroup.resources.filter(r => r.resourceUri.toString() === uriString)[0]
|| repository.indexGroup.resources.filter(r => r.resourceUri.toString() === uriString)[0];
}
}
......
......@@ -10,7 +10,7 @@ const localize = nls.config(process.env.VSCODE_NLS_CONFIG)();
import { ExtensionContext, workspace, window, Disposable, commands, Uri } from 'vscode';
import { findGit, Git, IGit } from './git';
import { Repository } from './repository';
import { ModelRegistry } from './modelRegistry';
import { Model } from './model';
import { GitSCMProvider } from './scmProvider';
import { CommandCenter } from './commands';
import { StatusBarCommands } from './statusbar';
......@@ -37,17 +37,17 @@ async function init(context: ExtensionContext, disposables: Disposable[]): Promi
const askpass = new Askpass();
const env = await askpass.getEnv();
const git = new Git({ gitPath: info.path, version: info.version, env });
const modelRegistry = new ModelRegistry();
const model = new Model();
if (!workspaceRootPath || !enabled) {
const commandCenter = new CommandCenter(git, modelRegistry, outputChannel, telemetryReporter);
const commandCenter = new CommandCenter(git, model, outputChannel, telemetryReporter);
disposables.push(commandCenter);
return;
}
const workspaceRoot = Uri.file(workspaceRootPath);
const repository = new Repository(git, workspaceRoot);
modelRegistry.register(workspaceRoot, repository);
model.register(workspaceRoot, repository);
outputChannel.appendLine(localize('using git', "Using git {0} from {1}", info.version, info.path));
......@@ -55,7 +55,7 @@ async function init(context: ExtensionContext, disposables: Disposable[]): Promi
git.onOutput.addListener('log', onOutput);
disposables.push(toDisposable(() => git.onOutput.removeListener('log', onOutput)));
const commandCenter = new CommandCenter(git, modelRegistry, outputChannel, telemetryReporter);
const commandCenter = new CommandCenter(git, model, outputChannel, telemetryReporter);
const statusBarCommands = new StatusBarCommands(repository);
const provider = new GitSCMProvider(repository, statusBarCommands);
const contentProvider = new GitContentProvider(repository);
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Uri, window, QuickPickItem, Disposable } from 'vscode';
import { GitErrorCodes } from './git';
import { Repository, IRepository, State } from './repository';
import { memoize } from './decorators';
import { toDisposable, filterEvent, once } from './util';
import * as path from 'path';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
class RepositoryPick implements QuickPickItem {
@memoize get label(): string { return path.basename(this.repositoryRoot.fsPath); }
@memoize get description(): string { return path.dirname(this.repositoryRoot.fsPath); }
constructor(protected repositoryRoot: Uri, public readonly repository: Repository) { }
}
export class Model implements IRepository {
private repositories: Map<Uri, Repository> = new Map<Uri, Repository>();
register(uri: Uri, repository: Repository): Disposable {
if (this.repositories.has(uri)) {
// TODO@Joao: what should happen?
throw new Error('Cant register repository with the same URI');
}
this.repositories.set(uri, repository);
const onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === State.NotAGitRepository);
const listener = onDidDisappearRepository(() => disposable.dispose());
const disposable = toDisposable(once(() => {
this.repositories.delete(uri);
listener.dispose();
}));
return disposable;
}
async pickRepository(): Promise<Repository | undefined> {
const picks = Array.from(this.repositories.entries(), ([uri, model]) => new RepositoryPick(uri, model));
const placeHolder = localize('pick repo', "Choose a repository");
const pick = await window.showQuickPick(picks, { placeHolder });
return pick && pick.repository;
}
getRepository(resource: Uri): Repository | undefined {
const resourcePath = resource.fsPath;
for (let [repositoryRoot, model] of this.repositories) {
const repositoryRootPath = repositoryRoot.fsPath;
const relativePath = path.relative(repositoryRootPath, resourcePath);
if (!/^\./.test(relativePath)) {
return model;
}
}
return undefined;
}
private async runByRepository<T>(resources: Uri[], fn: (repository: Repository, resources: Uri[]) => Promise<T>): Promise<T[]> {
const groups = resources.reduce((result, resource) => {
const repository = this.getRepository(resource);
// TODO@Joao: what should happen?
if (!repository) {
console.warn('Could not find git repository for ', resource);
return result;
}
const tuple = result.filter(p => p[0] === repository)[0];
if (tuple) {
tuple.resources.push(resource);
} else {
result.push({ repository, resources: [resource] });
}
return result;
}, [] as { repository: Repository, resources: Uri[] }[]);
const promises = groups
.map(({ repository, resources }) => this.run(repository, () => fn(repository as Repository, resources)));
return Promise.all(promises);
}
private async run<T>(repository: Repository, fn: () => Promise<T>): Promise<T> {
try {
return fn();
} catch (err) {
if (err.gitErrorCode === GitErrorCodes.NotAGitRepository) {
// do something about it
}
throw err;
}
}
// IRepository
async add(...resources: Uri[]): Promise<void> {
await this.runByRepository(resources, async (repository, resources) => repository.add(...resources));
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Uri, window, QuickPickItem } from 'vscode';
import { Repository } from './repository';
import { memoize } from './decorators';
import * as path from 'path';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
class ModelPick implements QuickPickItem {
@memoize get label(): string { return path.basename(this.repositoryRoot.fsPath); }
@memoize get description(): string { return path.dirname(this.repositoryRoot.fsPath); }
constructor(protected repositoryRoot: Uri, public readonly model: Repository) { }
}
export class ModelRegistry {
private models: Map<Uri, Repository> = new Map<Uri, Repository>();
register(uri: Uri, model): void {
this.models.set(uri, model);
}
async pickModel(): Promise<Repository | undefined> {
const picks = Array.from(this.models.entries(), ([uri, model]) => new ModelPick(uri, model));
const placeHolder = localize('pick repo', "Choose a repository");
const pick = await window.showQuickPick(picks, { placeHolder });
return pick && pick.model;
}
getModel(resource: Uri): Repository | undefined {
const resourcePath = resource.fsPath;
for (let [repositoryRoot, model] of this.models) {
const repositoryRootPath = repositoryRoot.fsPath;
const relativePath = path.relative(repositoryRootPath, resourcePath);
if (!/^\./.test(relativePath)) {
return model;
}
}
return undefined;
}
async resolve(resource: Uri): Promise<Repository | undefined> {
const model = this.getModel(resource);
if (model) {
return model;
}
const picks = Array.from(this.models.entries(), ([uri, model]) => new ModelPick(uri, model));
const placeHolder = localize('pick repo', "Choose a repository");
const pick = await window.showQuickPick(picks, { placeHolder });
if (pick) {
return pick.model;
}
return undefined;
}
}
......@@ -295,7 +295,11 @@ export interface CommitOptions {
signCommit?: boolean;
}
export class Repository implements Disposable {
export interface IRepository {
add(...resources: Uri[]): Promise<void>;
}
export class Repository implements IRepository, Disposable {
private _onDidChangeRepository = new EventEmitter<Uri>();
readonly onDidChangeRepository: Event<Uri> = this._onDidChangeRepository.event;
......
......@@ -56,7 +56,7 @@ export function done<T>(promise: Promise<T>): Promise<void> {
return promise.then<void>(() => void 0);
}
export function once<T>(event: Event<T>): Event<T> {
export function onceEvent<T>(event: Event<T>): Event<T> {
return (listener, thisArgs = null, disposables?) => {
const result = event(e => {
result.dispose();
......@@ -68,7 +68,19 @@ export function once<T>(event: Event<T>): Event<T> {
}
export function eventToPromise<T>(event: Event<T>): Promise<T> {
return new Promise<T>(c => once(event)(c));
return new Promise<T>(c => onceEvent(event)(c));
}
export function once(fn: (...args: any[]) => any): (...args: any[]) => any {
let didRun = false;
return (...args) => {
if (didRun) {
return;
}
return fn(...args);
};
}
// TODO@Joao: replace with Object.assign
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册