diff --git a/CHANGELOG.md b/CHANGELOG.md index 34e9c74..3cc2601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ## [Unreleased] +### Added + +- Adds _Open All Changes_, _Open All Changes with Working Tree_, _Open Files_, and _Open Revisions_ commands to files changed nodes in the views — closes [#760](https://github.com/eamodio/vscode-gitlens/issues/760) + ### Fixed - Fixes [#709](https://github.com/eamodio/vscode-gitlens/issues/709) - Checkout a remote branch as new local branch diff --git a/package.json b/package.json index 27fcb2c..e29b4fc 100644 --- a/package.json +++ b/package.json @@ -4411,22 +4411,22 @@ }, { "command": "gitlens.views.openChangedFileChanges", - "when": "viewItem =~ /gitlens:(commit|stash)\\b/", + "when": "viewItem =~ /gitlens:(commit|stash|results:files)\\b/", "group": "2_gitlens@1" }, { "command": "gitlens.views.openChangedFileChangesWithWorking", - "when": "viewItem =~ /gitlens:(commit|stash)\\b/", + "when": "viewItem =~ /gitlens:(commit|stash|results:files)\\b/", "group": "2_gitlens@2" }, { "command": "gitlens.views.openChangedFiles", - "when": "viewItem =~ /gitlens:(commit|stash)\\b/", + "when": "viewItem =~ /gitlens:(commit|stash|results:files)\\b/", "group": "3_gitlens@1" }, { "command": "gitlens.views.openChangedFileRevisions", - "when": "viewItem =~ /gitlens:(commit|stash)\\b/", + "when": "viewItem =~ /gitlens:(commit|stash|results:files)\\b/", "group": "3_gitlens@2" }, { @@ -4549,7 +4549,7 @@ }, { "command": "gitlens.views.openFileRevision", - "when": "viewItem =~ /gitlens:file\\b(?=.*?\\b\\+committed\\b)/", + "when": "viewItem =~ /gitlens:file\\b((?=.*?\\b\\+committed\\b)|:results)/", "group": "3_gitlens@2" }, { @@ -4580,12 +4580,12 @@ }, { "command": "gitlens.views.applyChanges", - "when": "viewItem =~ /gitlens:file\\b((?=.*?\\b\\+committed\\b)|results\\b)/", + "when": "viewItem =~ /gitlens:file\\b((?=.*?\\b\\+committed\\b)|:results\\b)/", "group": "5_gitlens_1@1" }, { "command": "gitlens.views.checkout", - "when": "viewItem =~ /gitlens:file\\b((?=.*?\\b\\+committed\\b)|results\\b)/", + "when": "viewItem =~ /gitlens:file\\b((?=.*?\\b\\+committed\\b)|:results\\b)/", "group": "5_gitlens_1@2" }, { @@ -4660,7 +4660,7 @@ }, { "command": "gitlens.views.exploreRepoRevision", - "when": "viewItem =~ /gitlens:(branch|commit|file\\b((?=.*?\\b\\+committed\\b)|results)|stash|tag)\\b/", + "when": "viewItem =~ /gitlens:(branch|commit|file\\b((?=.*?\\b\\+committed\\b)|:results)|stash|tag)\\b/", "group": "7_gitlens_more@1" }, { diff --git a/src/commands/diffWithWorking.ts b/src/commands/diffWithWorking.ts index c88ca5a..5ca9aee 100644 --- a/src/commands/diffWithWorking.ts +++ b/src/commands/diffWithWorking.ts @@ -88,7 +88,7 @@ export class DiffWithWorkingCommand extends ActiveEditorCommand { firstIfNotFound: true }); if (args.commit === undefined) { - return Messages.showFileNotUnderSourceControlWarningMessage('Unable to open compare'); + return window.showWarningMessage('Unable to open compare. File doesn\'t exist in the specified revision'); } } catch (ex) { diff --git a/src/views/nodes/resultsFilesNode.ts b/src/views/nodes/resultsFilesNode.ts index 238361a..4ac585c 100644 --- a/src/views/nodes/resultsFilesNode.ts +++ b/src/views/nodes/resultsFilesNode.ts @@ -20,8 +20,8 @@ export class ResultsFilesNode extends ViewNode { view: ViewWithFiles, parent: ViewNode, public readonly repoPath: string, - private readonly _ref1: string, - private readonly _ref2: string + public readonly ref1: string, + public readonly ref2: string ) { super(GitUri.fromRepoPath(repoPath), view, parent); } @@ -35,7 +35,7 @@ export class ResultsFilesNode extends ViewNode { if (diff === undefined) return []; let children: FileNode[] = [ - ...Iterables.map(diff, s => new ResultsFileNode(this.view, this, this.repoPath, s, this._ref1, this._ref2)) + ...Iterables.map(diff, s => new ResultsFileNode(this.view, this, this.repoPath, s, this.ref1, this.ref2)) ]; if (this.view.config.files.layout !== ViewFilesLayout.List) { @@ -97,7 +97,7 @@ export class ResultsFilesNode extends ViewNode { private _filesQueryResults: Promise | undefined; - private getFilesQueryResults() { + getFilesQueryResults() { if (this._filesQueryResults === undefined) { this._filesQueryResults = this.getFilesQueryResultsCore(); } @@ -106,7 +106,7 @@ export class ResultsFilesNode extends ViewNode { } private async getFilesQueryResultsCore(): Promise { - const diff = await Container.git.getDiffStatus(this.uri.repoPath!, this._ref1, this._ref2); + const diff = await Container.git.getDiffStatus(this.uri.repoPath!, this.ref1, this.ref2); return { label: `${Strings.pluralize('file', diff !== undefined ? diff.length : 0, { zero: 'No' })} changed`, diff --git a/src/views/viewCommands.ts b/src/views/viewCommands.ts index a224235..b9df13a 100644 --- a/src/views/viewCommands.ts +++ b/src/views/viewCommands.ts @@ -17,7 +17,6 @@ import { BuiltInCommands, CommandContext, setCommandContext } from '../constants import { Container } from '../container'; import { toGitLensFSUri } from '../git/fsProvider'; import { GitService, GitUri } from '../git/gitService'; -import { Arrays } from '../system'; import { BranchNode, BranchTrackingStatusNode, @@ -34,6 +33,7 @@ import { RemoteNode, RepositoryNode, ResultsFileNode, + ResultsFilesNode, StashFileNode, StashNode, StatusFileNode, @@ -384,7 +384,7 @@ export class ViewCommands { return commands.executeCommand(command.command, uri, args); } - private async openChangesWithWorking(node: ViewRefFileNode | StatusFileNode) { + private openChangesWithWorking(node: ViewRefFileNode | StatusFileNode) { if (!(node instanceof ViewRefFileNode) && !(node instanceof StatusFileNode)) return undefined; const args: DiffWithWorkingCommandArgs = { @@ -393,15 +393,6 @@ export class ViewCommands { preview: false } }; - - if (node instanceof ResultsFileNode) { - args.commit = await Container.git.getCommitForFile(node.repoPath, node.uri.fsPath, { - ref: node.uri.sha, - firstIfNotFound: true, - reverse: true - }); - } - return commands.executeCommand(Commands.DiffWithWorking, node.uri, args); } @@ -472,60 +463,142 @@ export class ViewCommands { } private async openChangedFileChanges( - node: CommitNode | StashNode, + node: CommitNode | StashNode | ResultsFilesNode, options: TextDocumentShowOptions = { preserveFocus: false, preview: false } ) { - if (!(node instanceof CommitNode) && !(node instanceof StashNode)) return; - - const repoPath = node.commit.repoPath; - const uris = node.commit.files.map(s => GitUri.fromFile(s, repoPath)); - - for (const uri of uris) { - await this.openDiffWith( - repoPath, - { - uri: uri, - sha: - node.commit.previousSha !== undefined ? node.commit.previousSha : GitService.deletedOrMissingSha - }, - { uri: uri, sha: node.commit.sha }, - options + if (!(node instanceof CommitNode) && !(node instanceof StashNode) && !(node instanceof ResultsFilesNode)) { + return; + } + + let repoPath: string; + let files; + let ref1: string; + let ref2: string; + + if (node instanceof ResultsFilesNode) { + const { diff } = await node.getFilesQueryResults(); + if (diff == null || diff.length === 0) return; + + repoPath = node.repoPath; + files = diff; + ref1 = node.ref1; + ref2 = node.ref2; + } + else { + repoPath = node.commit.repoPath; + files = node.commit.files; + ref1 = node.commit.previousSha !== undefined ? node.commit.previousSha : GitService.deletedOrMissingSha; + ref2 = node.commit.sha; + } + + if (files.length > 20) { + const result = await window.showWarningMessage( + `Are your sure you want to open all ${files.length} files?`, + { title: 'Yes' }, + { title: 'No', isCloseAffordance: true } ); + if (result === undefined || result.title === 'No') return; + } + + for (const file of files) { + if (file.status === 'A') continue; + + const uri1 = GitUri.fromFile(file, repoPath); + const uri2 = file.status === 'R' ? GitUri.fromFile(file, repoPath, ref2, true) : uri1; + + await this.openDiffWith(repoPath, { uri: uri1, sha: ref1 }, { uri: uri2, sha: ref2 }, options); } } private async openChangedFileChangesWithWorking( - node: CommitNode | StashNode, + node: CommitNode | StashNode | ResultsFilesNode, options: TextDocumentShowOptions = { preserveFocus: false, preview: false } ) { - if (!(node instanceof CommitNode) && !(node instanceof StashNode)) return; + if (!(node instanceof CommitNode) && !(node instanceof StashNode) && !(node instanceof ResultsFilesNode)) { + return; + } - const repoPath = node.commit.repoPath; - const uris = Arrays.filterMap(node.commit.files, f => - f.status !== 'D' ? GitUri.fromFile(f, repoPath) : undefined - ); + let repoPath: string; + let files; + let ref: string; - for (const uri of uris) { - const workingUri = await Container.git.getWorkingUri(repoPath, uri); - await this.openDiffWith( - repoPath, - { uri: uri, sha: node.commit.sha }, - { uri: workingUri || uri, sha: '' }, - options + if (node instanceof ResultsFilesNode) { + const { diff } = await node.getFilesQueryResults(); + if (diff == null || diff.length === 0) return; + + repoPath = node.repoPath; + files = diff; + ref = node.ref1 || node.ref2; + } + else { + repoPath = node.commit.repoPath; + files = node.commit.files; + ref = node.commit.sha; + } + + if (files.length > 20) { + const result = await window.showWarningMessage( + `Are your sure you want to open all ${files.length} files?`, + { title: 'Yes' }, + { title: 'No', isCloseAffordance: true } ); + if (result === undefined || result.title === 'No') return; + } + + for (const file of files) { + if (file.status === 'A' || file.status === 'D') continue; + + const args: DiffWithWorkingCommandArgs = { + showOptions: options + }; + + if (!(node instanceof ResultsFilesNode)) { + args.commit = node.commit.toFileCommit(file); + } + + const uri = GitUri.fromFile(file, repoPath, ref); + await commands.executeCommand(Commands.DiffWithWorking, uri, args); } } private async openChangedFiles( - node: CommitNode | StashNode, + node: CommitNode | StashNode | ResultsFilesNode, options: TextDocumentShowOptions = { preserveFocus: false, preview: false } ) { - if (!(node instanceof CommitNode) && !(node instanceof StashNode)) return; + if (!(node instanceof CommitNode) && !(node instanceof StashNode) && !(node instanceof ResultsFilesNode)) { + return; + } + + let repoPath: string; + let files; + let ref: string; + + if (node instanceof ResultsFilesNode) { + const { diff } = await node.getFilesQueryResults(); + if (diff == null || diff.length === 0) return; + + repoPath = node.repoPath; + files = diff; + ref = node.ref1 || node.ref2; + } + else { + repoPath = node.commit.repoPath; + files = node.commit.files; + ref = node.commit.sha; + } + + if (files.length > 20) { + const result = await window.showWarningMessage( + `Are your sure you want to open all ${files.length} files?`, + { title: 'Yes' }, + { title: 'No', isCloseAffordance: true } + ); + if (result === undefined || result.title === 'No') return; + } - const repoPath = node.commit.repoPath; - const uris = Arrays.filterMap(node.commit.files, f => GitUri.fromFile(f, repoPath, node.commit.sha)); + for (const file of files) { + const uri = GitUri.fromFile(file, repoPath, ref); - for (const uri of uris) { const args: OpenWorkingFileCommandArgs = { uri: uri, showOptions: options @@ -535,19 +608,46 @@ export class ViewCommands { } private async openChangedFileRevisions( - node: CommitNode | StashNode, + node: CommitNode | StashNode | ResultsFilesNode, options: TextDocumentShowOptions = { preserveFocus: false, preview: false } ) { - if (!(node instanceof CommitNode) && !(node instanceof StashNode)) return; - - const uris = Arrays.filterMap(node.commit.files, f => - GitUri.toRevisionUri( - f.status === 'D' ? node.commit.previousFileSha : node.commit.sha, - f, - node.commit.repoPath - ) - ); - for (const uri of uris) { + if (!(node instanceof CommitNode) && !(node instanceof StashNode) && !(node instanceof ResultsFilesNode)) { + return; + } + + let repoPath: string; + let files; + let ref1: string; + let ref2: string; + + if (node instanceof ResultsFilesNode) { + const { diff } = await node.getFilesQueryResults(); + if (diff == null || diff.length === 0) return; + + repoPath = node.repoPath; + files = diff; + ref1 = node.ref1; + ref2 = node.ref2; + } + else { + repoPath = node.commit.repoPath; + files = node.commit.files; + ref1 = node.commit.sha; + ref2 = node.commit.previousFileSha; + } + + if (files.length > 20) { + const result = await window.showWarningMessage( + `Are your sure you want to open all ${files.length} files?`, + { title: 'Yes' }, + { title: 'No', isCloseAffordance: true } + ); + if (result === undefined || result.title === 'No') return; + } + + for (const file of files) { + const uri = GitUri.toRevisionUri(file.status === 'D' ? ref2 : ref1, file, repoPath); + await openEditor(uri, options); } }