mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-27 12:04:04 +01:00
@@ -138,20 +138,22 @@ const ImageMimetypes = [
|
||||
'image/bmp'
|
||||
];
|
||||
|
||||
async function categorizeResourceByResolution(resources: Resource[]): Promise<{ merge: Resource[], resolved: Resource[], unresolved: Resource[] }> {
|
||||
async function categorizeResourceByResolution(resources: Resource[]): Promise<{ merge: Resource[], resolved: Resource[], unresolved: Resource[], deletionConflicts: Resource[] }> {
|
||||
const selection = resources.filter(s => s instanceof Resource) as Resource[];
|
||||
const merge = selection.filter(s => s.resourceGroupType === ResourceGroupType.Merge);
|
||||
const isBothAddedOrModified = (s: Resource) => s.type === Status.BOTH_MODIFIED || s.type === Status.BOTH_ADDED;
|
||||
const isAnyDeleted = (s: Resource) => s.type === Status.DELETED_BY_THEM || s.type === Status.DELETED_BY_US;
|
||||
const possibleUnresolved = merge.filter(isBothAddedOrModified);
|
||||
const promises = possibleUnresolved.map(s => grep(s.resourceUri.fsPath, /^<{7}|^={7}|^>{7}/));
|
||||
const unresolvedBothModified = await Promise.all<boolean>(promises);
|
||||
const resolved = possibleUnresolved.filter((s, i) => !unresolvedBothModified[i]);
|
||||
const deletionConflicts = merge.filter(s => isAnyDeleted(s));
|
||||
const unresolved = [
|
||||
...merge.filter(s => !isBothAddedOrModified(s)),
|
||||
...merge.filter(s => !isBothAddedOrModified(s) && !isAnyDeleted(s)),
|
||||
...possibleUnresolved.filter((s, i) => unresolvedBothModified[i])
|
||||
];
|
||||
|
||||
return { merge, resolved, unresolved };
|
||||
return { merge, resolved, unresolved, deletionConflicts };
|
||||
}
|
||||
|
||||
enum PushType {
|
||||
@@ -215,7 +217,10 @@ export class CommandCenter {
|
||||
right = toGitUri(resource.resourceUri, resource.resourceGroupType === ResourceGroupType.Index ? 'index' : 'wt', { submoduleOf: repository.root });
|
||||
}
|
||||
} else {
|
||||
left = await this.getLeftResource(resource);
|
||||
if (resource.type !== Status.DELETED_BY_THEM) {
|
||||
left = await this.getLeftResource(resource);
|
||||
}
|
||||
|
||||
right = await this.getRightResource(resource);
|
||||
}
|
||||
|
||||
@@ -242,7 +247,7 @@ export class CommandCenter {
|
||||
}
|
||||
|
||||
if (!left) {
|
||||
await commands.executeCommand<void>('vscode.open', right, opts);
|
||||
await commands.executeCommand<void>('vscode.open', right, opts, title);
|
||||
} else {
|
||||
await commands.executeCommand<void>('vscode.diff', left, right, title, opts);
|
||||
}
|
||||
@@ -310,10 +315,15 @@ export class CommandCenter {
|
||||
return this.getURI(resource.resourceUri, '');
|
||||
|
||||
case Status.INDEX_DELETED:
|
||||
case Status.DELETED_BY_THEM:
|
||||
case Status.DELETED:
|
||||
return this.getURI(resource.resourceUri, 'HEAD');
|
||||
|
||||
case Status.DELETED_BY_US:
|
||||
return this.getURI(resource.resourceUri, '~3');
|
||||
|
||||
case Status.DELETED_BY_THEM:
|
||||
return this.getURI(resource.resourceUri, '~2');
|
||||
|
||||
case Status.MODIFIED:
|
||||
case Status.UNTRACKED:
|
||||
case Status.IGNORED:
|
||||
@@ -344,13 +354,18 @@ export class CommandCenter {
|
||||
switch (resource.type) {
|
||||
case Status.INDEX_MODIFIED:
|
||||
case Status.INDEX_RENAMED:
|
||||
case Status.DELETED_BY_THEM:
|
||||
return `${basename} (Index)`;
|
||||
|
||||
case Status.MODIFIED:
|
||||
case Status.BOTH_ADDED:
|
||||
case Status.BOTH_MODIFIED:
|
||||
return `${basename} (Working Tree)`;
|
||||
|
||||
case Status.DELETED_BY_US:
|
||||
return `${basename} (Theirs)`;
|
||||
|
||||
case Status.DELETED_BY_THEM:
|
||||
return `${basename} (Ours)`;
|
||||
}
|
||||
|
||||
return '';
|
||||
@@ -704,7 +719,7 @@ export class CommandCenter {
|
||||
}
|
||||
|
||||
const selection = resourceStates.filter(s => s instanceof Resource) as Resource[];
|
||||
const { resolved, unresolved } = await categorizeResourceByResolution(selection);
|
||||
const { resolved, unresolved, deletionConflicts } = await categorizeResourceByResolution(selection);
|
||||
|
||||
if (unresolved.length > 0) {
|
||||
const message = unresolved.length > 1
|
||||
@@ -719,6 +734,20 @@ export class CommandCenter {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.runByRepository(deletionConflicts.map(r => r.resourceUri), async (repository, resources) => {
|
||||
for (const resource of resources) {
|
||||
await this._stageDeletionConflict(repository, resource);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
if (/Cancelled/.test(err.message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
const workingTree = selection.filter(s => s.resourceGroupType === ResourceGroupType.WorkingTree);
|
||||
const scmResources = [...workingTree, ...resolved, ...unresolved];
|
||||
|
||||
@@ -734,7 +763,19 @@ export class CommandCenter {
|
||||
@command('git.stageAll', { repository: true })
|
||||
async stageAll(repository: Repository): Promise<void> {
|
||||
const resources = repository.mergeGroup.resourceStates.filter(s => s instanceof Resource) as Resource[];
|
||||
const { merge, unresolved } = await categorizeResourceByResolution(resources);
|
||||
const { merge, unresolved, deletionConflicts } = await categorizeResourceByResolution(resources);
|
||||
|
||||
try {
|
||||
for (const deletionConflict of deletionConflicts) {
|
||||
await this._stageDeletionConflict(repository, deletionConflict.resourceUri);
|
||||
}
|
||||
} catch (err) {
|
||||
if (/Cancelled/.test(err.message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (unresolved.length > 0) {
|
||||
const message = unresolved.length > 1
|
||||
@@ -752,6 +793,41 @@ export class CommandCenter {
|
||||
await repository.add([]);
|
||||
}
|
||||
|
||||
private async _stageDeletionConflict(repository: Repository, uri: Uri): Promise<void> {
|
||||
const uriString = uri.toString();
|
||||
const resource = repository.mergeGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString)[0];
|
||||
|
||||
if (!resource) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (resource.type === Status.DELETED_BY_THEM) {
|
||||
const keepIt = localize('keep ours', "Keep Our Version");
|
||||
const deleteIt = localize('delete', "Delete File");
|
||||
const result = await window.showInformationMessage(localize('deleted by them', "File '{0}' was deleted by them and modified by us.\n\nWhat would you like to do?", path.basename(uri.fsPath)), { modal: true }, keepIt, deleteIt);
|
||||
|
||||
if (result === keepIt) {
|
||||
await repository.add([uri]);
|
||||
} else if (result === deleteIt) {
|
||||
await repository.rm([uri]);
|
||||
} else {
|
||||
throw new Error('Cancelled');
|
||||
}
|
||||
} else if (resource.type === Status.DELETED_BY_US) {
|
||||
const keepIt = localize('keep theirs', "Keep Their Version");
|
||||
const deleteIt = localize('delete', "Delete File");
|
||||
const result = await window.showInformationMessage(localize('deleted by us', "File '{0}' was deleted by us and modified by them.\n\nWhat would you like to do?", path.basename(uri.fsPath)), { modal: true }, keepIt, deleteIt);
|
||||
|
||||
if (result === keepIt) {
|
||||
await repository.add([uri]);
|
||||
} else if (result === deleteIt) {
|
||||
await repository.rm([uri]);
|
||||
} else {
|
||||
throw new Error('Cancelled');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@command('git.stageChange')
|
||||
async stageChange(uri: Uri, changes: LineChange[], index: number): Promise<void> {
|
||||
const textEditor = window.visibleTextEditors.filter(e => e.document.uri.toString() === uri.toString())[0];
|
||||
|
||||
@@ -116,6 +116,8 @@ export class GitContentProvider {
|
||||
const uriString = fileUri.toString();
|
||||
const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString);
|
||||
ref = indexStatus ? '' : 'HEAD';
|
||||
} else if (/^~\d$/.test(ref)) {
|
||||
ref = `:${ref[1]}`;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -900,6 +900,18 @@ export class Repository {
|
||||
await this.run(args);
|
||||
}
|
||||
|
||||
async rm(paths: string[]): Promise<void> {
|
||||
const args = ['rm', '--'];
|
||||
|
||||
if (!paths || !paths.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
args.push(...paths);
|
||||
|
||||
await this.run(args);
|
||||
}
|
||||
|
||||
async stage(path: string, data: string): Promise<void> {
|
||||
const child = this.stream(['hash-object', '--stdin', '-w', '--path', path], { stdio: [null, null, null] });
|
||||
child.stdin.end(data, 'utf8');
|
||||
|
||||
@@ -198,12 +198,14 @@ export class Resource implements SourceControlResourceState {
|
||||
return 'U';
|
||||
case Status.IGNORED:
|
||||
return 'I';
|
||||
case Status.DELETED_BY_THEM:
|
||||
return 'D';
|
||||
case Status.DELETED_BY_US:
|
||||
return 'D';
|
||||
case Status.INDEX_COPIED:
|
||||
case Status.BOTH_DELETED:
|
||||
case Status.ADDED_BY_US:
|
||||
case Status.DELETED_BY_THEM:
|
||||
case Status.ADDED_BY_THEM:
|
||||
case Status.DELETED_BY_US:
|
||||
case Status.BOTH_ADDED:
|
||||
case Status.BOTH_MODIFIED:
|
||||
return 'C';
|
||||
@@ -281,6 +283,7 @@ export const enum Operation {
|
||||
Diff = 'Diff',
|
||||
MergeBase = 'MergeBase',
|
||||
Add = 'Add',
|
||||
Remove = 'Remove',
|
||||
RevertFiles = 'RevertFiles',
|
||||
Commit = 'Commit',
|
||||
Clean = 'Clean',
|
||||
@@ -755,6 +758,10 @@ export class Repository implements Disposable {
|
||||
await this.run(Operation.Add, () => this.repository.add(resources.map(r => r.fsPath)));
|
||||
}
|
||||
|
||||
async rm(resources: Uri[]): Promise<void> {
|
||||
await this.run(Operation.Remove, () => this.repository.rm(resources.map(r => r.fsPath)));
|
||||
}
|
||||
|
||||
async stage(resource: Uri, contents: string): Promise<void> {
|
||||
const relativePath = path.relative(this.repository.root, resource.fsPath).replace(/\\/g, '/');
|
||||
await this.run(Operation.Stage, () => this.repository.stage(relativePath, contents));
|
||||
|
||||
@@ -263,16 +263,16 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
|
||||
|
||||
// --- commands
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.open', function (accessor: ServicesAccessor, args: [URI, IEditorOptions, EditorViewColumn]) {
|
||||
CommandsRegistry.registerCommand('_workbench.open', function (accessor: ServicesAccessor, args: [URI, IEditorOptions, EditorViewColumn, string?]) {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const openerService = accessor.get(IOpenerService);
|
||||
|
||||
const [resource, options, position] = args;
|
||||
const [resource, options, position, label] = args;
|
||||
|
||||
if (options || typeof position === 'number') {
|
||||
// use editor options or editor view column as a hint to use the editor service for opening
|
||||
return editorService.openEditor({ resource, options }, viewColumnToEditorGroup(editorGroupService, position)).then(_ => void 0);
|
||||
return editorService.openEditor({ resource, options, label }, viewColumnToEditorGroup(editorGroupService, position)).then(_ => void 0);
|
||||
}
|
||||
|
||||
if (resource && resource.scheme === 'command') {
|
||||
|
||||
@@ -77,7 +77,7 @@ CommandsRegistry.registerCommand(DiffAPICommand.ID, adjustHandler(DiffAPICommand
|
||||
|
||||
export class OpenAPICommand {
|
||||
public static ID = 'vscode.open';
|
||||
public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions): Thenable<any> {
|
||||
public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, label?: string): Thenable<any> {
|
||||
let options: ITextEditorOptions;
|
||||
let position: EditorViewColumn;
|
||||
|
||||
@@ -93,7 +93,8 @@ export class OpenAPICommand {
|
||||
return executor.executeCommand('_workbench.open', [
|
||||
resource,
|
||||
options,
|
||||
position
|
||||
position,
|
||||
label
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user