mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-27 12:04:04 +01:00
Git - polish discard changes dialogs (#240519)
* Git - polish git clean dialog * Remove duplication * More polish of the dialogs
This commit is contained in:
@@ -14,7 +14,7 @@ import { Model } from './model';
|
||||
import { GitResourceGroup, Repository, Resource, ResourceGroupType } from './repository';
|
||||
import { DiffEditorSelectionHunkToolbarContext, applyLineChanges, getIndexDiffInformation, getModifiedRange, getWorkingTreeDiffInformation, intersectDiffWithRange, invertLineChange, toLineChanges, toLineRanges } from './staging';
|
||||
import { fromGitUri, toGitUri, isGitUri, toMergeUris, toMultiFileDiffEditorUris } from './uri';
|
||||
import { DiagnosticSeverityConfig, dispose, getCommitShortHash, grep, isDefined, isDescendant, pathEquals, relativePath, toDiagnosticSeverity, truncate } from './util';
|
||||
import { DiagnosticSeverityConfig, dispose, getCommitShortHash, grep, isDefined, isDescendant, isWindows, pathEquals, relativePath, toDiagnosticSeverity, truncate } from './util';
|
||||
import { GitTimelineItem } from './timelineProvider';
|
||||
import { ApiRepository } from './api/api1';
|
||||
import { getRemoteSourceActions, pickRemoteSource } from './remoteSource';
|
||||
@@ -2017,84 +2017,12 @@ export class CommandCenter {
|
||||
return;
|
||||
}
|
||||
|
||||
const untrackedCount = scmResources.reduce((s, r) => s + (r.type === Status.UNTRACKED ? 1 : 0), 0);
|
||||
let message: string;
|
||||
let yes = l10n.t('Discard Changes');
|
||||
|
||||
if (scmResources.length === 1) {
|
||||
if (untrackedCount > 0) {
|
||||
message = l10n.t('Are you sure you want to DELETE {0}?\nThis is IRREVERSIBLE!\nThis file will be FOREVER LOST if you proceed.', path.basename(scmResources[0].resourceUri.fsPath));
|
||||
yes = l10n.t('Delete file');
|
||||
} else {
|
||||
if (scmResources[0].type === Status.DELETED) {
|
||||
yes = l10n.t('Restore file');
|
||||
message = l10n.t('Are you sure you want to restore {0}?', path.basename(scmResources[0].resourceUri.fsPath));
|
||||
} else {
|
||||
message = l10n.t('Are you sure you want to discard changes in {0}?', path.basename(scmResources[0].resourceUri.fsPath));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (scmResources.every(resource => resource.type === Status.DELETED)) {
|
||||
yes = l10n.t('Restore files');
|
||||
message = l10n.t('Are you sure you want to restore {0} files?', scmResources.length);
|
||||
} else {
|
||||
message = l10n.t('Are you sure you want to discard changes in {0} files?', scmResources.length);
|
||||
}
|
||||
|
||||
if (untrackedCount > 0) {
|
||||
message = `${message}\n\n${l10n.t('This will DELETE {0} untracked files!\nThis is IRREVERSIBLE!\nThese files will be FOREVER LOST.', untrackedCount)}`;
|
||||
}
|
||||
}
|
||||
|
||||
const pick = await window.showWarningMessage(message, { modal: true }, yes);
|
||||
|
||||
if (pick !== yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resources = scmResources.map(r => r.resourceUri);
|
||||
await this.runByRepository(resources, async (repository, resources) => repository.clean(resources));
|
||||
await this._cleanAll(scmResources);
|
||||
}
|
||||
|
||||
@command('git.cleanAll', { repository: true })
|
||||
async cleanAll(repository: Repository): Promise<void> {
|
||||
let resources = repository.workingTreeGroup.resourceStates;
|
||||
|
||||
if (resources.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trackedResources = resources.filter(r => r.type !== Status.UNTRACKED && r.type !== Status.IGNORED);
|
||||
const untrackedResources = resources.filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED);
|
||||
|
||||
if (untrackedResources.length === 0) {
|
||||
await this._cleanTrackedChanges(repository, resources);
|
||||
} else if (resources.length === 1) {
|
||||
await this._cleanUntrackedChange(repository, resources[0]);
|
||||
} else if (trackedResources.length === 0) {
|
||||
await this._cleanUntrackedChanges(repository, resources);
|
||||
} else { // resources.length > 1 && untrackedResources.length > 0 && trackedResources.length > 0
|
||||
const untrackedMessage = untrackedResources.length === 1
|
||||
? l10n.t('The following untracked file will be DELETED FROM DISK if discarded: {0}.', path.basename(untrackedResources[0].resourceUri.fsPath))
|
||||
: l10n.t('There are {0} untracked files which will be DELETED FROM DISK if discarded.', untrackedResources.length);
|
||||
|
||||
const message = l10n.t('{0}\n\nThis is IRREVERSIBLE, your current working set will be FOREVER LOST.', untrackedMessage, resources.length);
|
||||
|
||||
const yesTracked = trackedResources.length === 1
|
||||
? l10n.t('Discard 1 Tracked File', trackedResources.length)
|
||||
: l10n.t('Discard {0} Tracked Files', trackedResources.length);
|
||||
|
||||
const yesAll = l10n.t('Discard All {0} Files', resources.length);
|
||||
const pick = await window.showWarningMessage(message, { modal: true }, yesTracked, yesAll);
|
||||
|
||||
if (pick === yesTracked) {
|
||||
resources = trackedResources;
|
||||
} else if (pick !== yesAll) {
|
||||
return;
|
||||
}
|
||||
|
||||
await repository.clean(resources.map(r => r.resourceUri));
|
||||
}
|
||||
await this._cleanAll(repository.workingTreeGroup.resourceStates);
|
||||
}
|
||||
|
||||
@command('git.cleanAllTracked', { repository: true })
|
||||
@@ -2106,7 +2034,7 @@ export class CommandCenter {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._cleanTrackedChanges(repository, resources);
|
||||
await this._cleanTrackedChanges(resources);
|
||||
}
|
||||
|
||||
@command('git.cleanAllUntracked', { repository: true })
|
||||
@@ -2118,51 +2046,123 @@ export class CommandCenter {
|
||||
return;
|
||||
}
|
||||
|
||||
if (resources.length === 1) {
|
||||
await this._cleanUntrackedChange(repository, resources[0]);
|
||||
await this._cleanUntrackedChanges(resources);
|
||||
}
|
||||
|
||||
private async _cleanAll(resources: Resource[]): Promise<void> {
|
||||
if (resources.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trackedResources = resources.filter(r => r.type !== Status.UNTRACKED && r.type !== Status.IGNORED);
|
||||
const untrackedResources = resources.filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED);
|
||||
|
||||
if (untrackedResources.length === 0) {
|
||||
// Tracked files only
|
||||
await this._cleanTrackedChanges(resources);
|
||||
} else if (trackedResources.length === 0) {
|
||||
// Untracked files only
|
||||
await this._cleanUntrackedChanges(resources);
|
||||
} else {
|
||||
await this._cleanUntrackedChanges(repository, resources);
|
||||
// Tracked & Untracked files
|
||||
const [untrackedMessage, untrackedMessageDetail] = this.getDiscardUntrackedChangesDialogDetails(untrackedResources);
|
||||
|
||||
const trackedMessage = trackedResources.length === 1
|
||||
? l10n.t('\n\nAre you sure you want to discard changes in \'{0}\'?', path.basename(trackedResources[0].resourceUri.fsPath))
|
||||
: l10n.t('\n\nAre you sure you want to discard ALL changes in {0} files?', trackedResources.length);
|
||||
|
||||
const yesTracked = trackedResources.length === 1
|
||||
? l10n.t('Discard 1 Tracked File')
|
||||
: l10n.t('Discard All {0} Tracked Files', trackedResources.length);
|
||||
|
||||
const yesAll = l10n.t('Discard All {0} Files', resources.length);
|
||||
const pick = await window.showWarningMessage(`${untrackedMessage} ${untrackedMessageDetail}${trackedMessage}\n\nThis is IRREVERSIBLE!\nYour current working set will be FOREVER LOST if you proceed.`, { modal: true }, yesTracked, yesAll);
|
||||
|
||||
if (pick === yesTracked) {
|
||||
resources = trackedResources;
|
||||
} else if (pick !== yesAll) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resourceUris = resources.map(r => r.resourceUri);
|
||||
await this.runByRepository(resourceUris, async (repository, resources) => repository.clean(resources));
|
||||
}
|
||||
}
|
||||
|
||||
private async _cleanTrackedChanges(repository: Repository, resources: Resource[]): Promise<void> {
|
||||
private async _cleanTrackedChanges(resources: Resource[]): Promise<void> {
|
||||
const allResourcesDeleted = resources.every(r => r.type === Status.DELETED);
|
||||
|
||||
const message = allResourcesDeleted
|
||||
? resources.length === 1
|
||||
? l10n.t('Are you sure you want to restore \'{0}\'?', path.basename(resources[0].resourceUri.fsPath))
|
||||
: l10n.t('Are you sure you want to restore ALL {0} files?', resources.length)
|
||||
: resources.length === 1
|
||||
? l10n.t('Are you sure you want to discard changes in \'{0}\'?', path.basename(resources[0].resourceUri.fsPath))
|
||||
: l10n.t('Are you sure you want to discard ALL changes in {0} files?\n\nThis is IRREVERSIBLE!\nYour current working set will be FOREVER LOST if you proceed.', resources.length);
|
||||
|
||||
const yes = allResourcesDeleted
|
||||
? resources.length === 1
|
||||
? l10n.t('Restore File')
|
||||
: l10n.t('Restore All {0} Files', resources.length)
|
||||
: resources.length === 1
|
||||
? l10n.t('Discard File')
|
||||
: l10n.t('Discard All {0} Files', resources.length);
|
||||
|
||||
const pick = await window.showWarningMessage(message, { modal: true }, yes);
|
||||
|
||||
if (pick !== yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resourceUris = resources.map(r => r.resourceUri);
|
||||
await this.runByRepository(resourceUris, async (repository, resources) => repository.clean(resources));
|
||||
}
|
||||
|
||||
private async _cleanUntrackedChanges(resources: Resource[]): Promise<void> {
|
||||
const [message, messageDetail, primaryAction] = this.getDiscardUntrackedChangesDialogDetails(resources);
|
||||
const pick = await window.showWarningMessage(message, { detail: messageDetail, modal: true }, primaryAction);
|
||||
|
||||
if (pick !== primaryAction) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resourceUris = resources.map(r => r.resourceUri);
|
||||
await this.runByRepository(resourceUris, async (repository, resources) => repository.clean(resources));
|
||||
}
|
||||
|
||||
private getDiscardUntrackedChangesDialogDetails(resources: Resource[]): [string, string, string] {
|
||||
const config = workspace.getConfiguration('git');
|
||||
const untrackedChangesSoftDelete = config.get<boolean>('untrackedChangesSoftDelete', true) === true;
|
||||
|
||||
const messageWarning = !untrackedChangesSoftDelete
|
||||
? resources.length === 1
|
||||
? '\n\nThis is IRREVERSIBLE!\nThis file will be FOREVER LOST if you proceed.'
|
||||
: '\n\nThis is IRREVERSIBLE!\nThese files will be FOREVER LOST if you proceed.'
|
||||
: '';
|
||||
|
||||
const message = resources.length === 1
|
||||
? l10n.t('Are you sure you want to discard changes in {0}?', path.basename(resources[0].resourceUri.fsPath))
|
||||
: l10n.t('Are you sure you want to discard ALL changes in {0} files?\nThis is IRREVERSIBLE!\nYour current working set will be FOREVER LOST if you proceed.', resources.length);
|
||||
const yes = resources.length === 1
|
||||
? l10n.t('Discard 1 File')
|
||||
: l10n.t('Discard All {0} Files', resources.length);
|
||||
const pick = await window.showWarningMessage(message, { modal: true }, yes);
|
||||
? l10n.t('Are you sure you want to DELETE the following untracked file: \'{0}\'?{1}', path.basename(resources[0].resourceUri.fsPath), messageWarning)
|
||||
: l10n.t('Are you sure you want to DELETE the {0} untracked files?{1}', resources.length, messageWarning);
|
||||
|
||||
if (pick !== yes) {
|
||||
return;
|
||||
}
|
||||
const messageDetail = untrackedChangesSoftDelete
|
||||
? isWindows
|
||||
? resources.length === 1
|
||||
? 'You can restore this file from the Recycle Bin.'
|
||||
: 'You can restore these files from the Recycle Bin.'
|
||||
: resources.length === 1
|
||||
? 'You can restore this file from the Trash.'
|
||||
: 'You can restore these files from the Trash.'
|
||||
: '';
|
||||
|
||||
await repository.clean(resources.map(r => r.resourceUri));
|
||||
}
|
||||
const primaryAction = untrackedChangesSoftDelete
|
||||
? isWindows
|
||||
? l10n.t('Move to Recycle Bin')
|
||||
: l10n.t('Move to Trash')
|
||||
: resources.length === 1
|
||||
? l10n.t('Delete File')
|
||||
: l10n.t('Delete All {0} Files', resources.length);
|
||||
|
||||
private async _cleanUntrackedChange(repository: Repository, resource: Resource): Promise<void> {
|
||||
const message = l10n.t('Are you sure you want to DELETE {0}?\nThis is IRREVERSIBLE!\nThis file will be FOREVER LOST if you proceed.', path.basename(resource.resourceUri.fsPath));
|
||||
const yes = l10n.t('Delete file');
|
||||
const pick = await window.showWarningMessage(message, { modal: true }, yes);
|
||||
|
||||
if (pick !== yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
await repository.clean([resource.resourceUri]);
|
||||
}
|
||||
|
||||
private async _cleanUntrackedChanges(repository: Repository, resources: Resource[]): Promise<void> {
|
||||
const message = l10n.t('Are you sure you want to DELETE {0} files?\nThis is IRREVERSIBLE!\nThese files will be FOREVER LOST if you proceed.', resources.length);
|
||||
const yes = l10n.t('Delete Files');
|
||||
const pick = await window.showWarningMessage(message, { modal: true }, yes);
|
||||
|
||||
if (pick !== yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
await repository.clean(resources.map(r => r.resourceUri));
|
||||
return [message, messageDetail, primaryAction];
|
||||
}
|
||||
|
||||
private async smartCommit(
|
||||
|
||||
@@ -1373,7 +1373,7 @@ export class Repository implements Disposable {
|
||||
}
|
||||
|
||||
async clean(resources: Uri[]): Promise<void> {
|
||||
const config = workspace.getConfiguration('git', Uri.file(this.root));
|
||||
const config = workspace.getConfiguration('git');
|
||||
const untrackedChangesSoftDelete = config.get<boolean>('untrackedChangesSoftDelete', true) === true;
|
||||
|
||||
await this.run(
|
||||
|
||||
Reference in New Issue
Block a user