diff --git a/package.json b/package.json index fde24f10..d9746b3d 100644 --- a/package.json +++ b/package.json @@ -304,6 +304,15 @@ "dark": "icons/dark/clean.svg" } }, + { + "command": "svn.revertExplorer", + "title": "Revert with SVN", + "category": "SVN", + "icon": { + "light": "icons/light/clean.svg", + "dark": "icons/dark/clean.svg" + } + }, { "command": "svn.revertAll", "title": "Revert All Changes", @@ -483,6 +492,10 @@ "command": "svn.revert", "when": "config.svn.enabled && svnOpenRepositoryCount != 0" }, + { + "command": "svn.revertExplorer", + "when": "false" + }, { "command": "svn.update", "when": "config.svn.enabled && svnOpenRepositoryCount != 0" @@ -902,6 +915,11 @@ "command": "svn.changelist", "group": "9_svn", "when": "config.svn.enabled && svnOpenRepositoryCount != 0" + }, + { + "command": "svn.revertExplorer", + "group": "7_modification", + "when": "config.svn.enabled && svnOpenRepositoryCount != 0" } ] }, diff --git a/src/commands.ts b/src/commands.ts index 3aac4d1f..85dbd69e 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -37,6 +37,7 @@ import { Resolved } from "./commands/resolved"; import { Revert } from "./commands/revert"; import { RevertAll } from "./commands/revertAll"; import { RevertChange } from "./commands/revertChange"; +import { RevertExplorer } from "./commands/revertExplorer"; import { RevertSelectedRanges } from "./commands/revertSelectedRanges"; import { SwitchBranch } from "./commands/switchBranch"; import { Update } from "./commands/update"; @@ -86,4 +87,5 @@ export function registerCommands(model: Model, disposables: Disposable[]) { disposables.push(new OpenHeadFile()); disposables.push(new RevertAll()); disposables.push(new PickCommitMessage()); + disposables.push(new RevertExplorer()); } diff --git a/src/commands/revert.ts b/src/commands/revert.ts index c1b17118..0ed2f5de 100644 --- a/src/commands/revert.ts +++ b/src/commands/revert.ts @@ -1,5 +1,5 @@ import { SourceControlResourceState, window } from "vscode"; -import { SvnDepth } from "../common/types"; +import { checkAndPromptDepth, confirmRevert } from "../input/revert"; import { Command } from "./command"; export class Revert extends Command { @@ -10,33 +10,17 @@ export class Revert extends Command { public async execute(...resourceStates: SourceControlResourceState[]) { const selection = await this.getResourceStates(resourceStates); - if (selection.length === 0) { + if (selection.length === 0 || !(await confirmRevert())) { return; } - const yes = "Yes I'm sure"; - const answer = await window.showWarningMessage( - "Are you sure? This will wipe all local changes.", - { modal: true }, - yes - ); + const uris = selection.map(resource => resource.resourceUri); + const depth = await checkAndPromptDepth(uris); - if (answer !== yes) { + if (!depth) { return; } - const picks: any[] = []; - - for (const depth in SvnDepth) { - if (SvnDepth.hasOwnProperty(depth)) { - picks.push({ label: depth, description: SvnDepth[depth] }); - } - } - - const placeHolder = "Select revert depth"; - const pick = await window.showQuickPick(picks, { placeHolder }); - const uris = selection.map(resource => resource.resourceUri); - await this.runByRepository(uris, async (repository, resources) => { if (!repository) { return; @@ -45,7 +29,7 @@ export class Revert extends Command { const paths = resources.map(resource => resource.fsPath); try { - await repository.revert(paths, pick.label); + await repository.revert(paths, depth); } catch (error) { console.log(error); window.showErrorMessage("Unable to revert"); diff --git a/src/commands/revertAll.ts b/src/commands/revertAll.ts index b960f8ad..82a177db 100644 --- a/src/commands/revertAll.ts +++ b/src/commands/revertAll.ts @@ -1,5 +1,5 @@ import { SourceControlResourceGroup, window } from "vscode"; -import { SvnDepth } from "../common/types"; +import { checkAndPromptDepth, confirmRevert } from "../input/revert"; import { Command } from "./command"; export class RevertAll extends Command { @@ -10,33 +10,17 @@ export class RevertAll extends Command { public async execute(resourceGroup: SourceControlResourceGroup) { const resourceStates = resourceGroup.resourceStates; - if (resourceStates.length === 0) { + if (resourceStates.length === 0 || !(await confirmRevert())) { return; } - const yes = "Yes I'm sure"; - const answer = await window.showWarningMessage( - "Are you sure? This will wipe all local changes.", - { modal: true }, - yes - ); + const uris = resourceStates.map(resource => resource.resourceUri); + const depth = await checkAndPromptDepth(uris); - if (answer !== yes) { + if (!depth) { return; } - const picks: any[] = []; - - for (const depth in SvnDepth) { - if (SvnDepth.hasOwnProperty(depth)) { - picks.push({ label: depth, description: SvnDepth[depth] }); - } - } - - const placeHolder = "Select revert depth"; - const pick = await window.showQuickPick(picks, { placeHolder }); - const uris = resourceStates.map(resource => resource.resourceUri); - await this.runByRepository(uris, async (repository, resources) => { if (!repository) { return; @@ -45,7 +29,7 @@ export class RevertAll extends Command { const paths = resources.map(resource => resource.fsPath); try { - await repository.revert(paths, pick.label); + await repository.revert(paths, depth); } catch (error) { console.log(error); window.showErrorMessage("Unable to revert"); diff --git a/src/commands/revertExplorer.ts b/src/commands/revertExplorer.ts new file mode 100644 index 00000000..917ec56d --- /dev/null +++ b/src/commands/revertExplorer.ts @@ -0,0 +1,41 @@ +import { Uri, window } from "vscode"; +import { checkAndPromptDepth, confirmRevert } from "../input/revert"; +import { Command } from "./command"; + +export class RevertExplorer extends Command { + constructor() { + super("svn.revertExplorer"); + } + + public async execute(_mainUri?: Uri, allUris?: Uri[]) { + if (!allUris) { + return; + } + + const uris = allUris; + if (uris.length === 0 || !(await confirmRevert())) { + return; + } + + const depth = await checkAndPromptDepth(uris); + + if (!depth) { + return; + } + + await this.runByRepository(uris, async (repository, resources) => { + if (!repository) { + return; + } + + const paths = resources.map(resource => resource.fsPath); + + try { + await repository.revert(paths, depth); + } catch (error) { + console.log(error); + window.showErrorMessage("Unable to revert"); + } + }); + } +} diff --git a/src/input/revert.ts b/src/input/revert.ts new file mode 100644 index 00000000..b23888d8 --- /dev/null +++ b/src/input/revert.ts @@ -0,0 +1,64 @@ +import { Uri, window } from "vscode"; +import { SvnDepth } from "../common/types"; +import { lstat } from "../fs"; + +export async function confirmRevert() { + const yes = "Yes I'm sure"; + const answer = await window.showWarningMessage( + "Are you sure? This will wipe all local changes.", + { modal: true }, + yes + ); + + if (answer !== yes) { + return false; + } + + return true; +} + +export async function promptDepth() { + const picks: any[] = []; + + for (const depth in SvnDepth) { + if (SvnDepth.hasOwnProperty(depth)) { + picks.push({ label: depth, description: SvnDepth[depth] }); + } + } + + const placeHolder = "Select revert depth"; + const pick = await window.showQuickPick(picks, { placeHolder }); + if (!pick) { + return undefined; + } + return pick.label; +} + +export async function checkAndPromptDepth( + uris: Uri[], + defaultDepth: keyof typeof SvnDepth = "empty" +) { + // Without uris, force prompt + let hasDirectory = uris.length === 0; + + for (const uri of uris) { + if (uri.scheme !== "file") { + continue; + } + try { + const stat = await lstat(uri.fsPath); + if (stat.isDirectory()) { + hasDirectory = true; + break; + } + } catch (error) { + // ignore + } + } + + if (hasDirectory) { + return await promptDepth(); + } + + return defaultDepth; +} diff --git a/src/repository.ts b/src/repository.ts index 06aa1a02..b39f06df 100644 --- a/src/repository.ts +++ b/src/repository.ts @@ -816,8 +816,10 @@ export class Repository implements IRemoteRepository { ); } - public async revert(files: string[], depth: SvnDepth) { - return this.run(Operation.Revert, () => this.repository.revert(files, depth)); + public async revert(files: string[], depth: keyof typeof SvnDepth) { + return this.run(Operation.Revert, () => + this.repository.revert(files, depth) + ); } public async info(path: string) { diff --git a/src/svnRepository.ts b/src/svnRepository.ts index 2d8191b8..7c846c52 100644 --- a/src/svnRepository.ts +++ b/src/svnRepository.ts @@ -184,7 +184,8 @@ export class Repository { filePath = file; } - const isChild = uri.scheme === "file" && isDescendant(this.workspaceRoot, uri.fsPath); + const isChild = + uri.scheme === "file" && isDescendant(this.workspaceRoot, uri.fsPath); let target: string = filePath; @@ -194,7 +195,10 @@ export class Repository { if (revision) { args.push("-r", revision); - if (isChild && !["BASE", "COMMITTED", "PREV"].includes(revision.toUpperCase())) { + if ( + isChild && + !["BASE", "COMMITTED", "PREV"].includes(revision.toUpperCase()) + ) { const info = await this.getInfo(); target = info.url + "/" + target.replace(/\\/g, "/"); // TODO move to SvnRI @@ -292,9 +296,13 @@ export class Repository { const matches = result.stdout.match(/Committed revision (.*)\./i); if (matches && matches[0]) { - const sendedFiles = (result.stdout.match(/(Sending|Adding|Deleting)\s+/g) || []).length; + const sendedFiles = ( + result.stdout.match(/(Sending|Adding|Deleting)\s+/g) || [] + ).length; - const filesMessage = `${sendedFiles} ${sendedFiles === 1 ? "file" : "files"} commited`; + const filesMessage = `${sendedFiles} ${ + sendedFiles === 1 ? "file" : "files" + } commited`; return `${filesMessage}: revision ${matches[1]}.`; } @@ -453,7 +461,7 @@ export class Repository { return true; } - public async revert(files: string[], depth: SvnDepth) { + public async revert(files: string[], depth: keyof typeof SvnDepth) { files = files.map(file => this.removeAbsolutePath(file)); const result = await this.exec(["revert", "--depth", depth, ...files]); return result.stdout;