diff --git a/CHANGELOG.md b/CHANGELOG.md index 990dbd5..e4030ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,27 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ## [Unreleased] +### Added +- Adds new logo/icon +- Adds new Git terminal commands to the `GitLens` custom view - opens a `GitLens` terminal and sends the specified Git command to it + - Adds `Create Branch (via Terminal)...` command (`gitlens.terminalCreateBranch`) to branch node(s) of the `GitLens` custom view + - Adds `Delete Branch (via Terminal)` command (`gitlens.terminalDeleteBranch`) to branch node(s) of the `GitLens` custom view + - Adds `Rebase Branch to Remote (via Terminal)` command (`gitlens.terminalRebaseBranchToRemote`) to branch node(s) of the `GitLens` custom view + - Adds `Rebase Commit (via Terminal)` command (`gitlens.terminalRebaseCommit`) to commit node(s) of the `GitLens` custom view + - Adds `Reset Commit (via Terminal)` command (`gitlens.terminalResetCommit`) to commit node(s) of the `GitLens` custom view + +### Changed +- Delays (slightly) the initial loading of the `GitLens` custom view to improve startup performance + +### Fixed +- Fixes issues with certain commands failing when there is no current branch (rebase, detached HEAD, etc) + ## [6.0.0-beta2] - 2017-11-03 ATTENTION! To support multi-root workspaces some underlying fundamentals had to change, so please expect and report issues. Thanks! ### Added -- Adds multi-root workspace support +- Adds multi-root workspace support -- [Learn more](https://code.visualstudio.com/docs/editor/multi-root-workspaces) - Adds a progress indicator to the `Search Commits` command (`gitlens.showCommitSearch`) - Adds code search support to the `Search Commits` command (`gitlens.showCommitSearch`) -- closes [#127](https://github.com/eamodio/vscode-gitlens/issues/127) - Use `~` to search for commits with differences whose patch text contains added/removed lines that match `` @@ -27,7 +42,6 @@ ATTENTION! To support multi-root workspaces some underlying fundamentals had to - Optimizes event handling, executing git commands, and general processing to improve performance and reduce any lag - Optimizes current line hover annotations to only be computed on hover (i.e. lazily evaluated) to reduce the compute required when changing lines - Protects credentials from possibly being affected by poor network conditions via Git Credential Manager (GCM) for Windows environment variables -- Delays (slightly) the initial loading of the `GitLens` custom view to improve startup performance ### Fixed - Fixes jumpy code lens when deleting characters from a line with a Git code lens diff --git a/README.md b/README.md index 2ef8ea5..aac30ab 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,11 @@ [![](https://vsmarketplacebadge.apphb.com/rating/eamodio.gitlens.svg)](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) [![Chat at https://vscode-dev-community.slack.com/](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/chat-badge.png)](https://join.slack.com/t/vscode-dev-community/shared_invite/enQtMjIxOTgxNDE3NzM0LWU5M2ZiZDU1YjBlMzdlZjA2YjBjYzRhYTM5NTgzMTAxMjdiNWU0ZmQzYWI3MWU5N2Q1YjBiYmQ4MzY0NDE1MzY) +

+

+ logo +

