Git - polish discard changes dialogs (#240519)

* Git - polish git clean dialog

* Remove duplication

* More polish of the dialogs
This commit is contained in:
Ladislau Szomoru
2025-02-12 15:39:58 +01:00
committed by GitHub
parent 23213fe75d
commit deb13fce88
3 changed files with 115 additions and 116 deletions

View File

@@ -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(

View File

@@ -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(