37 Commits

Author SHA1 Message Date
Eric Amodio
821fa1cc3c Fixes #130 - Stops repeated welcome for some users
No idea why the version check fails, but hopefully this will help
2017-09-04 03:26:18 -04:00
Eric Amodio
3835193118 Updates images 2017-09-04 03:05:26 -04:00
Eric Amodio
d161084ccd Fixes issue with stashing untracked files 2017-09-04 03:04:05 -04:00
Eric Amodio
b9c4468cf7 Updates changelog 2017-09-04 01:46:29 -04:00
Eric Amodio
4bacb6fbff Hides remote menus if there are no remotes 2017-09-04 01:46:28 -04:00
Eric Amodio
c98755cc87 Adds refresh of custom view on settings change 2017-09-04 01:46:28 -04:00
Eric Amodio
6d759daaad Avoids remote disambiguation quick pick 2017-09-04 01:46:28 -04:00
Eric Amodio
5a42ce4ed4 Adds remote tracking branch to custom view
Adds setting to show remote tracking branch in custom view
2017-09-04 01:46:28 -04:00
Eric Amodio
a5af318269 Adds message in custom view if no remotes 2017-09-04 01:46:28 -04:00
Eric Amodio
5a2bd02402 Reworks branch parsing to include tracking info
Reworks current branch retrieval for better performance
2017-09-04 01:46:21 -04:00
Eric Amodio
2bba14260f Adds Open Branches in Remote command to the Remote custom view items
Adds Open Repository in Remote command to the Remote custom view items
2017-09-03 16:33:52 -04:00
Eric Amodio
22378d5f25 Updates changelog and readme 2017-09-03 16:03:33 -04:00
Eric Amodio
d1d1db18e2 Changes the icon of the File History custom view items
Provides more information rather than just a commit icon
2017-09-03 15:49:50 -04:00
Eric Amodio
3dab90709b Adds Open File command to History custom view item
Adds Open File in Remote command to History custom view item
2017-09-03 15:44:18 -04:00
Eric Amodio
f58d085352 Adds Open Branches in Remote command
Adds Open Branches in Remote command to the Branches custom view item
Adds Open Repository in Remote command to the Repository Status custom view item
2017-09-03 15:37:52 -04:00
Eric Amodio
a77bb36ee3 Preps v5.0.0-alpha.2 2017-09-03 13:47:13 -04:00
Eric Amodio
5cc9365fa1 Adds more info to the status nodes 2017-09-03 13:40:46 -04:00
Eric Amodio
a587108cab Adds Stash Changes command to the Stashes custom view item 2017-09-03 13:29:33 -04:00
Eric Amodio
1b4350e476 Removes Stash Unstaged Changes option
Changes Stash Changes icon to a +
2017-09-03 13:23:17 -04:00
Eric Amodio
586785cfb8 Changes stashes icon to match custom view 2017-09-03 13:22:40 -04:00
Eric Amodio
04df931902 Adds Refresh command to most custom view items
Updates custom view when repo changes (brute force for now)
2017-09-03 12:59:16 -04:00
Eric Amodio
d31eb25451 Adds Apply Changes command to custom view files
Adds Stash Changes command to SCM view items
2017-09-03 12:58:05 -04:00
Eric Amodio
35b16a78ba Adds status indicator to repo icon 2017-09-03 00:25:01 -04:00
Eric Amodio
825b9661fb Preps v5.0.0-alpha
Updates dependencies
2017-09-02 01:53:04 -04:00
Eric Amodio
9782a81e46 Adds new GitLens custom view 2017-09-02 01:43:08 -04:00
Eric Amodio
ed58dc3b49 Preps v4.5.0-beta 2017-08-30 12:25:51 -04:00
Eric Amodio
480dcb95fb Adds a file history explorer view 2017-08-30 12:25:07 -04:00
Eric Amodio
ef41176ea7 Defaults stashes format to ${filePath}
Adds message when there are no stashes
Cleans up the stash explorer
2017-08-30 12:25:07 -04:00
Eric Amodio
bcd83566a1 Reworks ExplorerNode base class
Adds TextExplorerNode for messages
2017-08-30 12:25:07 -04:00
Eric Amodio
ca089777db Adds ${filePath} support to status file formatting 2017-08-30 12:25:07 -04:00
Eric Amodio
a255eea949 Splits code lens out of GitService 2017-08-30 12:25:07 -04:00
Eric Amodio
1ffb42a090 Updates dependencies 2017-08-30 12:25:07 -04:00
Eric Amodio
bc2f1b192a Preps v4.4.3 2017-08-30 12:19:57 -04:00
Eric Amodio
7d99624068 Adds more logging to track down #130 2017-08-30 12:19:33 -04:00
Eric Amodio
c258d04381 Fixes #135 - Full-width chars break gutter annotations (really) 2017-08-30 11:46:41 -04:00
Eric Amodio
4c4926c8b5 Preps v4.4.2 2017-08-29 22:48:59 -04:00
Eric Amodio
6255b26fd2 Fixes #135 - Full-width chars break gutter annotations 2017-08-29 22:47:12 -04:00
102 changed files with 2756 additions and 1909 deletions

View File