+ # GitLens GitLens **supercharges** the built-in Visual Studio Code Git capabilities. It helps you to **visualize code authorship** at a glance via Git blame annotations and code lens, **seamlessly navigate and explore** the history of a file or branch, **gain valuable insights** via powerful comparison commands, and so much more. @@ -157,10 +162,9 @@ GitLens provides an unobtrusive blame annotation at the end of the current line, - Indicates which branch is the current branch and [optionally](#gitlens-custom-view-settings) shows the remote tracking branch - Expand each branch to easily see its revision (commit) history - Expand each revision (commit) to quickly see the set of files changed, complete with status indicators for adds, changes, renames, and deletes + - Provides a context menu on each revision (commit) with `Open Commit in Remote`, `Open All Changes`, `Open All Changes with Working Tree`, `Open Files`, `Open Revisions`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`, `Show Commit Details`, `Rebase Commit (via Terminal)`, `Reset Commit (via Terminal)`, and `Refresh` commands - Provides a context menu on each changed file with `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, `Open Revision in Remote`, `Apply Changes`, and `Show Commit File Details` commands - - - Provides a context menu on each revision (commit) with `Open Commit in Remote`, `Open All Changes`, `Open All Changes with Working Tree`, `Open Files`, `Open Revisions`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`, `Show Commit Details`, and `Refresh` commands - - Provides a context menu on each branch with `Open Branch in Remote`, and `Refresh` commands + - Provides a context menu on each branch with `Open Branch in Remote`, `Create Branch (via Terminal)...`, `Delete Branch (via Terminal)`, `Rebase Branch to Remote (via Terminal)`, and `Refresh` commands - Provides a context menu with `Open Branches in Remote`, and `Refresh` commands - `Remotes` node — provides a list of remotes @@ -168,16 +172,17 @@ GitLens provides an unobtrusive blame annotation at the end of the current line, - Expand each remote to see its list of branches - Expand each branch to easily see its revision (commit) history - Expand each revision (commit) to quickly see the set of files changed, complete with status indicators for adds, changes, renames, and deletes + - Provides a context menu on each revision (commit) with `Open Commit in Remote`, `Open All Changes`, `Open All Changes with Working Tree`, `Open Files`, `Open Revisions`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`,`Show Commit Details`, and `Refresh` commands - Provides a context menu on each changed file with `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, `Open Revision in Remote`, `Apply Changes`, `Show File History`, and `Show Commit File Details` commands - - Provides a context menu on each revision (commit) with `Open Commit in Remote`, `Open All Changes`, `Open All Changes with Working Tree`, `Open Files`, `Open Revisions`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`,`Show Commit Details`, and `Refresh` commands + - Provides a context menu on each remote branch with `Open Branch in Remote`, `Create Branch (via Terminal)...`, `Delete Branch (via Terminal)`, and `Refresh` commands - Provides a context menu on each remote with `Open Branches in Remote`, `Open Repository in Remote`, and `Refresh` commands - Provides a context menu with a `Refresh` command - `Stashes` node — provides a list of stashed changes - Expand each stash to quickly see the set of files stashed, complete with status indicators for adds, changes, renames, and deletes + - Provides a context menu on each stash with `Apply Stashed Changes` (confirmation required), `Delete Stashed Changes` (confirmation required), `Open All Changes`, `Open All Changes with Working Tree`, `Open Files`, `Open Revisions`, `Copy Commit Message to Clipboard`, and `Refresh` commands + - Provides a context menu on each stashed file with `Apply Changes`, `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, and `Show File History` commands - Provides a context menu with `Stash Changes`, and `Refresh` commands - - Provides a context menu on each stash with `Apply Stashed Changes` (confirmation required), `Delete Stashed Changes` (confirmation required), `Open All Changes`, `Open All Changes with Working Tree`, `Open Files`, `Open Revisions`, `Copy Commit Message to Clipboard`, and `Refresh` commands - - Provides a context menu on each stashed file with `Apply Changes`, `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, and `Show File History` commands - `History View` - provides the revision history of the active file diff --git a/package.json b/package.json index f727e5a..6c4e749 100644 --- a/package.json +++ b/package.json @@ -1319,6 +1319,31 @@ "command": "gitlens.gitExplorer.applyChanges", "title": "Apply Changes", "category": "GitLens" + }, + { + "command": "gitlens.gitExplorer.terminalCreateBranch", + "title": "Create Branch (via Terminal)...", + "category": "GitLens" + }, + { + "command": "gitlens.gitExplorer.terminalDeleteBranch", + "title": "Delete Branch (via Terminal)", + "category": "GitLens" + }, + { + "command": "gitlens.gitExplorer.terminalRebaseBranchToRemote", + "title": "Rebase Branch to Remote (via Terminal)", + "category": "GitLens" + }, + { + "command": "gitlens.gitExplorer.terminalRebaseCommit", + "title": "Rebase Commit (via Terminal)", + "category": "GitLens" + }, + { + "command": "gitlens.gitExplorer.terminalResetCommit", + "title": "Reset Commit (via Terminal)", + "category": "GitLens" } ], "menus": { @@ -1550,6 +1575,26 @@ { "command": "gitlens.gitExplorer.applyChanges", "when": "false" + }, + { + "command": "gitlens.gitExplorer.terminalCreateBranch", + "when": "false" + }, + { + "command": "gitlens.gitExplorer.terminalDeleteBranch", + "when": "false" + }, + { + "command": "gitlens.gitExplorer.terminalRebaseBranchToRemote", + "when": "false" + }, + { + "command": "gitlens.gitExplorer.terminalRebaseCommit", + "when": "false" + }, + { + "command": "gitlens.gitExplorer.terminalResetCommit", + "when": "false" } ], "editor/context": [ @@ -1815,15 +1860,70 @@ "view/item/context": [ { "command": "gitlens.openBranchesInRemote", - "when": "view == gitlens.gitExplorer && viewItem == gitlens:branches:remote", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:branches:remotes", + "group": "1_gitlens@1" + }, + { + "command": "gitlens.gitExplorer.terminalCreateBranch", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:branch-history", + "group": "8_gitlens@1" + }, + { + "command": "gitlens.gitExplorer.terminalDeleteBranch", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:branch-history", + "group": "8_gitlens@2" + }, + { + "command": "gitlens.openBranchInRemote", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:branch-history:tracking", + "group": "1_gitlens@1" + }, + { + "command": "gitlens.gitExplorer.terminalCreateBranch", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:branch-history:tracking", + "group": "8_gitlens@1" + }, + { + "command": "gitlens.gitExplorer.terminalDeleteBranch", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:branch-history:tracking", + "group": "8_gitlens@2" + }, + { + "command": "gitlens.gitExplorer.terminalCreateBranch", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:current-branch-history", + "group": "8_gitlens@1" + }, + { + "command": "gitlens.openBranchInRemote", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:current-branch-history:tracking", "group": "1_gitlens@1" }, { + "command": "gitlens.gitExplorer.terminalCreateBranch", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:current-branch-history:tracking", + "group": "8_gitlens@1" + }, + { + "command": "gitlens.gitExplorer.terminalRebaseBranchToRemote", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:current-branch-history:tracking", + "group": "8_gitlens@2" + }, + { "command": "gitlens.openBranchInRemote", - "when": "view == gitlens.gitExplorer && viewItem == gitlens:branch-history:remote", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:remote-branch-history", "group": "1_gitlens@1" }, { + "command": "gitlens.gitExplorer.terminalCreateBranch", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:remote-branch-history", + "group": "8_gitlens@1" + }, + { + "command": "gitlens.gitExplorer.terminalDeleteBranch", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:remote-branch-history", + "group": "8_gitlens@2" + }, + { "command": "gitlens.openCommitInRemote", "when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:commit", "group": "1_gitlens@1" @@ -1864,6 +1964,56 @@ "group": "5_gitlens@1" }, { + "command": "gitlens.openCommitInRemote", + "when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:commit:current", + "group": "1_gitlens@1" + }, + { + "command": "gitlens.gitExplorer.openChangedFileChanges", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:commit:current", + "group": "2_gitlens@1" + }, + { + "command": "gitlens.gitExplorer.openChangedFileChangesWithWorking", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:commit:current", + "group": "2_gitlens@1" + }, + { + "command": "gitlens.gitExplorer.openChangedFiles", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:commit:current", + "group": "3_gitlens@1" + }, + { + "command": "gitlens.gitExplorer.openChangedFileRevisions", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:commit:current", + "group": "3_gitlens@2" + }, + { + "command": "gitlens.copyShaToClipboard", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:commit:current", + "group": "4_gitlens@1" + }, + { + "command": "gitlens.copyMessageToClipboard", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:commit:current", + "group": "4_gitlens@2" + }, + { + "command": "gitlens.showQuickCommitDetails", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:commit:current", + "group": "5_gitlens@1" + }, + { + "command": "gitlens.gitExplorer.terminalRebaseCommit", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:commit:current", + "group": "8_gitlens@1" + }, + { + "command": "gitlens.gitExplorer.terminalResetCommit", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:commit:current", + "group": "8_gitlens@2" + }, + { "command": "gitlens.gitExplorer.openChanges", "when": "view == gitlens.gitExplorer && viewItem == gitlens:commit-file", "group": "1_gitlens@1" @@ -2059,6 +2209,11 @@ "group": "1_gitlens@2" }, { + "command": "gitlens.gitExplorer.terminalRebaseBranchToRemote", + "when": "view == gitlens.gitExplorer && viewItem == gitlens:status-upstream", + "group": "8_gitlens@1" + }, + { "command": "gitlens.gitExplorer.refreshNode", "when": "view == gitlens.gitExplorer && viewItem != gitlens:commit-file && viewItem != gitlens:stash-file && viewItem != gitlens:status-file", "group": "9_gitlens@1" diff --git a/src/constants.ts b/src/constants.ts index a900f4f..3357750 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -4,6 +4,7 @@ import { commands, TextEditor } from 'vscode'; export const ExtensionId = 'gitlens'; export const ExtensionKey = ExtensionId; export const ExtensionOutputChannelName = 'GitLens'; +export const ExtensionTerminalName = 'GitLens'; export const QualifiedExtensionId = `eamodio.${ExtensionId}`; export const ApplicationInsightsKey = 'a9c302f8-6483-4d01-b92c-c159c799c679'; diff --git a/src/views/branchHistoryNode.ts b/src/views/branchHistoryNode.ts index 91f3244..1bcc9a9 100644 --- a/src/views/branchHistoryNode.ts +++ b/src/views/branchHistoryNode.ts @@ -36,9 +36,20 @@ export class BranchHistoryNode extends ExplorerNode { name += ` ${GlyphChars.Space}${GlyphChars.ArrowLeftRight}${GlyphChars.Space} ${this.branch.tracking}`; } const item = new TreeItem(`${this.branch!.current ? `${GlyphChars.Check} ${GlyphChars.Space}` : ''}${name}`, TreeItemCollapsibleState.Collapsed); - item.contextValue = this.branch.tracking - ? ResourceType.RemoteBranchHistory - : ResourceType.BranchHistory; + + if (this.branch.remote) { + item.contextValue = ResourceType.RemoteBranchHistory; + } + else if (this.branch.current) { + item.contextValue = !!this.branch.tracking + ? ResourceType.CurrentBranchHistoryWithTracking + : ResourceType.CurrentBranchHistory; + } + else { + item.contextValue = !!this.branch.tracking + ? ResourceType.BranchHistoryWithTracking + : ResourceType.BranchHistory; + } item.iconPath = { dark: this.explorer.context.asAbsolutePath('images/dark/icon-branch.svg'), diff --git a/src/views/branchesNode.ts b/src/views/branchesNode.ts index fbdff24..f2ab9d2 100644 --- a/src/views/branchesNode.ts +++ b/src/views/branchesNode.ts @@ -29,7 +29,7 @@ export class BranchesNode extends ExplorerNode { const remotes = await this.repo.getRemotes(); item.contextValue = (remotes !== undefined && remotes.length > 0) - ? ResourceType.RemoteBranches + ? ResourceType.BranchesWithRemotes : ResourceType.Branches; item.iconPath = { diff --git a/src/views/commitNode.ts b/src/views/commitNode.ts index a87bde9..069e734 100644 --- a/src/views/commitNode.ts +++ b/src/views/commitNode.ts @@ -55,7 +55,10 @@ export class CommitNode extends ExplorerNode { dataFormat: this.explorer.git.config.defaultDateFormat } as ICommitFormatOptions), TreeItemCollapsibleState.Collapsed); - item.contextValue = ResourceType.Commit; + item.contextValue = (this.branch === undefined || this.branch.current) + ? ResourceType.CommitOnCurrentBranch + : ResourceType.Commit; + item.iconPath = { dark: this.explorer.context.asAbsolutePath('images/dark/icon-commit.svg'), light: this.explorer.context.asAbsolutePath('images/light/icon-commit.svg') diff --git a/src/views/explorerCommands.ts b/src/views/explorerCommands.ts index e8c148b..28873eb 100644 --- a/src/views/explorerCommands.ts +++ b/src/views/explorerCommands.ts @@ -1,15 +1,16 @@ import { Arrays } from '../system'; -import { commands, Disposable, TextDocumentShowOptions, Uri, workspace } from 'vscode'; -import { ExtensionKey } from '../constants'; -import { GitExplorer, GitExplorerView } from '../views/gitExplorer'; +import { commands, Disposable, InputBoxOptions, Terminal, TextDocumentShowOptions, Uri, window, workspace } from 'vscode'; +import { ExtensionKey, ExtensionTerminalName } from '../constants'; +import { BranchHistoryNode, ExplorerNode, GitExplorer, GitExplorerView } from '../views/gitExplorer'; import { configuration, GitExplorerFilesLayout } from '../configuration'; -import { CommitFileNode, CommitNode, StashNode } from './explorerNodes'; +import { CommitFileNode, CommitNode, StashNode, StatusUpstreamNode } from './explorerNodes'; import { Commands, DiffWithCommandArgs, DiffWithCommandArgsRevision, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, openEditor, OpenFileInRemoteCommandArgs, OpenFileRevisionCommandArgs } from '../commands'; import { GitService, GitUri } from '../gitService'; export class ExplorerCommands extends Disposable { private _disposable: Disposable | undefined; + private _terminal: Terminal | undefined; constructor( private explorer: GitExplorer @@ -35,6 +36,11 @@ export class ExplorerCommands extends Disposable { commands.registerCommand('gitlens.gitExplorer.openChangedFileChangesWithWorking', this.openChangedFileChangesWithWorking, this); commands.registerCommand('gitlens.gitExplorer.openChangedFileRevisions', this.openChangedFileRevisions, this); commands.registerCommand('gitlens.gitExplorer.applyChanges', this.applyChanges, this); + commands.registerCommand('gitlens.gitExplorer.terminalCreateBranch', this.terminalCreateBranch, this); + commands.registerCommand('gitlens.gitExplorer.terminalDeleteBranch', this.terminalDeleteBranch, this); + commands.registerCommand('gitlens.gitExplorer.terminalRebaseBranchToRemote', this.terminalRebaseBranchToRemote, this); + commands.registerCommand('gitlens.gitExplorer.terminalRebaseCommit', this.terminalRebaseCommit, this); + commands.registerCommand('gitlens.gitExplorer.terminalResetCommit', this.terminalResetCommit, this); } dispose() { @@ -129,4 +135,86 @@ export class ExplorerCommands extends Disposable { private async setFilesLayout(layout: GitExplorerFilesLayout) { return workspace.getConfiguration(ExtensionKey).update(configuration.name('gitExplorer')('files')('layout').value, layout, true); } + + async terminalCreateBranch(node: ExplorerNode) { + if (!(node instanceof BranchHistoryNode)) return; + + const name = await window.showInputBox({ + prompt: `Please provide a branch name (Press 'Enter' to confirm or 'Escape' to cancel)`, + placeHolder: `Branch name`, + value: node.branch.remote ? node.branch.getName() : undefined + } as InputBoxOptions); + if (name === undefined || name === '') return; + + const command = `branch ${node.branch.remote ? '-t ' : ''}${name} ${node.branch.name}`; + this.sendTerminalCommand(command); + } + + terminalDeleteBranch(node: ExplorerNode) { + if (!(node instanceof BranchHistoryNode)) return; + + const command = node.branch.remote + ? `push ${node.branch.remote} :${node.branch.name}` + : `branch -d ${node.branch.name}`; + this.sendTerminalCommand(command); + } + + terminalRebaseBranchToRemote(node: ExplorerNode) { + let command: string; + if (node instanceof BranchHistoryNode) { + if (!node.branch.current || !node.branch.tracking) return; + + command = `rebase -i ${node.branch.tracking}`; + } + else if (node instanceof StatusUpstreamNode) { + command = `rebase -i ${node.status.upstream}`; + } + else { + return; + } + + this.sendTerminalCommand(command); + } + + terminalRebaseCommit(node: ExplorerNode) { + if (!(node instanceof CommitNode)) return; + + const command = `rebase -i ${node.commit.sha}^`; + this.sendTerminalCommand(command); + } + + terminalResetCommit(node: ExplorerNode) { + if (!(node instanceof CommitNode)) return; + + const command = `reset --soft ${node.commit.sha}^`; + this.sendTerminalCommand(command); + } + + private ensureTerminal(): Terminal { + if (this._terminal === undefined) { + this._terminal = window.createTerminal(ExtensionTerminalName); + this._disposable = window.onDidCloseTerminal((e: Terminal) => { + if (e.name === ExtensionTerminalName) { + this._terminal = undefined; + this._disposable!.dispose(); + this._disposable = undefined; + } + }, this); + + this.explorer.context.subscriptions.push(this._disposable); + } + + return this._terminal; + } + + private sendTerminalCommand(command: string) { + // let git = GitService.getGitPath(); + // if (git.includes(' ')) { + // git = `"${git}"`; + // } + + const terminal = this.ensureTerminal(); + terminal.show(false); + terminal.sendText(`git ${command}`, false); + } } \ No newline at end of file diff --git a/src/views/explorerNode.ts b/src/views/explorerNode.ts index 8b94dc7..856fb57 100644 --- a/src/views/explorerNode.ts +++ b/src/views/explorerNode.ts @@ -6,10 +6,14 @@ import { RefreshNodeCommandArgs } from './gitExplorer'; export enum ResourceType { Branches = 'gitlens:branches', - RemoteBranches = 'gitlens:branches:remote', + BranchesWithRemotes = 'gitlens:branches:remotes', BranchHistory = 'gitlens:branch-history', - RemoteBranchHistory = 'gitlens:branch-history:remote', + BranchHistoryWithTracking = 'gitlens:branch-history:tracking', + CurrentBranchHistory = 'gitlens:current-branch-history', + CurrentBranchHistoryWithTracking = 'gitlens:current-branch-history:tracking', + RemoteBranchHistory = 'gitlens:remote-branch-history', Commit = 'gitlens:commit', + CommitOnCurrentBranch = 'gitlens:commit:current', CommitFile = 'gitlens:commit-file', FileHistory = 'gitlens:file-history', Folder = 'gitlens:folder',