From f2c2bad47ea6284d2c1ec5909e051450d21b1c5b Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 8 Apr 2019 02:19:31 -0400 Subject: [PATCH] Closes #690 - reworks reference pickers Removes "Branch or Tag" from all commands Changes openFileRevisionFrom allow entering references Fixes missing views.compare.selectForCompare from command palette --- CHANGELOG.md | 9 + README.md | 12 +- package.json | 30 +-- src/commands.ts | 6 +- src/commands/common.ts | 8 + src/commands/diffBranchWith.ts | 96 ++++++++++ src/commands/diffBranchWithBranch.ts | 89 --------- src/commands/diffDirectory.ts | 6 +- src/commands/diffWithBranch.ts | 82 -------- src/commands/diffWithRef.ts | 82 ++++++++ src/commands/diffWithRevision.ts | 36 ++-- src/commands/openBranchInRemote.ts | 4 +- src/commands/openFileInRemote.ts | 4 +- src/commands/openFileRevision.ts | 36 ++-- src/commands/openFileRevisionFrom.ts | 55 ++++++ src/commands/openFileRevisionFromBranch.ts | 58 ------ src/commands/showQuickBranchHistory.ts | 4 +- src/commands/showQuickFileHistory.ts | 38 ++-- src/git/models/models.ts | 5 + src/quickpicks.ts | 2 +- src/quickpicks/branchesAndTagsQuickPick.ts | 290 ----------------------------- src/quickpicks/commonQuickPicks.ts | 49 +++-- src/quickpicks/fileHistoryQuickPick.ts | 8 +- src/quickpicks/referencesQuickPick.ts | 281 ++++++++++++++++++++++++++++ src/views/nodes/compareNode.ts | 18 +- src/views/nodes/comparePickerNode.ts | 4 +- src/views/nodes/fileHistoryTrackerNode.ts | 6 +- src/views/nodes/lineHistoryTrackerNode.ts | 6 +- 28 files changed, 670 insertions(+), 654 deletions(-) create mode 100644 src/commands/diffBranchWith.ts delete mode 100644 src/commands/diffBranchWithBranch.ts delete mode 100644 src/commands/diffWithBranch.ts create mode 100644 src/commands/diffWithRef.ts create mode 100644 src/commands/openFileRevisionFrom.ts delete mode 100644 src/commands/openFileRevisionFromBranch.ts delete mode 100644 src/quickpicks/branchesAndTagsQuickPick.ts create mode 100644 src/quickpicks/referencesQuickPick.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c1e06c..f20b87a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,14 +25,22 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ### Changed +- Changes _Open Revision from..._ (`gitlens.openFileRevisionFrom`) command to allow entering references - Improves the behavior of the _Show More Commits_ & _Show More Results_ commands - no longer loses the context of the last selected item before showing more - Improves the behavior of the _Open Changes with Next Revision_ (`gitlens.diffWithNext`) command when in the diff editor - Improves the behavior of the _Open Changes with Previous Revision_ (`gitlens.diffWithPrevious`) command when in the diff editor - Improves the behavior of the _Open Changes with Working File_ (`gitlens.diffWithWorking`) command when in the diff editor +- Renames _Compare HEAD with Branch or Tag..._ (`gitlens.diffHeadWithBranch`) command to _Compare HEAD with..._ (`gitlens.diffHeadWith`) +- Renames _Compare Working Tree with Branch or Tag..._ (`gitlens.diffWorkingWithBranch`) command to _Compare Working Tree with..._ (`gitlens.diffWorkingWith`) +- Renames _Open Changes with Branch or Tag..._ (`gitlens.diffWithBranch`) command to _Open Changes with..._ (`gitlens.diffWithRef`) +- Renames _Open Revision from Branch or Tag..._ (`gitlens.openFileRevisionFromBranch`) command to _Open Revision from..._ (`gitlens.openFileRevisionFrom`) +- Renames _Compare Branch or Tag with..._ (`gitlens.views.compare.selectForCompare`) command to _Compare References..._ (`gitlens.views.compare.selectForCompare`) +- Renames _Choose from Branch or Tag History..._ quick pick item to _Show File History from..._ for better clarity and to reflect that references are now allowed - Updates the invite link to the [VS Code Development Community Slack](https://vscode-slack.amod.io) ### Removed +- Removes the requirement of prefixing reference comparisons with `#` — closes [#690](https://github.com/eamodio/vscode-gitlens/issues/690) - Removes the automatic suspension of the current line blame annotations while debugging — closes [#382](https://github.com/eamodio/vscode-gitlens/issues/382) ### Fixed @@ -41,6 +49,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p - Fixes [#683](https://github.com/eamodio/vscode-gitlens/issues/683) - log.showSignature leads to stray files being displayed - Fixes [#691](https://github.com/eamodio/vscode-gitlens/issues/691) - Auto-expand tree view on Swap Comparison - Fixes the behavior of the _Open Line Changes with Previous Revision_ (`gitlens.diffLineWithPrevious`) command to follow the line history much better +- Fixes missing _Compare References..._ (`gitlens.views.compare.selectForCompare`) from the command palette ## [9.5.1] - 2019-02-13 diff --git a/README.md b/README.md index 9b7a981..be88105 100644 --- a/README.md +++ b/README.md @@ -442,7 +442,7 @@ The search commits view provides the following features, A [customizable](#compare-view-settings- 'Jump to the Compare view settings') view to visualize comparisons between branches, tags, commits, and more -- A toolbar provides quick access to the _Compare Branch or Tag with..._, _Keep Results_, _Clear Results_, and _Refresh_ commands +- A toolbar provides quick access to the _Compare with..._, _Keep Results_, _Clear Results_, and _Refresh_ commands - A context menu provides _Automatic Layout_, _List Layout_, _Tree Layout_, _Open Settings_ commands The compare view provides the following features, @@ -593,13 +593,13 @@ The compare view provides the following features, - Provides easy access to the following comparison commands via the `Command Palette` as well as in context via the many provided quick pick menus -- Adds a _Directory Compare Working Tree with..._ command (`gitlens.diffDirectory`) to open the configured Git difftool to compare the working tree with the selected branch or tag +- Adds a _Directory Compare Working Tree with..._ command (`gitlens.diffDirectory`) to open the configured Git difftool to compare the working tree with the selected reference -- Adds a _Compare HEAD with Branch or Tag..._ command (`gitlens.diffHeadWithBranch`) to compare the index (HEAD) with the selected branch or tag +- Adds a _Compare HEAD with..._ command (`gitlens.diffHeadWith`) to compare the index (HEAD) with the selected reference -- Adds a _Compare Working Tree with Branch or Tag..._ command (`gitlens.diffWorkingWithBranch`) to compare the working tree with the selected branch or tag +- Adds a _Compare Working Tree with..._ command (`gitlens.diffWorkingWith`) to compare the working tree with the selected reference -- Adds an _Open Changes with Branch or Tag..._ command (`gitlens.diffWithBranch`) to compare the current file with the same file on the selected branch or tag +- Adds an _Open Changes with..._ command (`gitlens.diffWithRef`) to compare the current file with the same file on the selected reference - Adds an _Open Changes with Next Revision_ command (`gitlens.diffWithNext`) with a shortcut of `alt+.` to compare the current file/diff with the next commit revision @@ -625,7 +625,7 @@ The compare view provides the following features, - Adds an _Open Revision..._ command (`gitlens.openFileRevision`) to open the selected revision for the current file -- Adds an _Open Revision from Branch or Tag..._ command (`gitlens.openFileRevisionFromBranch`) to open the revision of the current file from the selected branch +- Adds an _Open Revision from..._ command (`gitlens.openFileRevisionFrom`) to open the revision of the current file from the selected reference - Adds an _Open Changes (with difftool)_ command (`gitlens.externalDiff`) to the source control group and source control resource context menus to open the changes of a file or set of files with the configured git difftool diff --git a/package.json b/package.json index 59edc04..3b0bbad 100644 --- a/package.json +++ b/package.json @@ -1834,18 +1834,18 @@ "category": "GitLens" }, { - "command": "gitlens.diffHeadWithBranch", - "title": "Compare HEAD with Branch or Tag...", + "command": "gitlens.diffHeadWith", + "title": "Compare HEAD with...", "category": "GitLens" }, { - "command": "gitlens.diffWorkingWithBranch", - "title": "Compare Working Tree with Branch or Tag...", + "command": "gitlens.diffWorkingWith", + "title": "Compare Working Tree with...", "category": "GitLens" }, { - "command": "gitlens.diffWithBranch", - "title": "Open Changes with Branch or Tag...", + "command": "gitlens.diffWithRef", + "title": "Open Changes with...", "category": "GitLens" }, { @@ -2138,8 +2138,8 @@ "category": "GitLens" }, { - "command": "gitlens.openFileRevisionFromBranch", - "title": "Open Revision from Branch or Tag...", + "command": "gitlens.openFileRevisionFrom", + "title": "Open Revision from...", "category": "GitLens" }, { @@ -2704,7 +2704,7 @@ }, { "command": "gitlens.views.compare.selectForCompare", - "title": "Compare Branch or Tag with...", + "title": "Compare References...", "category": "GitLens", "icon": { "dark": "images/dark/icon-compare-refs.svg", @@ -2904,15 +2904,15 @@ "when": "gitlens:enabled" }, { - "command": "gitlens.diffHeadWithBranch", + "command": "gitlens.diffHeadWith", "when": "gitlens:enabled" }, { - "command": "gitlens.diffWorkingWithBranch", + "command": "gitlens.diffWorkingWith", "when": "gitlens:enabled" }, { - "command": "gitlens.diffWithBranch", + "command": "gitlens.diffWithRef", "when": "gitlens:activeFileStatus =~ /tracked/" }, { @@ -3088,7 +3088,7 @@ "when": "gitlens:activeFileStatus =~ /tracked/" }, { - "command": "gitlens.openFileRevisionFromBranch", + "command": "gitlens.openFileRevisionFrom", "when": "gitlens:activeFileStatus =~ /tracked/" }, { @@ -3421,7 +3421,7 @@ }, { "command": "gitlens.views.compare.selectForCompare", - "when": "gitlens:enabled && gitlens.views.compare.enabled" + "when": "gitlens:enabled && config.gitlens.views.compare.enabled" }, { "command": "gitlens.views.compare.clear", @@ -3758,7 +3758,7 @@ "group": "1_gitlens@1" }, { - "command": "gitlens.diffWithBranch", + "command": "gitlens.diffWithRef", "when": "gitlens:enabled && config.gitlens.menus.scmItem.compare", "group": "1_gitlens@2" }, diff --git a/src/commands.ts b/src/commands.ts index fb17da0..3fcc2d9 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -6,14 +6,14 @@ export * from './commands/common'; export * from './commands/copyMessageToClipboard'; export * from './commands/copyRemoteFileUrlToClipboard'; export * from './commands/copyShaToClipboard'; -export * from './commands/diffBranchWithBranch'; +export * from './commands/diffBranchWith'; export * from './commands/diffDirectory'; export * from './commands/diffLineWithPrevious'; export * from './commands/diffLineWithWorking'; export * from './commands/diffWith'; -export * from './commands/diffWithBranch'; export * from './commands/diffWithNext'; export * from './commands/diffWithPrevious'; +export * from './commands/diffWithRef'; export * from './commands/diffWithRevision'; export * from './commands/diffWithWorking'; export * from './commands/externalDiff'; @@ -23,7 +23,7 @@ export * from './commands/openChangedFiles'; export * from './commands/openCommitInRemote'; export * from './commands/openFileInRemote'; export * from './commands/openFileRevision'; -export * from './commands/openFileRevisionFromBranch'; +export * from './commands/openFileRevisionFrom'; export * from './commands/openInRemote'; export * from './commands/openRepoInRemote'; export * from './commands/openWorkingFile'; diff --git a/src/commands/common.ts b/src/commands/common.ts index 14f8b52..13ebbe0 100644 --- a/src/commands/common.ts +++ b/src/commands/common.ts @@ -31,11 +31,17 @@ export enum Commands { CopyShaToClipboard = 'gitlens.copyShaToClipboard', DiffDirectory = 'gitlens.diffDirectory', DiffDirectoryWithHead = 'gitlens.diffDirectoryWithHead', + DiffHeadWith = 'gitlens.diffHeadWith', + // DEPRECATED DiffHeadWithBranch = 'gitlens.diffHeadWithBranch', + DiffWorkingWith = 'gitlens.diffWorkingWith', + // DEPRECATED DiffWorkingWithBranch = 'gitlens.diffWorkingWithBranch', ExternalDiffAll = 'gitlens.externalDiffAll', DiffWith = 'gitlens.diffWith', + // DEPRECATED DiffWithBranch = 'gitlens.diffWithBranch', + DiffWithRef = 'gitlens.diffWithRef', DiffWithNext = 'gitlens.diffWithNext', DiffWithNextInDiff = 'gitlens.diffWithNextInDiff', DiffWithPrevious = 'gitlens.diffWithPrevious', @@ -53,6 +59,8 @@ export enum Commands { OpenCommitInRemote = 'gitlens.openCommitInRemote', OpenFileInRemote = 'gitlens.openFileInRemote', OpenFileRevision = 'gitlens.openFileRevision', + OpenFileRevisionFrom = 'gitlens.openFileRevisionFrom', + // DEPRECATED OpenFileRevisionFromBranch = 'gitlens.openFileRevisionFromBranch', OpenInRemote = 'gitlens.openInRemote', OpenRepoInRemote = 'gitlens.openRepoInRemote', diff --git a/src/commands/diffBranchWith.ts b/src/commands/diffBranchWith.ts new file mode 100644 index 0000000..c4c08fa --- /dev/null +++ b/src/commands/diffBranchWith.ts @@ -0,0 +1,96 @@ +'use strict'; +import { TextEditor, Uri } from 'vscode'; +import { GlyphChars } from '../constants'; +import { Container } from '../container'; +import { Logger } from '../logger'; +import { Messages } from '../messages'; +import { CommandQuickPickItem, ReferencesQuickPick } from '../quickpicks'; +import { + ActiveEditorCommand, + command, + CommandContext, + Commands, + getCommandUri, + getRepoPathOrActiveOrPrompt +} from './common'; + +export interface DiffBranchWithCommandArgs { + ref1?: string; + ref2?: string; +} + +@command() +export class DiffBranchWithCommand extends ActiveEditorCommand { + constructor() { + super([ + Commands.DiffHeadWith, + Commands.DiffWorkingWith, + Commands.DiffHeadWithBranch, + Commands.DiffWorkingWithBranch + ]); + } + + protected preExecute(context: CommandContext, args: DiffBranchWithCommandArgs = {}) { + switch (context.command) { + case Commands.DiffHeadWith: + case Commands.DiffHeadWithBranch: + args.ref2 = 'HEAD'; + break; + + case Commands.DiffWorkingWith: + case Commands.DiffWorkingWithBranch: + args.ref2 = ''; + break; + } + + return this.execute(context.editor, context.uri, args); + } + + async execute(editor?: TextEditor, uri?: Uri, args: DiffBranchWithCommandArgs = {}) { + if (args.ref2 === undefined) return undefined; + + uri = getCommandUri(uri, editor); + + try { + const repoPath = await getRepoPathOrActiveOrPrompt( + uri, + editor, + `Compare in which repository${GlyphChars.Ellipsis}` + ); + if (!repoPath) return undefined; + + if (!args.ref1) { + let placeHolder; + switch (args.ref2) { + case '': + placeHolder = `Compare Working Tree with${GlyphChars.Ellipsis}`; + break; + case 'HEAD': + placeHolder = `Compare HEAD with${GlyphChars.Ellipsis}`; + break; + default: + placeHolder = `Compare ${args.ref2} with${GlyphChars.Ellipsis}`; + break; + } + + const pick = await new ReferencesQuickPick(repoPath).show(placeHolder, { + allowEnteringRefs: true + }); + if (pick === undefined) return undefined; + + if (pick instanceof CommandQuickPickItem) return pick.execute(); + + args.ref1 = pick.ref; + if (args.ref1 === undefined) return undefined; + } + + await Container.compareView.compare(repoPath, args.ref1, args.ref2); + + return undefined; + } + catch (ex) { + Logger.error(ex, 'DiffBranchWithBranchCommand'); + return Messages.showGenericErrorMessage('Unable to open branch compare'); + } + } +} diff --git a/src/commands/diffBranchWithBranch.ts b/src/commands/diffBranchWithBranch.ts deleted file mode 100644 index 29b1cef..0000000 --- a/src/commands/diffBranchWithBranch.ts +++ /dev/null @@ -1,89 +0,0 @@ -'use strict'; -import { TextEditor, Uri } from 'vscode'; -import { GlyphChars } from '../constants'; -import { Container } from '../container'; -import { Logger } from '../logger'; -import { Messages } from '../messages'; -import { BranchesAndTagsQuickPick, CommandQuickPickItem } from '../quickpicks'; -import { - ActiveEditorCommand, - command, - CommandContext, - Commands, - getCommandUri, - getRepoPathOrActiveOrPrompt -} from './common'; - -export interface DiffBranchWithBranchCommandArgs { - ref1?: string; - ref2?: string; -} - -@command() -export class DiffBranchWithBranchCommand extends ActiveEditorCommand { - constructor() { - super([Commands.DiffHeadWithBranch, Commands.DiffWorkingWithBranch]); - } - - protected preExecute(context: CommandContext, args: DiffBranchWithBranchCommandArgs = {}) { - switch (context.command) { - case Commands.DiffHeadWithBranch: - args.ref2 = 'HEAD'; - break; - - case Commands.DiffWorkingWithBranch: - args.ref2 = ''; - break; - } - - return this.execute(context.editor, context.uri, args); - } - - async execute(editor?: TextEditor, uri?: Uri, args: DiffBranchWithBranchCommandArgs = {}) { - if (args.ref2 === undefined) return undefined; - - uri = getCommandUri(uri, editor); - - try { - const repoPath = await getRepoPathOrActiveOrPrompt( - uri, - editor, - `Compare with branch or tag in which repository${GlyphChars.Ellipsis}` - ); - if (!repoPath) return undefined; - - if (!args.ref1) { - let placeHolder; - switch (args.ref2) { - case '': - placeHolder = `Compare Working Tree with${GlyphChars.Ellipsis}`; - break; - case 'HEAD': - placeHolder = `Compare HEAD with${GlyphChars.Ellipsis}`; - break; - default: - placeHolder = `Compare ${args.ref2} with${GlyphChars.Ellipsis}`; - break; - } - - const pick = await new BranchesAndTagsQuickPick(repoPath).show(placeHolder, { - allowCommitId: true - }); - if (pick === undefined) return undefined; - - if (pick instanceof CommandQuickPickItem) return pick.execute(); - - args.ref1 = pick.ref; - if (args.ref1 === undefined) return undefined; - } - - await Container.compareView.compare(repoPath, args.ref1, args.ref2); - - return undefined; - } - catch (ex) { - Logger.error(ex, 'DiffBranchWithBranchCommand'); - return Messages.showGenericErrorMessage('Unable to open branch compare'); - } - } -} diff --git a/src/commands/diffDirectory.ts b/src/commands/diffDirectory.ts index 5e8bad0..9969cba 100644 --- a/src/commands/diffDirectory.ts +++ b/src/commands/diffDirectory.ts @@ -4,7 +4,7 @@ import { BuiltInCommands, GlyphChars } from '../constants'; import { Container } from '../container'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { BranchesAndTagsQuickPick, CommandQuickPickItem } from '../quickpicks'; +import { CommandQuickPickItem, ReferencesQuickPick } from '../quickpicks'; import { CompareResultsNode } from '../views/nodes'; import { ActiveEditorCommand, @@ -71,9 +71,9 @@ export class DiffDirectoryCommand extends ActiveEditorCommand { if (!args.ref1) { args = { ...args }; - const pick = await new BranchesAndTagsQuickPick(repoPath).show( + const pick = await new ReferencesQuickPick(repoPath).show( `Compare Working Tree with${GlyphChars.Ellipsis}`, - { allowCommitId: true } + { allowEnteringRefs: true } ); if (pick === undefined) return undefined; diff --git a/src/commands/diffWithBranch.ts b/src/commands/diffWithBranch.ts deleted file mode 100644 index 7b14df1..0000000 --- a/src/commands/diffWithBranch.ts +++ /dev/null @@ -1,82 +0,0 @@ -'use strict'; -import * as paths from 'path'; -import { commands, TextDocumentShowOptions, TextEditor, Uri } from 'vscode'; -import { GlyphChars } from '../constants'; -import { Container } from '../container'; -import { GitService, GitUri } from '../git/gitService'; -import { Messages } from '../messages'; -import { BranchesAndTagsQuickPick, CommandQuickPickItem } from '../quickpicks'; -import { Strings } from '../system'; -import { ActiveEditorCommand, command, Commands, getCommandUri } from './common'; -import { DiffWithCommandArgs } from './diffWith'; - -export interface DiffWithBranchCommandArgs { - line?: number; - showOptions?: TextDocumentShowOptions; - - goBackCommand?: CommandQuickPickItem; -} - -@command() -export class DiffWithBranchCommand extends ActiveEditorCommand { - constructor() { - super(Commands.DiffWithBranch); - } - - async execute(editor?: TextEditor, uri?: Uri, args: DiffWithBranchCommandArgs = {}) { - uri = getCommandUri(uri, editor); - if (uri == null) return undefined; - - args = { ...args }; - if (args.line === undefined) { - args.line = editor == null ? 0 : editor.selection.active.line; - } - - const gitUri = await GitUri.fromUri(uri); - if (!gitUri.repoPath) return Messages.showNoRepositoryWarningMessage('Unable to open file compare'); - - const pick = await new BranchesAndTagsQuickPick(gitUri.repoPath).show( - `Compare ${paths.basename(gitUri.fsPath)} with${GlyphChars.Ellipsis}`, - { - allowCommitId: true, - goBack: args.goBackCommand - } - ); - if (pick === undefined) return undefined; - - if (pick instanceof CommandQuickPickItem) return pick.execute(); - - const ref = pick.ref; - if (ref === undefined) return undefined; - - let renamedUri: Uri | undefined; - let renamedTitle: string | undefined; - - // Check to see if this file has been renamed - const files = await Container.git.getDiffStatus(gitUri.repoPath, 'HEAD', ref, { filter: 'R' }); - if (files !== undefined) { - const fileName = Strings.normalizePath(paths.relative(gitUri.repoPath, gitUri.fsPath)); - const rename = files.find(s => s.fileName === fileName); - if (rename !== undefined && rename.originalFileName !== undefined) { - renamedUri = GitUri.resolveToUri(rename.originalFileName, gitUri.repoPath); - renamedTitle = `${paths.basename(rename.originalFileName)} (${GitService.shortenSha(ref)})`; - } - } - - const diffArgs: DiffWithCommandArgs = { - repoPath: gitUri.repoPath, - lhs: { - sha: pick.remote ? `remotes/${ref}` : ref, - uri: renamedUri || (gitUri as Uri), - title: renamedTitle || `${paths.basename(gitUri.fsPath)} (${GitService.shortenSha(ref)})` - }, - rhs: { - sha: '', - uri: gitUri as Uri - }, - line: args.line, - showOptions: args.showOptions - }; - return commands.executeCommand(Commands.DiffWith, diffArgs); - } -} diff --git a/src/commands/diffWithRef.ts b/src/commands/diffWithRef.ts new file mode 100644 index 0000000..e993722 --- /dev/null +++ b/src/commands/diffWithRef.ts @@ -0,0 +1,82 @@ +'use strict'; +import * as paths from 'path'; +import { commands, TextDocumentShowOptions, TextEditor, Uri } from 'vscode'; +import { GlyphChars } from '../constants'; +import { Container } from '../container'; +import { GitService, GitUri } from '../git/gitService'; +import { Messages } from '../messages'; +import { CommandQuickPickItem, ReferencesQuickPick } from '../quickpicks'; +import { Strings } from '../system'; +import { ActiveEditorCommand, command, Commands, getCommandUri } from './common'; +import { DiffWithCommandArgs } from './diffWith'; + +export interface DiffWithRefCommandArgs { + line?: number; + showOptions?: TextDocumentShowOptions; + + goBackCommand?: CommandQuickPickItem; +} + +@command() +export class DiffWithRefCommand extends ActiveEditorCommand { + constructor() { + super([Commands.DiffWithRef, Commands.DiffWithBranch]); + } + + async execute(editor?: TextEditor, uri?: Uri, args: DiffWithRefCommandArgs = {}) { + uri = getCommandUri(uri, editor); + if (uri == null) return undefined; + + args = { ...args }; + if (args.line === undefined) { + args.line = editor == null ? 0 : editor.selection.active.line; + } + + const gitUri = await GitUri.fromUri(uri); + if (!gitUri.repoPath) return Messages.showNoRepositoryWarningMessage('Unable to open file compare'); + + const pick = await new ReferencesQuickPick(gitUri.repoPath).show( + `Compare ${paths.basename(gitUri.fsPath)} with${GlyphChars.Ellipsis}`, + { + allowEnteringRefs: true, + goBack: args.goBackCommand + } + ); + if (pick === undefined) return undefined; + + if (pick instanceof CommandQuickPickItem) return pick.execute(); + + const ref = pick.ref; + if (ref === undefined) return undefined; + + let renamedUri: Uri | undefined; + let renamedTitle: string | undefined; + + // Check to see if this file has been renamed + const files = await Container.git.getDiffStatus(gitUri.repoPath, 'HEAD', ref, { filter: 'R' }); + if (files !== undefined) { + const fileName = Strings.normalizePath(paths.relative(gitUri.repoPath, gitUri.fsPath)); + const rename = files.find(s => s.fileName === fileName); + if (rename !== undefined && rename.originalFileName !== undefined) { + renamedUri = GitUri.resolveToUri(rename.originalFileName, gitUri.repoPath); + renamedTitle = `${paths.basename(rename.originalFileName)} (${GitService.shortenSha(ref)})`; + } + } + + const diffArgs: DiffWithCommandArgs = { + repoPath: gitUri.repoPath, + lhs: { + sha: pick.remote ? `remotes/${ref}` : ref, + uri: renamedUri || (gitUri as Uri), + title: renamedTitle || `${paths.basename(gitUri.fsPath)} (${GitService.shortenSha(ref)})` + }, + rhs: { + sha: '', + uri: gitUri as Uri + }, + line: args.line, + showOptions: args.showOptions + }; + return commands.executeCommand(Commands.DiffWith, diffArgs); + } +} diff --git a/src/commands/diffWithRevision.ts b/src/commands/diffWithRevision.ts index d5baeb1..ae04d94 100644 --- a/src/commands/diffWithRevision.ts +++ b/src/commands/diffWithRevision.ts @@ -2,16 +2,16 @@ import { commands, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode'; import { GlyphChars } from '../constants'; import { Container } from '../container'; -import { GitBranch, GitTag, GitUri } from '../git/gitService'; +import { GitBranch, GitReference, GitTag, GitUri } from '../git/gitService'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { ChooseFromBranchesAndTagsQuickPickItem, CommandQuickPickItem, FileHistoryQuickPick } from '../quickpicks'; +import { CommandQuickPickItem, FileHistoryQuickPick, ShowFileHistoryFromQuickPickItem } from '../quickpicks'; import { Iterables, Strings } from '../system'; import { ActiveEditorCommand, command, Commands, getCommandUri } from './common'; import { DiffWithCommandArgs } from './diffWith'; export interface DiffWithRevisionCommandArgs { - branchOrTag?: GitBranch | GitTag; + reference?: GitBranch | GitTag | GitReference; maxCount?: number; line?: number; @@ -37,7 +37,7 @@ export class DiffWithRevisionCommand extends ActiveEditorCommand { const gitUri = await GitUri.fromUri(uri); const placeHolder = `Compare ${gitUri.getFormattedPath({ - suffix: args.branchOrTag ? ` (${args.branchOrTag.name})` : undefined + suffix: args.reference ? ` (${args.reference.name})` : undefined })}${gitUri.sha ? ` ${Strings.pad(GlyphChars.Dot, 1, 1)} ${gitUri.shortSha}` : ''} with revision${ GlyphChars.Ellipsis }`; @@ -46,11 +46,11 @@ export class DiffWithRevisionCommand extends ActiveEditorCommand { try { const log = await Container.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, { maxCount: args.maxCount, - ref: (args.branchOrTag && args.branchOrTag.ref) || gitUri.sha + ref: (args.reference && args.reference.ref) || gitUri.sha }); if (log === undefined) { - if (args.branchOrTag) { - return window.showWarningMessage(`The file could not be found in ${args.branchOrTag.name}`); + if (args.reference) { + return window.showWarningMessage(`The file could not be found in ${args.reference.name}`); } return Messages.showFileNotUnderSourceControlWarningMessage('Unable to open history compare'); } @@ -86,16 +86,20 @@ export class DiffWithRevisionCommand extends ActiveEditorCommand { } commandArgs = { ...args }; + const icon = + args.reference instanceof GitTag + ? '$(tag) ' + : args.reference instanceof GitBranch + ? '$(git-branch) ' + : ''; const currentCommand = new CommandQuickPickItem( { label: `go back ${GlyphChars.ArrowBack}`, description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to history of ${ GlyphChars.Space }$(file-text) ${gitUri.getFormattedPath()}${ - args.branchOrTag - ? ` from ${GlyphChars.Space}${ - args.branchOrTag instanceof GitTag ? '$(tag)' : '$(git-branch)' - } ${args.branchOrTag.name}` + args.reference + ? ` from ${GlyphChars.Space}${icon}${args.reference.name}` : gitUri.sha ? ` from ${GlyphChars.Space}$(git-commit) ${gitUri.shortSha}` : '' @@ -125,14 +129,14 @@ export class DiffWithRevisionCommand extends ActiveEditorCommand { }); if (pick === undefined) return undefined; - if (pick instanceof ChooseFromBranchesAndTagsQuickPickItem) { - const branchOrTag = await pick.execute(); - if (branchOrTag === undefined) return undefined; - if (branchOrTag instanceof CommandQuickPickItem) return branchOrTag.execute(); + if (pick instanceof ShowFileHistoryFromQuickPickItem) { + const reference = await pick.execute(); + if (reference === undefined) return undefined; + if (reference instanceof CommandQuickPickItem) return reference.execute(); commandArgs = { ...args, - branchOrTag: branchOrTag.item + reference: reference.item }; return commands.executeCommand(Commands.DiffWithRevision, gitUri, commandArgs); } diff --git a/src/commands/openBranchInRemote.ts b/src/commands/openBranchInRemote.ts index 97acfe0..cae0df9 100644 --- a/src/commands/openBranchInRemote.ts +++ b/src/commands/openBranchInRemote.ts @@ -4,7 +4,7 @@ import { GlyphChars } from '../constants'; import { Container } from '../container'; import { GitUri, RemoteResourceType } from '../git/gitService'; import { Logger } from '../logger'; -import { BranchesAndTagsQuickPick, CommandQuickPickItem } from '../quickpicks'; +import { CommandQuickPickItem, ReferencesQuickPick } from '../quickpicks'; import { ActiveEditorCommand, command, @@ -53,7 +53,7 @@ export class OpenBranchInRemoteCommand extends ActiveEditorCommand { if (args.branch === undefined) { args = { ...args }; - const pick = await new BranchesAndTagsQuickPick(repoPath).show( + const pick = await new ReferencesQuickPick(repoPath).show( `Open which branch on remote${GlyphChars.Ellipsis}`, { autoPick: true, diff --git a/src/commands/openFileInRemote.ts b/src/commands/openFileInRemote.ts index 57342d7..e317e92 100644 --- a/src/commands/openFileInRemote.ts +++ b/src/commands/openFileInRemote.ts @@ -4,7 +4,7 @@ import { GlyphChars } from '../constants'; import { Container } from '../container'; import { GitUri, RemoteResourceType } from '../git/gitService'; import { Logger } from '../logger'; -import { BranchesAndTagsQuickPick, CommandQuickPickItem } from '../quickpicks'; +import { CommandQuickPickItem, ReferencesQuickPick } from '../quickpicks'; import { ActiveEditorCommand, command, @@ -80,7 +80,7 @@ export class OpenFileInRemoteCommand extends ActiveEditorCommand { if (args.branch === undefined && args.sha === undefined) { const branch = await Container.git.getBranch(gitUri.repoPath); if (branch === undefined || branch.tracking === undefined) { - const pick = await new BranchesAndTagsQuickPick(gitUri.repoPath).show( + const pick = await new ReferencesQuickPick(gitUri.repoPath).show( args.clipboard ? `Copy url for ${gitUri.getRelativePath()} to clipboard for which branch${ GlyphChars.Ellipsis diff --git a/src/commands/openFileRevision.ts b/src/commands/openFileRevision.ts index 9711164..f338bc1 100644 --- a/src/commands/openFileRevision.ts +++ b/src/commands/openFileRevision.ts @@ -3,15 +3,15 @@ import { CancellationTokenSource, commands, Range, TextDocumentShowOptions, Text import { FileAnnotationType } from '../configuration'; import { GlyphChars } from '../constants'; import { Container } from '../container'; -import { GitBranch, GitTag, GitUri } from '../git/gitService'; +import { GitBranch, GitReference, GitTag, GitUri } from '../git/gitService'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { ChooseFromBranchesAndTagsQuickPickItem, CommandQuickPickItem, FileHistoryQuickPick } from '../quickpicks'; +import { CommandQuickPickItem, FileHistoryQuickPick, ShowFileHistoryFromQuickPickItem } from '../quickpicks'; import { Iterables, Strings } from '../system'; import { ActiveEditorCommand, command, Commands, getCommandUri, openEditor } from './common'; export interface OpenFileRevisionCommandArgs { - branchOrTag?: GitBranch | GitTag; + reference?: GitBranch | GitTag | GitReference; uri?: Uri; maxCount?: number; @@ -69,7 +69,7 @@ export class OpenFileRevisionCommand extends ActiveEditorCommand { const gitUri = await GitUri.fromUri(uri); const placeHolder = `Open revision of ${gitUri.getFormattedPath({ - suffix: args.branchOrTag ? ` (${args.branchOrTag.name})` : undefined + suffix: args.reference ? ` (${args.reference.name})` : undefined })}${gitUri.sha ? ` ${Strings.pad(GlyphChars.Dot, 1, 1)} ${gitUri.shortSha}` : ''}${ GlyphChars.Ellipsis }`; @@ -78,11 +78,11 @@ export class OpenFileRevisionCommand extends ActiveEditorCommand { const log = await Container.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, { maxCount: args.maxCount, - ref: (args.branchOrTag && args.branchOrTag.ref) || gitUri.sha + ref: (args.reference && args.reference.ref) || gitUri.sha }); if (log === undefined) { - if (args.branchOrTag) { - return window.showWarningMessage(`The file could not be found in ${args.branchOrTag.name}`); + if (args.reference) { + return window.showWarningMessage(`The file could not be found in ${args.reference.name}`); } return Messages.showFileNotUnderSourceControlWarningMessage('Unable to open history compare'); } @@ -117,16 +117,20 @@ export class OpenFileRevisionCommand extends ActiveEditorCommand { } commandArgs = { ...args }; + const icon = + args.reference instanceof GitTag + ? '$(tag) ' + : args.reference instanceof GitBranch + ? '$(git-branch) ' + : ''; const currentCommand = new CommandQuickPickItem( { label: `go back ${GlyphChars.ArrowBack}`, description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to history of ${ GlyphChars.Space }$(file-text) ${gitUri.getFormattedPath()}${ - args.branchOrTag - ? ` from ${GlyphChars.Space}${ - args.branchOrTag instanceof GitTag ? '$(tag)' : '$(git-branch)' - } ${args.branchOrTag.name}` + args.reference + ? ` from ${GlyphChars.Space}${icon}${args.reference.name}` : gitUri.sha ? ` from ${GlyphChars.Space}$(git-commit) ${gitUri.shortSha}` : '' @@ -156,14 +160,14 @@ export class OpenFileRevisionCommand extends ActiveEditorCommand { }); if (pick === undefined) return undefined; - if (pick instanceof ChooseFromBranchesAndTagsQuickPickItem) { - const branchOrTag = await pick.execute(); - if (branchOrTag === undefined) return undefined; - if (branchOrTag instanceof CommandQuickPickItem) return branchOrTag.execute(); + if (pick instanceof ShowFileHistoryFromQuickPickItem) { + const reference = await pick.execute(); + if (reference === undefined) return undefined; + if (reference instanceof CommandQuickPickItem) return reference.execute(); commandArgs = { ...args, - branchOrTag: branchOrTag.item + reference: reference.item }; return commands.executeCommand(Commands.OpenFileRevision, gitUri, commandArgs); } diff --git a/src/commands/openFileRevisionFrom.ts b/src/commands/openFileRevisionFrom.ts new file mode 100644 index 0000000..ee5a75d --- /dev/null +++ b/src/commands/openFileRevisionFrom.ts @@ -0,0 +1,55 @@ +'use strict'; +import { Range, TextDocumentShowOptions, TextEditor, Uri } from 'vscode'; +import { GlyphChars } from '../constants'; +import { GitBranch, GitReference, GitTag, GitUri } from '../git/gitService'; +import { CommandQuickPickItem, ReferencesQuickPick } from '../quickpicks'; +import { Strings } from '../system'; +import { ActiveEditorCommand, command, Commands, getCommandUri, openEditor } from './common'; + +export interface OpenFileRevisionFromCommandArgs { + reference?: GitBranch | GitTag | GitReference; + + line?: number; + showOptions?: TextDocumentShowOptions; +} + +@command() +export class OpenFileRevisionFromCommand extends ActiveEditorCommand { + constructor() { + super([Commands.OpenFileRevisionFrom, Commands.OpenFileRevisionFromBranch]); + } + + async execute(editor: TextEditor | undefined, uri?: Uri, args: OpenFileRevisionFromCommandArgs = {}) { + uri = getCommandUri(uri, editor); + if (uri == null) return undefined; + + const gitUri = await GitUri.fromUri(uri); + if (!gitUri.repoPath) return undefined; + + if (args.reference === undefined) { + const placeHolder = `Open revision of ${gitUri.getFormattedPath()}${ + gitUri.sha ? ` ${Strings.pad(GlyphChars.Dot, 1, 1)} ${gitUri.shortSha}` : '' + } from${GlyphChars.Ellipsis}`; + + const pick = await new ReferencesQuickPick(gitUri.repoPath).show(placeHolder, { + allowEnteringRefs: true + }); + if (pick === undefined) return undefined; + if (pick instanceof CommandQuickPickItem) return pick.execute(); + + args.reference = pick.item; + } + + if (args.line !== undefined && args.line !== 0) { + if (args.showOptions === undefined) { + args.showOptions = {}; + } + args.showOptions.selection = new Range(args.line, 0, args.line, 0); + } + + return openEditor(GitUri.toRevisionUri(args.reference.ref, gitUri.fsPath, gitUri.repoPath), { + ...args.showOptions, + rethrow: true + }); + } +} diff --git a/src/commands/openFileRevisionFromBranch.ts b/src/commands/openFileRevisionFromBranch.ts deleted file mode 100644 index b310a6e..0000000 --- a/src/commands/openFileRevisionFromBranch.ts +++ /dev/null @@ -1,58 +0,0 @@ -'use strict'; -import { Range, TextDocumentShowOptions, TextEditor, Uri } from 'vscode'; -import { GlyphChars } from '../constants'; -import { GitBranch, GitTag, GitUri } from '../git/gitService'; -import { BranchesAndTagsQuickPick, BranchQuickPickItem, TagQuickPickItem } from '../quickpicks'; -import { Strings } from '../system'; -import { ActiveEditorCommand, command, Commands, getCommandUri, openEditor } from './common'; - -export interface OpenFileRevisionFromBranchCommandArgs { - branchOrTag?: GitBranch | GitTag; - - line?: number; - showOptions?: TextDocumentShowOptions; -} - -@command() -export class OpenFileRevisionFromBranchCommand extends ActiveEditorCommand { - constructor() { - super(Commands.OpenFileRevisionFromBranch); - } - - async execute(editor: TextEditor | undefined, uri?: Uri, args: OpenFileRevisionFromBranchCommandArgs = {}) { - uri = getCommandUri(uri, editor); - if (uri == null) return undefined; - - const gitUri = await GitUri.fromUri(uri); - if (!gitUri.repoPath) return undefined; - - if (args.branchOrTag === undefined) { - const placeHolder = `Open revision of ${gitUri.getFormattedPath()}${ - gitUri.sha ? ` ${Strings.pad(GlyphChars.Dot, 1, 1)} ${gitUri.shortSha}` : '' - } from Branch or Tag${GlyphChars.Ellipsis}`; - - const pick = await new BranchesAndTagsQuickPick(gitUri.repoPath).show(placeHolder, { - allowCommitId: false - }); - if (pick === undefined) return undefined; - - if (!(pick instanceof BranchQuickPickItem) && !(pick instanceof TagQuickPickItem)) { - return undefined; - } - - args.branchOrTag = pick.item; - } - - if (args.line !== undefined && args.line !== 0) { - if (args.showOptions === undefined) { - args.showOptions = {}; - } - args.showOptions.selection = new Range(args.line, 0, args.line, 0); - } - - return openEditor(GitUri.toRevisionUri(args.branchOrTag.ref, gitUri.fsPath, gitUri.repoPath), { - ...args.showOptions, - rethrow: true - }); - } -} diff --git a/src/commands/showQuickBranchHistory.ts b/src/commands/showQuickBranchHistory.ts index 82a133e..081fe21 100644 --- a/src/commands/showQuickBranchHistory.ts +++ b/src/commands/showQuickBranchHistory.ts @@ -5,7 +5,7 @@ import { Container } from '../container'; import { GitLog, GitUri } from '../git/gitService'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { BranchesAndTagsQuickPick, BranchHistoryQuickPick, CommandQuickPickItem } from '../quickpicks'; +import { BranchHistoryQuickPick, CommandQuickPickItem, ReferencesQuickPick } from '../quickpicks'; import { Strings } from '../system'; import { ActiveEditorCachedCommand, command, Commands, getCommandUri, getRepoPathOrActiveOrPrompt } from './common'; import { ShowQuickCommitDetailsCommandArgs } from './showQuickCommitDetails'; @@ -58,7 +58,7 @@ export class ShowQuickBranchHistoryCommand extends ActiveEditorCachedCommand { ); } - const pick = await new BranchesAndTagsQuickPick(repoPath).show( + const pick = await new ReferencesQuickPick(repoPath).show( `Show history for branch${GlyphChars.Ellipsis}`, { goBack: goBackCommand, diff --git a/src/commands/showQuickFileHistory.ts b/src/commands/showQuickFileHistory.ts index 8cc3100..55515a3 100644 --- a/src/commands/showQuickFileHistory.ts +++ b/src/commands/showQuickFileHistory.ts @@ -3,13 +3,13 @@ import * as paths from 'path'; import { commands, Range, TextEditor, Uri, window } from 'vscode'; import { GlyphChars } from '../constants'; import { Container } from '../container'; -import { GitBranch, GitLog, GitTag, GitUri } from '../git/gitService'; +import { GitBranch, GitLog, GitReference, GitTag, GitUri } from '../git/gitService'; import { Logger } from '../logger'; import { Messages } from '../messages'; import { - ChooseFromBranchesAndTagsQuickPickItem, CommandQuickPickItem, FileHistoryQuickPick, + ShowFileHistoryFromQuickPickItem, ShowFileHistoryInViewQuickPickItem } from '../quickpicks'; import { Iterables, Strings } from '../system'; @@ -17,7 +17,7 @@ import { ActiveEditorCachedCommand, command, CommandContext, Commands, getComman import { ShowQuickCommitFileDetailsCommandArgs } from './showQuickCommitFileDetails'; export interface ShowQuickFileHistoryCommandArgs { - branchOrTag?: GitBranch | GitTag; + reference?: GitBranch | GitTag | GitReference; log?: GitLog; maxCount?: number; range?: Range; @@ -57,7 +57,7 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand { args = { ...args }; const placeHolder = `${gitUri.getFormattedPath({ - suffix: args.branchOrTag ? ` (${args.branchOrTag.name})` : undefined + suffix: args.reference ? ` (${args.reference.name})` : undefined })}${gitUri.sha ? ` ${Strings.pad(GlyphChars.Dot, 1, 1)} ${gitUri.shortSha}` : ''}`; const progressCancellation = FileHistoryQuickPick.showProgress(placeHolder); @@ -67,11 +67,11 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand { args.log = await Container.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, { maxCount: args.maxCount, range: args.range, - ref: (args.branchOrTag && args.branchOrTag.ref) || gitUri.sha + ref: (args.reference && args.reference.ref) || gitUri.sha }); if (args.log === undefined) { - if (args.branchOrTag) { - return window.showWarningMessage(`The file could not be found in ${args.branchOrTag.name}`); + if (args.reference) { + return window.showWarningMessage(`The file could not be found in ${args.reference.name}`); } return Messages.showFileNotUnderSourceControlWarningMessage('Unable to show file history'); } @@ -111,6 +111,12 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand { } } + const icon = + args.reference instanceof GitTag + ? '$(tag) ' + : args.reference instanceof GitBranch + ? '$(git-branch) ' + : ''; // Create a command to get back to where we are right now const currentCommand = new CommandQuickPickItem( { @@ -118,10 +124,8 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand { description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to history of ${ GlyphChars.Space }$(file-text) ${paths.basename(gitUri.fsPath)}${ - args.branchOrTag - ? ` from ${GlyphChars.Space}${ - args.branchOrTag instanceof GitTag ? '$(tag)' : '$(git-branch)' - } ${args.branchOrTag.name}` + args.reference + ? ` from ${GlyphChars.Space}${icon}${args.reference.name}` : gitUri.sha ? ` from ${GlyphChars.Space}$(git-commit) ${gitUri.shortSha}` : '' @@ -152,21 +156,21 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand { args.log !== undefined ? new ShowFileHistoryInViewQuickPickItem( gitUri, - (args.branchOrTag && args.branchOrTag.ref) || gitUri.sha + (args.reference && args.reference.ref) || gitUri.sha ) : undefined }); if (pick === undefined) return undefined; - if (pick instanceof ChooseFromBranchesAndTagsQuickPickItem) { - const branchOrTag = await pick.execute(); - if (branchOrTag === undefined) return undefined; - if (branchOrTag instanceof CommandQuickPickItem) return branchOrTag.execute(); + if (pick instanceof ShowFileHistoryFromQuickPickItem) { + const reference = await pick.execute(); + if (reference === undefined) return undefined; + if (reference instanceof CommandQuickPickItem) return reference.execute(); const commandArgs: ShowQuickFileHistoryCommandArgs = { ...args, log: undefined, - branchOrTag: branchOrTag.item, + reference: reference.item, goBackCommand: currentCommand }; return commands.executeCommand(Commands.ShowQuickFileHistory, gitUri, commandArgs); diff --git a/src/git/models/models.ts b/src/git/models/models.ts index 5a486b1..d3dda89 100644 --- a/src/git/models/models.ts +++ b/src/git/models/models.ts @@ -1,5 +1,10 @@ 'use strict'; +export interface GitReference { + name: string; + ref: string; +} + export * from './blame'; export * from './blameCommit'; export * from './branch'; diff --git a/src/quickpicks.ts b/src/quickpicks.ts index 1f66862..d5f8700 100644 --- a/src/quickpicks.ts +++ b/src/quickpicks.ts @@ -1,6 +1,5 @@ 'use strict'; -export * from './quickpicks/branchesAndTagsQuickPick'; export * from './quickpicks/branchHistoryQuickPick'; export * from './quickpicks/commitFileQuickPick'; export * from './quickpicks/commitQuickPick'; @@ -8,6 +7,7 @@ export * from './quickpicks/commitsQuickPick'; export * from './quickpicks/commonQuickPicks'; export * from './quickpicks/fileHistoryQuickPick'; export * from './quickpicks/modesQuickPick'; +export * from './quickpicks/referencesQuickPick'; export * from './quickpicks/remotesQuickPick'; export * from './quickpicks/repositoriesQuickPick'; export * from './quickpicks/repoStatusQuickPick'; diff --git a/src/quickpicks/branchesAndTagsQuickPick.ts b/src/quickpicks/branchesAndTagsQuickPick.ts deleted file mode 100644 index 0587467..0000000 --- a/src/quickpicks/branchesAndTagsQuickPick.ts +++ /dev/null @@ -1,290 +0,0 @@ -'use strict'; -import { CancellationToken, CancellationTokenSource, QuickPickItem, window } from 'vscode'; -import { GlyphChars } from '../constants'; -import { Container } from '../container'; -import { GitBranch, GitService, GitTag } from '../git/gitService'; -import { Functions } from '../system'; -import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './commonQuickPicks'; - -export class RefQuickPickItem implements QuickPickItem { - label: string; - description: string; - detail: string | undefined; - - constructor(public readonly ref: string, checked?: boolean) { - this.label = `${checked ? `$(check)${GlyphChars.Space}` : GlyphChars.Space.repeat(4)} ${GitService.shortenSha( - ref - )}`; - this.description = ''; - } - - get current() { - return false; - } - - get item() { - return undefined; - } - - get remote() { - return false; - } -} - -export class BranchQuickPickItem implements QuickPickItem { - label: string; - description: string; - detail: string | undefined; - - constructor(public readonly branch: GitBranch, checked?: boolean) { - checked = checked || (checked === undefined && branch.current); - this.label = `${checked ? `$(check)${GlyphChars.Space}` : GlyphChars.Space.repeat(4)} ${branch.name}`; - this.description = branch.remote - ? `${GlyphChars.Space.repeat(2)} remote branch` - : branch.current - ? 'current branch' - : ''; - } - - get current() { - return this.branch.current; - } - - get item() { - return this.branch; - } - - get ref() { - return this.branch.name; - } - - get remote() { - return this.branch.remote; - } -} - -export class TagQuickPickItem implements QuickPickItem { - label: string; - description: string; - detail: string | undefined; - - constructor(public readonly tag: GitBranch | GitTag, checked?: boolean) { - this.label = `${checked ? `$(check)${GlyphChars.Space}` : GlyphChars.Space.repeat(4)} ${tag.name}`; - this.description = `${GlyphChars.Space.repeat(2)} tag`; - } - - get current() { - return false; - } - - get item() { - return this.tag; - } - - get ref() { - return this.tag.name; - } - - get remote() { - return false; - } -} - -export type BranchAndTagQuickPickResult = BranchQuickPickItem | TagQuickPickItem | RefQuickPickItem; - -export interface BranchesAndTagsQuickPickOptions { - allowCommitId?: boolean; - autoPick?: boolean; - checked?: string; - filters?: { - branches?(branch: GitBranch): boolean; - tags?(tag: GitTag): boolean; - }; - goBack?: CommandQuickPickItem; - include?: 'branches' | 'tags' | 'all'; -} - -export class BranchesAndTagsQuickPick { - constructor(public readonly repoPath: string | undefined) {} - - async show( - placeHolder: string, - options: BranchesAndTagsQuickPickOptions = {} - ): Promise { - const cancellation = new CancellationTokenSource(); - - let scope; - if (options.goBack) { - scope = await Container.keyboard.beginScope({ left: options.goBack }); - } - - let autoPick; - try { - let items = this.getItems(options, cancellation.token); - if (options.autoPick) { - items = items.then(itms => { - if (itms.length <= 1) { - autoPick = itms[0]; - cancellation.cancel(); - } - return itms; - }); - } - - let pick; - if (options.allowCommitId) { - placeHolder += `${GlyphChars.Space.repeat(3)}(use # to enter a commit id)`; - - const quickpick = window.createQuickPick(); - quickpick.busy = true; - quickpick.enabled = false; - quickpick.placeholder = placeHolder; - quickpick.ignoreFocusOut = getQuickPickIgnoreFocusOut(); - quickpick.show(); - - quickpick.items = await items; - quickpick.busy = false; - quickpick.enabled = true; - - pick = await new Promise(resolve => { - cancellation.token.onCancellationRequested(() => quickpick.hide()); - - quickpick.onDidHide(() => resolve(undefined)); - quickpick.onDidChangeValue(value => { - quickpick.title = - value && value.startsWith('#') - ? "Please enter a commit id (Press 'Enter' to confirm or 'Escape' to cancel)" - : undefined; - }); - quickpick.onDidAccept(async () => { - if (quickpick.selectedItems.length === 0) { - let ref = quickpick.value; - if (!ref || !ref.startsWith('#')) return; - - ref = ref.substr(1); - - quickpick.busy = true; - quickpick.enabled = false; - - if ( - this.repoPath === undefined || - (await Container.git.validateReference(this.repoPath, ref)) - ) { - resolve(new RefQuickPickItem(ref)); - } - else { - quickpick.title = 'You must enter a valid commit id'; - quickpick.busy = false; - quickpick.enabled = true; - return; - } - } - else { - resolve(quickpick.selectedItems[0]); - } - - quickpick.hide(); - }); - }); - - quickpick.dispose(); - } - else { - pick = await window.showQuickPick( - items, - { - placeHolder: placeHolder, - ignoreFocusOut: getQuickPickIgnoreFocusOut() - }, - cancellation.token - ); - } - - if (pick === undefined && autoPick !== undefined) { - pick = autoPick; - } - - if (pick === undefined) { - cancellation.cancel(); - } - - return pick; - } - finally { - if (scope !== undefined) { - await scope.dispose(); - } - - cancellation.dispose(); - } - } - - private async getItems(options: BranchesAndTagsQuickPickOptions, token: CancellationToken) { - const { checked, filters, goBack, include } = { include: 'all', ...options }; - - let branches; - let tags; - switch (include) { - case 'branches': { - const result = await Functions.cancellable(Container.git.getBranches(this.repoPath), token); - if (result === undefined || token.isCancellationRequested) return []; - - branches = result; - break; - } - case 'tags': { - const result = await Functions.cancellable(Container.git.getTags(this.repoPath), token); - if (result === undefined || token.isCancellationRequested) return []; - - tags = result; - break; - } - default: { - const result = await Functions.cancellable( - Promise.all([Container.git.getBranches(this.repoPath), Container.git.getTags(this.repoPath)]), - token - ); - if (result === undefined || token.isCancellationRequested) return []; - - [branches, tags] = result; - break; - } - } - - const items: (BranchQuickPickItem | TagQuickPickItem | CommandQuickPickItem)[] = []; - - if (branches !== undefined) { - const filter = - filters !== undefined && typeof filters.branches === 'function' ? filters.branches : undefined; - - branches.sort( - (a, b) => - (a.starred ? -1 : 1) - (b.starred ? -1 : 1) || - (b.remote ? -1 : 1) - (a.remote ? -1 : 1) || - a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }) - ); - for (const b of branches) { - if (filter !== undefined && !filter(b)) continue; - - items.push(new BranchQuickPickItem(b, checked !== undefined ? b.name === checked : undefined)); - } - } - - if (tags !== undefined) { - const filter = filters !== undefined && typeof filters.tags === 'function' ? filters.tags : undefined; - - tags.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' })); - for (const t of tags) { - if (filter !== undefined && !filter(t)) continue; - - items.push(new TagQuickPickItem(t, checked !== undefined ? t.name === checked : undefined)); - } - } - - if (goBack !== undefined) { - items.splice(0, 0, goBack); - } - - return items; - } -} diff --git a/src/quickpicks/commonQuickPicks.ts b/src/quickpicks/commonQuickPicks.ts index 6cb02a3..f15deab 100644 --- a/src/quickpicks/commonQuickPicks.ts +++ b/src/quickpicks/commonQuickPicks.ts @@ -15,12 +15,7 @@ import { Container } from '../container'; import { GitLog, GitLogCommit, GitRepoSearchBy, GitStashCommit, GitUri } from '../git/gitService'; import { KeyMapping, Keys } from '../keyboard'; import { Strings } from '../system'; -import { - BranchesAndTagsQuickPick, - BranchQuickPickItem, - RefQuickPickItem, - TagQuickPickItem -} from './branchesAndTagsQuickPick'; +import { ReferencesQuickPick, ReferencesQuickPickItem } from './referencesQuickPick'; export function getQuickPickIgnoreFocusOut() { return !configuration.get(configuration.name('advanced')('quickPick')('closeOnFocusOut').value); @@ -134,27 +129,6 @@ export class CommitQuickPickItem implemen } } -export class ChooseFromBranchesAndTagsQuickPickItem extends CommandQuickPickItem { - constructor( - private readonly repoPath: string, - private readonly placeHolder: string, - private readonly _goBack?: CommandQuickPickItem, - item: QuickPickItem = { - label: 'Choose from Branch or Tag History...', - description: `${Strings.pad(GlyphChars.Dash, 2, 2)} shows list of branches and tags` - } - ) { - super(item, undefined, undefined); - } - - execute(): Promise { - return new BranchesAndTagsQuickPick(this.repoPath).show(this.placeHolder, { - allowCommitId: true, - goBack: this._goBack - }); - } -} - export class KeyCommandQuickPickItem extends CommandQuickPickItem { constructor(command: Commands, args?: any[]) { super({ label: '', description: '' }, command, args); @@ -262,6 +236,27 @@ export class ShowCommitSearchResultsInViewQuickPickItem extends CommandQuickPick } } +export class ShowFileHistoryFromQuickPickItem extends CommandQuickPickItem { + constructor( + private readonly repoPath: string, + private readonly placeHolder: string, + private readonly _goBack?: CommandQuickPickItem, + item: QuickPickItem = { + label: 'Show File History from...', + description: `${Strings.pad(GlyphChars.Dash, 2, 2)} shows an alternate file history` + } + ) { + super(item, undefined, undefined); + } + + execute(): Promise { + return new ReferencesQuickPick(this.repoPath).show(this.placeHolder, { + allowEnteringRefs: true, + goBack: this._goBack + }); + } +} + export class ShowFileHistoryInViewQuickPickItem extends CommandQuickPickItem { constructor( public readonly uri: GitUri, diff --git a/src/quickpicks/fileHistoryQuickPick.ts b/src/quickpicks/fileHistoryQuickPick.ts index 491231a..7d456be 100644 --- a/src/quickpicks/fileHistoryQuickPick.ts +++ b/src/quickpicks/fileHistoryQuickPick.ts @@ -8,10 +8,10 @@ import { GitLog, GitUri, RemoteResource, RemoteResourceType } from '../git/gitSe import { KeyNoopCommand } from '../keyboard'; import { Iterables, Strings } from '../system'; import { - ChooseFromBranchesAndTagsQuickPickItem, CommandQuickPickItem, CommitQuickPickItem, getQuickPickIgnoreFocusOut, + ShowFileHistoryFromQuickPickItem, showQuickPickProgress } from './commonQuickPicks'; import { OpenRemotesCommandQuickPickItem } from './remotesQuickPick'; @@ -49,11 +49,7 @@ export class FileHistoryQuickPick { let index = 0; index++; - items.splice( - 0, - 0, - new ChooseFromBranchesAndTagsQuickPickItem(log.repoPath, placeHolder, options.currentCommand) - ); + items.splice(0, 0, new ShowFileHistoryFromQuickPickItem(log.repoPath, placeHolder, options.currentCommand)); if (options.showInViewCommand !== undefined) { index++; diff --git a/src/quickpicks/referencesQuickPick.ts b/src/quickpicks/referencesQuickPick.ts new file mode 100644 index 0000000..bd994e4 --- /dev/null +++ b/src/quickpicks/referencesQuickPick.ts @@ -0,0 +1,281 @@ +'use strict'; +import { CancellationToken, CancellationTokenSource, QuickPickItem, window } from 'vscode'; +import { GlyphChars } from '../constants'; +import { Container } from '../container'; +import { GitBranch, GitReference, GitService, GitTag } from '../git/gitService'; +import { Functions } from '../system'; +import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './commonQuickPicks'; + +export class RefQuickPickItem implements QuickPickItem { + label: string; + description: string; + detail: string | undefined; + + constructor(public readonly ref: string, checked?: boolean) { + this.label = `${checked ? `$(check)${GlyphChars.Space}` : GlyphChars.Space.repeat(4)} ${GitService.shortenSha( + ref + )}`; + this.description = ''; + } + + get current() { + return false; + } + + get item() { + const ref: GitReference = { name: this.ref, ref: this.ref }; + return ref; + } + + get remote() { + return false; + } +} + +export class BranchQuickPickItem implements QuickPickItem { + label: string; + description: string; + detail: string | undefined; + + constructor(public readonly branch: GitBranch, checked?: boolean) { + checked = checked || (checked === undefined && branch.current); + this.label = `${checked ? `$(check)${GlyphChars.Space}` : GlyphChars.Space.repeat(4)} ${branch.name}`; + this.description = branch.remote + ? `${GlyphChars.Space.repeat(2)} remote branch` + : branch.current + ? 'current branch' + : ''; + } + + get current() { + return this.branch.current; + } + + get item() { + return this.branch; + } + + get ref() { + return this.branch.name; + } + + get remote() { + return this.branch.remote; + } +} + +export class TagQuickPickItem implements QuickPickItem { + label: string; + description: string; + detail: string | undefined; + + constructor(public readonly tag: GitTag, checked?: boolean) { + this.label = `${checked ? `$(check)${GlyphChars.Space}` : GlyphChars.Space.repeat(4)} ${tag.name}`; + this.description = `${GlyphChars.Space.repeat(2)} tag`; + } + + get current() { + return false; + } + + get item() { + return this.tag; + } + + get ref() { + return this.tag.name; + } + + get remote() { + return false; + } +} + +export type ReferencesQuickPickItem = BranchQuickPickItem | TagQuickPickItem | RefQuickPickItem; + +export interface ReferencesQuickPickOptions { + allowEnteringRefs?: boolean; + autoPick?: boolean; + checked?: string; + filters?: { + branches?(branch: GitBranch): boolean; + tags?(tag: GitTag): boolean; + }; + goBack?: CommandQuickPickItem; + include?: 'branches' | 'tags' | 'all'; +} + +export class ReferencesQuickPick { + constructor(public readonly repoPath: string | undefined) {} + + async show( + placeHolder: string, + options: ReferencesQuickPickOptions = {} + ): Promise { + const cancellation = new CancellationTokenSource(); + + let scope; + if (options.goBack) { + scope = await Container.keyboard.beginScope({ left: options.goBack }); + } + + let autoPick; + try { + let items = this.getItems(options, cancellation.token); + if (options.autoPick) { + items = items.then(itms => { + if (itms.length <= 1) { + autoPick = itms[0]; + cancellation.cancel(); + } + return itms; + }); + } + + let pick; + if (options.allowEnteringRefs) { + placeHolder += `${GlyphChars.Space.repeat(3)}(select or enter a reference)`; + + const quickpick = window.createQuickPick(); + quickpick.busy = true; + quickpick.enabled = false; + quickpick.placeholder = placeHolder; + quickpick.ignoreFocusOut = getQuickPickIgnoreFocusOut(); + quickpick.show(); + + quickpick.items = await items; + quickpick.busy = false; + quickpick.enabled = true; + + pick = await new Promise(resolve => { + cancellation.token.onCancellationRequested(() => quickpick.hide()); + + quickpick.onDidHide(() => resolve(undefined)); + quickpick.onDidAccept(async () => { + if (quickpick.selectedItems.length === 0) { + quickpick.busy = true; + quickpick.enabled = false; + + const ref = quickpick.value; + if ( + this.repoPath === undefined || + (await Container.git.validateReference(this.repoPath, ref)) + ) { + resolve(new RefQuickPickItem(ref)); + } + else { + quickpick.title = 'You must enter a valid reference'; + quickpick.busy = false; + quickpick.enabled = true; + return; + } + } + else { + resolve(quickpick.selectedItems[0]); + } + + quickpick.hide(); + }); + }); + + quickpick.dispose(); + } + else { + pick = await window.showQuickPick( + items, + { + placeHolder: placeHolder, + ignoreFocusOut: getQuickPickIgnoreFocusOut() + }, + cancellation.token + ); + } + + if (pick === undefined && autoPick !== undefined) { + pick = autoPick; + } + + if (pick === undefined) { + cancellation.cancel(); + } + + return pick; + } + finally { + if (scope !== undefined) { + await scope.dispose(); + } + + cancellation.dispose(); + } + } + + private async getItems(options: ReferencesQuickPickOptions, token: CancellationToken) { + const { checked, filters, goBack, include } = { include: 'all', ...options }; + + let branches; + let tags; + switch (include) { + case 'branches': { + const result = await Functions.cancellable(Container.git.getBranches(this.repoPath), token); + if (result === undefined || token.isCancellationRequested) return []; + + branches = result; + break; + } + case 'tags': { + const result = await Functions.cancellable(Container.git.getTags(this.repoPath), token); + if (result === undefined || token.isCancellationRequested) return []; + + tags = result; + break; + } + default: { + const result = await Functions.cancellable( + Promise.all([Container.git.getBranches(this.repoPath), Container.git.getTags(this.repoPath)]), + token + ); + if (result === undefined || token.isCancellationRequested) return []; + + [branches, tags] = result; + break; + } + } + + const items: (BranchQuickPickItem | TagQuickPickItem | CommandQuickPickItem)[] = []; + + if (branches !== undefined) { + const filter = + filters !== undefined && typeof filters.branches === 'function' ? filters.branches : undefined; + + branches.sort( + (a, b) => + (a.starred ? -1 : 1) - (b.starred ? -1 : 1) || + (b.remote ? -1 : 1) - (a.remote ? -1 : 1) || + a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }) + ); + for (const b of branches) { + if (filter !== undefined && !filter(b)) continue; + + items.push(new BranchQuickPickItem(b, checked !== undefined ? b.name === checked : undefined)); + } + } + + if (tags !== undefined) { + const filter = filters !== undefined && typeof filters.tags === 'function' ? filters.tags : undefined; + + tags.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' })); + for (const t of tags) { + if (filter !== undefined && !filter(t)) continue; + + items.push(new TagQuickPickItem(t, checked !== undefined ? t.name === checked : undefined)); + } + } + + if (goBack !== undefined) { + items.splice(0, 0, goBack); + } + + return items; + } +} diff --git a/src/views/nodes/compareNode.ts b/src/views/nodes/compareNode.ts index 0c1661a..76d7f03 100644 --- a/src/views/nodes/compareNode.ts +++ b/src/views/nodes/compareNode.ts @@ -3,7 +3,7 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode'; import { getRepoPathOrPrompt } from '../../commands'; import { CommandContext, GlyphChars, NamedRef, setCommandContext } from '../../constants'; import { GitService } from '../../git/gitService'; -import { BranchesAndTagsQuickPick, CommandQuickPickItem } from '../../quickpicks'; +import { CommandQuickPickItem, ReferencesQuickPick } from '../../quickpicks'; import { debug, Functions, gate, Iterables, log } from '../../system'; import { CompareView } from '../compareView'; import { MessageNode } from './common'; @@ -139,9 +139,9 @@ export class CompareNode extends ViewNode { } if (ref === undefined) { - const pick = await new BranchesAndTagsQuickPick(repoPath).show( + const pick = await new ReferencesQuickPick(repoPath).show( `Compare ${this.getRefName(this._selectedRef.ref)} with${GlyphChars.Ellipsis}`, - { allowCommitId: true } + { allowEnteringRefs: true } ); if (pick === undefined || pick instanceof CommandQuickPickItem) return; @@ -158,19 +158,15 @@ export class CompareNode extends ViewNode { async selectForCompare(repoPath?: string, ref?: string | NamedRef) { if (repoPath === undefined) { - repoPath = await getRepoPathOrPrompt( - undefined, - `Select branch or tag for compare in which repository${GlyphChars.Ellipsis}` - ); + repoPath = await getRepoPathOrPrompt(undefined, `Compare in which repository${GlyphChars.Ellipsis}`); } if (repoPath === undefined) return; let autoCompare = false; if (ref === undefined) { - const pick = await new BranchesAndTagsQuickPick(repoPath).show( - `Select branch or tag for compare${GlyphChars.Ellipsis}`, - { allowCommitId: true } - ); + const pick = await new ReferencesQuickPick(repoPath).show(`Compare${GlyphChars.Ellipsis}`, { + allowEnteringRefs: true + }); if (pick === undefined || pick instanceof CommandQuickPickItem) return; ref = pick.ref; diff --git a/src/views/nodes/comparePickerNode.ts b/src/views/nodes/comparePickerNode.ts index 4a45c58..2e0cfd8 100644 --- a/src/views/nodes/comparePickerNode.ts +++ b/src/views/nodes/comparePickerNode.ts @@ -35,9 +35,9 @@ export class ComparePickerNode extends ViewNode { ); item.contextValue = ResourceType.ComparePicker; item.description = description; - item.tooltip = `Click to select branch or tag for compare${GlyphChars.Ellipsis}`; + item.tooltip = `Click to select or enter a reference for compare${GlyphChars.Ellipsis}`; item.command = { - title: `Select branch or tag for compare${GlyphChars.Ellipsis}`, + title: `Compare${GlyphChars.Ellipsis}`, command: this.view.getQualifiedCommand('selectForCompare') }; } diff --git a/src/views/nodes/fileHistoryTrackerNode.ts b/src/views/nodes/fileHistoryTrackerNode.ts index 02220cf..63e4d01 100644 --- a/src/views/nodes/fileHistoryTrackerNode.ts +++ b/src/views/nodes/fileHistoryTrackerNode.ts @@ -5,7 +5,7 @@ import { GlyphChars } from '../../constants'; import { Container } from '../../container'; import { GitCommitish, GitUri } from '../../git/gitService'; import { Logger } from '../../logger'; -import { BranchesAndTagsQuickPick, CommandQuickPickItem } from '../../quickpicks'; +import { CommandQuickPickItem, ReferencesQuickPick } from '../../quickpicks'; import { debug, Functions, gate, log } from '../../system'; import { FileHistoryView } from '../fileHistoryView'; import { MessageNode } from './common'; @@ -68,10 +68,10 @@ export class FileHistoryTrackerNode extends SubscribeableViewNode