@@ -4,17 +4,96 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
## [4.5.0-beta] - 2017-08-27 ## [5.0.0-alpha.2] - 2017-09-03
### Added ### Added
- Adds an all-new `Git File History` explorer to the Explorer activity -- enabled via `"gitlens.insiders": true` - Adds an all-new `GitLens` custom view to the Explorer activity
- Shows the commit history of the active file -- automatically tracks the active editor
- Provides toolbar buttons to `Refresh` - `Repository View` - provides a full repository explorer
- Provides a context menu with `Open Changes`, `Compare File with Working Tree`, `Open File`, `Open File Revision`, `Open File in Remote`, `Open File Revision in Remote`, and `Show Commit Details` commands
- Adds a `No stashed changes` message to the `Git Stashes` explorer when there are no stashes ![GitLens Repository view](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-git-custom-view-repository.png)
- Adds `${filePath}` token to file formatting
- `Repository Status` node — provides the status of the repository
- Provides the name of the current branch, its upstream tracking branch (if available), and its upstream status (if available)
- Provides indicator dots on the repository icon which denote the following:
- `None` - up-to-date with the upstream
- `Green` - ahead of the upstream
- `Red` - behind the upstream
- `Yellow` - both ahead of and behind the upstream
- Provides additional nodes, if the current branch is not synchronized with the upstream, to quickly see and explore the specific commits ahead and/or behind the upstream
- Provides a context menu with `Open Repository in Remote`, and `Refresh` commands
- `Branches` node — provides a list of the local branches
- Indicates which branch is the current branch and optionally 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 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`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`, `Open Files`, `Open Revisions`, `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 with `Open Branches in Remote`, and `Refresh` commands
- `Remotes` node — provides a list of remotes
- 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 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`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`, `Open Files`, `Open Revisions`, `Show Commit Details`, 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 with `Stash Changes`, and `Refresh` commands
- Provides a context menu on each stash with `Apply Stashed Changes` (confirmation required), `Delete Stashed Changes` (confirmation required), `Copy Commit Message to Clipboard`, `Open Files`, `Open Revisions`, and `Refresh` commands
- Provides a context menu on each stashed file with `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, `Apply Changes`, and `Show File History` commands
- `History View` - provides the revision history of the active file
![GitLens History view](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-git-custom-view-history.png)
- Automatically updates to track the active editor
- Provides a context menu with `Open File`, `Open File in Remote`, and `Refresh` commands
- Provides a context menu on each revision (commit) 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
- Quickly switch between views using the `Switch to Repository View` or `Switch to History View` commands
- Provides toolbar commands to `Search Commits`, `Switch to Repository View` or `Switch to History View`, and `Refresh`
- Adds `Open Branches in Remote` command (`gitlens.openBranchesInRemote`) - opens the branches in the supported remote service
- Adds `Stash Changes` command (`gitlens.stashSave`) to the source control group context menu -- can now stash a group of files
- Adds `Stash Changes` command (`gitlens.stashSave`) to the source control resource context menu -- can now stash individual files (works with multi-select too!)
- Adds `gitlens.gitExplorer.view` setting to specify the starting view (mode) of the `GitLens` custom view
- Adds `gitlens.gitExplorer.showTrackingBranch` setting to specify whether or not to show the tracking branch when displaying local branches in the `GitLens` custom view
- Adds `gitlens.gitExplorer.commitFormat` setting to specify the format of committed changes in the `GitLens` custom view
- Adds `gitlens.gitExplorer.commitFileFormat` setting to specify the format of a committed file in the `GitLens` custom view
- Adds `gitlens.gitExplorer.stashFormat` setting to specify the format of stashed changes in the `GitLens` custom view
- Adds `gitlens.gitExplorer.stashFileFormat` setting to specify the format of a stashed file in the `GitLens` custom view
- Adds `${filePath}` token to file formatting settings
### Changed ### Changed
- Changes `gitlens.stashExplorer.stashFileFormat` setting to defaults to `${filePath}` for better separator handling - Changes `Show Stashed Changes` option icon in repository status quick pick menu to match the `GitLens` custom view
- Changes `Stash Changes` option icon in stashed changes quick pick menu to a plus (+)
- Renames `Compare File with Previous` command (`gitlens.diffWithPrevious`) to `Compare File with Previous Revision`
- Renames `Compare File with Next Commit` command (`gitlens.diffWithNext`) to `Compare File with Next Revision`
- Renames `Compare File with Working Tree` command (`gitlens.diffWithWorking`) to `Compare File with Working Revision`
- Renames `Compare Line Commit with Previous` command (`gitlens.diffLineWithPrevious`) to `Compare Line Revision with Previous`
- Renames `Compare Line Commit with Working Tree` command (`gitlens.diffLineWithWorking`) to `Compare Line Revision with Working`
### Removed
- Removes `Git Stashes` custom view view - as it's functionality has been folded into the new `GitLens` custom view
- Removes `gitlens.stashExplorer.stashFormat` setting
- Removes `gitlens.stashExplorer.stashFileFormat` setting
- Removes `Stash Unstaged Changes` option from stashed changes quick pick menu -- didn't work as intended
- Removes the seeding of the commit search command from the clipboard
### Fixed
- Fixes an issue where remote branches couldn't be opened properly in their remote service
## [4.4.3] - 2017-08-30
## Fixed
- Fixes [#135](https://github.com/eamodio/vscode-gitlens/issues/135) - Full-width characters break gutter annotations (really this time)
## [4.4.2] - 2017-08-29
## Fixed
- Fixes [#135](https://github.com/eamodio/vscode-gitlens/issues/135) - Full-width characters break gutter annotations
## [4.4.1] - 2017-08-23 ## [4.4.1] - 2017-08-23
## Fixed ## Fixed
@@ -55,20 +134,20 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
## [4.3.1] - 2017-07-03 ## [4.3.1] - 2017-07-03
## Added ## Added
- Adds `gitlens.stashExplorer.enabled` setting to specify whether or not to show the `Git Stashes` explorer - Adds `gitlens.stashExplorer.enabled` setting to specify whether or not to show the `Git Stashes` custom view
- Adds `Toggle Git Stashes Explorer` command (`gitlens.stashExplorer.toggle`) - toggles the `Git Stashes` explorer on and off - Adds `Toggle Git Stashes Explorer` command (`gitlens.stashExplorer.toggle`) - toggles the `Git Stashes` custom view on and off
## Changed ## Changed
- Hides the `Git Stashes` explorer by default - Hides the `Git Stashes` custom view by default
## Fixed ## Fixed
- Fixes [#108](https://github.com/eamodio/vscode-gitlens/issues/108) - Option to remove stash explorer from the main explorer? - Fixes [#108](https://github.com/eamodio/vscode-gitlens/issues/108) - Option to remove stash explorer from the main explorer?
## [4.3.0] - 2017-07-03 ## [4.3.0] - 2017-07-03
## Added ## Added
- Adds `Git Stashes` view to the Explorer activity - Adds `Git Stashes` custom view to the Explorer activity
![Git Stashes view](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-git-stashes.png) ![Git Stashes view](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-git-stashes.png)
- Shows all of the stashed changes in the repository - Shows all of the stashed changes in the repository
- Provides toolbar buttons to `Stash Changes` and `Refresh` - Provides toolbar buttons to `Stash Changes` and `Refresh`

111
README.md
View File

@@ -10,7 +10,7 @@ GitLens **supercharges** the built-in Visual Studio Code Git capabilities. It he
GitLens provides an unobtrusive blame annotation at the end of the current line, a status bar item showing the commit information (author and date, by default) of the current line, code lens showing the most recent commit and # of authors of the file and/or code block, and many commands for exploring commits and histories, comparing and navigating revisions, stash access, repository status, and more. GitLens is also [highly customizable](#extension-settings) to meet your specific needs — find code lens intrusive or the current line blame annotation distracting — no problem, it is easy to [turn them off or change how they behave](#extension-settings). GitLens provides an unobtrusive blame annotation at the end of the current line, a status bar item showing the commit information (author and date, by default) of the current line, code lens showing the most recent commit and # of authors of the file and/or code block, and many commands for exploring commits and histories, comparing and navigating revisions, stash access, repository status, and more. GitLens is also [highly customizable](#extension-settings) to meet your specific needs — find code lens intrusive or the current line blame annotation distracting — no problem, it is easy to [turn them off or change how they behave](#extension-settings).
### Preview — featuring blame annotations, code lens, status bar details, quick pick menus for navigation and exploration, compare with previous, and more ### Preview — featuring blame annotations, code lens, status bar details, quick pick menus for navigation and exploration, compare with previous, and more
![GitLens preview](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/gitlens-preview.gif) ![GitLens preview](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/gitlens-preview.gif)
## Features ## Features
@@ -18,16 +18,16 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- Adds an unobtrusive, highly [customizable](#line-blame-annotation-settings) and [themeable](#theme-settings), **Git blame annotation** to the end of the current line ([optional](#line-blame-annotation-settings), on by default) - Adds an unobtrusive, highly [customizable](#line-blame-annotation-settings) and [themeable](#theme-settings), **Git blame annotation** to the end of the current line ([optional](#line-blame-annotation-settings), on by default)
![Line Blame Annotation](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-line-blame-annotation.png) ![Line Blame Annotation](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-line-blame-annotation.png)
- Contains the author, date, and message of the line's most recent commit, by [default](#line-blame-annotation-settings) - Contains the author, date, and message of the line's most recent commit, by [default](#line-blame-annotation-settings)
- Adds a `details` hover annotation to the current line annotation, which provides more commit details ([optional](#line-blame-annotation-settings), on by default) - Adds a `details` hover annotation to the current line annotation, which provides more commit details ([optional](#line-blame-annotation-settings), on by default)
- Adds a `changes` (diff) hover annotation to the current line annotation, which provides **instant** access to the line's previous version ([optional](#line-blame-annotation-settings), on by default) - Adds a `changes` (diff) hover annotation to the current line annotation, which provides **instant** access to the line's previous version ([optional](#line-blame-annotation-settings), on by default)
![Line Blame Annotations](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-line-blame-annotations.png) ![Line Blame Annotations](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-line-blame-annotations.png)
- Adds on-demand, beautiful, highly [customizable](#file-blame-annotation-settings) and [themeable](#theme-settings), **Git blame annotations** of the whole file - Adds on-demand, beautiful, highly [customizable](#file-blame-annotation-settings) and [themeable](#theme-settings), **Git blame annotations** of the whole file
![File Blame Annotation](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-file-blame-annotations.png) ![File Blame Annotation](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-file-blame-annotations.png)
- Choose between `gutter` (default) and `hover` [annotation styles](#file-blame-annotation-settings) - Choose between `gutter` (default) and `hover` [annotation styles](#file-blame-annotation-settings)
- Contains the commit message and date, by [default](#file-blame-annotation-settings) - Contains the commit message and date, by [default](#file-blame-annotation-settings)
- Adds a `details` hover annotation to the line's annotation, which provides more commit details ([optional](#file-blame-annotation-settings), on by default) - Adds a `details` hover annotation to the line's annotation, which provides more commit details ([optional](#file-blame-annotation-settings), on by default)
@@ -37,7 +37,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- Adds [customizable](#status-bar-settings) **blame information** about the current line to the **status bar** ([optional](#status-bar-settings), on by default) - Adds [customizable](#status-bar-settings) **blame information** about the current line to the **status bar** ([optional](#status-bar-settings), on by default)
![Status Bar Blame](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-status-bar.png) ![Status Bar Blame](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-status-bar.png)
- Contains the commit author and date, by [default](#status-bar-settings) - Contains the commit author and date, by [default](#status-bar-settings)
- Clicking the status bar item will, by [default](#status-bar-settings), show a **commit details quick pick menu** with commands for comparing, navigating and exploring commits, and more - Clicking the status bar item will, by [default](#status-bar-settings), show a **commit details quick pick menu** with commands for comparing, navigating and exploring commits, and more
- Provides [customizable](#status-bar-settings) click behavior — choose between one of the following - Provides [customizable](#status-bar-settings) click behavior — choose between one of the following
@@ -70,7 +70,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- Adds **code lens** to the top of the file and on code blocks ([optional](#code-lens-settings), on by default) - Adds **code lens** to the top of the file and on code blocks ([optional](#code-lens-settings), on by default)
![Git Code Lens](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-code-lens.png) ![Git Code Lens](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-code-lens.png)
- **Recent Change** — author and date of the most recent commit for the file or code block - **Recent Change** — author and date of the most recent commit for the file or code block
- Clicking the code lens will, by [default](#code-lens-settings), show a **commit file details quick pick menu** with commands for comparing, navigating and exploring commits, and more - Clicking the code lens will, by [default](#code-lens-settings), show a **commit file details quick pick menu** with commands for comparing, navigating and exploring commits, and more
- **Authors** — number of authors of the file or code block and the most prominent author (if there is more than one) - **Authors** — number of authors of the file or code block and the most prominent author (if there is more than one)
@@ -97,39 +97,75 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- Adds a `Compare File with Branch...` command (`gitlens.diffWithBranch`) to compare the active file with the same file on the selected branch - Adds a `Compare File with Branch...` command (`gitlens.diffWithBranch`) to compare the active file with the same file on the selected branch
- Adds a `Compare File with Next Commit` command (`gitlens.diffWithNext`) with a shortcut of `alt+.` to compare the active file/diff with the next commit revision - Adds a `Compare File with Next Revision` command (`gitlens.diffWithNext`) with a shortcut of `alt+.` to compare the active file/diff with the next commit revision
- Adds a `Compare File with Previous` command (`gitlens.diffWithPrevious`) with a shortcut of `alt+,` to compare the active file/diff with the previous commit revision - Adds a `Compare File with Previous Revision` command (`gitlens.diffWithPrevious`) with a shortcut of `alt+,` to compare the active file/diff with the previous commit revision
- Adds a `Compare Line Commit with Previous` command (`gitlens.diffLineWithPrevious`) with a shortcut of `shift+alt+,` to compare the active file/diff with the previous line commit revision - Adds a `Compare Line Revision with Previous` command (`gitlens.diffLineWithPrevious`) with a shortcut of `shift+alt+,` to compare the active file/diff with the previous line commit revision
- Adds a `Compare File with Revision...` command (`gitlens.diffWithRevision`) to compare the active file with the selected revision of the same file - Adds a `Compare File with Revision...` command (`gitlens.diffWithRevision`) to compare the active file with the selected revision of the same file
- Adds a `Compare File with Working Tree` command (`gitlens.diffWithWorking`) with a shortcut of `shift+alt+w` to compare the most recent commit revision of the active file/diff with the working tree - Adds a `Compare File with Working Revision` command (`gitlens.diffWithWorking`) with a shortcut of `shift+alt+w` to compare the most recent commit revision of the active file/diff with the working tree
- Adds a `Compare Line Commit with Working Tree` command (`gitlens.diffLineWithWorking`) with a shortcut of `alt+w` to compare the commit revision of the active line with the working tree - Adds a `Compare Line Revision with Working` command (`gitlens.diffLineWithWorking`) with a shortcut of `alt+w` to compare the commit revision of the active line with the working tree
### Navigate and Explore ### Navigate and Explore
- Adds a [customizable](#git-file-history-explorer-settings) `Git File History` explorer to the Explorer activity -- currently [insiders](#insiders) only - Adds a [customizable](#gitlens-custom-view-settings) `GitLens` custom view to the Explorer activity
- Shows the commit history of the active file -- automatically tracks the active editor - `Repository View` - provides a full repository explorer
- Provides toolbar buttons to `Refresh`
- Provides a context menu with `Open Changes`, `Compare File with Working Tree`, `Open File`, `Open File Revision`, `Open File in Remote`, `Open File Revision in Remote`, and `Show Commit Details` commands
- Adds a [customizable](#git-stashes-explorer-settings) `Git Stashes` explorer to the Explorer activity ![GitLens Repository view](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-git-custom-view-repository.png)
![Git Stashes explorer](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-git-stashes.png) - `Repository Status` node — provides the status of the repository
- Provides the name of the current branch, its upstream tracking branch (if available), and its upstream status (if available)
- Provides indicator dots on the repository icon which denote the following:
- `None` - up-to-date with the upstream
- `Green` - ahead of the upstream
- `Red` - behind the upstream
- `Yellow` - both ahead of and behind the upstream
- Provides additional nodes, if the current branch is not synchronized with the upstream, to quickly see and explore the specific commits ahead and/or behind the upstream
- Provides a context menu with `Open Repository in Remote`, and `Refresh` commands
- Shows all of the stashed changes in the repository - `Branches` node — provides a list of the local branches
- Provides toolbar buttons to `Stash Changes` and `Refresh` - Indicates which branch is the current branch and [optionally](#gitlens-custom-view-settings) shows the remote tracking branch
- Provides a context menu with `Apply Stashed Changes` and `Delete Stashed Changes` commands — both require a confirmation - Expand each branch to easily see its revision (commit) history
- Expand each stash to quickly see the set of files changed, complete with status indicators for adds, changes, renames, and deletes - 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 with `Open Changes`, `Open File`, `Open Stashed File`, `Open File in Remote`, and `Compare File with Working Tree` 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`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`, `Open Files`, `Open Revisions`, `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 with `Open Branches in Remote`, and `Refresh` commands
- `Remotes` node — provides a list of remotes
- 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 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`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`, `Open Files`, `Open Revisions`, `Show Commit Details`, 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 with `Stash Changes`, and `Refresh` commands
- Provides a context menu on each stash with `Apply Stashed Changes` (confirmation required), `Delete Stashed Changes` (confirmation required), `Copy Commit Message to Clipboard`, `Open Files`, `Open Revisions`, and `Refresh` commands
- Provides a context menu on each stashed file with `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, `Apply Changes`, and `Show File History` commands
- `History View` - provides the revision history of the active file
![GitLens History view](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-git-custom-view-history.png)
- Automatically updates to track the active editor
- Provides a context menu with `Open File`, `Open File in Remote`, and `Refresh` commands
- Provides a context menu on each revision (commit) 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
- Quickly switch between views using the `Switch to Repository View` or `Switch to History View` commands
- Provides toolbar commands to `Search Commits`, `Switch to Repository View` or `Switch to History View`, and `Refresh`
- Adds a `Search Commits` command (`gitlens.showCommitSearch`) with a shortcut of `alt+/` to search for commits by message, author, file(s), or commit id - Adds a `Search Commits` command (`gitlens.showCommitSearch`) with a shortcut of `alt+/` to search for commits by message, author, file(s), or commit id
- Adds commands to open files, commits, branches, and the repository in the supported remote services, currently **BitBucket, GitHub, GitLab, and Visual Studio Team Services** — only available if a Git upstream service is configured in the repository - Adds commands to open files, commits, branches, and the repository in the supported remote services, currently **BitBucket, GitHub, GitLab, and Visual Studio Team Services** — only available if a Git upstream service is configured in the repository
- `Open Branches in Remote` command (`gitlens.openBranchesInRemote`) — opens the branches in the supported remote service
- `Open Branch in Remote` command (`gitlens.openBranchInRemote`) — opens the current branch commits in the supported remote service - `Open Branch in Remote` command (`gitlens.openBranchInRemote`) — opens the current branch commits in the supported remote service
- `Open Commit in Remote` command (`gitlens.openCommitInRemote`) — opens the commit revision of the active line in the supported remote service - `Open Commit in Remote` command (`gitlens.openCommitInRemote`) — opens the commit revision of the active line in the supported remote service
- `Open File in Remote` command (`gitlens.openFileInRemote`) — opens the active file/revision in the supported remote service - `Open File in Remote` command (`gitlens.openFileInRemote`) — opens the active file/revision in the supported remote service
@@ -137,7 +173,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- Adds a `Show Current Branch History` command (`gitlens.showQuickRepoHistory`) with a shortcut of `shift+alt+h` to show a paged **branch history quick pick menu** of the current branch for exploring its commit history - Adds a `Show Current Branch History` command (`gitlens.showQuickRepoHistory`) with a shortcut of `shift+alt+h` to show a paged **branch history quick pick menu** of the current branch for exploring its commit history
![Branch History Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-branch-history.png) ![Branch History Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-branch-history.png)
- Provides entries to `Show Commit Search` and `Open Branch in <remote-service>` when available - Provides entries to `Show Commit Search` and `Open Branch in <remote-service>` when available
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available - Navigate back to the previous quick pick menu via `alt+left arrow`, if available
@@ -148,7 +184,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- Adds a `Show File History` command (`gitlens.showQuickFileHistory`) to show a paged **file history quick pick menu** of the active file for exploring its commit history - Adds a `Show File History` command (`gitlens.showQuickFileHistory`) to show a paged **file history quick pick menu** of the active file for exploring its commit history
![File History Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-file-history.png) ![File History Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-file-history.png)
- Provides entries to `Show Branch History` and `Open File in <remote-service>` when available - Provides entries to `Show Branch History` and `Open File in <remote-service>` when available
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available - Navigate back to the previous quick pick menu via `alt+left arrow`, if available
@@ -156,7 +192,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- Adds a `Show Commit Details` command (`gitlens.showQuickCommitDetails`) to show a **commit details quick pick menu** of the most recent commit of the active file - Adds a `Show Commit Details` command (`gitlens.showQuickCommitDetails`) to show a **commit details quick pick menu** of the most recent commit of the active file
![Commit Details Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-commit-details.png) ![Commit Details Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-commit-details.png)
- Quickly see the set of files changed in the commit, complete with status indicators for adds, changes, renames, and deletes - Quickly see the set of files changed in the commit, complete with status indicators for adds, changes, renames, and deletes
- Provides entries to `Copy to Clipboard`, `Directory Compare`, `Open Changed Files`, `Open File in <remote-service>` when available, and more - Provides entries to `Copy to Clipboard`, `Directory Compare`, `Open Changed Files`, `Open File in <remote-service>` when available, and more
@@ -166,7 +202,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- Adds a `Show Commit File Details` command (`gitlens.showQuickCommitFileDetails`) with a shortcut of `alt+c` to show a **file commit details quick pick menu** of the most recent commit of the active file - Adds a `Show Commit File Details` command (`gitlens.showQuickCommitFileDetails`) with a shortcut of `alt+c` to show a **file commit details quick pick menu** of the most recent commit of the active file
![Commit File Details Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-commit-file-details.png) ![Commit File Details Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-commit-file-details.png)
- Provides entries to `Show Commit Details`, `Show File History`, `Compare File with...`, `Copy to Clipboard`, `Open File`, `Open File in <remote-service>` when available, and more - Provides entries to `Show Commit Details`, `Show File History`, `Compare File with...`, `Copy to Clipboard`, `Open File`, `Open File in <remote-service>` when available, and more
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available - Navigate back to the previous quick pick menu via `alt+left arrow`, if available
@@ -174,7 +210,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- Adds a `Show Repository Status` command (`gitlens.showQuickRepoStatus`) with a shortcut of `alt+s` to show a **repository status quick pick menu** for visualizing the current repository status - Adds a `Show Repository Status` command (`gitlens.showQuickRepoStatus`) with a shortcut of `alt+s` to show a **repository status quick pick menu** for visualizing the current repository status
![Repository Status Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-repo-status.png) ![Repository Status Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-repo-status.png)
- Quickly see upstream status (if an Git upstream is configured) — complete with ahead and behind information - Quickly see upstream status (if an Git upstream is configured) — complete with ahead and behind information
- If you are ahead of the upstream, an entry will be shown with the number of commits ahead. Choosing it will show a limited **branch history quick pick menu** containing just the commits ahead of the upstream - If you are ahead of the upstream, an entry will be shown with the number of commits ahead. Choosing it will show a limited **branch history quick pick menu** containing just the commits ahead of the upstream
@@ -186,14 +222,14 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- Adds a `Show Stashed Changes` command (`gitlens.showQuickStashList`) to show a **stashed changes quick pick menu** for exploring your repository stash history - Adds a `Show Stashed Changes` command (`gitlens.showQuickStashList`) to show a **stashed changes quick pick menu** for exploring your repository stash history
![Stashed Changes Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-stash-list.png) ![Stashed Changes Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-stash-list.png)
- Provides entries to `Stash Changes` - Provides entries to `Stash Changes`
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available - Navigate back to the previous quick pick menu via `alt+left arrow`, if available
- Choosing a stash entry shows a **stash details quick pick menu** which is very similar to the **commit details quick pick menu** above - Choosing a stash entry shows a **stash details quick pick menu** which is very similar to the **commit details quick pick menu** above
![Stash Details Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-stash-details.png) ![Stash Details Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-stash-details.png)
- Quickly see the set of files changed in the stash, complete with status indicators for adds, changes, renames, and deletes - Quickly see the set of files changed in the stash, complete with status indicators for adds, changes, renames, and deletes
- Provides entries to `Copy Message to Clipboard`, `Directory Compare`, and `Open Changed Files` - Provides entries to `Copy Message to Clipboard`, `Directory Compare`, and `Open Changed Files`
@@ -223,6 +259,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- Adds a `Apply Stashed Changes` command (`gitlens.stashApply`) to chose a stash entry to apply to the working tree from a quick pick menu - Adds a `Apply Stashed Changes` command (`gitlens.stashApply`) to chose a stash entry to apply to the working tree from a quick pick menu
- Adds a `Stash Changes` command (`gitlens.stashSave`) to save any working tree changes to the stash — can optionally provide a stash message - Adds a `Stash Changes` command (`gitlens.stashSave`) to save any working tree changes to the stash — can optionally provide a stash message
- Also adds the command to the Source Control items context menu to stash an individual or group of files, works with multi-select too!
## Insiders ## Insiders
@@ -295,18 +332,16 @@ GitLens is highly customizable and provides many configuration settings to allow
|`gitlens.codeLens.customLocationSymbols`|Specifies the set of document symbols where Git code lens will be shown in the document |`gitlens.codeLens.customLocationSymbols`|Specifies the set of document symbols where Git code lens will be shown in the document
|`gitlens.codeLens.perLanguageLocations`|Specifies where Git code lens will be shown in the document for the specified languages |`gitlens.codeLens.perLanguageLocations`|Specifies where Git code lens will be shown in the document for the specified languages
### Git File History Explorer Settings ### GitLens Custom View Settings
|Name | Description |Name | Description
|-----|------------ |-----|------------
|`gitlens.fileHistoryExplorer.commitFormat`|Specifies the format of committed changes in the `Git File History` explorer <br />Available tokens<br /> ${id} - commit id<br /> ${author} - commit author<br /> ${message} - commit message<br /> ${ago} - relative commit date (e.g. 1 day ago)<br /> ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)<br /> ${authorAgo} - commit author, relative commit date<br />See https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting |`gitlens.gitExplorer.view`|Specifies the starting view (mode) of the `GitLens` custom view<br />`history` - shows the commit history of the active file<br />`repository` - shows a repository explorer"
|`gitlens.gitExplorer.showTrackingBranch`|Specifies whether or not to show the tracking branch when displaying local branches in the `GitLens` custom view"
### Git Stashes Explorer Settings |`gitlens.gitExplorer.commitFormat`|Specifies the format of committed changes in the `GitLens` custom view<br />Available tokens<br /> ${id} - commit id<br /> ${author} - commit author<br /> ${message} - commit message<br /> ${ago} - relative commit date (e.g. 1 day ago)<br /> ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)<br /> ${authorAgo} - commit author, relative commit date<br />See https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting
|`gitlens.gitExplorer.commitFileFormat`|Specifies the format of a committed file in the `GitLens` custom view<br />Available tokens<br /> ${file} - file name<br /> ${filePath} - file name and path<br /> ${path} - file path
|Name | Description |`gitlens.gitExplorer.stashFormat`|Specifies the format of stashed changes in the `GitLens` custom view<br />Available tokens<br /> ${id} - commit id<br /> ${author} - commit author<br /> ${message} - commit message<br /> ${ago} - relative commit date (e.g. 1 day ago)<br /> ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)<br /> ${authorAgo} - commit author, relative commit date<br />See https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting
|-----|------------ |`gitlens.gitExplorer.stashFileFormat`|Specifies the format of a stashed file in the `GitLens` custom view<br />Available tokens<br /> ${file} - file name<br /> ${filePath} - file name and path<br /> ${path} - file path
|`gitlens.stashExplorer.stashFormat`|Specifies the format of stashed changes in the `Git Stashes` explorer <br />Available tokens<br /> ${id} - commit id<br /> ${author} - commit author<br /> ${message} - commit message<br /> ${ago} - relative commit date (e.g. 1 day ago)<br /> ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)<br /> ${authorAgo} - commit author, relative commit date<br />See https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting
|`gitlens.stashExplorer.stashFileFormat`|Specifies the format of a stashed file in the `Git Stashes` explorer <br />Available tokens<br /> ${file} - file name<br /> ${filePath} - file name and path<br /> ${path} - file path
### Status Bar Settings ### Status Bar Settings

View File

@@ -1 +1,5 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><title>Layer 1</title><rect height="11" width="3" y="3" x="7" fill="#C5C5C5"/><rect height="3" width="11" y="7" x="3" fill="#C5C5C5"/></svg> <?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<rect fill="#C5C5C5" x="6.5" y="5.5" width="3" height="11"/>
<rect fill="#C5C5C5" x="2.5" y="9.5" width="11" height="3"/>
</svg>

Before

Width:  |  Height:  |  Size: 203 B

After

Width:  |  Height:  |  Size: 253 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#C5C5C5" d="m13,8c0,-1.11 -0.89,-2 -2,-2c-1.11,0 -2,0.89 -2,2c0,0.73 0.41,1.38 1,1.72l0,0.3c-0.02,0.52 -0.23,0.98 -0.63,1.38c-0.4,0.4 -0.86,0.61 -1.38,0.63c-0.83,0.02 -1.48,0.16 -2,0.45l0,-4.76c0.59,-0.34 1,-0.98 1,-1.72c0,-1.11 -0.89,-2 -2,-2c-1.11,0 -1.99,0.89 -1.99,2c0,0.73 0.41,1.38 1,1.72l0,6.56c-0.59,0.35 -1,0.99 -1,1.72c0,1.11 0.89,2 2,2c1.11,0 2,-0.89 2,-2c0,-0.53 -0.2,-1 -0.53,-1.36c0.09,-0.06 0.48,-0.41 0.59,-0.47c0.25,-0.11 0.56,-0.17 0.94,-0.17c1.05,-0.05 1.95,-0.45 2.75,-1.25c0.8,-0.8 1.2,-1.98 1.25,-3.02l-0.02,0c0.61,-0.36 1.02,-1 1.02,-1.73l0,0zm-8,-3.2c0.66,0 1.2,0.55 1.2,1.2c0,0.65 -0.55,1.2 -1.2,1.2c-0.65,0 -1.2,-0.55 -1.2,-1.2c0,-0.65 0.55,-1.2 1.2,-1.2l0,0zm0,12.41c-0.66,0 -1.2,-0.55 -1.2,-1.2c0,-0.65 0.55,-1.2 1.2,-1.2c0.65,0 1.2,0.55 1.2,1.2c0,0.65 -0.55,1.2 -1.2,1.2l0,0zm6,-8c-0.66,0 -1.2,-0.55 -1.2,-1.2c0,-0.65 0.55,-1.2 1.2,-1.2c0.65,0 1.2,0.55 1.2,1.2c0,0.65 -0.55,1.2 -1.2,1.2l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="256" height="256" viewBox="0 0 14 16" xml:space="preserve"> <svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#C5C5C5" d="M10.86 7c-.45-1.72-2-3-3.86-3-1.86 0-3.41 1.28-3.86 3H0v2h3.14c.45 1.72 2 3 3.86 3 1.86 0 3.41-1.28 3.86-3H14V7h-3.14zM7 10.2c-1.22 0-2.2-.98-2.2-2.2 0-1.22.98-2.2 2.2-2.2 1.22 0 2.2.98 2.2 2.2 0 1.22-.98 2.2-2.2 2.2z"></path> <path fill="#C5C5C5" d="m11.86,10c-0.45,-1.72 -2,-3 -3.86,-3c-1.86,0 -3.41,1.28 -3.86,3l-3.14,0l0,2l3.14,0c0.45,1.72 2,3 3.86,3c1.86,0 3.41,-1.28 3.86,-3l3.14,0l0,-2l-3.14,0zm-3.86,3.2c-1.22,0 -2.2,-0.98 -2.2,-2.2c0,-1.22 0.98,-2.2 2.2,-2.2c1.22,0 2.2,0.98 2.2,2.2c0,1.22 -0.98,2.2 -2.2,2.2z"></path>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 480 B

After

Width:  |  Height:  |  Size: 428 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#C5C5C5" d="m9,15l2,0l-3,3l-3,-3l2,0l0,-5l2,0l0,5l0,0zm3,-8c0,-0.44 -0.91,-3 -4.5,-3c-2.42,0 -4.5,1.92 -4.5,4c-1.98,0 -3,1.52 -3,3c0,1.53 1,3 3,3l3,0l0,-1.3l-3,0c-1.62,0 -1.7,-1.42 -1.7,-1.7c0,-0.17 0.05,-1.7 1.7,-1.7l1.3,0l0,-1.3c0,-1.39 1.56,-2.7 3.2,-2.7c2.55,0 3.13,1.55 3.2,1.8l0,1.2l1.3,0c0.81,0 2.7,0.22 2.7,2.2c0,2.09 -2.25,2.2 -2.7,2.2l-2,0l0,1.3l2,0c2.08,0 4,-1.16 4,-3.5c0,-2.44 -1.92,-3.5 -4,-3.5l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 557 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#C5C5C5" d="m9,16l-2,0l0,-7l5,0l0,2l-3,0l0,5l0,0zm-1,-12c-2.19,0 -4.13,1.02 -5.41,2.59l-1.59,-1.59l0,4l4,0l-1.5,-1.5c1.05,-1.33 2.67,-2.2 4.5,-2.2c3.14,0 5.7,2.56 5.7,5.7c0,3.14 -2.56,5.7 -5.7,5.7c-3.14,0 -5.7,-2.56 -5.7,-5.7c0,-0.34 0.03,-0.67 0.09,-1l-1.31,0c-0.05,0.33 -0.08,0.66 -0.08,1c0,3.86 3.14,7 7,7c3.86,0 7,-3.14 7,-7c0,-3.86 -3.14,-7 -7,-7l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 500 B

View File

@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"> <svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M13.451 5.609l-.579-.939-1.068.812-.076.094c-.335.415-.927 1.341-1.124 2.876l-.021.165.033.163.071.345c0 1.654-1.346 3-3 3a2.98 2.98 0 0 1-2.107-.868 2.98 2.98 0 0 1-.873-2.111 3.004 3.004 0 0 1 2.351-2.929v2.926s2.528-2.087 2.984-2.461h.012L13.115 4.1 8.196 0H7.059v2.404A6.759 6.759 0 0 0 .938 9.125c0 1.809.707 3.508 1.986 4.782a6.707 6.707 0 0 0 4.784 1.988 6.758 6.758 0 0 0 6.75-6.75 6.741 6.741 0 0 0-1.007-3.536z" fill="#2D2D30"/> <path fill="#C5C5C5" d="m12.901149,9.091851l-0.094,0.071c-0.269,0.333 -0.746,1.096 -0.91,2.375c0.057,0.277 0.092,0.495 0.092,0.545c0,2.206 -1.794,4 -4,4a3.986,3.986 0 0 1 -2.817,-1.164a3.987,3.987 0 0 1 -1.163,-2.815c0,-2.206 1.794,-4 4,-4l0.351,0.025l0,1.85s1.626,-1.342 1.631,-1.339l1.869,-1.577l-3.5,-2.917l0,2.218l-0.371,-0.03a5.75,5.75 0 0 0 -4.055,9.826a5.75,5.75 0 0 0 9.826,-4.056a5.725,5.725 0 0 0 -0.859,-3.012z" />
<path d="M12.6 6.134l-.094.071c-.269.333-.746 1.096-.91 2.375.057.277.092.495.092.545 0 2.206-1.794 4-4 4a3.986 3.986 0 0 1-2.817-1.164 3.987 3.987 0 0 1-1.163-2.815c0-2.206 1.794-4 4-4l.351.025v1.85S9.685 5.679 9.69 5.682l1.869-1.577-3.5-2.917v2.218l-.371-.03a5.75 5.75 0 0 0-4.055 9.826 5.75 5.75 0 0 0 9.826-4.056 5.725 5.725 0 0 0-.859-3.012z" fill="#C5C5C5"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 987 B

After

Width:  |  Height:  |  Size: 553 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#C5C5C5" d="m12.519592,15.157073l-9.039184,0c-1.367793,0 -2.480539,-1.112828 -2.480539,-2.480622s1.112746,-2.480539 2.480539,-2.480539l0.281314,0c0.349178,-0.47173 0.888523,-0.771191 1.476341,-0.815025c0.321917,-1.471535 1.619609,-2.537962 3.165139,-2.537962c1.221543,0 2.341663,0.699516 2.889377,1.772488c0.083027,-0.008618 0.166386,-0.012926 0.250076,-0.012926c1.190221,0 2.194667,0.874105 2.378785,2.027867c0.670514,0.460047 1.07869,1.225023 1.07869,2.046096c0,1.367793 -1.112828,2.480622 -2.480539,2.480622z" />
</svg>

After

Width:  |  Height:  |  Size: 655 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#C5C5C5" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
<ellipse fill="#32cd32" stroke="#C5C5C5" stroke-width="0.5" rx="3" ry="3" cx="13" cy="4" />
</svg>

After

Width:  |  Height:  |  Size: 586 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#C5C5C5" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
<ellipse fill="#cd3131" stroke="#C5C5C5" stroke-width="0.5" rx="3" ry="3" cx="13" cy="4" />
</svg>

After

Width:  |  Height:  |  Size: 586 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#C5C5C5" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
<ellipse fill="#cdcd32" stroke="#C5C5C5" stroke-width="0.5" rx="3" ry="3" cx="13" cy="4" />
</svg>

After

Width:  |  Height:  |  Size: 586 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#C5C5C5" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 490 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#C5C5C5" d="m14.414133,16.199437l-3.415492,-3.433421c0.627518,-0.878526 0.995065,-1.945307 0.995065,-3.110698c0,-2.967265 -2.411463,-5.378728 -5.378728,-5.378728c-2.967265,0 -5.378728,2.411463 -5.378728,5.378728c0,2.967265 2.411463,5.378728 5.378728,5.378728c1.165391,0 2.223207,-0.367546 3.110698,-0.995065l3.433421,3.415492c0.170326,0.179291 0.403405,0.268936 0.627518,0.268936c0.224114,0 0.466156,-0.080681 0.627518,-0.268936c0.349617,-0.349617 0.349617,-0.914384 0,-1.264001l0,0.008965zm-7.799155,-2.330782c-2.321817,0 -4.213337,-1.891519 -4.213337,-4.213337c0,-2.321817 1.891519,-4.213337 4.213337,-4.213337c2.321817,0 4.213337,1.891519 4.213337,4.213337c0,2.321817 -1.891519,4.213337 -4.213337,4.213337l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 857 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16px" height="22px">
<path fill="#C5C5C5" d="m14.687501,11.955358l-1.079554,-6.821251c-0.076429,-0.458572 -0.477679,-0.821607 -0.955357,-0.821607l-9.30518,0c-0.477679,0 -0.878929,0.363036 -0.955357,0.821607l-1.079554,6.821251l0,4.776787c0,0.525447 0.429911,0.955357 0.955357,0.955357l11.464288,0c0.525447,0 0.955357,-0.429911 0.955357,-0.955357l0,-4.776787l0,0zm-3.133572,0.525447l-0.420357,0.850268c-0.162411,0.324821 -0.496786,0.535 -0.869375,0.535l-4.547501,0c-0.363036,0 -0.687857,-0.210179 -0.850268,-0.525447l-0.420357,-0.869375c-0.162411,-0.315268 -0.496786,-0.525447 -0.850268,-0.525447l-1.327947,0l0.955357,-6.687501l9.553573,0l0.955357,6.687501l-1.318393,0c-0.372589,0 -0.697411,0.210179 -0.869375,0.525447l0.009554,0.009554z" />
</svg>

After

Width:  |  Height:  |  Size: 893 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#C5C5C5" d="m7,12l-2,0l3,-3l3,3l-2,0l0,5l-2,0l0,-5l0,0zm5,-4c0,-0.44 -0.91,-3 -4.5,-3c-2.42,0 -4.5,1.92 -4.5,4c-1.98,0 -3,1.52 -3,3c0,1.53 1,3 3,3l3,0l0,-1.3l-3,0c-1.62,0 -1.7,-1.42 -1.7,-1.7c0,-0.17 0.05,-1.7 1.7,-1.7l1.3,0l0,-1.3c0,-1.39 1.56,-2.7 3.2,-2.7c2.55,0 3.13,1.55 3.2,1.8l0,1.2l1.3,0c0.81,0 2.7,0.22 2.7,2.2c0,2.09 -2.25,2.2 -2.7,2.2l-2,0l0,1.3l2,0c2.08,0 4,-1.16 4,-3.5c0,-2.44 -1.92,-3.5 -4,-3.5l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 558 B

View File

@@ -1 +1,5 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><title>Layer 1</title><rect height="11" width="3" y="3" x="7" fill="#424242"/><rect height="3" width="11" y="7" x="3" fill="#424242"/></svg> <?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<rect fill="#424242" x="6.5" y="5.5" width="3" height="11"/>
<rect fill="#424242" x="2.5" y="9.5" width="11" height="3"/>
</svg>

Before

Width:  |  Height:  |  Size: 203 B

After

Width:  |  Height:  |  Size: 253 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#424242" d="m13,8c0,-1.11 -0.89,-2 -2,-2c-1.11,0 -2,0.89 -2,2c0,0.73 0.41,1.38 1,1.72l0,0.3c-0.02,0.52 -0.23,0.98 -0.63,1.38c-0.4,0.4 -0.86,0.61 -1.38,0.63c-0.83,0.02 -1.48,0.16 -2,0.45l0,-4.76c0.59,-0.34 1,-0.98 1,-1.72c0,-1.11 -0.89,-2 -2,-2c-1.11,0 -1.99,0.89 -1.99,2c0,0.73 0.41,1.38 1,1.72l0,6.56c-0.59,0.35 -1,0.99 -1,1.72c0,1.11 0.89,2 2,2c1.11,0 2,-0.89 2,-2c0,-0.53 -0.2,-1 -0.53,-1.36c0.09,-0.06 0.48,-0.41 0.59,-0.47c0.25,-0.11 0.56,-0.17 0.94,-0.17c1.05,-0.05 1.95,-0.45 2.75,-1.25c0.8,-0.8 1.2,-1.98 1.25,-3.02l-0.02,0c0.61,-0.36 1.02,-1 1.02,-1.73l0,0zm-8,-3.2c0.66,0 1.2,0.55 1.2,1.2c0,0.65 -0.55,1.2 -1.2,1.2c-0.65,0 -1.2,-0.55 -1.2,-1.2c0,-0.65 0.55,-1.2 1.2,-1.2l0,0zm0,12.41c-0.66,0 -1.2,-0.55 -1.2,-1.2c0,-0.65 0.55,-1.2 1.2,-1.2c0.65,0 1.2,0.55 1.2,1.2c0,0.65 -0.55,1.2 -1.2,1.2l0,0zm6,-8c-0.66,0 -1.2,-0.55 -1.2,-1.2c0,-0.65 0.55,-1.2 1.2,-1.2c0.65,0 1.2,0.55 1.2,1.2c0,0.65 -0.55,1.2 -1.2,1.2l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="256" height="256" viewBox="0 0 14 16" xml:space="preserve"> <svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#6c6c6c" d="M10.86 7c-.45-1.72-2-3-3.86-3-1.86 0-3.41 1.28-3.86 3H0v2h3.14c.45 1.72 2 3 3.86 3 1.86 0 3.41-1.28 3.86-3H14V7h-3.14zM7 10.2c-1.22 0-2.2-.98-2.2-2.2 0-1.22.98-2.2 2.2-2.2 1.22 0 2.2.98 2.2 2.2 0 1.22-.98 2.2-2.2 2.2z"></path> <path fill="#424242" d="m11.86,10c-0.45,-1.72 -2,-3 -3.86,-3c-1.86,0 -3.41,1.28 -3.86,3l-3.14,0l0,2l3.14,0c0.45,1.72 2,3 3.86,3c1.86,0 3.41,-1.28 3.86,-3l3.14,0l0,-2l-3.14,0zm-3.86,3.2c-1.22,0 -2.2,-0.98 -2.2,-2.2c0,-1.22 0.98,-2.2 2.2,-2.2c1.22,0 2.2,0.98 2.2,2.2c0,1.22 -0.98,2.2 -2.2,2.2z"></path>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 480 B

After

Width:  |  Height:  |  Size: 428 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#424242" d="m9,15l2,0l-3,3l-3,-3l2,0l0,-5l2,0l0,5l0,0zm3,-8c0,-0.44 -0.91,-3 -4.5,-3c-2.42,0 -4.5,1.92 -4.5,4c-1.98,0 -3,1.52 -3,3c0,1.53 1,3 3,3l3,0l0,-1.3l-3,0c-1.62,0 -1.7,-1.42 -1.7,-1.7c0,-0.17 0.05,-1.7 1.7,-1.7l1.3,0l0,-1.3c0,-1.39 1.56,-2.7 3.2,-2.7c2.55,0 3.13,1.55 3.2,1.8l0,1.2l1.3,0c0.81,0 2.7,0.22 2.7,2.2c0,2.09 -2.25,2.2 -2.7,2.2l-2,0l0,1.3l2,0c2.08,0 4,-1.16 4,-3.5c0,-2.44 -1.92,-3.5 -4,-3.5l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 557 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#424242" d="m9,16l-2,0l0,-7l5,0l0,2l-3,0l0,5l0,0zm-1,-12c-2.19,0 -4.13,1.02 -5.41,2.59l-1.59,-1.59l0,4l4,0l-1.5,-1.5c1.05,-1.33 2.67,-2.2 4.5,-2.2c3.14,0 5.7,2.56 5.7,5.7c0,3.14 -2.56,5.7 -5.7,5.7c-3.14,0 -5.7,-2.56 -5.7,-5.7c0,-0.34 0.03,-0.67 0.09,-1l-1.31,0c-0.05,0.33 -0.08,0.66 -0.08,1c0,3.86 3.14,7 7,7c3.86,0 7,-3.14 7,-7c0,-3.86 -3.14,-7 -7,-7l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 500 B

View File

@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"> <svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d='M13.451 5.609l-.579-.939-1.068.812-.076.094c-.335.415-.927 1.341-1.124 2.876l-.021.165.033.163.071.345c0 1.654-1.346 3-3 3a2.98 2.98 0 0 1-2.107-.868 2.98 2.98 0 0 1-.873-2.111 3.004 3.004 0 0 1 2.351-2.929v2.926s2.528-2.087 2.984-2.461h.012L13.115 4.1 8.196 0H7.059v2.404A6.759 6.759 0 0 0 .938 9.125c0 1.809.707 3.508 1.986 4.782a6.707 6.707 0 0 0 4.784 1.988 6.758 6.758 0 0 0 6.75-6.75 6.741 6.741 0 0 0-1.007-3.536z' fill='#F6F6F6'/> <path fill="#424242" d="m12.901149,9.091851l-0.094,0.071c-0.269,0.333 -0.746,1.096 -0.91,2.375c0.057,0.277 0.092,0.495 0.092,0.545c0,2.206 -1.794,4 -4,4a3.986,3.986 0 0 1 -2.817,-1.164a3.987,3.987 0 0 1 -1.163,-2.815c0,-2.206 1.794,-4 4,-4l0.351,0.025l0,1.85s1.626,-1.342 1.631,-1.339l1.869,-1.577l-3.5,-2.917l0,2.218l-0.371,-0.03a5.75,5.75 0 0 0 -4.055,9.826a5.75,5.75 0 0 0 9.826,-4.056a5.725,5.725 0 0 0 -0.859,-3.012z" />
<path d='M12.6 6.134l-.094.071c-.269.333-.746 1.096-.91 2.375.057.277.092.495.092.545 0 2.206-1.794 4-4 4a3.986 3.986 0 0 1-2.817-1.164 3.987 3.987 0 0 1-1.163-2.815c0-2.206 1.794-4 4-4l.351.025v1.85S9.685 5.679 9.69 5.682l1.869-1.577-3.5-2.917v2.218l-.371-.03a5.75 5.75 0 0 0-4.055 9.826 5.75 5.75 0 0 0 9.826-4.056 5.725 5.725 0 0 0-.859-3.012z' fill='#424242'/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 987 B

After

Width:  |  Height:  |  Size: 553 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#424242" d="m12.519592,15.157073l-9.039184,0c-1.367793,0 -2.480539,-1.112828 -2.480539,-2.480622s1.112746,-2.480539 2.480539,-2.480539l0.281314,0c0.349178,-0.47173 0.888523,-0.771191 1.476341,-0.815025c0.321917,-1.471535 1.619609,-2.537962 3.165139,-2.537962c1.221543,0 2.341663,0.699516 2.889377,1.772488c0.083027,-0.008618 0.166386,-0.012926 0.250076,-0.012926c1.190221,0 2.194667,0.874105 2.378785,2.027867c0.670514,0.460047 1.07869,1.225023 1.07869,2.046096c0,1.367793 -1.112828,2.480622 -2.480539,2.480622z" />
</svg>

After

Width:  |  Height:  |  Size: 655 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#424242" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
<ellipse fill="#32cd32" stroke="#424242" stroke-width="0.5" rx="3" ry="3" cx="13" cy="4" />
</svg>

After

Width:  |  Height:  |  Size: 586 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#424242" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
<ellipse fill="#cd3131" stroke="#424242" stroke-width="0.5" rx="3" ry="3" cx="13" cy="4" />
</svg>

After

Width:  |  Height:  |  Size: 586 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#424242" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
<ellipse fill="#cdcd32" stroke="#424242" stroke-width="0.5" rx="3" ry="3" cx="13" cy="4" />
</svg>

After

Width:  |  Height:  |  Size: 586 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#424242" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 490 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#424242" d="m14.414133,16.199437l-3.415492,-3.433421c0.627518,-0.878526 0.995065,-1.945307 0.995065,-3.110698c0,-2.967265 -2.411463,-5.378728 -5.378728,-5.378728c-2.967265,0 -5.378728,2.411463 -5.378728,5.378728c0,2.967265 2.411463,5.378728 5.378728,5.378728c1.165391,0 2.223207,-0.367546 3.110698,-0.995065l3.433421,3.415492c0.170326,0.179291 0.403405,0.268936 0.627518,0.268936c0.224114,0 0.466156,-0.080681 0.627518,-0.268936c0.349617,-0.349617 0.349617,-0.914384 0,-1.264001l0,0.008965zm-7.799155,-2.330782c-2.321817,0 -4.213337,-1.891519 -4.213337,-4.213337c0,-2.321817 1.891519,-4.213337 4.213337,-4.213337c2.321817,0 4.213337,1.891519 4.213337,4.213337c0,2.321817 -1.891519,4.213337 -4.213337,4.213337l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 857 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16px" height="22px">
<path fill="#424242" d="m14.687501,11.955358l-1.079554,-6.821251c-0.076429,-0.458572 -0.477679,-0.821607 -0.955357,-0.821607l-9.30518,0c-0.477679,0 -0.878929,0.363036 -0.955357,0.821607l-1.079554,6.821251l0,4.776787c0,0.525447 0.429911,0.955357 0.955357,0.955357l11.464288,0c0.525447,0 0.955357,-0.429911 0.955357,-0.955357l0,-4.776787l0,0zm-3.133572,0.525447l-0.420357,0.850268c-0.162411,0.324821 -0.496786,0.535 -0.869375,0.535l-4.547501,0c-0.363036,0 -0.687857,-0.210179 -0.850268,-0.525447l-0.420357,-0.869375c-0.162411,-0.315268 -0.496786,-0.525447 -0.850268,-0.525447l-1.327947,0l0.955357,-6.687501l9.553573,0l0.955357,6.687501l-1.318393,0c-0.372589,0 -0.697411,0.210179 -0.869375,0.525447l0.009554,0.009554z" />
</svg>

After

Width:  |  Height:  |  Size: 893 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#424242" d="m7,12l-2,0l3,-3l3,3l-2,0l0,5l-2,0l0,-5l0,0zm5,-4c0,-0.44 -0.91,-3 -4.5,-3c-2.42,0 -4.5,1.92 -4.5,4c-1.98,0 -3,1.52 -3,3c0,1.53 1,3 3,3l3,0l0,-1.3l-3,0c-1.62,0 -1.7,-1.42 -1.7,-1.7c0,-0.17 0.05,-1.7 1.7,-1.7l1.3,0l0,-1.3c0,-1.39 1.56,-2.7 3.2,-2.7c2.55,0 3.13,1.55 3.2,1.8l0,1.2l1.3,0c0.81,0 2.7,0.22 2.7,2.2c0,2.09 -2.25,2.2 -2.7,2.2l-2,0l0,1.3l2,0c2.08,0 4,-1.16 4,-3.5c0,-2.44 -1.92,-3.5 -4,-3.5l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 558 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 12 KiB

61
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "gitlens", "name": "gitlens",
"version": "4.4.1", "version": "5.0.0-alpha.2",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -16,7 +16,7 @@
"integrity": "sha1-qjuL2ivlErGuCgV7lC6GnDcKVWk=", "integrity": "sha1-qjuL2ivlErGuCgV7lC6GnDcKVWk=",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/node": "8.0.25" "@types/node": "8.0.26"
} }
}, },
"@types/mocha": { "@types/mocha": {
@@ -26,9 +26,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "8.0.25", "version": "8.0.26",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.25.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.26.tgz",
"integrity": "sha512-zT+t9841g1HsjLtPMCYxmb1U4pcZ2TOegAKiomlmj6bIziuaEYHUavxLE9NRwdntY0vOCrgHho6OXjDX7fm/Kw==", "integrity": "sha512-wbKN0MB4XsjdnSE04HiCzLoBDirGCM6zXrqavSj44nZnPFYpnrTF64E9O6Xmf0ca/IuKK/BHUcXwMiwk92gW6Q==",
"dev": true "dev": true
}, },
"@types/tmp": { "@types/tmp": {
@@ -660,7 +660,7 @@
"requires": { "requires": {
"asynckit": "0.4.0", "asynckit": "0.4.0",
"combined-stream": "1.0.5", "combined-stream": "1.0.5",
"mime-types": "2.1.16" "mime-types": "2.1.17"
} }
}, },
"from": { "from": {
@@ -999,7 +999,7 @@
"is-typedarray": "1.0.0", "is-typedarray": "1.0.0",
"isstream": "0.1.2", "isstream": "0.1.2",
"json-stringify-safe": "5.0.1", "json-stringify-safe": "5.0.1",
"mime-types": "2.1.16", "mime-types": "2.1.17",
"oauth-sign": "0.8.2", "oauth-sign": "0.8.2",
"qs": "6.3.2", "qs": "6.3.2",
"stringstream": "0.0.5", "stringstream": "0.0.5",
@@ -1271,9 +1271,9 @@
"integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==" "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA=="
}, },
"ignore": { "ignore": {
"version": "3.3.4", "version": "3.3.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.4.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.5.tgz",
"integrity": "sha512-KjHyHxUgicfgFiTJaIA9DoeY3TIQz5thaKqm35re7RTVVB7zjF1fTMIDMXM4GUUBipR4FW8BvGnA115pZ/AxQQ==" "integrity": "sha512-JLH93mL8amZQhh/p6mfQgVBH3M6epNq3DfsXsTSuSrInVjwyYlFE1nv2AgfRCC8PoOhM0jwQ5v8s9LgbK7yGDw=="
}, },
"inflight": { "inflight": {
"version": "1.0.6", "version": "1.0.6",
@@ -1728,7 +1728,7 @@
"normalize-path": "2.1.1", "normalize-path": "2.1.1",
"object.omit": "2.0.1", "object.omit": "2.0.1",
"parse-glob": "3.0.4", "parse-glob": "3.0.4",
"regex-cache": "0.4.3" "regex-cache": "0.4.4"
}, },
"dependencies": { "dependencies": {
"is-extglob": { "is-extglob": {
@@ -1749,18 +1749,18 @@
} }
}, },
"mime-db": { "mime-db": {
"version": "1.29.0", "version": "1.30.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.29.0.tgz", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
"integrity": "sha1-SNJtI1WJZRcErFkWygYAGRQmaHg=", "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=",
"dev": true "dev": true
}, },
"mime-types": { "mime-types": {
"version": "2.1.16", "version": "2.1.17",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
"integrity": "sha1-K4WKUuXs1RbbiXrCvodIeDBpjiM=", "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
"dev": true, "dev": true,
"requires": { "requires": {
"mime-db": "1.29.0" "mime-db": "1.30.0"
} }
}, },
"minimatch": { "minimatch": {
@@ -2080,13 +2080,12 @@
} }
}, },
"regex-cache": { "regex-cache": {
"version": "0.4.3", "version": "0.4.4",
"resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz",
"integrity": "sha1-mxpsNdTQ3871cRrmUejp09cRQUU=", "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"is-equal-shallow": "0.1.3", "is-equal-shallow": "0.1.3"
"is-primitive": "2.0.0"
} }
}, },
"remove-trailing-separator": { "remove-trailing-separator": {
@@ -2132,7 +2131,7 @@
"is-typedarray": "1.0.0", "is-typedarray": "1.0.0",
"isstream": "0.1.2", "isstream": "0.1.2",
"json-stringify-safe": "5.0.1", "json-stringify-safe": "5.0.1",
"mime-types": "2.1.16", "mime-types": "2.1.17",
"oauth-sign": "0.8.2", "oauth-sign": "0.8.2",
"performance-now": "0.2.0", "performance-now": "0.2.0",
"qs": "6.4.0", "qs": "6.4.0",
@@ -2489,13 +2488,13 @@
"resolve": "1.4.0", "resolve": "1.4.0",
"semver": "5.4.1", "semver": "5.4.1",
"tslib": "1.7.1", "tslib": "1.7.1",
"tsutils": "2.8.1" "tsutils": "2.8.2"
} }
}, },
"tsutils": { "tsutils": {
"version": "2.8.1", "version": "2.8.2",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.8.1.tgz", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.8.2.tgz",
"integrity": "sha1-N3FATnyp8L7fXZGaR6SxiQpo7/8=", "integrity": "sha1-LBSGukMSYIRbCsb5Aq/Z1wio6mo=",
"dev": true, "dev": true,
"requires": { "requires": {
"tslib": "1.7.1" "tslib": "1.7.1"
@@ -2515,9 +2514,9 @@
"optional": true "optional": true
}, },
"typescript": { "typescript": {
"version": "2.4.2", "version": "2.5.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.2.tgz",
"integrity": "sha1-+DlfhdRZJ2BnyYiqQYN6j4KHCEQ=", "integrity": "sha1-A4qV99m7tCCxvzW6MdTFwd0//jQ=",
"dev": true "dev": true
}, },
"unique-stream": { "unique-stream": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "gitlens", "name": "gitlens",
"version": "4.5.0-beta", "version": "5.0.0-alpha.2",
"author": { "author": {
"name": "Eric Amodio", "name": "Eric Amodio",
"email": "eamodio@gmail.com" "email": "eamodio@gmail.com"
@@ -413,20 +413,39 @@
"default": null, "default": null,
"description": "Specifies how all absolute dates will be formatted by default\nSee https://momentjs.com/docs/#/displaying/format/ for valid formats" "description": "Specifies how all absolute dates will be formatted by default\nSee https://momentjs.com/docs/#/displaying/format/ for valid formats"
}, },
"gitlens.fileHistoryExplorer.commitFormat": { "gitlens.gitExplorer.commitFormat": {
"type": "string", "type": "string",
"default": "${message} \u00a0\u2022\u00a0 ${authorAgo} \u00a0\u2022\u00a0 ${id}", "default": "${message} \u00a0\u2022\u00a0 ${authorAgo} \u00a0\u2022\u00a0 ${id}",
"description": "Specifies the format of committed changes in the `Git File History` explorer\nAvailable tokens\n ${id} - commit id\n ${author} - commit author\n ${message} - commit message\n ${ago} - relative commit date (e.g. 1 day ago)\n ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)\n ${authorAgo} - commit author, relative commit date\nSee https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting" "description": "Specifies the format of committed changes in the `GitLens` custom view\nAvailable tokens\n ${id} - commit id\n ${author} - commit author\n ${message} - commit message\n ${ago} - relative commit date (e.g. 1 day ago)\n ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)\n ${authorAgo} - commit author, relative commit date\nSee https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting"
}, },
"gitlens.stashExplorer.stashFormat": { "gitlens.gitExplorer.commitFileFormat": {
"type": "string",
"default": "${message}",
"description": "Specifies the format of stashed changes in the `Git Stashes` explorer\nAvailable tokens\n ${id} - commit id\n ${author} - commit author\n ${message} - commit message\n ${ago} - relative commit date (e.g. 1 day ago)\n ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)\n ${authorAgo} - commit author, relative commit date\nSee https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting"
},
"gitlens.stashExplorer.stashFileFormat": {
"type": "string", "type": "string",
"default": "${filePath}", "default": "${filePath}",
"description": "Specifies the format of a stashed file in the `Git Stashes` explorer\nAvailable tokens\n ${file} - file name\n ${filePath} - file name and path\n ${path} - file path" "description": "Specifies the format of a committed file in the `GitLens` custom view\nAvailable tokens\n ${file} - file name\n ${filePath} - file name and path\n ${path} - file path"
},
"gitlens.gitExplorer.showTrackingBranch": {
"type": "boolean",
"default": true,
"description": "Specifies whether or not to show the tracking branch when displaying local branches in the `GitLens` custom view"
},
"gitlens.gitExplorer.stashFormat": {
"type": "string",
"default": "${message}",
"description": "Specifies the format of stashed changes in the `GitLens` custom view\nAvailable tokens\n ${id} - commit id\n ${author} - commit author\n ${message} - commit message\n ${ago} - relative commit date (e.g. 1 day ago)\n ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)\n ${authorAgo} - commit author, relative commit date\nSee https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting"
},
"gitlens.gitExplorer.stashFileFormat": {
"type": "string",
"default": "${filePath}",
"description": "Specifies the format of a stashed file in the `GitLens` custom view\nAvailable tokens\n ${file} - file name\n ${filePath} - file name and path\n ${path} - file path"
},
"gitlens.gitExplorer.view": {
"type": "string",
"default": "repository",
"enum": [
"history",
"repository"
],
"description": "Specifies the starting view (mode) of the `GitLens` custom view\n `history` - shows the commit history of the active file\n `repository` - shows a repository explorer"
}, },
"gitlens.statusBar.enabled": { "gitlens.statusBar.enabled": {
"type": "boolean", "type": "boolean",
@@ -772,17 +791,17 @@
}, },
{ {
"command": "gitlens.diffWithNext", "command": "gitlens.diffWithNext",
"title": "Compare File with Next Commit", "title": "Compare File with Next Revision",
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.diffWithPrevious", "command": "gitlens.diffWithPrevious",
"title": "Compare File with Previous", "title": "Compare File with Previous Revision",
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.diffLineWithPrevious", "command": "gitlens.diffLineWithPrevious",
"title": "Compare Line Commit with Previous", "title": "Compare Line Revision with Previous",
"category": "GitLens" "category": "GitLens"
}, },
{ {
@@ -792,12 +811,12 @@
}, },
{ {
"command": "gitlens.diffWithWorking", "command": "gitlens.diffWithWorking",
"title": "Compare File with Working Tree", "title": "Compare File with Working Revision",
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.diffLineWithWorking", "command": "gitlens.diffLineWithWorking",
"title": "Compare Line Commit with Working Tree", "title": "Compare Line Revision with Working",
"category": "GitLens" "category": "GitLens"
}, },
{ {
@@ -864,7 +883,11 @@
{ {
"command": "gitlens.showCommitSearch", "command": "gitlens.showCommitSearch",
"title": "Search Commits", "title": "Search Commits",
"category": "GitLens" "category": "GitLens",
"icon": {
"dark": "images/dark/icon-search.svg",
"light": "images/light/icon-search.svg"
}
}, },
{ {
"command": "gitlens.showFileHistory", "command": "gitlens.showFileHistory",
@@ -931,6 +954,11 @@
"title": "Open Changed Files", "title": "Open Changed Files",
"category": "GitLens" "category": "GitLens"
}, },
{
"command": "gitlens.openBranchesInRemote",
"title": "Open Branches in Remote",
"category": "GitLens"
},
{ {
"command": "gitlens.openBranchInRemote", "command": "gitlens.openBranchInRemote",
"title": "Open Branch in Remote", "title": "Open Branch in Remote",
@@ -985,66 +1013,61 @@
} }
}, },
{ {
"command": "gitlens.fileHistoryExplorer.refresh", "command": "gitlens.gitExplorer.switchToHistoryView",
"title": "Refresh", "title": "Switch to History View",
"category": "GitLens", "category": "GitLens",
"icon": { "icon": {
"dark": "images/dark/icon-refresh.svg", "dark": "images/dark/icon-history.svg",
"light": "images/light/icon-refresh.svg" "light": "images/light/icon-history.svg"
} }
}, },
{ {
"command": "gitlens.fileHistoryExplorer.openChanges", "command": "gitlens.gitExplorer.switchToRepositoryView",
"title": "Switch to Repository View",
"category": "GitLens",
"icon": {
"dark": "images/dark/icon-repo.svg",
"light": "images/light/icon-repo.svg"
}
},
{
"command": "gitlens.gitExplorer.openChanges",
"title": "Open Changes", "title": "Open Changes",
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.fileHistoryExplorer.openFile", "command": "gitlens.gitExplorer.openChangesWithWorking",
"title": "Open Changes with Working Tree",
"category": "GitLens"
},
{
"command": "gitlens.gitExplorer.openFile",
"title": "Open File", "title": "Open File",
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.fileHistoryExplorer.openFileRevision", "command": "gitlens.gitExplorer.openFileRevision",
"title": "Open File Revision", "title": "Open Revision",
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.fileHistoryExplorer.openFileInRemote", "command": "gitlens.gitExplorer.openFileRevisionInRemote",
"title": "Open File in Remote", "title": "Open Revision in Remote",
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.fileHistoryExplorer.openFileRevisionInRemote", "command": "gitlens.gitExplorer.openChangedFiles",
"title": "Open File Revision in Remote", "title": "Open Files",
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.stashExplorer.refresh", "command": "gitlens.gitExplorer.openChangedFileRevisions",
"title": "Refresh", "title": "Open Revisions",
"category": "GitLens",
"icon": {
"dark": "images/dark/icon-refresh.svg",
"light": "images/light/icon-refresh.svg"
}
},
{
"command": "gitlens.stashExplorer.openChanges",
"title": "Open Changes",
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.stashExplorer.openFile", "command": "gitlens.gitExplorer.applyChanges",
"title": "Open File", "title": "Apply Changes",
"category": "GitLens"
},
{
"command": "gitlens.stashExplorer.openStashedFile",
"title": "Open Stashed File",
"category": "GitLens"
},
{
"command": "gitlens.stashExplorer.openFileInRemote",
"title": "Open File in Remote",
"category": "GitLens" "category": "GitLens"
} }
], ],
@@ -1170,6 +1193,10 @@
"command": "gitlens.openChangedFiles", "command": "gitlens.openChangedFiles",
"when": "gitlens:enabled" "when": "gitlens:enabled"
}, },
{
"command": "gitlens.openBranchesInRemote",
"when": "gitlens:hasRemotes"
},
{ {
"command": "gitlens.openBranchInRemote", "command": "gitlens.openBranchInRemote",
"when": "gitlens:hasRemotes" "when": "gitlens:hasRemotes"
@@ -1203,47 +1230,43 @@
"when": "false" "when": "false"
}, },
{ {
"command": "gitlens.fileHistoryExplorer.refresh", "command": "gitlens.gitExplorer.switchToHistoryView",
"when": "gitlens:gitExplorer:view == repository"
},
{
"command": "gitlens.gitExplorer.switchToRepositoryView",
"when": "gitlens:gitExplorer:view == history"
},
{
"command": "gitlens.gitExplorer.openChanges",
"when": "false" "when": "false"
}, },
{ {
"command": "gitlens.fileHistoryExplorer.openChanges", "command": "gitlens.gitExplorer.openChangesWithWorking",
"when": "false" "when": "false"
}, },
{ {
"command": "gitlens.fileHistoryExplorer.openFile", "command": "gitlens.gitExplorer.openFile",
"when": "false" "when": "false"
}, },
{ {
"command": "gitlens.fileHistoryExplorer.openFileRevision", "command": "gitlens.gitExplorer.openFileRevision",
"when": "false" "when": "false"
}, },
{ {
"command": "gitlens.fileHistoryExplorer.openFileInRemote", "command": "gitlens.gitExplorer.openFileRevisionInRemote",
"when": "false" "when": "false"
}, },
{ {
"command": "gitlens.fileHistoryExplorer.openFileRevisionInRemote", "command": "gitlens.gitExplorer.openChangedFiles",
"when": "false" "when": "false"
}, },
{ {
"command": "gitlens.stashExplorer.refresh", "command": "gitlens.gitExplorer.openChangedFileRevisions",
"when": "false" "when": "false"
}, },
{ {
"command": "gitlens.stashExplorer.openChanges", "command": "gitlens.gitExplorer.applyChanges",
"when": "false"
},
{
"command": "gitlens.stashExplorer.openFile",
"when": "false"
},
{
"command": "gitlens.stashExplorer.openStashedFile",
"when": "false"
},
{
"command": "gitlens.stashExplorer.openFileInRemote",
"when": "false" "when": "false"
} }
], ],
@@ -1318,12 +1341,12 @@
}, },
{ {
"command": "gitlens.openFileInRemote", "command": "gitlens.openFileInRemote",
"when": "gitlens:enabled && config.gitlens.advanced.menus.editorTitleContext.remote", "when": "gitlens:enabled && gitlens:hasRemotes && config.gitlens.advanced.menus.editorTitleContext.remote",
"group": "1_gitlens" "group": "1_gitlens"
}, },
{ {
"command": "gitlens.openRepoInRemote", "command": "gitlens.openRepoInRemote",
"when": "gitlens:enabled && config.gitlens.advanced.menus.editorTitleContext.remote", "when": "gitlens:enabled && gitlens:hasRemotes && config.gitlens.advanced.menus.editorTitleContext.remote",
"group": "1_gitlens" "group": "1_gitlens"
}, },
{ {
@@ -1355,7 +1378,7 @@
"editor/title/context": [ "editor/title/context": [
{ {
"command": "gitlens.openFileInRemote", "command": "gitlens.openFileInRemote",
"when": "gitlens:enabled && config.gitlens.advanced.menus.editorTitleContext.remote", "when": "gitlens:enabled && gitlens:hasRemotes && config.gitlens.advanced.menus.editorTitleContext.remote",
"group": "1_gitlens" "group": "1_gitlens"
}, },
{ {
@@ -1382,7 +1405,7 @@
"explorer/context": [ "explorer/context": [
{ {
"command": "gitlens.openFileInRemote", "command": "gitlens.openFileInRemote",
"when": "gitlens:enabled && config.gitlens.advanced.menus.explorerContext.remote", "when": "gitlens:enabled && gitlens:hasRemotes && config.gitlens.advanced.menus.explorerContext.remote",
"group": "navigation@100" "group": "navigation@100"
}, },
{ {
@@ -1411,6 +1434,11 @@
"command": "gitlens.closeUnchangedFiles", "command": "gitlens.closeUnchangedFiles",
"when": "gitlens:enabled", "when": "gitlens:enabled",
"group": "1_gitlens@2" "group": "1_gitlens@2"
},
{
"command": "gitlens.stashSave",
"when": "gitlens:enabled",
"group": "2_gitlens@1"
} }
], ],
"scm/resourceState/context": [ "scm/resourceState/context": [
@@ -1428,120 +1456,215 @@
"command": "gitlens.showQuickFileHistory", "command": "gitlens.showQuickFileHistory",
"when": "gitlens:enabled", "when": "gitlens:enabled",
"group": "1_gitlens_1@1" "group": "1_gitlens_1@1"
},
{
"command": "gitlens.stashSave",
"when": "gitlens:enabled",
"group": "2_gitlens@1"
} }
], ],
"view/title": [ "view/title": [
{ {
"command": "gitlens.gitExplorer.refresh", "command": "gitlens.showCommitSearch",
"when": "gitlens:enabled && view == gitlens.gitExplorer", "when": "gitlens:enabled && view == gitlens.gitExplorer",
"group": "navigation"
},
{
"command": "gitlens.fileHistoryExplorer.refresh",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer",
"group": "navigation"
},
{
"command": "gitlens.stashSave",
"when": "gitlens:enabled && view == gitlens.stashExplorer",
"group": "navigation@1" "group": "navigation@1"
}, },
{ {
"command": "gitlens.stashExplorer.refresh", "command": "gitlens.gitExplorer.switchToHistoryView",
"when": "gitlens:enabled && view == gitlens.stashExplorer", "when": "gitlens:enabled && view == gitlens.gitExplorer && gitlens:gitExplorer:view == repository",
"group": "navigation@2" "group": "navigation@2"
},
{
"command": "gitlens.gitExplorer.switchToRepositoryView",
"when": "gitlens:enabled && view == gitlens.gitExplorer && gitlens:gitExplorer:view == history",
"group": "navigation@3"
},
{
"command": "gitlens.gitExplorer.refresh",
"when": "gitlens:enabled && view == gitlens.gitExplorer",
"group": "navigation@4"
} }
], ],
"view/item/context": [ "view/item/context": [
{
"command": "gitlens.openBranchesInRemote",
"when": "gitlens:enabled && gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:branches",
"group": "1_gitlens@1"
},
{
"command": "gitlens.openBranchInRemote",
"when": "gitlens:enabled && gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:branch-history",
"group": "1_gitlens@1"
},
{ {
"command": "gitlens.openCommitInRemote", "command": "gitlens.openCommitInRemote",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == commit", "when": "gitlens:enabled && gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "1_gitlens@1"
},
{
"command": "gitlens.copyShaToClipboard",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "2_gitlens@1"
},
{
"command": "gitlens.copyMessageToClipboard",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "2_gitlens@2"
},
{
"command": "gitlens.gitExplorer.openChangedFiles",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "3_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openChangedFileRevisions",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "3_gitlens@2"
},
{
"command": "gitlens.showQuickCommitDetails",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "4_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openChanges",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "1_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openChangesWithWorking",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "1_gitlens@2"
},
{
"command": "gitlens.gitExplorer.openFile",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "2_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openFileRevision",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "2_gitlens@2"
},
{
"command": "gitlens.openFileInRemote",
"when": "gitlens:enabled && gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "3_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openFileRevisionInRemote",
"when": "gitlens:enabled && gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "3_gitlens@2"
},
{
"command": "gitlens.gitExplorer.applyChanges",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "4_gitlens@1"
},
{
"command": "gitlens.showQuickFileHistory",
"when": "gitlens:isTracked && view == gitlens.gitExplorer && viewItem == gitlens:commit-file && gitlens:gitExplorer:view == repository",
"group": "5_gitlens@1"
},
{
"command": "gitlens.showQuickCommitFileDetails",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "5_gitlens@2"
},
{
"command": "gitlens.gitExplorer.openFile",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:file-history",
"group": "1_gitlens@1" "group": "1_gitlens@1"
}, },
{ {
"command": "gitlens.openFileInRemote", "command": "gitlens.openFileInRemote",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == commit-file", "when": "gitlens:enabled && gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:file-history",
"group": "1_gitlens@1"
},
{
"command": "gitlens.diffWithPrevious",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == commit-file",
"group": "2_gitlens@1"
},
{
"command": "gitlens.diffWithWorking",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == commit-file",
"group": "2_gitlens@2"
},
{
"command": "gitlens.fileHistoryExplorer.openChanges",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
"group": "1_gitlens@1"
},
{
"command": "gitlens.diffWithWorking",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
"group": "1_gitlens@2" "group": "1_gitlens@2"
}, },
{ {
"command": "gitlens.fileHistoryExplorer.openFile", "command": "gitlens.openBranchesInRemote",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file", "when": "gitlens:enabled && gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:remote",
"group": "2_gitlens@1" "group": "1_gitlens@1"
}, },
{ {
"command": "gitlens.fileHistoryExplorer.openFileRevision", "command": "gitlens.openRepoInRemote",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file", "when": "gitlens:enabled && gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:remote",
"group": "2_gitlens@2" "group": "1_gitlens@2"
}, },
{ {
"command": "gitlens.fileHistoryExplorer.openFileInRemote", "command": "gitlens.stashSave",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file", "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stashes",
"group": "2_gitlens@3" "group": "1_gitlens@1"
},
{
"command": "gitlens.fileHistoryExplorer.openFileRevisionInRemote",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
"group": "2_gitlens@4"
},
{
"command": "gitlens.showQuickCommitDetails",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
"group": "3_gitlens@1"
}, },
{ {
"command": "gitlens.stashApply", "command": "gitlens.stashApply",
"when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == stash-commit", "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash",
"group": "3_gitlens@1"
},
{
"command": "gitlens.stashDelete",
"when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == stash-commit",
"group": "3_gitlens@1"
},
{
"command": "gitlens.stashExplorer.openChanges",
"when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == commit-file",
"group": "1_gitlens@1" "group": "1_gitlens@1"
}, },
{ {
"command": "gitlens.stashExplorer.openFile", "command": "gitlens.stashDelete",
"when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == commit-file", "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash",
"group": "1_gitlens@2" "group": "1_gitlens@2"
}, },
{ {
"command": "gitlens.stashExplorer.openStashedFile", "command": "gitlens.copyMessageToClipboard",
"when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == commit-file", "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash",
"group": "1_gitlens@3" "group": "2_gitlens@1"
}, },
{ {
"command": "gitlens.stashExplorer.openFileInRemote", "command": "gitlens.gitExplorer.openChangedFiles",
"when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == commit-file", "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash",
"group": "1_gitlens@4" "group": "3_gitlens@1"
}, },
{ {
"command": "gitlens.diffWithWorking", "command": "gitlens.gitExplorer.openChangedFileRevisions",
"when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == commit-file", "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash",
"group": "3_gitlens@2"
},
{
"command": "gitlens.gitExplorer.openChanges",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "1_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openChangesWithWorking",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "1_gitlens@2"
},
{
"command": "gitlens.gitExplorer.openFile",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "2_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openFileRevision",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "2_gitlens@2" "group": "2_gitlens@2"
},
{
"command": "gitlens.openFileInRemote",
"when": "gitlens:enabled && gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "3_gitlens@1"
},
{
"command": "gitlens.gitExplorer.applyChanges",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "4_gitlens@1"
},
{
"command": "gitlens.showQuickFileHistory",
"when": "gitlens:isTracked && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "5_gitlens@1"
},
{
"command": "gitlens.openRepoInRemote",
"when": "gitlens:enabled && gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:status",
"group": "1_gitlens@1"
},
{
"command": "gitlens.gitExplorer.refresh",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem != gitlens:commit-file && viewItem != gitlens:stash-file",
"group": "9_gitlens@1"
} }
] ]
}, },
@@ -1640,13 +1763,8 @@
"views": { "views": {
"explorer": [ "explorer": [
{ {
"id": "gitlens.fileHistoryExplorer", "id": "gitlens.gitExplorer",
"name": "Git File History", "name": "GitLens",
"when": "gitlens:enabled && config.gitlens.insiders"
},
{
"id": "gitlens.stashExplorer",
"name": "Git Stashes",
"when": "gitlens:enabled" "when": "gitlens:enabled"
} }
] ]
@@ -1670,7 +1788,7 @@
"applicationinsights": "0.21.0", "applicationinsights": "0.21.0",
"copy-paste": "1.3.0", "copy-paste": "1.3.0",
"iconv-lite": "0.4.18", "iconv-lite": "0.4.18",
"ignore": "3.3.4", "ignore": "3.3.5",
"lodash.debounce": "4.0.8", "lodash.debounce": "4.0.8",
"lodash.escaperegexp": "4.1.2", "lodash.escaperegexp": "4.1.2",
"lodash.isequal": "4.5.0", "lodash.isequal": "4.5.0",
@@ -1684,11 +1802,11 @@
"@types/copy-paste": "1.1.30", "@types/copy-paste": "1.1.30",
"@types/iconv-lite": "0.0.1", "@types/iconv-lite": "0.0.1",
"@types/mocha": "2.2.42", "@types/mocha": "2.2.42",
"@types/node": "8.0.25", "@types/node": "8.0.26",
"@types/tmp": "0.0.33", "@types/tmp": "0.0.33",
"mocha": "3.5.0", "mocha": "3.5.0",
"tslint": "5.7.0", "tslint": "5.7.0",
"typescript": "2.4.2", "typescript": "2.5.2",
"vscode": "1.1.5" "vscode": "1.1.5"
} }
} }

View File

@@ -61,7 +61,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
gutter.renderOptions = { ...gutter.renderOptions }; gutter.renderOptions = { ...gutter.renderOptions };
gutter.renderOptions.before = { gutter.renderOptions.before = {
...gutter.renderOptions.before, ...gutter.renderOptions.before,
...{ contentText: GlyphChars.Space.repeat(gutter.renderOptions!.before!.contentText!.length) } ...{ contentText: GlyphChars.Space.repeat(Strings.getWidth(gutter.renderOptions!.before!.contentText!)) }
}; };
if (separateLines) { if (separateLines) {

View File

@@ -14,6 +14,7 @@ export * from './commands/diffWithPrevious';
export * from './commands/diffWithRevision'; export * from './commands/diffWithRevision';
export * from './commands/diffWithWorking'; export * from './commands/diffWithWorking';
export * from './commands/openChangedFiles'; export * from './commands/openChangedFiles';
export * from './commands/openBranchesInRemote';
export * from './commands/openBranchInRemote'; export * from './commands/openBranchInRemote';
export * from './commands/openCommitInRemote'; export * from './commands/openCommitInRemote';
export * from './commands/openFileInRemote'; export * from './commands/openFileInRemote';

View File

@@ -1,6 +1,7 @@
'use strict'; 'use strict';
import { commands, Disposable, SourceControlResourceGroup, SourceControlResourceState, TextDocumentShowOptions, TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode'; import { commands, Disposable, SourceControlResourceGroup, SourceControlResourceState, TextDocumentShowOptions, TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
import { ExplorerNode } from '../views/explorerNodes'; import { ExplorerNode } from '../views/explorerNodes';
import { GitBranch, GitCommit, GitRemote } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { Telemetry } from '../telemetry'; import { Telemetry } from '../telemetry';
@@ -18,6 +19,7 @@ export type Commands =
'gitlens.diffWithWorking' | 'gitlens.diffWithWorking' |
'gitlens.diffLineWithWorking' | 'gitlens.diffLineWithWorking' |
'gitlens.openChangedFiles' | 'gitlens.openChangedFiles' |
'gitlens.openBranchesInRemote' |
'gitlens.openBranchInRemote' | 'gitlens.openBranchInRemote' |
'gitlens.openCommitInRemote' | 'gitlens.openCommitInRemote' |
'gitlens.openFileInRemote' | 'gitlens.openFileInRemote' |
@@ -58,6 +60,7 @@ export const Commands = {
DiffWithWorking: 'gitlens.diffWithWorking' as Commands, DiffWithWorking: 'gitlens.diffWithWorking' as Commands,
DiffLineWithWorking: 'gitlens.diffLineWithWorking' as Commands, DiffLineWithWorking: 'gitlens.diffLineWithWorking' as Commands,
OpenChangedFiles: 'gitlens.openChangedFiles' as Commands, OpenChangedFiles: 'gitlens.openChangedFiles' as Commands,
OpenBranchesInRemote: 'gitlens.openBranchesInRemote' as Commands,
OpenBranchInRemote: 'gitlens.openBranchInRemote' as Commands, OpenBranchInRemote: 'gitlens.openBranchInRemote' as Commands,
OpenCommitInRemote: 'gitlens.openCommitInRemote' as Commands, OpenCommitInRemote: 'gitlens.openCommitInRemote' as Commands,
OpenFileInRemote: 'gitlens.openFileInRemote' as Commands, OpenFileInRemote: 'gitlens.openFileInRemote' as Commands,
@@ -125,6 +128,22 @@ export interface CommandViewContext extends CommandBaseContext {
node: ExplorerNode; node: ExplorerNode;
} }
export function isCommandViewContextWithBranch(context: CommandContext): context is CommandViewContext & { node: (ExplorerNode & { branch: GitBranch }) } {
return context.type === 'view' && (context.node as any).branch && (context.node as any).branch instanceof GitBranch;
}
interface ICommandViewContextWithCommit<T extends GitCommit> extends CommandViewContext {
node: (ExplorerNode & { commit: T });
}
export function isCommandViewContextWithCommit<T extends GitCommit>(context: CommandContext): context is ICommandViewContextWithCommit<T> {
return context.type === 'view' && (context.node as any).commit && (context.node as any).commit instanceof GitCommit;
}
export function isCommandViewContextWithRemote(context: CommandContext): context is CommandViewContext & { node: (ExplorerNode & { remote: GitRemote }) } {
return context.type === 'view' && (context.node as any).remote && (context.node as any).remote instanceof GitRemote;
}
export type CommandContext = CommandScmGroupsContext | CommandScmStatesContext | CommandUnknownContext | CommandUriContext | CommandViewContext; export type CommandContext = CommandScmGroupsContext | CommandScmStatesContext | CommandUnknownContext | CommandUriContext | CommandViewContext;
function isScmResourceGroup(group: any): group is SourceControlResourceGroup { function isScmResourceGroup(group: any): group is SourceControlResourceGroup {

View File

@@ -1,7 +1,7 @@
'use strict'; 'use strict';
import { Iterables } from '../system'; import { Iterables } from '../system';
import { TextEditor, Uri, window } from 'vscode'; import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common'; import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
import { GitService, GitUri } from '../gitService'; import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { copy } from 'copy-paste'; import { copy } from 'copy-paste';
@@ -17,6 +17,16 @@ export class CopyMessageToClipboardCommand extends ActiveEditorCommand {
super(Commands.CopyMessageToClipboard); super(Commands.CopyMessageToClipboard);
} }
protected async preExecute(context: CommandContext, args: CopyMessageToClipboardCommandArgs = {}): Promise<any> {
if (isCommandViewContextWithCommit(context)) {
args = { ...args };
args.sha = context.node.commit.sha;
return this.execute(context.editor, context.node.commit.uri, args);
}
return this.execute(context.editor, context.uri, args);
}
async execute(editor?: TextEditor, uri?: Uri, args: CopyMessageToClipboardCommandArgs = {}): Promise<any> { async execute(editor?: TextEditor, uri?: Uri, args: CopyMessageToClipboardCommandArgs = {}): Promise<any> {
uri = getCommandUri(uri, editor); uri = getCommandUri(uri, editor);
@@ -64,7 +74,7 @@ export class CopyMessageToClipboardCommand extends ActiveEditorCommand {
// Get the full commit message -- since blame only returns the summary // Get the full commit message -- since blame only returns the summary
const commit = await this.git.getLogCommit(gitUri.repoPath, gitUri.fsPath, args.sha); const commit = await this.git.getLogCommit(gitUri.repoPath, gitUri.fsPath, args.sha);
if (!commit) return undefined; if (commit === undefined) return undefined;
args.message = commit.message; args.message = commit.message;
} }

View File

@@ -1,7 +1,7 @@
'use strict'; 'use strict';
import { Iterables } from '../system'; import { Iterables } from '../system';
import { TextEditor, Uri, window } from 'vscode'; import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common'; import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
import { GitService, GitUri } from '../gitService'; import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { copy } from 'copy-paste'; import { copy } from 'copy-paste';
@@ -16,6 +16,16 @@ export class CopyShaToClipboardCommand extends ActiveEditorCommand {
super(Commands.CopyShaToClipboard); super(Commands.CopyShaToClipboard);
} }
protected async preExecute(context: CommandContext, args: CopyShaToClipboardCommandArgs = {}): Promise<any> {
if (isCommandViewContextWithCommit(context)) {
args = { ...args };
args.sha = context.node.commit.sha;
return this.execute(context.editor, context.node.commit.uri, args);
}
return this.execute(context.editor, context.uri, args);
}
async execute(editor?: TextEditor, uri?: Uri, args: CopyShaToClipboardCommandArgs = {}): Promise<any> { async execute(editor?: TextEditor, uri?: Uri, args: CopyShaToClipboardCommandArgs = {}): Promise<any> {
uri = getCommandUri(uri, editor); uri = getCommandUri(uri, editor);
@@ -45,7 +55,7 @@ export class CopyShaToClipboardCommand extends ActiveEditorCommand {
try { try {
const blame = await this.git.getBlameForLine(gitUri, blameline); const blame = await this.git.getBlameForLine(gitUri, blameline);
if (!blame) return undefined; if (blame === undefined) return undefined;
args.sha = blame.commit.sha; args.sha = blame.commit.sha;
} }

View File

@@ -2,7 +2,7 @@
import { Iterables } from '../system'; import { Iterables } from '../system';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode'; import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common'; import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands, GlyphChars } from '../constants'; import { BuiltInCommands, FakeSha, GlyphChars } from '../constants';
import { DiffWithWorkingCommandArgs } from './diffWithWorking'; import { DiffWithWorkingCommandArgs } from './diffWithWorking';
import { GitCommit, GitService, GitUri } from '../gitService'; import { GitCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
@@ -14,6 +14,10 @@ export interface DiffWithPreviousCommandArgs {
line?: number; line?: number;
range?: Range; range?: Range;
showOptions?: TextDocumentShowOptions; showOptions?: TextDocumentShowOptions;
allowMissingPrevious?: boolean;
leftTitlePrefix?: string;
rightTitlePrefix?: string;
} }
export class DiffWithPreviousCommand extends ActiveEditorCommand { export class DiffWithPreviousCommand extends ActiveEditorCommand {
@@ -51,12 +55,12 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
} }
} }
if (args.commit.previousSha === undefined) return Messages.showCommitHasNoPreviousCommitWarningMessage(args.commit); if (args.commit.previousSha === undefined && !args.allowMissingPrevious) return Messages.showCommitHasNoPreviousCommitWarningMessage(args.commit);
try { try {
const [rhs, lhs] = await Promise.all([ const [rhs, lhs] = await Promise.all([
this.git.getVersionedFile(args.commit.repoPath, args.commit.uri.fsPath, args.commit.sha), this.git.getVersionedFile(args.commit.repoPath, args.commit.uri.fsPath, args.commit.sha),
this.git.getVersionedFile(args.commit.repoPath, args.commit.previousUri.fsPath, args.commit.previousSha) this.git.getVersionedFile(args.commit.repoPath, args.commit.previousUri.fsPath, args.commit.previousSha === undefined ? FakeSha : args.commit.previousSha)
]); ]);
if (args.line !== undefined && args.line !== 0) { if (args.line !== undefined && args.line !== 0) {
@@ -69,7 +73,9 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
await commands.executeCommand(BuiltInCommands.Diff, await commands.executeCommand(BuiltInCommands.Diff,
Uri.file(lhs), Uri.file(lhs),
Uri.file(rhs), Uri.file(rhs),
`${path.basename(args.commit.previousUri.fsPath)} (${args.commit.previousShortSha}) ${GlyphChars.ArrowLeftRight} ${path.basename(args.commit.uri.fsPath)} (${args.commit.shortSha})`, args.commit.previousShortSha === undefined
? `${path.basename(args.commit.uri.fsPath)} (${args.rightTitlePrefix || ''}${args.commit.shortSha})`
: `${path.basename(args.commit.previousUri.fsPath)} (${args.leftTitlePrefix || ''}${args.commit.previousShortSha}) ${GlyphChars.ArrowLeftRight} ${path.basename(args.commit.uri.fsPath)} (${args.rightTitlePrefix || ''}${args.commit.shortSha})`,
args.showOptions); args.showOptions);
} }
catch (ex) { catch (ex) {

View File

@@ -1,7 +1,7 @@
'use strict'; 'use strict';
import { Arrays } from '../system'; import { Arrays } from '../system';
import { commands, TextEditor, Uri, window } from 'vscode'; import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common'; import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithBranch } from './common';
import { GlyphChars } from '../constants'; import { GlyphChars } from '../constants';
import { GitService, GitUri } from '../gitService'; import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
@@ -10,6 +10,7 @@ import { OpenInRemoteCommandArgs } from './openInRemote';
export interface OpenBranchInRemoteCommandArgs { export interface OpenBranchInRemoteCommandArgs {
branch?: string; branch?: string;
remote?: string;
} }
export class OpenBranchInRemoteCommand extends ActiveEditorCommand { export class OpenBranchInRemoteCommand extends ActiveEditorCommand {
@@ -18,6 +19,18 @@ export class OpenBranchInRemoteCommand extends ActiveEditorCommand {
super(Commands.OpenBranchInRemote); super(Commands.OpenBranchInRemote);
} }
protected async preExecute(context: CommandContext, args: OpenBranchInRemoteCommandArgs = {}): Promise<any> {
if (isCommandViewContextWithBranch(context)) {
args = { ...args };
args.branch = context.node.branch.name;
args.remote = context.node.branch.getRemote();
return this.execute(context.editor, context.uri, args);
}
return this.execute(context.editor, context.uri, args);
}
async execute(editor?: TextEditor, uri?: Uri, args: OpenBranchInRemoteCommandArgs = {}) { async execute(editor?: TextEditor, uri?: Uri, args: OpenBranchInRemoteCommandArgs = {}) {
uri = getCommandUri(uri, editor); uri = getCommandUri(uri, editor);
@@ -47,6 +60,7 @@ export class OpenBranchInRemoteCommand extends ActiveEditorCommand {
type: 'branch', type: 'branch',
branch: args.branch branch: args.branch
}, },
remote: args.remote,
remotes remotes
} as OpenInRemoteCommandArgs); } as OpenInRemoteCommandArgs);
} }

View File

@@ -0,0 +1,53 @@
'use strict';
import { Arrays } from '../system';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithRemote } from './common';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { OpenInRemoteCommandArgs } from './openInRemote';
export interface OpenBranchesInRemoteCommandArgs {
remote?: string;
}
export class OpenBranchesInRemoteCommand extends ActiveEditorCommand {
constructor(private git: GitService) {
super(Commands.OpenBranchesInRemote);
}
protected async preExecute(context: CommandContext, args: OpenBranchesInRemoteCommandArgs = {}): Promise<any> {
if (isCommandViewContextWithRemote(context)) {
args = { ...args };
args.remote = context.node.remote.name;
return this.execute(context.editor, context.uri, args);
}
return this.execute(context.editor, context.uri, args);
}
async execute(editor?: TextEditor, uri?: Uri, args: OpenBranchesInRemoteCommandArgs = {}) {
uri = getCommandUri(uri, editor);
const gitUri = uri && await GitUri.fromUri(uri, this.git);
const repoPath = gitUri === undefined ? this.git.repoPath : gitUri.repoPath;
if (!repoPath) return undefined;
try {
const remotes = Arrays.uniqueBy(await this.git.getRemotes(repoPath), r => r.url, r => !!r.provider);
return commands.executeCommand(Commands.OpenInRemote, uri, {
resource: {
type: 'branches'
},
remote: args.remote,
remotes
} as OpenInRemoteCommandArgs);
}
catch (ex) {
Logger.error(ex, 'OpenBranchesInRemoteCommand');
return window.showErrorMessage(`Unable to open branches in remote provider. See output channel for more details`);
}
}
}

View File

@@ -1,12 +1,11 @@
'use strict'; 'use strict';
import { Arrays } from '../system'; import { Arrays } from '../system';
import { commands, TextEditor, Uri, window } from 'vscode'; import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, CommandContext, Commands, getCommandUri } from './common'; import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
import { GitBlameCommit, GitService, GitUri } from '../gitService'; import { GitBlameCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { Messages } from '../messages'; import { Messages } from '../messages';
import { OpenInRemoteCommandArgs } from './openInRemote'; import { OpenInRemoteCommandArgs } from './openInRemote';
import { CommitNode } from '../views/explorerNodes';
export interface OpenCommitInRemoteCommandArgs { export interface OpenCommitInRemoteCommandArgs {
sha?: string; sha?: string;
@@ -19,7 +18,7 @@ export class OpenCommitInRemoteCommand extends ActiveEditorCommand {
} }
protected async preExecute(context: CommandContext, args: OpenCommitInRemoteCommandArgs = {}): Promise<any> { protected async preExecute(context: CommandContext, args: OpenCommitInRemoteCommandArgs = {}): Promise<any> {
if (context.type === 'view' && context.node instanceof CommitNode) { if (isCommandViewContextWithCommit(context)) {
args = { ...args }; args = { ...args };
args.sha = context.node.commit.sha; args.sha = context.node.commit.sha;
return this.execute(context.editor, context.node.commit.uri, args); return this.execute(context.editor, context.node.commit.uri, args);

View File

@@ -1,7 +1,7 @@
'use strict'; 'use strict';
import { Arrays } from '../system'; import { Arrays } from '../system';
import { commands, Range, TextEditor, Uri, window } from 'vscode'; import { commands, Range, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common'; import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
import { GitService, GitUri } from '../gitService'; import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { OpenInRemoteCommandArgs } from './openInRemote'; import { OpenInRemoteCommandArgs } from './openInRemote';
@@ -16,6 +16,16 @@ export class OpenFileInRemoteCommand extends ActiveEditorCommand {
super(Commands.OpenFileInRemote); super(Commands.OpenFileInRemote);
} }
protected async preExecute(context: CommandContext, args: OpenFileInRemoteCommandArgs = {}): Promise<any> {
if (isCommandViewContextWithCommit(context)) {
args = { ...args };
args.range = false;
return this.execute(context.editor, context.node.commit.uri, args);
}
return this.execute(context.editor, context.uri, args);
}
async execute(editor?: TextEditor, uri?: Uri, args: OpenFileInRemoteCommandArgs = { range: true }) { async execute(editor?: TextEditor, uri?: Uri, args: OpenFileInRemoteCommandArgs = { range: true }) {
uri = getCommandUri(uri, editor); uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined; if (uri === undefined) return undefined;

View File

@@ -8,6 +8,7 @@ import { Logger } from '../logger';
import { CommandQuickPickItem, OpenRemoteCommandQuickPickItem, RemotesQuickPick } from '../quickPicks'; import { CommandQuickPickItem, OpenRemoteCommandQuickPickItem, RemotesQuickPick } from '../quickPicks';
export interface OpenInRemoteCommandArgs { export interface OpenInRemoteCommandArgs {
remote?: string;
remotes?: GitRemote[]; remotes?: GitRemote[];
resource?: RemoteResource; resource?: RemoteResource;
@@ -26,8 +27,17 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
args = { ...args }; args = { ...args };
if (args.remotes === undefined || args.resource === undefined) return undefined; if (args.remotes === undefined || args.resource === undefined) return undefined;
if (args.remote !== undefined) {
const remotes = args.remotes.filter(r => r.name === args.remote);
// Only filter if we get some results
if (remotes.length > 0) {
args.remotes = remotes;
}
}
try { try {
if (args.remotes.length === 1) { if (args.remotes.length === 1) {
this.ensureRemoteBranchName(args);
const command = new OpenRemoteCommandQuickPickItem(args.remotes[0], args.resource); const command = new OpenRemoteCommandQuickPickItem(args.remotes[0], args.resource);
return command.execute(); return command.execute();
} }
@@ -35,16 +45,7 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
let placeHolder = ''; let placeHolder = '';
switch (args.resource.type) { switch (args.resource.type) {
case 'branch': case 'branch':
// Check to see if the remote is in the branch this.ensureRemoteBranchName(args);
const index = args.resource.branch.indexOf('/');
if (index >= 0) {
const remoteName = args.resource.branch.substring(0, index);
const remote = args.remotes.find(r => r.name === remoteName);
if (remote !== undefined) {
args.resource.branch = args.resource.branch.substring(index + 1);
args.remotes = [remote];
}
}
placeHolder = `open ${args.resource.branch} branch in${GlyphChars.Ellipsis}`; placeHolder = `open ${args.resource.branch} branch in${GlyphChars.Ellipsis}`;
break; break;
@@ -54,6 +55,10 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
break; break;
case 'file': case 'file':
placeHolder = `open ${args.resource.fileName} in${GlyphChars.Ellipsis}`;
break;
case 'revision':
if (args.resource.commit !== undefined && args.resource.commit instanceof GitLogCommit) { if (args.resource.commit !== undefined && args.resource.commit instanceof GitLogCommit) {
if (args.resource.commit.status === 'D') { if (args.resource.commit.status === 'D') {
args.resource.sha = args.resource.commit.previousSha; args.resource.sha = args.resource.commit.previousSha;
@@ -71,10 +76,6 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
placeHolder = `open ${args.resource.fileName}${shaSuffix} in${GlyphChars.Ellipsis}`; placeHolder = `open ${args.resource.fileName}${shaSuffix} in${GlyphChars.Ellipsis}`;
} }
break; break;
case 'working-file':
placeHolder = `open ${args.resource.fileName} in${GlyphChars.Ellipsis}`;
break;
} }
if (args.remotes.length === 1) { if (args.remotes.length === 1) {
@@ -93,4 +94,19 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
return window.showErrorMessage(`Unable to open in remote provider. See output channel for more details`); return window.showErrorMessage(`Unable to open in remote provider. See output channel for more details`);
} }
} }
private ensureRemoteBranchName(args: OpenInRemoteCommandArgs) {
if (args.remotes === undefined || args.resource === undefined || args.resource.type !== 'branch') return;
// Check to see if the remote is in the branch
const index = args.resource.branch.indexOf('/');
if (index >= 0) {
const remoteName = args.resource.branch.substring(0, index);
const remote = args.remotes.find(r => r.name === remoteName);
if (remote !== undefined) {
args.resource.branch = args.resource.branch.substring(index + 1);
args.remotes = [remote];
}
}
}
} }

View File

@@ -1,18 +1,32 @@
'use strict'; 'use strict';
import { Arrays } from '../system'; import { Arrays } from '../system';
import { commands, TextEditor, Uri, window } from 'vscode'; import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common'; import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithRemote } from './common';
import { GitService, GitUri } from '../gitService'; import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { OpenInRemoteCommandArgs } from './openInRemote'; import { OpenInRemoteCommandArgs } from './openInRemote';
export interface OpenRepoInRemoteCommandArgs {
remote?: string;
}
export class OpenRepoInRemoteCommand extends ActiveEditorCommand { export class OpenRepoInRemoteCommand extends ActiveEditorCommand {
constructor(private git: GitService) { constructor(private git: GitService) {
super(Commands.OpenRepoInRemote); super(Commands.OpenRepoInRemote);
} }
async execute(editor?: TextEditor, uri?: Uri) { protected async preExecute(context: CommandContext, args: OpenRepoInRemoteCommandArgs = {}): Promise<any> {
if (isCommandViewContextWithRemote(context)) {
args = { ...args };
args.remote = context.node.remote.name;
return this.execute(context.editor, context.uri, args);
}
return this.execute(context.editor, context.uri, args);
}
async execute(editor?: TextEditor, uri?: Uri, args: OpenRepoInRemoteCommandArgs = {}) {
uri = getCommandUri(uri, editor); uri = getCommandUri(uri, editor);
const gitUri = uri && await GitUri.fromUri(uri, this.git); const gitUri = uri && await GitUri.fromUri(uri, this.git);
@@ -21,11 +35,13 @@ export class OpenRepoInRemoteCommand extends ActiveEditorCommand {
if (!repoPath) return undefined; if (!repoPath) return undefined;
try { try {
const remotes = Arrays.uniqueBy(await this.git.getRemotes(repoPath), _ => _.url, _ => !!_.provider); const remotes = Arrays.uniqueBy(await this.git.getRemotes(repoPath), r => r.url, r => !!r.provider);
return commands.executeCommand(Commands.OpenInRemote, uri, { return commands.executeCommand(Commands.OpenInRemote, uri, {
resource: { resource: {
type: 'repo' type: 'repo'
}, },
remote: args.remote,
remotes remotes
} as OpenInRemoteCommandArgs); } as OpenInRemoteCommandArgs);
} }

View File

@@ -8,7 +8,6 @@ import { Logger } from '../logger';
import { Messages } from '../messages'; import { Messages } from '../messages';
import { CommandQuickPickItem, CommitsQuickPick } from '../quickPicks'; import { CommandQuickPickItem, CommitsQuickPick } from '../quickPicks';
import { ShowQuickCommitDetailsCommandArgs } from './showQuickCommitDetails'; import { ShowQuickCommitDetailsCommandArgs } from './showQuickCommitDetails';
import { paste } from 'copy-paste';
const searchByRegex = /^([@:#])/; const searchByRegex = /^([@:#])/;
const searchByMap = new Map<string, GitRepoSearchBy>([ const searchByMap = new Map<string, GitRepoSearchBy>([
@@ -49,12 +48,6 @@ export class ShowCommitSearchCommand extends ActiveEditorCachedCommand {
args.search = `#${blameLine.commit.shortSha}`; args.search = `#${blameLine.commit.shortSha}`;
} }
} }
if (!args.search) {
args.search = await new Promise<string>((resolve, reject) => {
paste((err: Error, content: string) => resolve(err ? '' : content));
});
}
} }
} }
catch (ex) { catch (ex) {

View File

@@ -2,7 +2,7 @@
import { commands, Position, Range, TextEditor, TextEditorEdit, Uri, window } from 'vscode'; import { commands, Position, Range, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
import { Commands, EditorCommand, getCommandUri } from './common'; import { Commands, EditorCommand, getCommandUri } from './common';
import { BuiltInCommands } from '../constants'; import { BuiltInCommands } from '../constants';
import { GitExplorer } from '../views/gitExplorer'; // import { GitExplorer } from '../views/gitExplorer';
import { GitService, GitUri } from '../gitService'; import { GitService, GitUri } from '../gitService';
import { Messages } from '../messages'; import { Messages } from '../messages';
import { Logger } from '../logger'; import { Logger } from '../logger';
@@ -15,7 +15,7 @@ export interface ShowFileHistoryCommandArgs {
export class ShowFileHistoryCommand extends EditorCommand { export class ShowFileHistoryCommand extends EditorCommand {
constructor(private git: GitService, private explorer?: GitExplorer) { constructor(private git: GitService) {
super(Commands.ShowFileHistory); super(Commands.ShowFileHistory);
} }
@@ -33,10 +33,10 @@ export class ShowFileHistoryCommand extends EditorCommand {
const gitUri = await GitUri.fromUri(uri, this.git); const gitUri = await GitUri.fromUri(uri, this.git);
try { try {
if (this.explorer !== undefined) { // if (this.explorer !== undefined) {
this.explorer.addHistory(gitUri); // this.explorer.addHistory(gitUri);
return undefined; // return undefined;
} // }
const locations = await this.git.getLogLocations(gitUri, args.sha, args.line); const locations = await this.git.getLogLocations(gitUri, args.sha, args.line);
if (locations === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to show file history'); if (locations === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to show file history');

View File

@@ -1,9 +1,8 @@
'use strict'; 'use strict';
import { Strings } from '../system'; import { Strings } from '../system';
import { commands, TextEditor, Uri, window } from 'vscode'; import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, CommandContext, Commands, getCommandUri } from './common'; import { ActiveEditorCachedCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
import { GlyphChars } from '../constants'; import { GlyphChars } from '../constants';
import { CommitNode } from '../views/explorerNodes';
import { GitCommit, GitLog, GitLogCommit, GitService, GitUri } from '../gitService'; import { GitCommit, GitLog, GitLogCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { CommandQuickPickItem, CommitDetailsQuickPick, CommitWithFileStatusQuickPickItem } from '../quickPicks'; import { CommandQuickPickItem, CommitDetailsQuickPick, CommitWithFileStatusQuickPickItem } from '../quickPicks';
@@ -27,7 +26,7 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
protected async preExecute(context: CommandContext, ...args: any[]): Promise<any> { protected async preExecute(context: CommandContext, ...args: any[]): Promise<any> {
if (context.type === 'view') { if (context.type === 'view') {
if (context.node instanceof CommitNode) { if (isCommandViewContextWithCommit(context)) {
args = [{ sha: context.node.uri.sha, commit: context.node.commit }]; args = [{ sha: context.node.uri.sha, commit: context.node.commit }];
} }
else { else {

View File

@@ -1,7 +1,7 @@
'use strict'; 'use strict';
import { Strings } from '../system'; import { Strings } from '../system';
import { TextEditor, Uri, window } from 'vscode'; import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common'; import { ActiveEditorCachedCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
import { GlyphChars } from '../constants'; import { GlyphChars } from '../constants';
import { GitCommit, GitLog, GitLogCommit, GitService, GitUri } from '../gitService'; import { GitCommit, GitLog, GitLogCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
@@ -24,6 +24,18 @@ export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCachedCommand
super(Commands.ShowQuickCommitFileDetails); super(Commands.ShowQuickCommitFileDetails);
} }
protected async preExecute(context: CommandContext, ...args: any[]): Promise<any> {
if (context.type === 'view') {
if (isCommandViewContextWithCommit(context)) {
args = [{ sha: context.node.uri.sha, commit: context.node.commit }];
}
else {
args = [{ sha: context.node.uri.sha }];
}
}
return this.execute(context.editor, context.uri, ...args);
}
async execute(editor?: TextEditor, uri?: Uri, args: ShowQuickCommitFileDetailsCommandArgs = {}) { async execute(editor?: TextEditor, uri?: Uri, args: ShowQuickCommitFileDetailsCommandArgs = {}) {
uri = getCommandUri(uri, editor); uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined; if (uri === undefined) return undefined;

View File

@@ -1,13 +1,11 @@
'use strict'; 'use strict';
import { Strings } from '../system'; import { Strings } from '../system';
import { MessageItem, window } from 'vscode'; import { MessageItem, window } from 'vscode';
import { GitService, GitStashCommit } from '../gitService'; import { Command, CommandContext, Commands, isCommandViewContextWithCommit } from './common';
import { Command, CommandContext, Commands } from './common';
import { GlyphChars } from '../constants'; import { GlyphChars } from '../constants';
import { CommitQuickPickItem, StashListQuickPick } from '../quickPicks'; import { GitService, GitStashCommit } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { CommandQuickPickItem } from '../quickPicks'; import { CommandQuickPickItem, CommitQuickPickItem, StashListQuickPick } from '../quickPicks';
import { StashCommitNode } from '../views/stashCommitNode';
export interface StashApplyCommandArgs { export interface StashApplyCommandArgs {
confirm?: boolean; confirm?: boolean;
@@ -24,12 +22,9 @@ export class StashApplyCommand extends Command {
} }
protected async preExecute(context: CommandContext, args: StashApplyCommandArgs = { confirm: true, deleteAfter: false }) { protected async preExecute(context: CommandContext, args: StashApplyCommandArgs = { confirm: true, deleteAfter: false }) {
if (context.type === 'view' && context.node instanceof StashCommitNode) { if (isCommandViewContextWithCommit<GitStashCommit>(context)) {
args = { ...args }; args = { ...args };
args.stashItem = { stashName: context.node.commit.stashName, message: context.node.commit.message };
const stash = context.node.commit;
args.stashItem = { stashName: stash.stashName, message: stash.message };
return this.execute(args); return this.execute(args);
} }

View File

@@ -1,11 +1,10 @@
'use strict'; 'use strict';
import { MessageItem, window } from 'vscode'; import { MessageItem, window } from 'vscode';
import { Command, CommandContext, Commands } from './common'; import { Command, CommandContext, Commands, isCommandViewContextWithCommit } from './common';
import { GlyphChars } from '../constants'; import { GlyphChars } from '../constants';
import { GitService } from '../gitService'; import { GitService, GitStashCommit } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { CommandQuickPickItem } from '../quickPicks'; import { CommandQuickPickItem } from '../quickPicks';
import { StashCommitNode } from '../views/stashCommitNode';
export interface StashDeleteCommandArgs { export interface StashDeleteCommandArgs {
confirm?: boolean; confirm?: boolean;
@@ -21,12 +20,9 @@ export class StashDeleteCommand extends Command {
} }
protected async preExecute(context: CommandContext, args: StashDeleteCommandArgs = { confirm: true }) { protected async preExecute(context: CommandContext, args: StashDeleteCommandArgs = { confirm: true }) {
if (context.type === 'view' && context.node instanceof StashCommitNode) { if (isCommandViewContextWithCommit<GitStashCommit>(context)) {
args = { ...args }; args = { ...args };
args.stashItem = { stashName: context.node.commit.stashName, message: context.node.commit.message };
const stash = context.node.commit;
args.stashItem = { stashName: stash.stashName, message: stash.message };
return this.execute(args); return this.execute(args);
} }

View File

@@ -1,13 +1,14 @@
'use strict'; 'use strict';
import { InputBoxOptions, window } from 'vscode'; import { InputBoxOptions, Uri, window } from 'vscode';
import { GitService } from '../gitService'; import { GitService } from '../gitService';
import { CommandContext } from '../commands';
import { Command, Commands } from './common'; import { Command, Commands } from './common';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { CommandQuickPickItem } from '../quickPicks'; import { CommandQuickPickItem } from '../quickPicks';
export interface StashSaveCommandArgs { export interface StashSaveCommandArgs {
message?: string; message?: string;
unstagedOnly?: boolean; uris?: Uri[];
goBackCommand?: CommandQuickPickItem; goBackCommand?: CommandQuickPickItem;
} }
@@ -18,16 +19,28 @@ export class StashSaveCommand extends Command {
super(Commands.StashSave); super(Commands.StashSave);
} }
async execute(args: StashSaveCommandArgs = { unstagedOnly: false }) { protected async preExecute(context: CommandContext, args: StashSaveCommandArgs = {}): Promise<any> {
if (!this.git.repoPath) return undefined; if (context.type === 'scm-states') {
args = { ...args };
args = { ...args }; args.uris = context.scmResourceStates.map(s => s.resourceUri);
if (args.unstagedOnly === undefined) { return this.execute(args);
args.unstagedOnly = false;
} }
if (context.type === 'scm-groups') {
args = { ...args };
args.uris = context.scmResourceGroups.reduce<Uri[]>((a, b) => a.concat(b.resourceStates.map(s => s.resourceUri)), []);
return this.execute(args);
}
return this.execute(args);
}
async execute(args: StashSaveCommandArgs = { }) {
if (!this.git.repoPath) return undefined;
try { try {
if (args.message == null) { if (args.message == null) {
args = { ...args };
args.message = await window.showInputBox({ args.message = await window.showInputBox({
prompt: `Please provide a stash message`, prompt: `Please provide a stash message`,
placeHolder: `Stash message` placeHolder: `Stash message`
@@ -35,7 +48,7 @@ export class StashSaveCommand extends Command {
if (args.message === undefined) return args.goBackCommand === undefined ? undefined : args.goBackCommand.execute(); if (args.message === undefined) return args.goBackCommand === undefined ? undefined : args.goBackCommand.execute();
} }
return await this.git.stashSave(this.git.repoPath, args.message, args.unstagedOnly); return await this.git.stashSave(this.git.repoPath, args.message, args.uris);
} }
catch (ex) { catch (ex) {
Logger.error(ex, 'StashSaveCommand'); Logger.error(ex, 'StashSaveCommand');

View File

@@ -2,6 +2,7 @@
import { FileAnnotationType } from './annotations/annotationController'; import { FileAnnotationType } from './annotations/annotationController';
import { Commands } from './commands'; import { Commands } from './commands';
import { LineAnnotationType } from './currentLineController'; import { LineAnnotationType } from './currentLineController';
import { GitExplorerView } from './views/gitExplorer';
import { OutputLevel } from './logger'; import { OutputLevel } from './logger';
export { ExtensionKey } from './constants'; export { ExtensionKey } from './constants';
@@ -296,19 +297,11 @@ export interface IConfig {
defaultDateFormat: string | null; defaultDateFormat: string | null;
fileHistoryExplorer: {
commitFormat: string;
// commitFileFormat: string;
// dateFormat: string | null;
};
gitExplorer: { gitExplorer: {
view: GitExplorerView;
showTrackingBranch: boolean;
commitFormat: string; commitFormat: string;
commitFileFormat: string; commitFileFormat: string;
// dateFormat: string | null;
};
stashExplorer: {
stashFormat: string; stashFormat: string;
stashFileFormat: string; stashFileFormat: string;
// dateFormat: string | null; // dateFormat: string | null;

View File

@@ -8,6 +8,8 @@ export const QualifiedExtensionId = `eamodio.${ExtensionId}`;
export const ApplicationInsightsKey = 'a9c302f8-6483-4d01-b92c-c159c799c679'; export const ApplicationInsightsKey = 'a9c302f8-6483-4d01-b92c-c159c799c679';
export const FakeSha = 'ffffffffffffffffffffffffffffffffffffffff';
export type BuiltInCommands = 'cursorMove' | export type BuiltInCommands = 'cursorMove' |
'editor.action.showReferences' | 'editor.action.showReferences' |
'editor.action.toggleRenderWhitespace' | 'editor.action.toggleRenderWhitespace' |
@@ -40,23 +42,25 @@ export const BuiltInCommands = {
}; };
export type CommandContext = export type CommandContext =
'gitlens:annotationStatus' |
'gitlens:canToggleCodeLens' | 'gitlens:canToggleCodeLens' |
'gitlens:enabled' | 'gitlens:enabled' |
'gitlens:hasRemotes' | 'gitlens:hasRemotes' |
'gitlens:gitExplorer:view' |
'gitlens:isBlameable' | 'gitlens:isBlameable' |
'gitlens:isRepository' | 'gitlens:isRepository' |
'gitlens:isTracked' | 'gitlens:isTracked' |
'gitlens:key' | 'gitlens:key';
'gitlens:annotationStatus';
export const CommandContext = { export const CommandContext = {
AnnotationStatus: 'gitlens:annotationStatus' as CommandContext,
CanToggleCodeLens: 'gitlens:canToggleCodeLens' as CommandContext, CanToggleCodeLens: 'gitlens:canToggleCodeLens' as CommandContext,
Enabled: 'gitlens:enabled' as CommandContext, Enabled: 'gitlens:enabled' as CommandContext,
GitExplorerView: 'gitlens:gitExplorer:view' as CommandContext,
HasRemotes: 'gitlens:hasRemotes' as CommandContext, HasRemotes: 'gitlens:hasRemotes' as CommandContext,
IsBlameable: 'gitlens:isBlameable' as CommandContext, IsBlameable: 'gitlens:isBlameable' as CommandContext,
IsRepository: 'gitlens:isRepository' as CommandContext, IsRepository: 'gitlens:isRepository' as CommandContext,
IsTracked: 'gitlens:isTracked' as CommandContext, IsTracked: 'gitlens:isTracked' as CommandContext,
Key: 'gitlens:key' as CommandContext, Key: 'gitlens:key' as CommandContext
AnnotationStatus: 'gitlens:annotationStatus' as CommandContext
}; };
export function setCommandContext(key: CommandContext | string, value: any) { export function setCommandContext(key: CommandContext | string, value: any) {
@@ -77,6 +81,7 @@ export type GlyphChars = '\u21a9' |
'\u2194' | '\u2194' |
'\u21e8' | '\u21e8' |
'\u2191' | '\u2191' |
'\u2713' |
'\u2014' | '\u2014' |
'\u2022' | '\u2022' |
'\u2026' | '\u2026' |
@@ -90,6 +95,7 @@ export const GlyphChars = {
ArrowLeftRight: '\u2194' as GlyphChars, ArrowLeftRight: '\u2194' as GlyphChars,
ArrowRightHollow: '\u21e8' as GlyphChars, ArrowRightHollow: '\u21e8' as GlyphChars,
ArrowUp: '\u2191' as GlyphChars, ArrowUp: '\u2191' as GlyphChars,
Check: '\u2713' as GlyphChars,
Dash: '\u2014' as GlyphChars, Dash: '\u2014' as GlyphChars,
Dot: '\u2022' as GlyphChars, Dot: '\u2022' as GlyphChars,
Ellipsis: '\u2026' as GlyphChars, Ellipsis: '\u2026' as GlyphChars,

View File

@@ -476,11 +476,11 @@ export class CurrentLineController extends Disposable {
break; break;
case StatusBarCommand.DiffWithPrevious: case StatusBarCommand.DiffWithPrevious:
this._statusBarItem.command = Commands.DiffLineWithPrevious; this._statusBarItem.command = Commands.DiffLineWithPrevious;
this._statusBarItem.tooltip = 'Compare Line Commit with Previous'; this._statusBarItem.tooltip = 'Compare Line Revision with Previous';
break; break;
case StatusBarCommand.DiffWithWorking: case StatusBarCommand.DiffWithWorking:
this._statusBarItem.command = Commands.DiffLineWithWorking; this._statusBarItem.command = Commands.DiffLineWithWorking;
this._statusBarItem.tooltip = 'Compare Line Commit with Working Tree'; this._statusBarItem.tooltip = 'Compare Line Revision with Working';
break; break;
case StatusBarCommand.ToggleCodeLens: case StatusBarCommand.ToggleCodeLens:
this._statusBarItem.tooltip = 'Toggle Git CodeLens'; this._statusBarItem.tooltip = 'Toggle Git CodeLens';

View File

@@ -3,7 +3,7 @@
import { commands, ExtensionContext, extensions, languages, window, workspace } from 'vscode'; import { commands, ExtensionContext, extensions, languages, window, workspace } from 'vscode';
import { AnnotationController } from './annotations/annotationController'; import { AnnotationController } from './annotations/annotationController';
import { CloseUnchangedFilesCommand, OpenChangedFilesCommand } from './commands'; import { CloseUnchangedFilesCommand, OpenChangedFilesCommand } from './commands';
import { OpenBranchInRemoteCommand, OpenCommitInRemoteCommand, OpenFileInRemoteCommand, OpenInRemoteCommand, OpenRepoInRemoteCommand } from './commands'; import { OpenBranchesInRemoteCommand, OpenBranchInRemoteCommand, OpenCommitInRemoteCommand, OpenFileInRemoteCommand, OpenInRemoteCommand, OpenRepoInRemoteCommand } from './commands';
import { CopyMessageToClipboardCommand, CopyShaToClipboardCommand } from './commands'; import { CopyMessageToClipboardCommand, CopyShaToClipboardCommand } from './commands';
import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithBranchCommand, DiffWithNextCommand, DiffWithPreviousCommand, DiffWithRevisionCommand, DiffWithWorkingCommand } from './commands'; import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithBranchCommand, DiffWithNextCommand, DiffWithPreviousCommand, DiffWithRevisionCommand, DiffWithWorkingCommand } from './commands';
import { ResetSuppressedWarningsCommand } from './commands'; import { ResetSuppressedWarningsCommand } from './commands';
@@ -20,9 +20,7 @@ import { ApplicationInsightsKey, CommandContext, ExtensionKey, QualifiedExtensio
import { CodeLensController } from './codeLensController'; import { CodeLensController } from './codeLensController';
import { CurrentLineController, LineAnnotationType } from './currentLineController'; import { CurrentLineController, LineAnnotationType } from './currentLineController';
import { GitContentProvider } from './gitContentProvider'; import { GitContentProvider } from './gitContentProvider';
// import { GitExplorer } from './views/gitExplorer'; import { GitExplorer } from './views/gitExplorer';
import { FileHistoryExplorer } from './views/fileHistoryExplorer';
import { StashExplorer } from './views/stashExplorer';
import { GitRevisionCodeLensProvider } from './gitRevisionCodeLensProvider'; import { GitRevisionCodeLensProvider } from './gitRevisionCodeLensProvider';
import { GitContextTracker, GitService } from './gitService'; import { GitContextTracker, GitService } from './gitService';
import { Keyboard } from './keyboard'; import { Keyboard } from './keyboard';
@@ -94,11 +92,7 @@ export async function activate(context: ExtensionContext) {
context.subscriptions.push(new Keyboard()); context.subscriptions.push(new Keyboard());
// const explorer = new GitExplorer(context, git); context.subscriptions.push(window.registerTreeDataProvider('gitlens.gitExplorer', new GitExplorer(context, git)));
// context.subscriptions.push(window.registerTreeDataProvider('gitlens.gitExplorer', explorer));
context.subscriptions.push(window.registerTreeDataProvider('gitlens.fileHistoryExplorer', new FileHistoryExplorer(context, git)));
context.subscriptions.push(window.registerTreeDataProvider('gitlens.stashExplorer', new StashExplorer(context, git)));
context.subscriptions.push(commands.registerTextEditorCommand('gitlens.computingFileAnnotations', () => { })); context.subscriptions.push(commands.registerTextEditorCommand('gitlens.computingFileAnnotations', () => { }));
@@ -114,6 +108,7 @@ export async function activate(context: ExtensionContext) {
context.subscriptions.push(new DiffWithPreviousCommand(git)); context.subscriptions.push(new DiffWithPreviousCommand(git));
context.subscriptions.push(new DiffWithRevisionCommand(git)); context.subscriptions.push(new DiffWithRevisionCommand(git));
context.subscriptions.push(new DiffWithWorkingCommand(git)); context.subscriptions.push(new DiffWithWorkingCommand(git));
context.subscriptions.push(new OpenBranchesInRemoteCommand(git));
context.subscriptions.push(new OpenBranchInRemoteCommand(git)); context.subscriptions.push(new OpenBranchInRemoteCommand(git));
context.subscriptions.push(new OpenCommitInRemoteCommand(git)); context.subscriptions.push(new OpenCommitInRemoteCommand(git));
context.subscriptions.push(new OpenFileInRemoteCommand(git)); context.subscriptions.push(new OpenFileInRemoteCommand(git));
@@ -279,10 +274,13 @@ async function notifyOnNewGitLensVersion(context: ExtensionContext, version: str
const previousVersion = context.globalState.get<string>(WorkspaceState.GitLensVersion); const previousVersion = context.globalState.get<string>(WorkspaceState.GitLensVersion);
if (previousVersion === undefined) { if (previousVersion === undefined) {
Logger.log(`GitLens first-time install`);
await Messages.showWelcomeMessage(); await Messages.showWelcomeMessage();
return; return;
} }
Logger.log(`GitLens upgraded from v${previousVersion} to v${version}`);
const [major, minor] = version.split('.'); const [major, minor] = version.split('.');
const [prevMajor, prevMinor] = previousVersion.split('.'); const [prevMajor, prevMinor] = previousVersion.split('.');
if (major === prevMajor && minor === prevMinor) return; if (major === prevMajor && minor === prevMinor) return;

View File

@@ -10,6 +10,7 @@ import * as iconv from 'iconv-lite';
export { IGit }; export { IGit };
export * from './models/models'; export * from './models/models';
export * from './parsers/blameParser'; export * from './parsers/blameParser';
export * from './parsers/branchParser';
export * from './parsers/diffParser'; export * from './parsers/diffParser';
export * from './parsers/logParser'; export * from './parsers/logParser';
export * from './parsers/stashParser'; export * from './parsers/stashParser';
@@ -31,10 +32,13 @@ const GitWarnings = [
/Not a git repository/, /Not a git repository/,
/is outside repository/, /is outside repository/,
/no such path/, /no such path/,
/does not have any commits/ /does not have any commits/,
/Path \'.*?\' does not exist in/,
/Path \'.*?\' exists on disk, but not in/,
/no upstream configured for branch/
]; ];
async function gitCommand(options: { cwd: string, encoding?: string }, ...args: any[]) { async function gitCommand(options: { cwd: string, encoding?: string, onError?: (ex: Error) => string | undefined }, ...args: any[]) {
try { try {
// Fixes https://github.com/eamodio/vscode-gitlens/issues/73 // Fixes https://github.com/eamodio/vscode-gitlens/issues/73
// See https://stackoverflow.com/questions/4144417/how-to-handle-asian-characters-in-file-names-in-git-on-os-x // See https://stackoverflow.com/questions/4144417/how-to-handle-asian-characters-in-file-names-in-git-on-os-x
@@ -48,6 +52,11 @@ async function gitCommand(options: { cwd: string, encoding?: string }, ...args:
return iconv.decode(Buffer.from(s, 'binary'), opts.encoding); return iconv.decode(Buffer.from(s, 'binary'), opts.encoding);
} }
catch (ex) { catch (ex) {
if (options.onError !== undefined) {
const result = options.onError(ex);
if (result !== undefined) return result;
}
const msg = ex && ex.toString(); const msg = ex && ex.toString();
if (msg) { if (msg) {
for (const warning of GitWarnings) { for (const warning of GitWarnings) {
@@ -166,15 +175,33 @@ export class Git {
return gitCommand({ cwd: root }, ...params, `--`, file); return gitCommand({ cwd: root }, ...params, `--`, file);
} }
static branch(repoPath: string, all: boolean) { static branch(repoPath: string, options: { all: boolean } = { all: false }) {
const params = [`branch`]; const params = [`branch`, `-vv`];
if (all) { if (options.all) {
params.push(`-a`); params.push(`-a`);
} }
return gitCommand({ cwd: repoPath }, ...params); return gitCommand({ cwd: repoPath }, ...params);
} }
static branch_current(repoPath: string) {
const params = [`rev-parse`, `--abbrev-ref`, `--symbolic-full-name`, `@`, `@{u}`];
const onError = (ex: Error) => {
if (/no upstream configured for branch/.test(ex && ex.toString())) {
return ex.message.split('\n')[0];
}
return undefined;
};
return gitCommand({ cwd: repoPath, onError: onError }, ...params);
}
static checkout(repoPath: string, fileName: string, sha: string) {
const [file, root] = Git.splitPath(fileName, repoPath);
return gitCommand({ cwd: root }, `checkout`, sha, `--`, file);
}
static async config_get(key: string, repoPath?: string) { static async config_get(key: string, repoPath?: string) {
try { try {
return await gitCommand({ cwd: repoPath || '' }, `config`, `--get`, key); return await gitCommand({ cwd: repoPath || '' }, `config`, `--get`, key);
@@ -320,11 +347,18 @@ export class Git {
return gitCommand({ cwd: repoPath }, ...defaultStashParams); return gitCommand({ cwd: repoPath }, ...defaultStashParams);
} }
static stash_save(repoPath: string, message?: string, unstagedOnly: boolean = false) { static stash_push(repoPath: string, pathspecs: string[], message?: string) {
const params = [`stash`, `save`, `--include-untracked`]; const params = [`stash`, `push`, `-u`];
if (unstagedOnly) { if (message) {
params.push(`--keep-index`); params.push(`-m`);
params.push(message);
} }
params.splice(params.length, 0, `--`, ...pathspecs);
return gitCommand({ cwd: repoPath }, ...params);
}
static stash_save(repoPath: string, message?: string) {
const params = [`stash`, `save`, `-u`];
if (message) { if (message) {
params.push(message); params.push(message);
} }

View File

@@ -5,15 +5,9 @@ export class GitBranch {
current: boolean; current: boolean;
name: string; name: string;
remote: boolean; remote: boolean;
tracking?: string;
constructor(branch: string) { constructor(public readonly repoPath: string, branch: string, current: boolean = false, tracking?: string) {
branch = branch.trim();
if (branch.startsWith('* ')) {
branch = branch.substring(2);
this.current = true;
}
if (branch.startsWith('remotes/')) { if (branch.startsWith('remotes/')) {
branch = branch.substring(8); branch = branch.substring(8);
this.remote = true; this.remote = true;
@@ -24,6 +18,24 @@ export class GitBranch {
branch = branch.substring(0, index); branch = branch.substring(0, index);
} }
this.current = current;
this.name = branch; this.name = branch;
this.tracking = tracking;
}
getName(): string {
return this.remote
? this.name.substring(this.name.indexOf('/') + 1)
: this.name;
}
getRemote(): string | undefined {
if (this.remote) return GitBranch.getRemote(this.name);
if (this.tracking !== undefined) return GitBranch.getRemote(this.tracking);
return undefined;
}
static getRemote(branch: string): string {
return branch.substring(0, branch.indexOf('/'));
} }
} }

View File

@@ -0,0 +1,24 @@
'use strict';
import { GitBranch } from './../git';
const branchWithTrackingRegex = /^(\*?)\s+(.+?)\s+([0-9,a-f]+)\s+(?:\[(.*?\/.*?)(?:\:|\]))?/gm;
export class GitBranchParser {
static parse(data: string, repoPath: string): GitBranch[] | undefined {
if (!data) return undefined;
const branches: GitBranch[] = [];
let match: RegExpExecArray | null = null;
do {
match = branchWithTrackingRegex.exec(data);
if (match == null) break;
branches.push(new GitBranch(repoPath, match[2], match[1] === '*', match[4]));
} while (match != null);
if (!branches.length) return undefined;
return branches;
}
}

View File

@@ -12,6 +12,10 @@ export class BitbucketService extends RemoteProvider {
return 'Bitbucket'; return 'Bitbucket';
} }
protected getUrlForBranches(): string {
return `${this.baseUrl}/branches`;
}
protected getUrlForBranch(branch: string): string { protected getUrlForBranch(branch: string): string {
return `${this.baseUrl}/commits/branch/${branch}`; return `${this.baseUrl}/commits/branch/${branch}`;
} }

View File

@@ -12,6 +12,10 @@ export class GitHubService extends RemoteProvider {
return 'GitHub'; return 'GitHub';
} }
protected getUrlForBranches(): string {
return `${this.baseUrl}/branches`;
}
protected getUrlForBranch(branch: string): string { protected getUrlForBranch(branch: string): string {
return `${this.baseUrl}/commits/${branch}`; return `${this.baseUrl}/commits/${branch}`;
} }

View File

@@ -3,20 +3,23 @@ import { commands, Range, Uri } from 'vscode';
import { BuiltInCommands } from '../../constants'; import { BuiltInCommands } from '../../constants';
import { GitLogCommit } from '../../gitService'; import { GitLogCommit } from '../../gitService';
export type RemoteResourceType = 'branch' | 'commit' | 'file' | 'repo' | 'working-file'; export type RemoteResourceType = 'branch' | 'branches' | 'commit' | 'file' | 'repo' | 'revision';
export type RemoteResource = { type: 'branch', branch: string } | export type RemoteResource =
{ type: 'branch', branch: string } |
{ type: 'branches' } |
{ type: 'commit', sha: string } | { type: 'commit', sha: string } |
{ type: 'file', branch?: string, commit?: GitLogCommit, fileName: string, range?: Range, sha?: string } | { type: 'file', branch?: string, fileName: string, range?: Range } |
{ type: 'repo' } | { type: 'repo' } |
{ type: 'working-file', branch?: string, fileName: string, range?: Range }; { type: 'revision', branch?: string, commit?: GitLogCommit, fileName: string, range?: Range, sha?: string };
export function getNameFromRemoteResource(resource: RemoteResource) { export function getNameFromRemoteResource(resource: RemoteResource) {
switch (resource.type) { switch (resource.type) {
case 'branch': return 'Branch'; case 'branch': return 'Branch';
case 'branches': return 'Branches';
case 'commit': return 'Commit'; case 'commit': return 'Commit';
case 'file': return 'File'; case 'file': return 'File';
case 'repo': return 'Repository'; case 'repo': return 'Repository';
case 'working-file': return 'Working File'; case 'revision': return 'Revision';
default: return ''; default: return '';
} }
} }
@@ -31,6 +34,7 @@ export abstract class RemoteProvider {
return `https://${this.domain}/${this.path}`; return `https://${this.domain}/${this.path}`;
} }
protected abstract getUrlForBranches(): string;
protected abstract getUrlForBranch(branch: string): string; protected abstract getUrlForBranch(branch: string): string;
protected abstract getUrlForCommit(sha: string): string; protected abstract getUrlForCommit(sha: string): string;
protected abstract getUrlForFile(fileName: string, branch?: string, sha?: string, range?: Range): string; protected abstract getUrlForFile(fileName: string, branch?: string, sha?: string, range?: Range): string;
@@ -43,16 +47,12 @@ export abstract class RemoteProvider {
open(resource: RemoteResource): Promise<{} | undefined> { open(resource: RemoteResource): Promise<{} | undefined> {
switch (resource.type) { switch (resource.type) {
case 'branch': case 'branch': return this.openBranch(resource.branch);
return this.openBranch(resource.branch); case 'branches': return this.openBranches();
case 'commit': case 'commit': return this.openCommit(resource.sha);
return this.openCommit(resource.sha); case 'file': return this.openFile(resource.fileName, resource.branch, undefined, resource.range);
case 'file': case 'repo': return this.openRepo();
return this.openFile(resource.fileName, resource.branch, resource.sha, resource.range); case 'revision': return this.openFile(resource.fileName, resource.branch, resource.sha, resource.range);
case 'repo':
return this.openRepo();
case 'working-file':
return this.openFile(resource.fileName, resource.branch, undefined, resource.range);
} }
} }
@@ -60,6 +60,10 @@ export abstract class RemoteProvider {
return this._openUrl(this.baseUrl); return this._openUrl(this.baseUrl);
} }
openBranches() {
return this._openUrl(this.getUrlForBranches());
}
openBranch(branch: string) { openBranch(branch: string) {
return this._openUrl(this.getUrlForBranch(branch)); return this._openUrl(this.getUrlForBranch(branch));
} }

View File

@@ -12,6 +12,10 @@ export class VisualStudioService extends RemoteProvider {
return 'Visual Studio Team Services'; return 'Visual Studio Team Services';
} }
protected getUrlForBranches(): string {
return `${this.baseUrl}/branches`;
}
protected getUrlForBranch(branch: string): string { protected getUrlForBranch(branch: string): string {
return `${this.baseUrl}/?version=GB${branch}&_a=history`; return `${this.baseUrl}/?version=GB${branch}&_a=history`;
} }

View File

@@ -1,5 +1,5 @@
'use strict'; 'use strict';
import { ExtensionContext, TextDocumentContentProvider, Uri, window } from 'vscode'; import { CancellationToken, ExtensionContext, TextDocumentContentProvider, Uri, window } from 'vscode';
import { DocumentSchemes } from './constants'; import { DocumentSchemes } from './constants';
import { GitService } from './gitService'; import { GitService } from './gitService';
import { Logger } from './logger'; import { Logger } from './logger';
@@ -11,7 +11,7 @@ export class GitContentProvider implements TextDocumentContentProvider {
constructor(context: ExtensionContext, private git: GitService) { } constructor(context: ExtensionContext, private git: GitService) { }
async provideTextDocumentContent(uri: Uri): Promise<string> { async provideTextDocumentContent(uri: Uri, token: CancellationToken): Promise<string | undefined> {
const data = GitService.fromGitContentUri(uri); const data = GitService.fromGitContentUri(uri);
const fileName = data.originalFileName || data.fileName; const fileName = data.originalFileName || data.fileName;
try { try {
@@ -23,8 +23,8 @@ export class GitContentProvider implements TextDocumentContentProvider {
} }
catch (ex) { catch (ex) {
Logger.error(ex, 'GitContentProvider', 'getVersionedFileText'); Logger.error(ex, 'GitContentProvider', 'getVersionedFileText');
await window.showErrorMessage(`Unable to show Git revision ${data.sha.substring(0, 8)} of '${path.relative(data.repoPath, fileName)}'`); window.showErrorMessage(`Unable to show Git revision ${data.sha.substring(0, 8)} of '${path.relative(data.repoPath, fileName)}'`);
return ''; return undefined;
} }
} }
} }

View File

@@ -32,13 +32,12 @@ export class GitRevisionCodeLensProvider implements CodeLensProvider {
const lenses: CodeLens[] = []; const lenses: CodeLens[] = [];
const commit = await this.git.getLogCommit(gitUri.repoPath, gitUri.fsPath, gitUri.sha, { firstIfMissing: true, previous: true }); const commit = await this.git.getLogCommit(gitUri.repoPath, gitUri.fsPath, gitUri.sha, { firstIfMissing: true, previous: true });
if (!commit) return lenses; if (commit === undefined) return lenses;
lenses.push(new GitDiffWithWorkingCodeLens(this.git, commit.uri.fsPath, commit, new Range(0, 0, 0, 1)));
if (commit.previousSha) { if (commit.previousSha) {
lenses.push(new GitDiffWithPreviousCodeLens(this.git, commit.previousUri.fsPath, commit, new Range(0, 1, 0, 2))); lenses.push(new GitDiffWithPreviousCodeLens(this.git, commit.previousUri.fsPath, commit, new Range(0, 0, 0, 1)));
} }
lenses.push(new GitDiffWithWorkingCodeLens(this.git, commit.uri.fsPath, commit, new Range(0, 1, 0, 2)));
return lenses; return lenses;
} }
@@ -51,7 +50,7 @@ export class GitRevisionCodeLensProvider implements CodeLensProvider {
_resolveDiffWithWorkingTreeCodeLens(lens: GitDiffWithWorkingCodeLens, token: CancellationToken): Thenable<CodeLens> { _resolveDiffWithWorkingTreeCodeLens(lens: GitDiffWithWorkingCodeLens, token: CancellationToken): Thenable<CodeLens> {
lens.command = { lens.command = {
title: `Compare ${lens.commit.shortSha} with Working Tree`, title: `Compare Revision (${lens.commit.shortSha}) with Working`,
command: Commands.DiffWithWorking, command: Commands.DiffWithWorking,
arguments: [ arguments: [
Uri.file(lens.fileName), Uri.file(lens.fileName),
@@ -66,7 +65,7 @@ export class GitRevisionCodeLensProvider implements CodeLensProvider {
_resolveGitDiffWithPreviousCodeLens(lens: GitDiffWithPreviousCodeLens, token: CancellationToken): Thenable<CodeLens> { _resolveGitDiffWithPreviousCodeLens(lens: GitDiffWithPreviousCodeLens, token: CancellationToken): Thenable<CodeLens> {
lens.command = { lens.command = {
title: `Compare ${lens.commit.shortSha} with Previous ${lens.commit.previousShortSha}`, title: `Compare Revision (${lens.commit.shortSha}) with Previous (${lens.commit.previousShortSha})`,
command: Commands.DiffWithPrevious, command: Commands.DiffWithPrevious,
arguments: [ arguments: [
Uri.file(lens.fileName), Uri.file(lens.fileName),

View File

@@ -3,7 +3,7 @@ import { Functions, Iterables, Objects } from './system';
import { Disposable, Event, EventEmitter, FileSystemWatcher, Location, Position, Range, TextDocument, TextDocumentChangeEvent, TextEditor, Uri, workspace } from 'vscode'; import { Disposable, Event, EventEmitter, FileSystemWatcher, Location, Position, Range, TextDocument, TextDocumentChangeEvent, TextEditor, Uri, workspace } from 'vscode';
import { IConfig } from './configuration'; import { IConfig } from './configuration';
import { DocumentSchemes, ExtensionKey, GlyphChars } from './constants'; import { DocumentSchemes, ExtensionKey, GlyphChars } from './constants';
import { Git, GitAuthor, GitBlame, GitBlameCommit, GitBlameLine, GitBlameLines, GitBlameParser, GitBranch, GitCommit, GitDiff, GitDiffChunkLine, GitDiffParser, GitLog, GitLogCommit, GitLogParser, GitRemote, GitStash, GitStashParser, GitStatus, GitStatusFile, GitStatusParser, IGit, setDefaultEncoding } from './git/git'; import { Git, GitAuthor, GitBlame, GitBlameCommit, GitBlameLine, GitBlameLines, GitBlameParser, GitBranch, GitBranchParser, GitCommit, GitDiff, GitDiffChunkLine, GitDiffParser, GitLog, GitLogCommit, GitLogParser, GitRemote, GitStash, GitStashParser, GitStatus, GitStatusFile, GitStatusParser, IGit, setDefaultEncoding } from './git/git';
import { GitUri, IGitCommitInfo, IGitUriData } from './git/gitUri'; import { GitUri, IGitCommitInfo, IGitUriData } from './git/gitUri';
import { Logger } from './logger'; import { Logger } from './logger';
import * as fs from 'fs'; import * as fs from 'fs';
@@ -64,7 +64,11 @@ export const GitRepoSearchBy = {
Sha: 'sha' as GitRepoSearchBy Sha: 'sha' as GitRepoSearchBy
}; };
type RepoChangedReasons = 'stash' | 'unknown'; export type RepoChangedReasons = 'stash' | 'unknown';
export const RepoChangedReasons = {
Stash: 'stash' as RepoChangedReasons,
Unknown: 'unknown' as RepoChangedReasons
};
export class GitService extends Disposable { export class GitService extends Disposable {
@@ -277,6 +281,13 @@ export class GitService extends Disposable {
} }
} }
checkoutFile(uri: GitUri, sha?: string) {
sha = sha || uri.sha;
Logger.log(`checkoutFile('${uri.repoPath}', '${uri.fsPath}', ${sha})`);
return Git.checkout(uri.repoPath!, uri.fsPath, sha!);
}
private async _fileExists(repoPath: string, fileName: string): Promise<boolean> { private async _fileExists(repoPath: string, fileName: string): Promise<boolean> {
return await new Promise<boolean>((resolve, reject) => fs.exists(path.resolve(repoPath, fileName), resolve)); return await new Promise<boolean>((resolve, reject) => fs.exists(path.resolve(repoPath, fileName), resolve));
} }
@@ -561,17 +572,16 @@ export class GitService extends Disposable {
async getBranch(repoPath: string): Promise<GitBranch | undefined> { async getBranch(repoPath: string): Promise<GitBranch | undefined> {
Logger.log(`getBranch('${repoPath}')`); Logger.log(`getBranch('${repoPath}')`);
const data = await Git.branch(repoPath, false); const data = await Git.branch_current(repoPath);
const branches = data.split('\n').filter(_ => !!_).map(_ => new GitBranch(_)); const branch = data.split('\n');
return branches.find(_ => _.current); return new GitBranch(repoPath, branch[0], true, branch[1]);
} }
async getBranches(repoPath: string): Promise<GitBranch[]> { async getBranches(repoPath: string): Promise<GitBranch[]> {
Logger.log(`getBranches('${repoPath}')`); Logger.log(`getBranches('${repoPath}')`);
const data = await Git.branch(repoPath, true); const data = await Git.branch(repoPath, { all: true });
const branches = data.split('\n').filter(_ => !!_).map(_ => new GitBranch(_)); return GitBranchParser.parse(data, repoPath) || [];
return branches;
} }
getCacheEntryKey(fileName: string): string; getCacheEntryKey(fileName: string): string;
@@ -1014,10 +1024,12 @@ export class GitService extends Disposable {
return Git.stash_delete(repoPath, stashName); return Git.stash_delete(repoPath, stashName);
} }
stashSave(repoPath: string, message?: string, unstagedOnly: boolean = false) { stashSave(repoPath: string, message?: string, uris?: Uri[]) {
Logger.log(`stashSave('${repoPath}', ${message}, ${unstagedOnly})`); Logger.log(`stashSave('${repoPath}', ${message}, ${uris})`);
return Git.stash_save(repoPath, message, unstagedOnly); if (uris === undefined) return Git.stash_save(repoPath, message);
const pathspecs = uris.map(u => Git.splitPath(u.fsPath, repoPath)[0]);
return Git.stash_push(repoPath, pathspecs, message);
} }
static getGitPath(gitPath?: string): Promise<IGit> { static getGitPath(gitPath?: string): Promise<IGit> {

View File

@@ -2,6 +2,7 @@
import { commands, ExtensionContext, Uri, window } from 'vscode'; import { commands, ExtensionContext, Uri, window } from 'vscode';
import { BuiltInCommands } from './constants'; import { BuiltInCommands } from './constants';
import { GitCommit } from './gitService'; import { GitCommit } from './gitService';
import { Logger } from './logger';
import * as moment from 'moment'; import * as moment from 'moment';
export type SuppressedKeys = 'suppressCommitHasNoPreviousCommitWarning' | export type SuppressedKeys = 'suppressCommitHasNoPreviousCommitWarning' |
@@ -10,7 +11,8 @@ export type SuppressedKeys = 'suppressCommitHasNoPreviousCommitWarning' |
'suppressGitVersionWarning' | 'suppressGitVersionWarning' |
'suppressLineUncommittedWarning' | 'suppressLineUncommittedWarning' |
'suppressNoRepositoryWarning' | 'suppressNoRepositoryWarning' |
'suppressUpdateNotice'; 'suppressUpdateNotice' |
'suppressWelcomeNotice';
export const SuppressedKeys = { export const SuppressedKeys = {
CommitHasNoPreviousCommitWarning: 'suppressCommitHasNoPreviousCommitWarning' as SuppressedKeys, CommitHasNoPreviousCommitWarning: 'suppressCommitHasNoPreviousCommitWarning' as SuppressedKeys,
CommitNotFoundWarning: 'suppressCommitNotFoundWarning' as SuppressedKeys, CommitNotFoundWarning: 'suppressCommitNotFoundWarning' as SuppressedKeys,
@@ -18,7 +20,8 @@ export const SuppressedKeys = {
GitVersionWarning: 'suppressGitVersionWarning' as SuppressedKeys, GitVersionWarning: 'suppressGitVersionWarning' as SuppressedKeys,
LineUncommittedWarning: 'suppressLineUncommittedWarning' as SuppressedKeys, LineUncommittedWarning: 'suppressLineUncommittedWarning' as SuppressedKeys,
NoRepositoryWarning: 'suppressNoRepositoryWarning' as SuppressedKeys, NoRepositoryWarning: 'suppressNoRepositoryWarning' as SuppressedKeys,
UpdateNotice: 'suppressUpdateNotice' as SuppressedKeys UpdateNotice: 'suppressUpdateNotice' as SuppressedKeys,
WelcomeNotice: 'suppressWelcomeNotice' as SuppressedKeys
}; };
export class Messages { export class Messages {
@@ -64,7 +67,7 @@ export class Messages {
static async showWelcomeMessage(): Promise<string | undefined> { static async showWelcomeMessage(): Promise<string | undefined> {
const viewDocs = 'View Docs'; const viewDocs = 'View Docs';
const result = await window.showInformationMessage(`Thank you for choosing GitLens! GitLens is powerful, feature rich, and highly configurable, so please be sure to view the docs and tailor it to suit your needs.`, viewDocs); const result = await Messages._showMessage('info', `Thank you for choosing GitLens! GitLens is powerful, feature rich, and highly configurable, so please be sure to view the docs and tailor it to suit your needs.`, SuppressedKeys.WelcomeNotice, null, viewDocs);
if (result === viewDocs) { if (result === viewDocs) {
commands.executeCommand(BuiltInCommands.Open, Uri.parse('https://marketplace.visualstudio.com/items/eamodio.gitlens')); commands.executeCommand(BuiltInCommands.Open, Uri.parse('https://marketplace.visualstudio.com/items/eamodio.gitlens'));
} }
@@ -72,7 +75,12 @@ export class Messages {
} }
private static async _showMessage(type: 'info' | 'warn' | 'error', message: string, suppressionKey: SuppressedKeys, dontShowAgain: string | null = 'Don\'t Show Again', ...actions: any[]): Promise<string | undefined> { private static async _showMessage(type: 'info' | 'warn' | 'error', message: string, suppressionKey: SuppressedKeys, dontShowAgain: string | null = 'Don\'t Show Again', ...actions: any[]): Promise<string | undefined> {
if (Messages.context.globalState.get(suppressionKey, false)) return undefined; Logger.log(`ShowMessage(${type}, '${message}', ${suppressionKey}, ${dontShowAgain})`);
if (Messages.context.globalState.get(suppressionKey, false)) {
Logger.log(`ShowMessage(${type}, ${message}, ${suppressionKey}, ${dontShowAgain}) skipped`);
return undefined;
}
if (dontShowAgain !== null) { if (dontShowAgain !== null) {
actions.push(dontShowAgain); actions.push(dontShowAgain);
@@ -94,10 +102,13 @@ export class Messages {
} }
if (dontShowAgain === null || result === dontShowAgain) { if (dontShowAgain === null || result === dontShowAgain) {
Logger.log(`ShowMessage(${type}, '${message}', ${suppressionKey}, ${dontShowAgain}) don't show again requested`);
await Messages.context.globalState.update(suppressionKey, true); await Messages.context.globalState.update(suppressionKey, true);
return undefined;
if (result === dontShowAgain) return undefined;
} }
Logger.log(`ShowMessage(${type}, '${message}', ${suppressionKey}, ${dontShowAgain}) returned ${result}`);
return result; return result;
} }
} }

View File

@@ -69,28 +69,31 @@ export class CommitWithFileStatusQuickPickItem extends OpenFileCommandQuickPickI
export class OpenCommitFilesCommandQuickPickItem extends OpenFilesCommandQuickPickItem { export class OpenCommitFilesCommandQuickPickItem extends OpenFilesCommandQuickPickItem {
constructor(commit: GitLogCommit, item?: QuickPickItem) { constructor(commit: GitLogCommit, versioned: boolean = false, item?: QuickPickItem) {
const uris = commit.fileStatuses.map(s => (s.status === 'D') const repoPath = commit.repoPath;
? GitService.toGitContentUri(commit.previousSha!, commit.previousShortSha!, s.fileName, commit.repoPath, s.originalFileName) const uris = commit.fileStatuses
: GitService.toGitContentUri(commit.sha, commit.shortSha, s.fileName, commit.repoPath, s.originalFileName)); .filter(s => s.status !== 'D')
.map(s => GitUri.fromFileStatus(s, repoPath));
super(uris, item || { super(uris, item || {
label: `$(file-symlink-file) Open Changed Files`, label: `$(file-symlink-file) Open Changed Files`,
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} in ${GlyphChars.Space}$(git-commit) ${commit.shortSha}` description: ''
// detail: `Opens all of the changed files in $(git-commit) ${commit.shortSha}` // detail: `Opens all of the changed file in the working tree`
}); });
} }
} }
export class OpenCommitWorkingTreeFilesCommandQuickPickItem extends OpenFilesCommandQuickPickItem { export class OpenCommitFileRevisionsCommandQuickPickItem extends OpenFilesCommandQuickPickItem {
constructor(commit: GitLogCommit, item?: QuickPickItem) {
const uris = commit.fileStatuses
.filter(s => s.status !== 'D')
.map(s => GitService.toGitContentUri(commit.sha, commit.shortSha, s.fileName, commit.repoPath, s.originalFileName));
constructor(commit: GitLogCommit, versioned: boolean = false, item?: QuickPickItem) {
const repoPath = commit.repoPath;
const uris = commit.fileStatuses.filter(_ => _.status !== 'D').map(_ => GitUri.fromFileStatus(_, repoPath));
super(uris, item || { super(uris, item || {
label: `$(file-symlink-file) Open Changed Working Files`, label: `$(file-symlink-file) Open Changed Revisions`,
description: '' description: `${Strings.pad(GlyphChars.Dash, 2, 3)} in ${GlyphChars.Space}$(git-commit) ${commit.shortSha}`
// detail: `Opens all of the changed file in the working tree` // detail: `Opens all of the changed files in $(git-commit) ${commit.shortSha}`
}); });
} }
} }
@@ -196,8 +199,8 @@ export class CommitDetailsQuickPick {
} as ShowQuickCommitDetailsCommandArgs } as ShowQuickCommitDetailsCommandArgs
])); ]));
items.push(new OpenCommitFilesCommandQuickPickItem(commit)); items.push(new OpenCommitFilesCommandQuickPickItem(commit));
items.push(new OpenCommitWorkingTreeFilesCommandQuickPickItem(commit)); items.push(new OpenCommitFileRevisionsCommandQuickPickItem(commit));
if (goBackCommand) { if (goBackCommand) {
items.splice(0, 0, goBackCommand); items.splice(0, 0, goBackCommand);

View File

@@ -4,7 +4,7 @@ import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, CopyMessageToClipboardCommandArgs, CopyShaToClipboardCommandArgs, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, ShowQuickCommitDetailsCommandArgs, ShowQuickCommitFileDetailsCommandArgs, ShowQuickFileHistoryCommandArgs } from '../commands'; import { Commands, CopyMessageToClipboardCommandArgs, CopyShaToClipboardCommandArgs, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, ShowQuickCommitDetailsCommandArgs, ShowQuickCommitFileDetailsCommandArgs, ShowQuickFileHistoryCommandArgs } from '../commands';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem } from './common'; import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem } from './common';
import { GlyphChars } from '../constants'; import { GlyphChars } from '../constants';
import { GitBranch, GitLog, GitLogCommit, GitService, GitUri, RemoteResource } from '../gitService'; import { GitLog, GitLogCommit, GitService, GitUri, RemoteResource } from '../gitService';
import { Keyboard, KeyCommand, KeyNoopCommand } from '../keyboard'; import { Keyboard, KeyCommand, KeyNoopCommand } from '../keyboard';
import { OpenRemotesCommandQuickPickItem } from './remotes'; import { OpenRemotesCommandQuickPickItem } from './remotes';
import * as moment from 'moment'; import * as moment from 'moment';
@@ -12,6 +12,17 @@ import * as path from 'path';
export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPickItem { export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
constructor(commit: GitLogCommit, item?: QuickPickItem) {
const uri = Uri.file(path.resolve(commit.repoPath, commit.fileName));
super(uri, item || {
label: `$(file-symlink-file) Open File`,
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)}`
});
}
}
export class OpenCommitFileRevisionCommandQuickPickItem extends OpenFileCommandQuickPickItem {
constructor(commit: GitLogCommit, item?: QuickPickItem) { constructor(commit: GitLogCommit, item?: QuickPickItem) {
let description: string; let description: string;
let uri: Uri; let uri: Uri;
@@ -24,23 +35,12 @@ export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPick
description = `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)} in ${GlyphChars.Space}$(git-commit) ${commit.shortSha}`; description = `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)} in ${GlyphChars.Space}$(git-commit) ${commit.shortSha}`;
} }
super(uri, item || { super(uri, item || {
label: `$(file-symlink-file) Open File`, label: `$(file-symlink-file) Open Revision`,
description: description description: description
}); });
} }
} }
export class OpenCommitWorkingTreeFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
constructor(commit: GitLogCommit, item?: QuickPickItem) {
const uri = Uri.file(path.resolve(commit.repoPath, commit.fileName));
super(uri, item || {
label: `$(file-symlink-file) Open Working File`,
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)}`
});
}
}
export class CommitFileDetailsQuickPick { export class CommitFileDetailsQuickPick {
static async show(git: GitService, commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, fileLog?: GitLog): Promise<CommandQuickPickItem | undefined> { static async show(git: GitService, commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, fileLog?: GitLog): Promise<CommandQuickPickItem | undefined> {
@@ -74,7 +74,7 @@ export class CommitFileDetailsQuickPick {
if (commit.previousSha) { if (commit.previousSha) {
items.push(new CommandQuickPickItem({ items.push(new CommandQuickPickItem({
label: `$(git-compare) Compare File with Previous`, label: `$(git-compare) Compare File with Previous Revision`,
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.previousShortSha} ${GlyphChars.Space} $(git-compare) ${GlyphChars.Space} $(git-commit) ${commit.shortSha}` description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.previousShortSha} ${GlyphChars.Space} $(git-compare) ${GlyphChars.Space} $(git-commit) ${commit.shortSha}`
}, Commands.DiffWithPrevious, [ }, Commands.DiffWithPrevious, [
commit.uri, commit.uri,
@@ -87,7 +87,7 @@ export class CommitFileDetailsQuickPick {
if (commit.workingFileName) { if (commit.workingFileName) {
items.push(new CommandQuickPickItem({ items.push(new CommandQuickPickItem({
label: `$(git-compare) Compare File with Working Tree`, label: `$(git-compare) Compare File with Working Revision`,
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.shortSha} ${GlyphChars.Space} $(git-compare) ${GlyphChars.Space} $(file-text) ${workingName}` description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.shortSha} ${GlyphChars.Space} $(git-compare) ${GlyphChars.Space} $(file-text) ${workingName}`
}, Commands.DiffWithWorking, [ }, Commands.DiffWithWorking, [
Uri.file(path.resolve(commit.repoPath, commit.workingFileName)), Uri.file(path.resolve(commit.repoPath, commit.workingFileName)),
@@ -120,26 +120,26 @@ export class CommitFileDetailsQuickPick {
])); ]));
} }
items.push(new OpenCommitFileCommandQuickPickItem(commit));
if (commit.workingFileName && commit.status !== 'D') { if (commit.workingFileName && commit.status !== 'D') {
items.push(new OpenCommitWorkingTreeFileCommandQuickPickItem(commit)); items.push(new OpenCommitFileCommandQuickPickItem(commit));
} }
items.push(new OpenCommitFileRevisionCommandQuickPickItem(commit));
const remotes = Arrays.uniqueBy(await git.getRemotes(commit.repoPath), _ => _.url, _ => !!_.provider); const remotes = Arrays.uniqueBy(await git.getRemotes(commit.repoPath), _ => _.url, _ => !!_.provider);
if (remotes.length) { if (remotes.length) {
if (!stash) { if (commit.workingFileName && commit.status !== 'D') {
const branch = await git.getBranch(commit.repoPath || git.repoPath);
items.push(new OpenRemotesCommandQuickPickItem(remotes, { items.push(new OpenRemotesCommandQuickPickItem(remotes, {
type: 'file', type: 'file',
fileName: commit.fileName, fileName: commit.workingFileName,
commit branch: branch!.name
} as RemoteResource, currentCommand)); } as RemoteResource, currentCommand));
} }
if (commit.workingFileName && commit.status !== 'D') { if (!stash) {
const branch = await git.getBranch(commit.repoPath || git.repoPath) as GitBranch;
items.push(new OpenRemotesCommandQuickPickItem(remotes, { items.push(new OpenRemotesCommandQuickPickItem(remotes, {
type: 'working-file', type: 'revision',
fileName: commit.workingFileName, fileName: commit.fileName,
branch: branch.name commit
} as RemoteResource, currentCommand)); } as RemoteResource, currentCommand));
} }
} }

View File

@@ -139,7 +139,7 @@ export class FileHistoryQuickPick {
const remotes = Arrays.uniqueBy(await git.getRemotes(uri.repoPath!), _ => _.url, _ => !!_.provider); const remotes = Arrays.uniqueBy(await git.getRemotes(uri.repoPath!), _ => _.url, _ => !!_.provider);
if (remotes.length) { if (remotes.length) {
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, { items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, {
type: 'file', type: 'revision',
branch: branch!.name, branch: branch!.name,
fileName: uri.getRelativePath(), fileName: uri.getRelativePath(),
sha: uri.sha sha: uri.sha

View File

@@ -38,12 +38,24 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
description = `$(git-branch) ${resource.branch}`; description = `$(git-branch) ${resource.branch}`;
break; break;
case 'branches':
description = `$(git-branch) Branches`;
break;
case 'commit': case 'commit':
const shortSha = resource.sha.substring(0, 8); const shortSha = resource.sha.substring(0, 8);
description = `$(git-commit) ${shortSha}`; description = `$(git-commit) ${shortSha}`;
break; break;
case 'file': case 'file':
description = `$(file-text) ${path.basename(resource.fileName)}`;
break;
case 'repo':
description = `$(repo) Repository`;
break;
case 'revision':
if (resource.commit !== undefined && resource.commit instanceof GitLogCommit) { if (resource.commit !== undefined && resource.commit instanceof GitLogCommit) {
if (resource.commit.status === 'D') { if (resource.commit.status === 'D') {
resource.sha = resource.commit.previousSha; resource.sha = resource.commit.previousSha;
@@ -59,14 +71,6 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
description = `$(file-text) ${path.basename(resource.fileName)}${shortFileSha ? ` in ${GlyphChars.Space}$(git-commit) ${shortFileSha}` : ''}`; description = `$(file-text) ${path.basename(resource.fileName)}${shortFileSha ? ` in ${GlyphChars.Space}$(git-commit) ${shortFileSha}` : ''}`;
} }
break; break;
case 'repo':
description = `$(repo) Repository`;
break;
case 'working-file':
description = `$(file-text) ${path.basename(resource.fileName)}`;
break;
} }
const remote = remotes[0]; const remote = remotes[0];

View File

@@ -156,7 +156,7 @@ export class RepoStatusQuickPick {
} }
items.splice(0, 0, new CommandQuickPickItem({ items.splice(0, 0, new CommandQuickPickItem({
label: `$(repo) Show Stashed Changes`, label: `$(inbox) Show Stashed Changes`,
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows stashed changes in the repository` description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows stashed changes in the repository`
}, Commands.ShowQuickStashList, [ }, Commands.ShowQuickStashList, [
new GitUri(Uri.file(status.repoPath), { fileName: '', repoPath: status.repoPath }), new GitUri(Uri.file(status.repoPath), { fileName: '', repoPath: status.repoPath }),

View File

@@ -14,21 +14,10 @@ export class StashListQuickPick {
if (mode === 'list') { if (mode === 'list') {
items.splice(0, 0, new CommandQuickPickItem({ items.splice(0, 0, new CommandQuickPickItem({
label: `$(repo-push) Stash Unstaged Changes`, label: `$(plus) Stash Changes`,
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} stashes only unstaged changes`
}, Commands.StashSave, [
{
unstagedOnly: true,
goBackCommand: currentCommand
} as StashSaveCommandArgs
]));
items.splice(0, 0, new CommandQuickPickItem({
label: `$(repo-force-push) Stash Changes`,
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} stashes all changes` description: `${Strings.pad(GlyphChars.Dash, 2, 3)} stashes all changes`
}, Commands.StashSave, [ }, Commands.StashSave, [
{ {
unstagedOnly: false,
goBackCommand: currentCommand goBackCommand: currentCommand
} as StashSaveCommandArgs } as StashSaveCommandArgs
])); ]));

View File

@@ -102,7 +102,23 @@ export namespace Strings {
} }
export function truncate(s: string, truncateTo?: number) { export function truncate(s: string, truncateTo?: number) {
if (!s || truncateTo === undefined || getWidth(s) <= truncateTo) return s; if (!s || truncateTo === undefined) return s;
return `${s.substring(0, truncateTo - 1)}\u2026`;
const len = getWidth(s);
if (len <= truncateTo) return s;
if (len === s.length) return `${s.substring(0, truncateTo - 1)}\u2026`;
// Skip ahead to start as far as we can by assuming all the double-width characters won't be truncated
let chars = Math.floor(truncateTo / (len / s.length));
let count = getWidth(s.substring(0, chars));
while (count < truncateTo) {
count += getWidth(s[chars++]);
}
if (count >= truncateTo) {
chars--;
}
return `${s.substring(0, chars)}\u2026`;
} }
} }

View File

@@ -8,22 +8,32 @@ import { GitBranch, GitService, GitUri } from '../gitService';
export class BranchHistoryNode extends ExplorerNode { export class BranchHistoryNode extends ExplorerNode {
readonly resourceType: ResourceType = 'branch-history'; readonly resourceType: ResourceType = 'gitlens:branch-history';
constructor(public readonly branch: GitBranch, uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) { constructor(public readonly branch: GitBranch, uri: GitUri, private readonly template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri); super(uri);
} }
async getChildren(): Promise<CommitNode[]> { async getChildren(): Promise<ExplorerNode[]> {
const log = await this.git.getLogForRepo(this.uri.repoPath!, this.branch.name); const log = await this.git.getLogForRepo(this.uri.repoPath!, this.branch.name);
if (log === undefined) return []; if (log === undefined) return [];
return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.git.config.gitExplorer.commitFormat, this.context, this.git))]; return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.template, this.context, this.git))];
} }
getTreeItem(): TreeItem { async getTreeItem(): Promise<TreeItem> {
const item = new TreeItem(`${this.branch.name}${this.branch.current ? ` ${GlyphChars.Dash} current` : ''}`, TreeItemCollapsibleState.Collapsed); let name = this.branch.getName();
if (!this.branch.remote && this.branch.tracking !== undefined && this.git.config.gitExplorer.showTrackingBranch) {
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.resourceType; item.contextValue = this.resourceType;
item.iconPath = {
dark: this.context.asAbsolutePath('images/dark/icon-branch.svg'),
light: this.context.asAbsolutePath('images/light/icon-branch.svg')
};
return item; return item;
} }
} }

View File

@@ -7,22 +7,29 @@ import { GitService, GitUri } from '../gitService';
export class BranchesNode extends ExplorerNode { export class BranchesNode extends ExplorerNode {
readonly resourceType: ResourceType = 'branches'; readonly resourceType: ResourceType = 'gitlens:branches';
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) { constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri); super(uri);
} }
async getChildren(): Promise<BranchHistoryNode[]> { async getChildren(): Promise<ExplorerNode[]> {
const branches = await this.git.getBranches(this.uri.repoPath!); const branches = await this.git.getBranches(this.uri.repoPath!);
if (branches === undefined) return []; if (branches === undefined) return [];
return [...Iterables.filterMap(branches.sort(_ => _.current ? 0 : 1), b => b.remote ? undefined : new BranchHistoryNode(b, this.uri, this.context, this.git))]; branches.sort((a, b) => (a.current ? -1 : 1) - (b.current ? -1 : 1) || a.name.localeCompare(b.name));
return [...Iterables.filterMap(branches, b => b.remote ? undefined : new BranchHistoryNode(b, this.uri, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
} }
getTreeItem(): TreeItem { getTreeItem(): TreeItem {
const item = new TreeItem(`Branches`, TreeItemCollapsibleState.Collapsed); const item = new TreeItem(`Branches`, TreeItemCollapsibleState.Expanded);
item.contextValue = this.resourceType; item.contextValue = this.resourceType;
item.iconPath = {
dark: this.context.asAbsolutePath('images/dark/icon-branch.svg'),
light: this.context.asAbsolutePath('images/light/icon-branch.svg')
};
return item; return item;
} }
} }

View File

@@ -7,9 +7,9 @@ import * as path from 'path';
export class CommitFileNode extends ExplorerNode { export class CommitFileNode extends ExplorerNode {
readonly resourceType: ResourceType = 'commit-file'; readonly resourceType: ResourceType = 'gitlens:commit-file';
constructor(public readonly status: IGitStatusFile, public commit: GitCommit, private template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) { constructor(public readonly status: IGitStatusFile, public commit: GitCommit, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(new GitUri(Uri.file(path.resolve(commit.repoPath, status.fileName)), { repoPath: commit.repoPath, fileName: status.fileName, sha: commit.sha })); super(new GitUri(Uri.file(path.resolve(commit.repoPath, status.fileName)), { repoPath: commit.repoPath, fileName: status.fileName, sha: commit.sha }));
} }
@@ -25,7 +25,7 @@ export class CommitFileNode extends ExplorerNode {
} }
} }
const item = new TreeItem(StatusFileFormatter.fromTemplate(this.template, this.status), TreeItemCollapsibleState.None); const item = new TreeItem(StatusFileFormatter.fromTemplate(this.git.config.gitExplorer.commitFileFormat, this.status), TreeItemCollapsibleState.None);
item.contextValue = this.resourceType; item.contextValue = this.resourceType;
const icon = getGitStatusIcon(this.status.status); const icon = getGitStatusIcon(this.status.status);
@@ -40,8 +40,18 @@ export class CommitFileNode extends ExplorerNode {
} }
getCommand(): Command | undefined { getCommand(): Command | undefined {
let allowMissingPrevious = false;
let prefix = undefined;
if (this.status.status === 'A') {
allowMissingPrevious = true;
prefix = 'added in ';
}
else if (this.status.status === 'D') {
prefix = 'deleted in ';
}
return { return {
title: 'Compare File with Previous', title: 'Compare File with Previous Revision',
command: Commands.DiffWithPrevious, command: Commands.DiffWithPrevious,
arguments: [ arguments: [
GitUri.fromFileStatus(this.status, this.commit.repoPath), GitUri.fromFileStatus(this.status, this.commit.repoPath),
@@ -51,7 +61,9 @@ export class CommitFileNode extends ExplorerNode {
showOptions: { showOptions: {
preserveFocus: true, preserveFocus: true,
preview: true preview: true
} },
allowMissingPrevious: allowMissingPrevious,
rightTitlePrefix: prefix
} as DiffWithPreviousCommandArgs } as DiffWithPreviousCommandArgs
] ]
}; };

View File

@@ -4,13 +4,14 @@ import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'v
import { Commands, DiffWithPreviousCommandArgs } from '../commands'; import { Commands, DiffWithPreviousCommandArgs } from '../commands';
import { CommitFileNode } from './commitFileNode'; import { CommitFileNode } from './commitFileNode';
import { ExplorerNode, ResourceType } from './explorerNode'; import { ExplorerNode, ResourceType } from './explorerNode';
import { CommitFormatter, GitCommit, GitService, GitUri } from '../gitService'; import { CommitFormatter, getGitStatusIcon, GitLogCommit, GitService, GitUri } from '../gitService';
import * as path from 'path';
export class CommitNode extends ExplorerNode { export class CommitNode extends ExplorerNode {
readonly resourceType: ResourceType = 'commit'; readonly resourceType: ResourceType = 'gitlens:commit';
constructor(public readonly commit: GitCommit, private template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) { constructor(public readonly commit: GitLogCommit, private readonly template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(new GitUri(commit.uri, commit)); super(new GitUri(commit.uri, commit));
} }
@@ -23,7 +24,7 @@ export class CommitNode extends ExplorerNode {
const commit = Iterables.first(log.commits.values()); const commit = Iterables.first(log.commits.values());
if (commit === undefined) return []; if (commit === undefined) return [];
return [...Iterables.map(commit.fileStatuses, s => new CommitFileNode(s, commit, this.git.config.gitExplorer.commitFileFormat, this.context, this.git))]; return [...Iterables.map(commit.fileStatuses, s => new CommitFileNode(s, commit, this.context, this.git))];
} }
getTreeItem(): TreeItem { getTreeItem(): TreeItem {
@@ -31,24 +32,42 @@ export class CommitNode extends ExplorerNode {
if (this.commit.type === 'file') { if (this.commit.type === 'file') {
item.collapsibleState = TreeItemCollapsibleState.None; item.collapsibleState = TreeItemCollapsibleState.None;
item.command = this.getCommand(); item.command = this.getCommand();
item.contextValue = 'commit-file'; const resourceType: ResourceType = 'gitlens:commit-file';
item.contextValue = resourceType;
const icon = getGitStatusIcon(this.commit.status!);
item.iconPath = {
dark: this.context.asAbsolutePath(path.join('images', 'dark', icon)),
light: this.context.asAbsolutePath(path.join('images', 'light', icon))
};
} }
else { else {
item.collapsibleState = TreeItemCollapsibleState.Collapsed; item.collapsibleState = TreeItemCollapsibleState.Collapsed;
item.contextValue = this.resourceType; item.contextValue = this.resourceType;
}
item.iconPath = { item.iconPath = {
dark: this.context.asAbsolutePath('images/dark/icon-commit.svg'), dark: this.context.asAbsolutePath('images/dark/icon-commit.svg'),
light: this.context.asAbsolutePath('images/light/icon-commit.svg') light: this.context.asAbsolutePath('images/light/icon-commit.svg')
}; };
}
return item; return item;
} }
getCommand(): Command | undefined { getCommand(): Command | undefined {
let allowMissingPrevious = false;
let prefix = undefined;
const status = this.commit.fileStatuses[0];
if (status.status === 'A') {
allowMissingPrevious = true;
prefix = 'added in ';
}
else if (status.status === 'D') {
prefix = 'deleted in ';
}
return { return {
title: 'Compare File with Previous', title: 'Compare File with Previous Revision',
command: Commands.DiffWithPrevious, command: Commands.DiffWithPrevious,
arguments: [ arguments: [
new GitUri(this.uri, this.commit), new GitUri(this.uri, this.commit),
@@ -58,7 +77,9 @@ export class CommitNode extends ExplorerNode {
showOptions: { showOptions: {
preserveFocus: true, preserveFocus: true,
preview: true preview: true
} },
allowMissingPrevious: allowMissingPrevious,
rightTitlePrefix: prefix
} as DiffWithPreviousCommandArgs } as DiffWithPreviousCommandArgs
] ]
}; };

View File

@@ -2,7 +2,22 @@
import { Command, Event, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { Command, Event, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { GitUri } from '../gitService'; import { GitUri } from '../gitService';
export declare type ResourceType = 'text' | 'status' | 'branches' | 'repository' | 'branch-history' | 'file-history' | 'stash-history' | 'commit' | 'stash-commit' | 'commit-file'; export declare type ResourceType =
'gitlens:branches' |
'gitlens:branch-history' |
'gitlens:commit' |
'gitlens:commit-file' |
'gitlens:file-history' |
'gitlens:history' |
'gitlens:message' |
'gitlens:remote' |
'gitlens:remotes' |
'gitlens:repository' |
'gitlens:stash' |
'gitlens:stash-file' |
'gitlens:stashes' |
'gitlens:status' |
'gitlens:status-upstream';
export abstract class ExplorerNode { export abstract class ExplorerNode {
@@ -22,11 +37,11 @@ export abstract class ExplorerNode {
refresh?(): void; refresh?(): void;
} }
export class TextExplorerNode extends ExplorerNode { export class MessageNode extends ExplorerNode {
readonly resourceType: ResourceType = 'text'; readonly resourceType: ResourceType = 'gitlens:message';
constructor(private text: string) { constructor(private message: string) {
super(new GitUri()); super(new GitUri());
} }
@@ -35,7 +50,7 @@ export class TextExplorerNode extends ExplorerNode {
} }
getTreeItem(): TreeItem | Promise<TreeItem> { getTreeItem(): TreeItem | Promise<TreeItem> {
const item = new TreeItem(this.text, TreeItemCollapsibleState.None); const item = new TreeItem(this.message, TreeItemCollapsibleState.None);
item.contextValue = this.resourceType; item.contextValue = this.resourceType;
return item; return item;
} }

View File

@@ -6,7 +6,12 @@ export * from './branchHistoryNode';
export * from './commitFileNode'; export * from './commitFileNode';
export * from './commitNode'; export * from './commitNode';
export * from './fileHistoryNode'; export * from './fileHistoryNode';
export * from './historyNode';
export * from './remoteNode';
export * from './remotesNode';
export * from './repositoryNode'; export * from './repositoryNode';
export * from './stashCommitNode'; export * from './stashFileNode';
export * from './stashNode'; export * from './stashNode';
export * from './stashesNode';
export * from './statusNode'; export * from './statusNode';
export * from './statusUpstreamNode';

View File

@@ -1,89 +0,0 @@
'use strict';
// import { Arrays } from '../system';
import { commands, Event, EventEmitter, ExtensionContext, TextEditor, TreeDataProvider, TreeItem, Uri, window } from 'vscode';
import { Commands, DiffWithPreviousCommandArgs, openEditor, OpenFileInRemoteCommandArgs } from '../commands';
import { UriComparer } from '../comparers';
import { CommitNode, ExplorerNode, FileHistoryNode, TextExplorerNode } from './explorerNodes';
import { GitService, GitUri } from '../gitService';
export * from './explorerNodes';
export class FileHistoryExplorer implements TreeDataProvider<ExplorerNode> {
private _node?: ExplorerNode;
private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
public get onDidChangeTreeData(): Event<ExplorerNode> {
return this._onDidChangeTreeData.event;
}
constructor(private context: ExtensionContext, private git: GitService) {
commands.registerCommand('gitlens.fileHistoryExplorer.refresh', this.refresh, this);
commands.registerCommand('gitlens.fileHistoryExplorer.openChanges', this.openChanges, this);
commands.registerCommand('gitlens.fileHistoryExplorer.openFile', this.openFile, this);
commands.registerCommand('gitlens.fileHistoryExplorer.openFileRevision', this.openFileRevision, this);
commands.registerCommand('gitlens.fileHistoryExplorer.openFileInRemote', this.openFileInRemote, this);
commands.registerCommand('gitlens.fileHistoryExplorer.openFileRevisionInRemote', this.openFileRevisionInRemote, this);
context.subscriptions.push(window.onDidChangeActiveTextEditor(this.onActiveEditorChanged, this));
this._node = this.getRootNode(window.activeTextEditor);
}
async getTreeItem(node: ExplorerNode): Promise<TreeItem> {
return node.getTreeItem();
}
async getChildren(node?: ExplorerNode): Promise<ExplorerNode[]> {
if (this._node === undefined) return [new TextExplorerNode('No active file')];
if (node === undefined) return this._node.getChildren();
return node.getChildren();
}
private getRootNode(editor: TextEditor | undefined): ExplorerNode | undefined {
if (window.visibleTextEditors.length === 0) return undefined;
if (editor === undefined) return this._node;
const uri = this.git.getGitUriForFile(editor.document.uri) || new GitUri(editor.document.uri, { repoPath: this.git.repoPath, fileName: editor.document.uri.fsPath });
if (UriComparer.equals(uri, this._node && this._node.uri)) return this._node;
return new FileHistoryNode(uri, this.context, this.git);
}
private onActiveEditorChanged(editor: TextEditor | undefined) {
const node = this.getRootNode(editor);
if (node === this._node) return;
this.refresh();
}
refresh(node?: ExplorerNode) {
this._node = node || this.getRootNode(window.activeTextEditor);
this._onDidChangeTreeData.fire();
}
private openChanges(node: CommitNode) {
const command = node.getCommand();
if (command === undefined || command.arguments === undefined) return;
const [uri, args] = command.arguments as [Uri, DiffWithPreviousCommandArgs];
args.showOptions!.preview = false;
return commands.executeCommand(command.command, uri, args);
}
private openFile(node: CommitNode) {
return openEditor(node.uri, { preserveFocus: true, preview: false });
}
private openFileRevision(node: CommitNode) {
return openEditor(GitService.toGitContentUri(node.uri), { preserveFocus: true, preview: false });
}
private async openFileInRemote(node: CommitNode) {
return commands.executeCommand(Commands.OpenFileInRemote, node.commit.uri, { range: false } as OpenFileInRemoteCommandArgs);
}
private async openFileRevisionInRemote(node: CommitNode) {
return commands.executeCommand(Commands.OpenFileInRemote, new GitUri(node.commit.uri, node.commit), { range: false } as OpenFileInRemoteCommandArgs);
}
}

View File

@@ -2,13 +2,12 @@
import { Iterables } from '../system'; import { Iterables } from '../system';
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { CommitNode } from './commitNode'; import { CommitNode } from './commitNode';
import { ExplorerNode, ResourceType, TextExplorerNode } from './explorerNode'; import { ExplorerNode, MessageNode, ResourceType } from './explorerNode';
import { GitService, GitUri } from '../gitService'; import { GitService, GitUri } from '../gitService';
export class FileHistoryNode extends ExplorerNode { export class FileHistoryNode extends ExplorerNode {
static readonly rootType: ResourceType = 'file-history'; readonly resourceType: ResourceType = 'gitlens:file-history';
readonly resourceType: ResourceType = 'file-history';
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) { constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri); super(uri);
@@ -16,14 +15,20 @@ export class FileHistoryNode extends ExplorerNode {
async getChildren(): Promise<ExplorerNode[]> { async getChildren(): Promise<ExplorerNode[]> {
const log = await this.git.getLogForFile(this.uri.repoPath, this.uri.fsPath, this.uri.sha); const log = await this.git.getLogForFile(this.uri.repoPath, this.uri.fsPath, this.uri.sha);
if (log === undefined) return [new TextExplorerNode('No file history')]; if (log === undefined) return [new MessageNode('No file history')];
return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.git.config.fileHistoryExplorer.commitFormat, this.context, this.git))]; return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
} }
getTreeItem(): TreeItem { getTreeItem(): TreeItem {
const item = new TreeItem(`History of ${this.uri.getFormattedPath()}`, TreeItemCollapsibleState.Expanded); const item = new TreeItem(`${this.uri.getFormattedPath()}`, TreeItemCollapsibleState.Expanded);
item.contextValue = this.resourceType; item.contextValue = this.resourceType;
item.iconPath = {
dark: this.context.asAbsolutePath('images/dark/icon-history.svg'),
light: this.context.asAbsolutePath('images/light/icon-history.svg')
};
return item; return item;
} }
} }

View File

@@ -1,73 +1,199 @@
'use strict'; 'use strict';
// import { Functions } from '../system'; import { Functions, Objects } from '../system';
import { commands, Event, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem, Uri } from 'vscode'; import { commands, Event, EventEmitter, ExtensionContext, TextDocumentShowOptions, TextEditor, TreeDataProvider, TreeItem, Uri, window, workspace } from 'vscode';
import { Commands, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, openEditor, OpenFileInRemoteCommandArgs } from '../commands';
import { UriComparer } from '../comparers'; import { UriComparer } from '../comparers';
import { ExplorerNode, FileHistoryNode, RepositoryNode, ResourceType, StashNode } from './explorerNodes'; import { ExtensionKey, IConfig } from '../configuration';
import { GitService, GitUri } from '../gitService'; import { CommandContext, setCommandContext } from '../constants';
import { CommitFileNode, CommitNode, ExplorerNode, HistoryNode, MessageNode, RepositoryNode, StashNode } from './explorerNodes';
import { GitService, GitUri, RepoChangedReasons } from '../gitService';
export * from './explorerNodes'; export * from './explorerNodes';
export type GitExplorerView =
'history' |
'repository';
export const GitExplorerView = {
History: 'history' as GitExplorerView,
Repository: 'repository' as GitExplorerView
};
export interface OpenFileRevisionCommandArgs {
uri?: Uri;
showOptions?: TextDocumentShowOptions;
}
export class GitExplorer implements TreeDataProvider<ExplorerNode> { export class GitExplorer implements TreeDataProvider<ExplorerNode> {
// private _refreshDebounced: () => void; private _config: IConfig;
private _root?: ExplorerNode;
private _view: GitExplorerView = GitExplorerView.Repository;
private _onDidChangeTreeData = new EventEmitter<ExplorerNode>(); private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
public get onDidChangeTreeData(): Event<ExplorerNode> { public get onDidChangeTreeData(): Event<ExplorerNode> {
return this._onDidChangeTreeData.event; return this._onDidChangeTreeData.event;
} }
private _roots: ExplorerNode[] = []; constructor(private readonly context: ExtensionContext, private readonly git: GitService) {
commands.registerCommand('gitlens.gitExplorer.switchToHistoryView', () => this.switchTo(GitExplorerView.History), this);
commands.registerCommand('gitlens.gitExplorer.switchToRepositoryView', () => this.switchTo(GitExplorerView.Repository), this);
commands.registerCommand('gitlens.gitExplorer.refresh', this.refresh, this);
commands.registerCommand('gitlens.gitExplorer.openChanges', this.openChanges, this);
commands.registerCommand('gitlens.gitExplorer.openChangesWithWorking', this.openChangesWithWorking, this);
commands.registerCommand('gitlens.gitExplorer.openFile', this.openFile, this);
commands.registerCommand('gitlens.gitExplorer.openFileRevision', this.openFileRevision, this);
commands.registerCommand('gitlens.gitExplorer.openFileRevisionInRemote', this.openFileRevisionInRemote, this);
commands.registerCommand('gitlens.gitExplorer.openChangedFiles', this.openChangedFiles, this);
commands.registerCommand('gitlens.gitExplorer.openChangedFileRevisions', this.openChangedFileRevisions, this);
commands.registerCommand('gitlens.gitExplorer.applyChanges', this.applyChanges, this);
constructor(private context: ExtensionContext, private git: GitService) { context.subscriptions.push(this.git.onDidChangeRepo(this.onRepoChanged, this));
commands.registerCommand('gitlens.gitExplorer.refresh', () => this.refresh());
// this._refreshDebounced = Functions.debounce(this.refresh.bind(this), 250); const fn = Functions.debounce(this.onActiveEditorChanged, 500);
context.subscriptions.push(window.onDidChangeActiveTextEditor(fn, this));
context.subscriptions.push(workspace.onDidChangeConfiguration(this.onConfigurationChanged, this));
// const editor = window.activeTextEditor; this.onConfigurationChanged();
// const uri = (editor !== undefined && editor.document !== undefined) this._view = this._config.gitExplorer.view;
// ? new GitUri(editor.document.uri, { repoPath: git.repoPath, fileName: editor.document.uri.fsPath }) setCommandContext(CommandContext.GitExplorerView, this._view);
// : new GitUri(Uri.file(git.repoPath), { repoPath: git.repoPath, fileName: git.repoPath }); this._root = this.getRootNode();
const uri = new GitUri(Uri.file(git.repoPath), { repoPath: git.repoPath, fileName: git.repoPath });
this._roots.push(new RepositoryNode(uri, context, git));
} }
async getTreeItem(node: ExplorerNode): Promise<TreeItem> { async getTreeItem(node: ExplorerNode): Promise<TreeItem> {
// if (node.onDidChangeTreeData !== undefined) {
// node.onDidChangeTreeData(() => setTimeout(this._refreshDebounced, 1));
// }
return node.getTreeItem(); return node.getTreeItem();
} }
async getChildren(node?: ExplorerNode): Promise<ExplorerNode[]> { async getChildren(node?: ExplorerNode): Promise<ExplorerNode[]> {
if (this._roots.length === 0) return []; if (this._root === undefined) {
if (node === undefined) return this._roots; if (this._view === GitExplorerView.History) return [new MessageNode('No active file; no history to show')];
return [];
}
if (node === undefined) return this._root.getChildren();
return node.getChildren(); return node.getChildren();
} }
addHistory(uri: GitUri) { private getRootNode(editor?: TextEditor): ExplorerNode | undefined {
this._add(uri, FileHistoryNode); const uri = new GitUri(Uri.file(this.git.repoPath), { repoPath: this.git.repoPath, fileName: this.git.repoPath });
}
addStash(uri: GitUri) { switch (this._view) {
this._add(uri, StashNode); case GitExplorerView.History: return this.getHistoryNode(editor || window.activeTextEditor);
} case GitExplorerView.Repository: return new RepositoryNode(uri, this.context, this.git);
private _add<T extends ExplorerNode>(uri: GitUri, type: { new (uri: GitUri, context: ExtensionContext, git: GitService): T, rootType: ResourceType }) {
if (!this._roots.some(_ => _.resourceType === type.rootType && UriComparer.equals(uri, _.uri))) {
this._roots.push(new type(uri, this.context, this.git));
} }
return undefined;
}
private getHistoryNode(editor: TextEditor | undefined): ExplorerNode | undefined {
if (window.visibleTextEditors.length === 0) return undefined;
if (editor === undefined) return this._root;
const uri = this.git.getGitUriForFile(editor.document.uri) || new GitUri(editor.document.uri, { repoPath: this.git.repoPath, fileName: editor.document.uri.fsPath });
if (UriComparer.equals(uri, this._root && this._root.uri)) return this._root;
return new HistoryNode(uri, this.context, this.git);
}
private onActiveEditorChanged(editor: TextEditor | undefined) {
if (this._view !== GitExplorerView.History) return;
const root = this.getRootNode(editor);
if (root === this._root) return;
this._root = root;
this.refresh(undefined, root);
}
private onConfigurationChanged() {
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
if (!Objects.areEquivalent(cfg.gitExplorer, this._config && this._config.gitExplorer)) {
setTimeout(() => {
this._root = this.getRootNode(window.activeTextEditor);
this.refresh();
}, 1);
}
this._config = cfg;
}
private onRepoChanged(reasons: RepoChangedReasons[]) {
if (this._view !== GitExplorerView.Repository) return;
this.refresh(); this.refresh();
} }
clear() { refresh(node?: ExplorerNode, root?: ExplorerNode) {
this._roots = []; if (root === undefined && this._view === GitExplorerView.History) {
this._root = this.getRootNode(window.activeTextEditor);
}
this._onDidChangeTreeData.fire(node);
}
switchTo(view: GitExplorerView) {
if (this._view === view) return;
this._view = view;
setCommandContext(CommandContext.GitExplorerView, this._view);
this._root = this.getRootNode(window.activeTextEditor);
this.refresh(); this.refresh();
} }
refresh() { private async applyChanges(node: CommitNode | StashNode) {
this._onDidChangeTreeData.fire(); await this.git.checkoutFile(node.uri);
return this.openFile(node);
}
private openChanges(node: CommitNode | StashNode) {
const command = node.getCommand();
if (command === undefined || command.arguments === undefined) return;
const [uri, args] = command.arguments as [Uri, DiffWithPreviousCommandArgs];
args.showOptions!.preview = false;
return commands.executeCommand(command.command, uri, args);
}
private openChangesWithWorking(node: CommitNode | StashNode) {
const args: DiffWithWorkingCommandArgs = {
commit: node.commit,
showOptions: {
preserveFocus: true,
preview: false
}
};
return commands.executeCommand(Commands.DiffWithWorking, new GitUri(node.commit.uri, node.commit), args);
}
private openFile(node: CommitNode | StashNode) {
return openEditor(node.uri, { preserveFocus: true, preview: false });
}
private openFileRevision(node: CommitNode | StashNode | CommitFileNode, options: OpenFileRevisionCommandArgs = { showOptions: { preserveFocus: true, preview: false } }) {
return openEditor(options.uri || GitService.toGitContentUri(node.uri), options.showOptions || { preserveFocus: true, preview: false });
}
private async openChangedFiles(node: CommitNode | StashNode, options: TextDocumentShowOptions = { preserveFocus: false, preview: false }) {
const repoPath = node.commit.repoPath;
const uris = node.commit.fileStatuses.filter(s => s.status !== 'D').map(s => GitUri.fromFileStatus(s, repoPath));
for (const uri of uris) {
await openEditor(uri, options);
}
}
private async openChangedFileRevisions(node: CommitNode | StashNode, options: TextDocumentShowOptions = { preserveFocus: false, preview: false }) {
const uris = node.commit.fileStatuses
.filter(s => s.status !== 'D')
.map(s => GitService.toGitContentUri(node.commit.sha, node.commit.shortSha, s.fileName, node.commit.repoPath, s.originalFileName));
for (const uri of uris) {
await openEditor(uri, options);
}
}
private async openFileRevisionInRemote(node: CommitNode | StashNode) {
return commands.executeCommand(Commands.OpenFileInRemote, new GitUri(node.commit.uri, node.commit), { range: false } as OpenFileInRemoteCommandArgs);
} }
} }

30
src/views/historyNode.ts Normal file
View File

@@ -0,0 +1,30 @@
'use strict';
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ExplorerNode, ResourceType } from './explorerNode';
import { FileHistoryNode } from './fileHistoryNode';
import { GitService, GitUri } from '../gitService';
export class HistoryNode extends ExplorerNode {
readonly resourceType: ResourceType = 'gitlens:history';
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri);
}
async getChildren(): Promise<ExplorerNode[]> {
return [new FileHistoryNode(this.uri, this.context, this.git)];
}
getTreeItem(): TreeItem {
const item = new TreeItem(`${this.uri.getFormattedPath()}`, TreeItemCollapsibleState.Expanded);
item.contextValue = this.resourceType;
item.iconPath = {
dark: this.context.asAbsolutePath('images/dark/icon-history.svg'),
light: this.context.asAbsolutePath('images/light/icon-history.svg')
};
return item;
}
}

35
src/views/remoteNode.ts Normal file
View File

@@ -0,0 +1,35 @@
'use strict';
import { Iterables } from '../system';
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { BranchHistoryNode } from './branchHistoryNode';
import { ExplorerNode, ResourceType } from './explorerNode';
import { GitRemote, GitService, GitUri } from '../gitService';
export class RemoteNode extends ExplorerNode {
readonly resourceType: ResourceType = 'gitlens:remote';
constructor(public readonly remote: GitRemote, uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri);
}
async getChildren(): Promise<ExplorerNode[]> {
const branches = await this.git.getBranches(this.uri.repoPath!);
if (branches === undefined) return [];
branches.sort((a, b) => a.name.localeCompare(b.name));
return [...Iterables.filterMap(branches, b => !b.remote || !b.name.startsWith(this.remote.name) ? undefined : new BranchHistoryNode(b, this.uri, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
}
getTreeItem(): TreeItem {
const item = new TreeItem(this.remote.name, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
// item.iconPath = {
// dark: this.context.asAbsolutePath('images/dark/icon-remote.svg'),
// light: this.context.asAbsolutePath('images/light/icon-remote.svg')
// };
return item;
}
}

35
src/views/remotesNode.ts Normal file
View File

@@ -0,0 +1,35 @@
'use strict';
import { Arrays, Iterables } from '../system';
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ExplorerNode, MessageNode, ResourceType } from './explorerNode';
import { GitService, GitUri } from '../gitService';
import { RemoteNode } from './remoteNode';
export class RemotesNode extends ExplorerNode {
readonly resourceType: ResourceType = 'gitlens:remotes';
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri);
}
async getChildren(): Promise<ExplorerNode[]> {
const remotes = Arrays.uniqueBy(await this.git.getRemotes(this.uri.repoPath!), r => r.url, r => !!r.provider);
if (remotes === undefined || remotes.length === 0) return [new MessageNode('No remotes configured')];
remotes.sort((a, b) => a.name.localeCompare(b.name));
return [...Iterables.map(remotes, r => new RemoteNode(r, this.uri, this.context, this.git))];
}
getTreeItem(): TreeItem {
const item = new TreeItem(`Remotes`, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
item.iconPath = {
dark: this.context.asAbsolutePath('images/dark/icon-remote.svg'),
light: this.context.asAbsolutePath('images/light/icon-remote.svg')
};
return item;
}
}

View File

@@ -4,12 +4,13 @@ import { BranchesNode } from './branchesNode';
import { GlyphChars } from '../constants'; import { GlyphChars } from '../constants';
import { ExplorerNode, ResourceType } from './explorerNode'; import { ExplorerNode, ResourceType } from './explorerNode';
import { GitService, GitUri } from '../gitService'; import { GitService, GitUri } from '../gitService';
// import { StatusNode } from './statusNode'; import { RemotesNode } from './remotesNode';
import { StatusNode } from './statusNode';
import { StashesNode } from './stashesNode';
export class RepositoryNode extends ExplorerNode { export class RepositoryNode extends ExplorerNode {
static readonly rootType: ResourceType = 'repository'; readonly resourceType: ResourceType = 'gitlens:repository';
readonly resourceType: ResourceType = 'repository';
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) { constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri); super(uri);
@@ -17,8 +18,10 @@ export class RepositoryNode extends ExplorerNode {
async getChildren(): Promise<ExplorerNode[]> { async getChildren(): Promise<ExplorerNode[]> {
return [ return [
// new StatusNode(this.uri, this.context, this.git), new StatusNode(this.uri, this.context, this.git),
new BranchesNode(this.uri, this.context, this.git) new BranchesNode(this.uri, this.context, this.git),
new RemotesNode(this.uri, this.context, this.git),
new StashesNode(this.uri, this.context, this.git)
]; ];
} }

View File

@@ -1,46 +0,0 @@
'use strict';
import { Event, EventEmitter, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { CommitFileNode } from './commitFileNode';
import { ExplorerNode, ResourceType } from './explorerNode';
import { CommitFormatter, GitService, GitStashCommit, GitUri } from '../gitService';
export class StashCommitNode extends ExplorerNode {
readonly resourceType: ResourceType = 'stash-commit';
private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
public get onDidChangeTreeData(): Event<ExplorerNode> {
return this._onDidChangeTreeData.event;
}
constructor(public readonly commit: GitStashCommit, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(new GitUri(commit.uri, commit));
}
async getChildren(): Promise<CommitFileNode[]> {
return Promise.resolve((this.commit as GitStashCommit).fileStatuses.map(_ => new CommitFileNode(_, this.commit, this.git.config.stashExplorer.stashFileFormat, this.context, this.git)));
}
getTreeItem(): TreeItem {
const label = CommitFormatter.fromTemplate(this.git.config.stashExplorer.stashFormat, this.commit, this.git.config.defaultDateFormat);
const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
// item.command = {
// title: 'Show Stash Details',
// command: Commands.ShowQuickCommitDetails,
// arguments: [
// new GitUri(commit.uri, commit),
// {
// commit: this.commit,
// sha: this.commit.sha
// } as ShowQuickCommitDetailsCommandArgs
// ]
// };
return item;
}
refresh() {
this._onDidChangeTreeData.fire();
}
}

View File

@@ -1,75 +0,0 @@
'use strict';
// import { Functions } from '../system';
import { commands, Event, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem, Uri } from 'vscode';
import { Commands, DiffWithPreviousCommandArgs, openEditor, OpenFileInRemoteCommandArgs } from '../commands';
import { ExplorerNode, StashCommitNode, StashNode } from './explorerNodes';
import { GitService, GitUri } from '../gitService';
export * from './explorerNodes';
export class StashExplorer implements TreeDataProvider<ExplorerNode> {
private _node: ExplorerNode;
private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
public get onDidChangeTreeData(): Event<ExplorerNode> {
return this._onDidChangeTreeData.event;
}
constructor(private context: ExtensionContext, private git: GitService) {
commands.registerCommand('gitlens.stashExplorer.refresh', this.refresh, this);
commands.registerCommand('gitlens.stashExplorer.openChanges', this.openChanges, this);
commands.registerCommand('gitlens.stashExplorer.openFile', this.openFile, this);
commands.registerCommand('gitlens.stashExplorer.openStashedFile', this.openStashedFile, this);
commands.registerCommand('gitlens.stashExplorer.openFileInRemote', this.openFileInRemote, this);
context.subscriptions.push(this.git.onDidChangeRepo(this.onRepoChanged, this));
this._node = this.getRootNode();
}
async getTreeItem(node: ExplorerNode): Promise<TreeItem> {
return node.getTreeItem();
}
async getChildren(node?: ExplorerNode): Promise<ExplorerNode[]> {
if (node === undefined) return this._node.getChildren();
return node.getChildren();
}
private getRootNode(): ExplorerNode {
const uri = new GitUri(Uri.file(this.git.repoPath), { repoPath: this.git.repoPath, fileName: this.git.repoPath });
return new StashNode(uri, this.context, this.git);
}
private onRepoChanged(reasons: ('stash' | 'unknown')[]) {
if (!reasons.includes('stash')) return;
this.refresh();
}
refresh() {
this._onDidChangeTreeData.fire();
}
private openChanges(node: StashCommitNode) {
const command = node.getCommand();
if (command === undefined || command.arguments === undefined) return;
const [uri, args] = command.arguments as [Uri, DiffWithPreviousCommandArgs];
args.showOptions!.preview = false;
return commands.executeCommand(command.command, uri, args);
}
private openFile(node: StashCommitNode) {
return openEditor(node.uri, { preserveFocus: true, preview: false });
}
private openStashedFile(node: StashCommitNode) {
return openEditor(GitService.toGitContentUri(node.uri), { preserveFocus: true, preview: false });
}
private openFileInRemote(node: StashCommitNode) {
return commands.executeCommand(Commands.OpenFileInRemote, node.commit.uri, { range: false } as OpenFileInRemoteCommandArgs);
}
}

View File

@@ -0,0 +1,14 @@
'use strict';
import { ExtensionContext } from 'vscode';
import { ResourceType } from './explorerNode';
import { GitService, GitStashCommit, IGitStatusFile } from '../gitService';
import { CommitFileNode } from './commitFileNode';
export class StashFileNode extends CommitFileNode {
readonly resourceType: ResourceType = 'gitlens:stash-file';
constructor(readonly status: IGitStatusFile, readonly commit: GitStashCommit, readonly context: ExtensionContext, readonly git: GitService) {
super(status, commit, context, git);
}
}

View File

@@ -1,29 +1,35 @@
'use strict'; 'use strict';
import { Iterables } from '../system'; import { Event, EventEmitter, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { ExplorerNode, ResourceType } from './explorerNode';
import { ExplorerNode, ResourceType, TextExplorerNode } from './explorerNode'; import { CommitFormatter, GitService, GitStashCommit, GitUri } from '../gitService';
import { GitService, GitUri } from '../gitService'; import { StashFileNode } from './stashFileNode';
import { StashCommitNode } from './stashCommitNode';
export class StashNode extends ExplorerNode { export class StashNode extends ExplorerNode {
static readonly rootType: ResourceType = 'stash-history'; readonly resourceType: ResourceType = 'gitlens:stash';
readonly resourceType: ResourceType = 'stash-history';
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) { private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
super(uri); public get onDidChangeTreeData(): Event<ExplorerNode> {
} return this._onDidChangeTreeData.event;
}
constructor(public readonly commit: GitStashCommit, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(new GitUri(commit.uri, commit));
}
async getChildren(): Promise<ExplorerNode[]> { async getChildren(): Promise<ExplorerNode[]> {
const stash = await this.git.getStashList(this.uri.repoPath!); return Promise.resolve((this.commit as GitStashCommit).fileStatuses.map(s => new StashFileNode(s, this.commit, this.context, this.git)));
if (stash === undefined) return [new TextExplorerNode('No stashed changes')];
return [...Iterables.map(stash.commits.values(), c => new StashCommitNode(c, this.context, this.git))];
} }
getTreeItem(): TreeItem { getTreeItem(): TreeItem {
const item = new TreeItem(`Stashed Changes`, TreeItemCollapsibleState.Collapsed); const label = CommitFormatter.fromTemplate(this.git.config.gitExplorer.stashFormat, this.commit, this.git.config.defaultDateFormat);
const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType; item.contextValue = this.resourceType;
return item; return item;
} }
refresh() {
this._onDidChangeTreeData.fire();
}
} }

34
src/views/stashesNode.ts Normal file
View File

@@ -0,0 +1,34 @@
'use strict';
import { Iterables } from '../system';
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ExplorerNode, MessageNode, ResourceType } from './explorerNode';
import { GitService, GitUri } from '../gitService';
import { StashNode } from './stashNode';
export class StashesNode extends ExplorerNode {
readonly resourceType: ResourceType = 'gitlens:stashes';
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri);
}
async getChildren(): Promise<ExplorerNode[]> {
const stash = await this.git.getStashList(this.uri.repoPath!);
if (stash === undefined) return [new MessageNode('No stashed changes')];
return [...Iterables.map(stash.commits.values(), c => new StashNode(c, this.context, this.git))];
}
getTreeItem(): TreeItem {
const item = new TreeItem(`Stashes`, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
item.iconPath = {
dark: this.context.asAbsolutePath('images/dark/icon-stash.svg'),
light: this.context.asAbsolutePath('images/light/icon-stash.svg')
};
return item;
}
}

Some files were not shown because too many files have changed in this diff Show More