90 Commits

Author SHA1 Message Date
Eric Amodio
c5f57172e4 Fixes issue where work tree files could no show up 2017-09-23 23:36:43 -04:00
Eric Amodio
dd0a636e24 Preps v5.2.0 2017-09-23 23:29:49 -04:00
Eric Amodio
393ec351f0 Adds gitlens.gitExplorer.includeWorkingTree setting
Adds auto-update for working trree
Fixes issues with working tree status
2017-09-23 16:29:56 -04:00
Eric Amodio
99d6da9c90 Fixes issue with whitespace collapsing in decorations 2017-09-22 01:01:43 -04:00
Eric Amodio
0eb202b8ae Closes #150 - adds auto view to custom view 2017-09-22 00:48:57 -04:00
Eric Amodio
65736a6ce7 Adds watching of creates/deletes inside .git 2017-09-21 18:43:46 -04:00
Eric Amodio
ed42eba8b8 Fixes issue with untracked files being counted properly 2017-09-21 18:43:12 -04:00
Eric Amodio
2245d82319 Preps v5.2.0-beta 2017-09-20 01:41:32 -04:00
Eric Amodio
f7df845dfe Adds working tree status to custom view (insiders)
Hides working changed files behind insiders flag
Unhides Changed Files node from behind insiders flag
Adds changed file count to Changed Files node label
Adds icon to Changed Files node
Adds upstream branch to upstream status nodes
Sorts files in the Changed Files node
2017-09-20 01:37:18 -04:00
Eric Amodio
712544fab8 Adds git diff --shortstat support 2017-09-20 01:10:47 -04:00
Eric Amodio
a114e2de87 Changes the file sort in the custom view 2017-09-20 01:09:19 -04:00
Eric Amodio
70071448d6 Closes #146 - Attempts to deal with emoji in gutter
Adds ability to automagically set the width of the gutter annotations
2017-09-20 01:01:21 -04:00
Eric Amodio
a10376385a Preps v5.1.1-beta 2017-09-17 22:41:47 -04:00
Eric Amodio
41d25803d8 Updates slack links 2017-09-17 14:38:59 -04:00
Eric Amodio
3802b43027 Adds merged PR to changelog
Adds new contributor to readme
2017-09-17 11:28:42 -04:00
Amanda Cameron
04ea3b7971 Apply Review Comments. 2017-09-17 11:20:21 -04:00
Amanda Cameron
6d7f44e091 Fix GitLab integration's multi-line selection. 2017-09-17 11:20:21 -04:00
Eric Amodio
3a1caa2e0d Disables a set of context menu items by default 2017-09-17 02:44:10 -04:00
Eric Amodio
3f7058bd48 Fixes wrong setting used to control menu commands 2017-09-17 02:43:46 -04:00
Eric Amodio
71d17bcc2f Closes #139 - adds changed files node to repository status
Reworks commit-file nodes
2017-09-17 02:34:09 -04:00
Eric Amodio
a69afdb6ef Closes #144 - support disabling the custom view 2017-09-17 01:43:41 -04:00
Eric Amodio
26c6346b84 Changes default commit format in custom view 2017-09-16 12:06:18 -04:00
Eric Amodio
3a17605017 Preps v5.1.0 2017-09-15 21:03:52 -04:00
Eric Amodio
2c9a26e47b Fixes untracked files not showing in stash list 2017-09-15 18:12:22 -04:00
Eric Amodio
1c7785fd52 Adds note for closed issue in changelog 2017-09-15 17:39:39 -04:00
Eric Amodio
079f7b7f36 Switches to use a unicode arrow for the external link icon 2017-09-15 17:39:39 -04:00
Eric Amodio
bedc1a05f5 Preps v5.1.0-beta 2017-09-15 17:39:39 -04:00
Eric Amodio
858d9ec578 Fixes issue with stashes w/ only untracked files 2017-09-15 17:39:38 -04:00
Eric Amodio
2809991096 Closes #116 - adds full commit msg to annotations
Switches to use HoverProvider for hovers in file blames
2017-09-15 17:38:37 -04:00
Eric Amodio
f6019454b6 Adds open in remote to hover annotations
Optimizes annotation computation (cache by commit)
2017-09-14 23:43:41 -04:00
Eric Amodio
f0bdf3e2c3 Always caches remotes 2017-09-14 22:46:40 -04:00
Eric Amodio
0fdf856c27 Adds performance logging 2017-09-14 22:45:23 -04:00
Eric Amodio
aacf7cc2b5 Reworks date parsing, formatting etc for perf
Isolates moment.js
2017-09-14 21:52:51 -04:00
Eric Amodio
11eacb27a1 Preps v5.0.0 2017-09-12 19:06:15 -04:00
Eric Amodio
543d39246f Closes #138 - adds ignore whitespace setting 2017-09-12 17:46:22 -04:00
Eric Amodio
6837414f22 Adds more details to remotes in custom view 2017-09-12 15:48:36 -04:00
Eric Amodio
ea6fdbaaf2 Adds groupBy function 2017-09-12 15:47:57 -04:00
Eric Amodio
ccc29e3dfc Reworks remote parsing
Combines same url into same remote
Adds a change event for custom remote providers
Adds a repo change event for custom remote providers
2017-09-12 15:46:44 -04:00
Eric Amodio
48814d4213 Changes show all commits icon 2017-09-12 13:02:04 -04:00
Eric Amodio
c3dd83cf3c Updates documention with remotes exanple 2017-09-12 11:56:17 -04:00
Eric Amodio
77482f4930 Adds openChangedFileChanges to custom view
Adds openChangedFileChangesWithWorking to custom view
Removes unneeded context checks from custom view commands
2017-09-12 11:11:03 -04:00
Eric Amodio
503b2a3785 Fixes issue where the revision wasn't properly opened
Adds ability to provide a branch to open file in remote
2017-09-12 10:38:51 -04:00
Eric Amodio
9464f7e79f Preps v5.0.0-beta.2 2017-09-11 23:51:27 -04:00
Eric Amodio
77ae37c54c Adds rudimentary "paging" to custom view branch history 2017-09-11 23:47:51 -04:00
Eric Amodio
e20ec552b7 Removes branches remote commands if no remotes
Removes branch remote commands if not tracked
2017-09-11 23:41:47 -04:00
Eric Amodio
f911447c5e Formats svg like other icons 2017-09-11 23:38:47 -04:00
Eric Amodio
38c44c808d Fixes double getRemotes call 2017-09-11 21:32:52 -04:00
Eric Amodio
655afb358e Fixes double hovers on blank lines 2017-09-11 21:32:52 -04:00
Eric Amodio
21e0963600 Adds bitbucket server support 2017-09-11 21:32:40 -04:00
Eric Amodio
62580da702 Preps v5.0.0-beta 2017-09-11 03:02:56 -04:00
Eric Amodio
6b97c107eb Updates dependencies 2017-09-11 02:27:49 -04:00
Eric Amodio
92b57580b8 Fixes #120 - Adds custom remotes support 2017-09-11 02:09:32 -04:00
Eric Amodio
e400f27c84 Adds refs changes into repo watcher
Catches branch & remote changes
2017-09-11 00:50:40 -04:00
Eric Amodio
4221e06ae3 Removes history limit for custom view 2017-09-11 00:41:32 -04:00
Eric Amodio
a2dc65c044 Adds message truncation at newline 2017-09-11 00:41:11 -04:00
Eric Amodio
4102bdd471 Reworks git command error handling
Switches to use the new diffWith command
2017-09-11 00:39:52 -04:00
Eric Amodio
d420d82ab2 Removes unneeded shortSha parameter 2017-09-11 00:36:38 -04:00
Eric Amodio
260874fa1d Adds better filename sanitization 2017-09-10 17:44:26 -04:00
Eric Amodio
9d83fbcacb Switches to use the new diffWith command 2017-09-09 15:48:44 -04:00
Eric Amodio
a50f04c569 Adds commands to hover links 2017-09-09 01:57:12 -04:00
Eric Amodio
df0599a832 Adds shortenSha method 2017-09-09 00:43:20 -04:00
Eric Amodio
f05d236e79 Updates dependencies
Updates to latest vscode engine
2017-09-08 18:47:25 -04:00
Eric Amodio
1b7610857a Fixes issue where repo change wasn't fired in some cases
Consolidates repo watching into a single watcher
Adds debounce to repo changes in the custom view
2017-09-08 18:02:43 -04:00
Eric Amodio
04d2c00ebf Fixes issue with branch name truncations (rebase) 2017-09-08 17:03:21 -04:00
Eric Amodio
ece34dba32 Cleans up some command overrides 2017-09-05 21:30:07 -04:00
Eric Amodio
68fcbf713d Fixes regression with not opening line in remote 2017-09-05 21:29:22 -04:00
Eric Amodio
e192c547b1 Fixes #130 - Stops repeated welcome for some users
No idea why the version check fails, but hopefully this will help
2017-09-04 03:30:19 -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
124 changed files with 3893 additions and 2120 deletions

View File

@@ -4,7 +4,47 @@ 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/).
## [5.0.0-alpha] - 2017-09-02
## [Unreleased]
## [5.2.0] - 2017-09-23
### Added
- Adds new `Changed Files` node to the `Repository Status` node of the `GitLens` custom view's `Repository View` -- closes [#139](https://github.com/eamodio/vscode-gitlens/issues/139)
- Provides a at-a-glance view of all "working" changes
- Expands to a file-based view of all changed files in the working tree (enabled via `"gitlens.insiders": true`) and/or all files in all commits ahead of the upstream
- Adds optional (on by default) working tree status information to the `Repository Status` node in the `GitLens` custom view
- Adds `auto` value to `gitlens.gitExplorer.view` setting - closes [#150](https://github.com/eamodio/vscode-gitlens/issues/150)
- Adds `gitlens.gitExplorer.enabled` setting to specify whether or not to show the `GitLens` custom view - closes [#144](https://github.com/eamodio/vscode-gitlens/issues/144)
- Adds `gitlens.gitExplorer.includeWorkingTree` setting to specify whether or not to include working tree files inside the `Repository Status` node of the `GitLens` custom view
- Adds `gitlens.gitExplorer.statusFileFormat` setting to the format of the status of a working or committed file in the `GitLens` custom view
### Changed
- Changes the sorting (now alphabetical) of files shown in the `GitLens` custom view
- Changes the default of the `gitlens.gitExplorer.view` setting to `auto`
- Changes the default of the `gitlens.gitExplorer.commitFormat` setting to add parentheses around the commit id
- Removes many menu items from `editor/title` & `editor/title/context` by default -- can be re-enabled via the `gitlens.advanced.menus` setting
### Fixed
- Fixes [#146](https://github.com/eamodio/vscode-gitlens/issues/146) - Blame gutter annotation issue when commit contains emoji
- Fixes an issue when running `Open File in Remote` with a multi-line selection wasn't properly opening the selection in GitLab -- thanks to [PR #145](https://github.com/eamodio/vscode-gitlens/pull/145) by Amanda Cameron ([@AmandaCameron](https://github.com/AmandaCameron))!
- Fixes an issue where the `gitlens.advanced.menus` setting wasn't controlling all the menu items properly
## [5.1.0] - 2017-09-15
### Added
- Adds full (multi-line) commit message to the `details` hover annotations -- closes [#116](https://github.com/eamodio/vscode-gitlens/issues/116)
- Adds an external link icon to the `details` hover annotations to run the `Open Commit in Remote` command (`gitlens.openCommitInRemote`)
### Changed
- Optimizes performance of the providing blame annotations, especially for large files (saw a ~78% improvement on some files)
- Optimizes date handling (parsing and formatting) for better performance and reduced memory consumption
### Removed
- Removes `gitlens.annotations.file.recentChanges.hover.wholeLine` setting as it didn't really make sense
### Fixed
- Fixes an issue where stashes with only untracked files would not show in the `Stashes` node of the GitLens custom view
- Fixes an issue where stashes with untracked files would not show its untracked files in the GitLens custom view
## [5.0.0] - 2017-09-12
### Added
- Adds an all-new `GitLens` custom view to the Explorer activity
@@ -12,55 +52,99 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
![GitLens Repository view](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-git-custom-view-repository.png)
- `Status` node — provides the status of current branch
- Indicates whether or not the current branch is up-to-date with its upstream branch
- If the branch is out of sync, additional nodes will be shown with the specific commits ahead and/or behind the upstream
- `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
- 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`, 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`, and `Show Commit Details` commands
- Provides a context menu on each branch with an `Open Branch in Remote` command
- Provides a context menu on each changed file with `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, `Open Revision in Remote`, `Apply Changes`, `Show File History`, and `Show Commit File Details` commands
- Provides a context menu on each revision (commit) with `Open Commit in Remote`, `Open All Changes`, `Open All Changes with Working Tree`, `Open Files`, `Open Revisions`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`, `Show Commit Details`, and `Refresh` commands
- Provides a context menu on each 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 the remote branches
- See `Branches` node above for details
- `Remotes` node — provides a list of remotes
- Indicates the direction of the remote (fetch, push, both), remote service (if applicable), and repository path
- 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`, `Open All Changes`, `Open All Changes with Working Tree`, `Open Files`, `Open Revisions`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`,`Show Commit Details`, and `Refresh` commands
- Provides a context menu on each remote with `Open Branches in Remote`, `Open Repository in Remote`, and `Refresh` commands
- Provides a context menu with a `Refresh` command
- `Stashes` node — provides a list of stashed changes
- Expand each stash to quickly see the set of files stashed, complete with status indicators for adds, changes, renames, and deletes
- Provides a context menu on each stash with `Apply Stashed Changes` (confirmation required), `Delete Stashed Changes` (confirmation required), `Copy Commit Message to Clipboard`, `Open Files`, and `Open Revisions` 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`, and `Show File History` commands
- Provides a context menu with `Stash Changes`, and `Refresh` commands
- Provides a context menu on each stash with `Apply Stashed Changes` (confirmation required), `Delete Stashed Changes` (confirmation required), `Open All Changes`, `Open All Changes with Working Tree`, `Open Files`, `Open Revisions`, `Copy Commit Message to Clipboard`, and `Refresh` commands
- Provides a context menu on each stashed file with `Apply Changes`, `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, and `Show File History` commands
- `History View` - provides the revision history of the active file
![GitLens History view](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-git-custom-view-history.png)
- Automatically updates to track the active editor
- 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`, and `Show Commit File Details` commands
- 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 buttons to `Search Commits`, `Switch to Repository View` or `Switch to History View`, and `Refresh`
- Provides toolbar commands to `Search Commits`, `Switch to Repository View` or `Switch to History View`, and `Refresh`
- Adds `${filePath}` token to file formatting
- Adds all-new interactivity to the hover annotations
![Hover Annotations](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-line-blame-annotations.png)
- Adds the following command-links to the `details` hover annotation
- Clicking the commit id will run the `Show Commit Details` command (`gitlens.showQuickCommitDetails`)
- Adds the following command-links to the `changes` hover annotation
- Clicking on `Changes` will run the `Compare File Revisions` command (`gitlens.diffWith`)
- Clicking the current and previous commit ids will run the `Show Commit Details` command (`gitlens.showQuickCommitDetails`)
- Adds support for remote services with custom domains -- closes [#120](https://github.com/eamodio/vscode-gitlens/issues/120)
- Adds support for the Bitbucket Server (previously called Stash) remote service -- closes [#120](https://github.com/eamodio/vscode-gitlens/issues/120)
- Adds `gitlens.blame.ignoreWhitespace` setting to specify whether or not to ignore whitespace when comparing revisions during blame operations -- closes [#138](https://github.com/eamodio/vscode-gitlens/issues/138)
- Adds `Compare File Revisions` command (`gitlens.diffWith`) - compares the specified file revisions
- 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
- Renames `Compare File with Previous` command (gitlens.diffWithPrevious) to `Compare File with Previous Revision`
- 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`
- Renames `gitlens.stashExplorer.stashFormat` setting to `gitlens.gitExplorer.stashFormat`
- Renames `gitlens.stashExplorer.stashFileFormat` setting to `gitlens.gitExplorer.stashFileFormat`
- Changes `gitlens.gitExplorer.stashFileFormat` setting to defaults to `${filePath}` for better separator handling
## Removed
- Removes the `Git Stashes` custom view view - as it's functionality has been folded into the new `GitLens` custom view
### 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
### Fixed
- Fixes an issue where double hover annotations could be shown on blank lines
- Fixes an issue where remote branches couldn't be opened properly in their remote service
- Fixes [#130](https://github.com/eamodio/vscode-gitlens/issues/130) - First-run "Thank you for choosing GitLens! [...]" info message shown on every start up
- Fixes an issue where sometimes diffs (via branch name) wouldn't open properly
- Fixes an issue where remotes are queried more than once on startup
## [4.4.3] - 2017-08-30
## Fixed
@@ -121,9 +205,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
## [4.3.0] - 2017-07-03
## Added
- 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)
- Shows all of the stashed changes in the repository
- Provides toolbar buttons to `Stash Changes` and `Refresh`
- Provides a context menu with `Apply Stashed Changes` and `Delete Stashed Changes` commands - both require a confirmation
@@ -132,7 +213,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
## [4.2.0] - 2017-06-27
## Added
- Adds `Compare File with Revision...` command (`gitlens.diffWithRevision`) - compare the active file with the selected revision of the same file
- Adds `Compare File with Revision...` command (`gitlens.diffWithRevision`) - compares the active file with the selected revision of the same file
- Adds `Open Changed Files` command (`gitlens.openChangedFiles`) to the source control group context menu
- Adds `Close Unchanged Files` command (`gitlens.closeUnchangedFiles`) to the source control group context menu
- Adds `Open File in Remote` command (`gitlens.openFileInRemote`) to the source control resource context menu
@@ -186,7 +267,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Fixed
- Fixes excessive memory usage when parsing diffs
- Fixes extra newline in multiline commit messages
- Fixes extra newline in multi-line commit messages
- Fixes (again) [#33](https://github.com/eamodio/vscode-gitlens/issues/33) - Commit messages can causes markdown formatting in hovers
## [4.0.1] - 2017-06-09
@@ -270,7 +351,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Added
- Improves performance
- Reduces the number of git calls on known "untrackables"
- Caches many more git commands to reduce git command roundtrips and parsing
- Caches many more git commands to reduce git command round-trips and parsing
- Increases the debounce (delay) on cursor movement to reduce lag when navigating around a file
- Adds diff information (the line's previous version) into the active line hover when the current line is uncommitted
- Adds `gitlens.statusBar.alignment` settings to control the alignment of the status bar -- thanks to [PR #72](https://github.com/eamodio/vscode-gitlens/pull/72) by Zack Schuster ([@zackschuster](https://github.com/zackschuster))!
@@ -461,7 +542,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Fixed
- Fixes issue with `gitlens.diffWithPrevious` command execution via code lens when the code lens was not at the document/file level
- Fixes issue where full shas were displayed on the file/blame history explorers
- Fixes [#30](https://github.com/eamodio/vscode-gitlens/issues/30) - Diff with Working Tree fails from repo/commit quickpick list if file was renamed (and the commit was before the rename)
- Fixes [#30](https://github.com/eamodio/vscode-gitlens/issues/30) - Diff with Working Tree fails from repo/commit quick pick list if file was renamed (and the commit was before the rename)
- Fixes various other quick pick menu command issues when a file was renamed
- Fixes various issues when caching is disabled
- Fixes issues with parsing commits history
@@ -564,7 +645,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
## [2.9.0]
### Changed
- To accomodate the realization that blame information is invalid when a file has unsaved changes, the following behavior changes have been made
- To accommodate the realization that blame information is invalid when a file has unsaved changes, the following behavior changes have been made
- Status bar blame information will hide
- Code lens change to a `Cannot determine...` message and become unclickable
- Many menu choices and commands will hide
@@ -602,7 +683,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Fixed
- Fixes [#34](https://github.com/eamodio/vscode-gitlens/issues/34) - Open file should open the selected version of the file
- Fixes some issue where some editors opened by the quickpick would not be opened in preview tabs
- Fixes some issue where some editors opened by the quick pick would not be opened in preview tabs
- Fixes issue where copy to clipboard commands would fail if there was no active editor
- Fixes issue where active line annotations would show for opened versioned files
- Fixes issue where code lens compare commands on opened versioned files would fail

View File

@@ -1,7 +1,7 @@
[![](https://vsmarketplacebadge.apphb.com/version/eamodio.gitlens.svg)](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)
[![](https://vsmarketplacebadge.apphb.com/installs/eamodio.gitlens.svg)](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)
[![](https://vsmarketplacebadge.apphb.com/rating/eamodio.gitlens.svg)](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)
[![Chat at https://vscode-gitlens.slack.com/](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/chat-badge.png)](https://join.slack.com/t/vscode-gitlens/shared_invite/MjIxOTgxNDE3NzM0LTE1MDE2Nzk1MTgtMjkwMmZjMzcxNQ)
[![Chat at https://vscode-dev-community.slack.com/](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/chat-badge.png)](https://join.slack.com/t/vscode-dev-community/shared_invite/enQtMjIxOTgxNDE3NzM0LWU5M2ZiZDU1YjBlMzdlZjA2YjBjYzRhYTM5NTgzMTAxMjdiNWU0ZmQzYWI3MWU5N2Q1YjBiYmQ4MzY0NDE1MzY)
# GitLens
@@ -21,7 +21,11 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
![Line Blame Annotation](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/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)
- Adds a `details` hover annotation to the current line annotation, which provides more commit details ([optional](#line-blame-annotation-settings), on by default)
- Clicking the commit id will run the `Show Commit Details` command (`gitlens.showQuickCommitDetails`)
- 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)
- Clicking on `Changes` will run the `Compare File Revisions` command (`gitlens.diffWith`)
- Clicking the current and previous commit ids will run the `Show Commit Details` command (`gitlens.showQuickCommitDetails`)
- Clicking on external link icon will run the the `Open Commit in Remote` command (`gitlens.openCommitInRemote`)
![Line Blame Annotations](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-line-blame-annotations.png)
@@ -31,6 +35,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- Choose between `gutter` (default) and `hover` [annotation styles](#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)
- Clicking the commit id will run the `Show Commit Details` command (`gitlens.showQuickCommitDetails`)
- Adds a `heatmap` (age) indicator to the gutter annotations (on right edge by [default](#file-blame-annotation-settings)), which provides an easy, at-a-glance way to tell the age of a line ([optional](#file-blame-annotation-settings), on by default)
- Indicator ranges from bright yellow (newer) to dark brown (older)
- Press `Escape` to quickly toggle the annotations off
@@ -61,7 +66,10 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- Adds on-demand, [customizable](#file-recent-changes-annotation-settings) and [themeable](#theme-settings), **recent changes annotations** of the whole file
- Highlights all of lines changed in the most recent commit
- Adds a `details` hover annotation to each line, which provides more commit details ([optional](#file-blame-annotation-settings), on by default)
- Clicking the commit id will run the `Show Commit Details` command (`gitlens.showQuickCommitDetails`)
- Adds a `changes` (diff) hover annotation to each line, which provides **instant** access to the line's previous version ([optional](#file-recent-changes-annotation-settings), on by default)
- Clicking on `Changes` will run the `Compare File Revisions` command (`gitlens.diffWith`)
- Clicking the current and previous commit ids will run the `Show Commit Details` command (`gitlens.showQuickCommitDetails`)
- Press `Escape` to quickly toggle the annotations off
- Adds `Toggle Recent File Changes Annotations` command (`gitlens.toggleFileRecentChanges`) to toggle the recent changes annotations on and off
@@ -117,39 +125,62 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
![GitLens Repository view](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-git-custom-view-repository.png)
- `Status` node — provides the status of current branch
- Indicates whether or not the current branch is up-to-date with its upstream branch
- If the branch is out of sync, additional nodes will be shown with the specific commits ahead and/or behind the upstream
- `Repository Status` node — provides the status of the repository
- Provides the name of the current branch, [optionally](#gitlens-custom-view-settings) its working tree status, and its upstream tracking branch and 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 upstream status nodes, if the current branch is tracking a remote branch and
- is behind the upstream — quickly see and explore the specific commits behind the upstream (i.e. commits that haven't been pulled)
- is ahead of the upstream — quickly see and explore the specific commits ahead of the upstream (i.e. commits that haven't been pushed)
- `Changed Files` node — provides a at-a-glance view of all "working" changes
- Expands to a file-based view of all changed files in the working tree ([optionally](#gitlens-custom-view-settings)) and/or all files in all commits ahead of 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
- Indicates which branch is the current branch and [optionally](#gitlens-custom-view-settings) shows the remote tracking branch
- Expand each branch to easily see its revision (commit) history
- Expand each revision (commit) to quickly see the set of files changed, complete with status indicators for adds, changes, renames, and deletes
- Provides a context menu on each changed file with `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, `Open Revision in Remote`, 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`, and `Show Commit Details` commands
- Provides a context menu on each branch with an `Open Branch in Remote` command
- 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
- `Remotes` node — provides a list of the remote branches
- See `Branches` node above for details
- Provides a context menu on each revision (commit) with `Open Commit in Remote`, `Open All Changes`, `Open All Changes with Working Tree`, `Open Files`, `Open Revisions`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`, `Show Commit Details`, and `Refresh` commands
- Provides a context menu on each branch with `Open Branch in Remote`, and `Refresh` commands
- Provides a context menu with `Open Branches in Remote`, and `Refresh` commands
- `Remotes` node — provides a list of remotes
- Indicates the direction of the remote (fetch, push, both), remote service (if applicable), and repository path
- 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`, `Show File History`, and `Show Commit File Details` commands
- Provides a context menu on each revision (commit) with `Open Commit in Remote`, `Open All Changes`, `Open All Changes with Working Tree`, `Open Files`, `Open Revisions`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`,`Show Commit Details`, and `Refresh` commands
- Provides a context menu on each remote with `Open Branches in Remote`, `Open Repository in Remote`, and `Refresh` commands
- Provides a context menu with a `Refresh` command
- `Stashes` node — provides a list of stashed changes
- Expand each stash to quickly see the set of files stashed, complete with status indicators for adds, changes, renames, and deletes
- Provides a context menu on each stash with `Apply Stashed Changes` (confirmation required), `Delete Stashed Changes` (confirmation required), `Copy Commit Message to Clipboard`, `Open Files`, and `Open Revisions` 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`, and `Show File History` commands
- Provides a context menu with `Stash Changes`, and `Refresh` commands
- Provides a context menu on each stash with `Apply Stashed Changes` (confirmation required), `Delete Stashed Changes` (confirmation required), `Open All Changes`, `Open All Changes with Working Tree`, `Open Files`, `Open Revisions`, `Copy Commit Message to Clipboard`, and `Refresh` commands
- Provides a context menu on each stashed file with `Apply Changes`, `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, and `Show File History` commands
- `History View` - provides the revision history of the active file
![GitLens History view](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-git-custom-view-history.png)
- Automatically updates to track the active editor
- 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`, and `Show Commit File Details` commands
- 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 buttons to `Search Commits`, `Switch to Repository View` or `Switch to History View`, and `Refresh`
- 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 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
- Also supports [remote services with custom domains](#custom-remotes-settings), such as **BitBucket, Bitbucket Server (previously called Stash), GitHub, GitHub Enterprise, GitLab**
- `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 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
@@ -243,6 +274,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 `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
@@ -260,7 +292,11 @@ GitLens is highly customizable and provides many configuration settings to allow
|`gitlens.insiders`|Opts into the insiders channel -- provides access to upcoming features
|`gitlens.outputLevel`|Specifies how much (if any) output will be sent to the GitLens output channel
### Blame Annotation Settings
### Blame Settings
|Name | Description
|-----|------------
|`gitlens.blame.ignoreWhitespace`|Specifies whether or not to ignore whitespace when comparing revisions during blame operations
#### File Blame Annotation Settings
@@ -300,7 +336,6 @@ GitLens is highly customizable and provides many configuration settings to allow
|`gitlens.recentChanges.file.lineHighlight.locations`|Specifies where the highlights of the recently changed lines will be shown<br />`gutter` - adds a gutter glyph<br />`line` - adds a full-line highlight background color<br />`overviewRuler` - adds a decoration to the overviewRuler (scroll bar)
|`gitlens.annotations.file.recentChanges.hover.details`|Specifies whether or not to provide a commit details hover annotation
|`gitlens.annotations.file.recentChanges.hover.changes`|Specifies whether or not to provide a changes (diff) hover annotation
|`gitlens.annotations.file.recentChanges.hover.wholeLine`|Specifies whether or not to trigger hover annotations over the whole line
### Code Lens Settings
@@ -311,7 +346,7 @@ GitLens is highly customizable and provides many configuration settings to allow
|`gitlens.codeLens.recentChange.command`|Specifies the command to be executed when the `recent change` code lens is clicked<br />`gitlens.toggleFileBlame` - toggles file blame annotations<br />`gitlens.showBlameHistory` - opens the blame history explorer<br />`gitlens.showFileHistory` - opens the file history explorer<br />`gitlens.diffWithPrevious` - compares the current committed file with the previous commit<br />`gitlens.showQuickCommitDetails` - shows a commit details quick pick<br />`gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick<br />`gitlens.showQuickFileHistory` - shows a file history quick pick<br />`gitlens.showQuickRepoHistory` - shows a branch history quick pick
|`gitlens.codeLens.authors.enabled`|Specifies whether or not to show an `authors` code lens showing number of authors of the file or code block and the most prominent author (if there is more than one)
|`gitlens.codeLens.authors.command`|Specifies the command to be executed when the `authors` code lens is clicked<br />`gitlens.toggleFileBlame` - toggles file blame annotations<br />`gitlens.showBlameHistory` - opens the blame history explorer<br />`gitlens.showFileHistory` - opens the file history explorer<br />`gitlens.diffWithPrevious` - compares the current committed file with the previous commit<br />`gitlens.showQuickCommitDetails` - shows a commit details quick pick<br />`gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick<br />`gitlens.showQuickFileHistory` - shows a file history quick pick<br />`gitlens.showQuickRepoHistory` - shows a branch history quick pick
|`gitlens.codeLens.locations`|Specifies where Git code lens will be shown in the document<br />`document` - adds code lens at the top of the document<br />`containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc)<br />`blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines<br />`custom` - adds code lens at the start of symbols contained in `gitlens.codeLens.locationCustomSymbols`
|`gitlens.codeLens.locations`|Specifies where Git code lens will be shown in the document<br />`document` - adds code lens at the top of the document<br />`containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc)<br />`blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines<br />`custom` - adds code lens at the start of symbols contained in `gitlens.codeLens.locationCustomSymbols`
|`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
@@ -319,11 +354,21 @@ GitLens is highly customizable and provides many configuration settings to allow
|Name | Description
|-----|------------
|`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.enabled`|Specifies whether or not to show the `GitLens` custom view"
|`gitlens.gitExplorer.view`|Specifies the starting view (mode) of the `GitLens` custom view<br /> `auto` - shows the last selected view, defaults to `repository`<br />`history` - shows the commit history of the active file<br />`repository` - shows a repository explorer"
|`gitlens.gitExplorer.includeWorkingTree`|Specifies whether or not to include working tree files inside the `Repository Status` node of the `GitLens` custom view
|`gitlens.gitExplorer.showTrackingBranch`|Specifies whether or not to show the tracking branch when displaying local branches in the `GitLens` custom view"
|`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
|`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.gitExplorer.statusFileFormat`|Specifies the format of the status of a working or committed file in the `GitLens` custom view<br />Available tokens<br /> ${file} - file name<br /> ${filePath} - file name and path<br /> ${path} - file path<br />${working} - optional indicator if the file is uncommitted
### Custom Remotes Settings
|Name | Description
|-----|------------
|`gitlens.remotes`|Specifies any custom domains for remote (code-hosting) services<br />Example: ```"gitlens.remotes": [{ "domain": "git.corporate-url.com", "type": "GitHub" }]```
### Status Bar Settings
@@ -384,6 +429,7 @@ GitLens is highly customizable and provides many configuration settings to allow
A big thanks to the people that have contributed to this project:
- Amanda Cameron ([@AmandaCameron](https://github.com/AmandaCameron)) — [contributions](https://github.com/eamodio/vscode-gitlens/commits?author=AmandaCameron))
- Peng Lyu ([@rebornix](https://github.com/rebornix)) — [contributions](https://github.com/eamodio/vscode-gitlens/commits?author=rebornix))
- Aurelio Ogliari ([@nobitagit](https://github.com/nobitagit)) — [contributions](https://github.com/eamodio/vscode-gitlens/commits?author=nobitagit)
- Johannes Rieken ([@jrieken](https://github.com/jrieken)) — [contributions](https://github.com/eamodio/vscode-gitlens/commits?author=jrieken))

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" xmlns="http://www.w3.org/2000/svg">
<path fill="#C5C5C5" d="m7.5,10l2,0l0,1l-2,0l0,2l-1,0l0,-2l-2,0l0,-1l2,0l0,-2l1,0l0,2l0,0zm-3,6l5,0l0,-1l-5,0l0,1l0,0zm4.5,-11l3.5,3.5l0,9.5c0,0.55 -0.45,1 -1,1l-9,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l6.5,0l0,0zm2.5,4l-3,-3l-6,0l0,12l9,0l0,-9l0,0zm-1.5,-6l-5.5,0l0,1l5,0l4,4l0,8l1,0l0,-8.5l-4.5,-4.5l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 437 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

@@ -1,4 +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">
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<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>

Before

Width:  |  Height:  |  Size: 893 B

After

Width:  |  Height:  |  Size: 846 B

View File

@@ -1,6 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#7F4E7E" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
C
!
</text>
</svg>

Before

Width:  |  Height:  |  Size: 431 B

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#6C6C6C" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
?
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 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.24,10.4c0.19,1.28 -0.2,2.62 -1.2,3.6c-1.47,1.45 -3.74,1.63 -5.41,0.54l1.17,-1.14l-4.3,-0.6l0.6,4.2l1.31,-1.26c2.36,1.74 5.7,1.57 7.84,-0.54c1.24,-1.23 1.81,-2.85 1.74,-4.46l-1.75,-0.34l0,0zm-7.28,-2.4c1.47,-1.45 3.74,-1.63 5.41,-0.54l-1.17,1.14l4.3,0.6l-0.6,-4.2l-1.31,1.26c-2.36,-1.74 -5.7,-1.57 -7.85,0.54c-1.24,1.23 -1.8,2.85 -1.73,4.46l1.75,0.35c-0.19,-1.28 0.2,-2.63 1.2,-3.61l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 546 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.5,11l2.5,2.5c0,0.55 -0.45,1 -1,1l-4,0l0,-1l3.5,0l-2,-2l-7,0l-2,2l3.5,0l0,1l-4,0c-0.55,0 -1,-0.45 -1,-1l2.5,-2.5l-2.5,-2.5c0,-0.55 0.45,-1 1,-1l4,0l0,1l-3.5,0l2,2l7,0l2,-2l-3.5,0l0,-1l4,0c0.55,0 1,0.45 1,1l-2.5,2.5l0,0zm-5.5,-1.5l2,0l0,-3l2,0l-3,-3l-3,3l2,0l0,3l0,0zm2,3l-2,0l0,3l-2,0l3,3l3,-3l-2,0l0,-3l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 467 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="22" xmlns="http://www.w3.org/2000/svg">
<path fill="#424242" d="m7.5,10l2,0l0,1l-2,0l0,2l-1,0l0,-2l-2,0l0,-1l2,0l0,-2l1,0l0,2l0,0zm-3,6l5,0l0,-1l-5,0l0,1l0,0zm4.5,-11l3.5,3.5l0,9.5c0,0.55 -0.45,1 -1,1l-9,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l6.5,0l0,0zm2.5,4l-3,-3l-6,0l0,12l9,0l0,-9l0,0zm-1.5,-6l-5.5,0l0,1l5,0l4,4l0,8l1,0l0,-8.5l-4.5,-4.5l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 437 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

@@ -1,4 +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">
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<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>

Before

Width:  |  Height:  |  Size: 893 B

After

Width:  |  Height:  |  Size: 846 B

View File

@@ -1,6 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#9B4F96" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
C
!
</text>
</svg>

Before

Width:  |  Height:  |  Size: 431 B

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#6C6C6C" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
?
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 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.24,10.4c0.19,1.28 -0.2,2.62 -1.2,3.6c-1.47,1.45 -3.74,1.63 -5.41,0.54l1.17,-1.14l-4.3,-0.6l0.6,4.2l1.31,-1.26c2.36,1.74 5.7,1.57 7.84,-0.54c1.24,-1.23 1.81,-2.85 1.74,-4.46l-1.75,-0.34l0,0zm-7.28,-2.4c1.47,-1.45 3.74,-1.63 5.41,-0.54l-1.17,1.14l4.3,0.6l-0.6,-4.2l-1.31,1.26c-2.36,-1.74 -5.7,-1.57 -7.85,0.54c-1.24,1.23 -1.8,2.85 -1.73,4.46l1.75,0.35c-0.19,-1.28 0.2,-2.63 1.2,-3.61l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 546 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.5,11l2.5,2.5c0,0.55 -0.45,1 -1,1l-4,0l0,-1l3.5,0l-2,-2l-7,0l-2,2l3.5,0l0,1l-4,0c-0.55,0 -1,-0.45 -1,-1l2.5,-2.5l-2.5,-2.5c0,-0.55 0.45,-1 1,-1l4,0l0,1l-3.5,0l2,2l7,0l2,-2l-3.5,0l0,-1l4,0c0.55,0 1,0.45 1,1l-2.5,2.5l0,0zm-5.5,-1.5l2,0l0,-3l2,0l-3,-3l-3,3l2,0l0,3l0,0zm2,3l-2,0l0,3l-2,0l3,3l3,-3l-2,0l0,-3l0,0z" />
</svg>

After

Width:  |  Height:  |  Size: 467 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: 23 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

74
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "gitlens",
"version": "5.0.0-alpha",
"version": "5.2.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -16,19 +16,19 @@
"integrity": "sha1-qjuL2ivlErGuCgV7lC6GnDcKVWk=",
"dev": true,
"requires": {
"@types/node": "8.0.26"
"@types/node": "8.0.28"
}
},
"@types/mocha": {
"version": "2.2.42",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.42.tgz",
"integrity": "sha512-b6gVDoxEbAQGwbV7gSzeFw/hy3/eEAokztktdzl4bHvGgb9K5zW4mVQDlVYch2w31m8t/J7L2iqhQvz3r5edCQ==",
"version": "2.2.43",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.43.tgz",
"integrity": "sha512-xNlAmH+lRJdUMXClMTI9Y0pRqIojdxfm7DHsIxoB2iTzu3fnPmSMEN8SsSx0cdwV36d02PWCWaDUoZPDSln+xw==",
"dev": true
},
"@types/node": {
"version": "8.0.26",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.26.tgz",
"integrity": "sha512-wbKN0MB4XsjdnSE04HiCzLoBDirGCM6zXrqavSj44nZnPFYpnrTF64E9O6Xmf0ca/IuKK/BHUcXwMiwk92gW6Q==",
"version": "8.0.28",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.28.tgz",
"integrity": "sha512-HupkFXEv3O3KSzcr3Ylfajg0kaerBg1DyaZzRBBQfrU3NN1mTBRE7sCveqHwXLS5Yrjvww8qFzkzYQQakG9FuQ==",
"dev": true
},
"@types/tmp": {
@@ -47,11 +47,6 @@
"json-stable-stringify": "1.0.1"
}
},
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
},
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
@@ -348,7 +343,7 @@
"resolved": "https://registry.npmjs.org/copy-paste/-/copy-paste-1.3.0.tgz",
"integrity": "sha1-p+bEocKP3t8rCB5yuX3y75X0ce0=",
"requires": {
"iconv-lite": "0.4.18",
"iconv-lite": "0.4.19",
"sync-exec": "0.6.2"
}
},
@@ -1248,6 +1243,12 @@
"sntp": "1.0.9"
}
},
"he": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
"dev": true
},
"hoek": {
"version": "2.16.3",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
@@ -1266,9 +1267,9 @@
}
},
"iconv-lite": {
"version": "0.4.18",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz",
"integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA=="
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
"integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
},
"ignore": {
"version": "3.3.5",
@@ -1330,11 +1331,6 @@
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"is-glob": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
@@ -1788,9 +1784,9 @@
}
},
"mocha": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.0.tgz",
"integrity": "sha512-pIU2PJjrPYvYRqVpjXzj76qltO9uBYI7woYAMoxbSefsa+vqAfptjoeevd6bUgwD0mPIO+hv9f7ltvsNreL2PA==",
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.2.tgz",
"integrity": "sha512-iH5zl7afRZl1GvD0pnrRlazgc9Z/o34pXWmTFi8xNIMFKXgNL1SoBTDDb9sJfbV/sJV/j8X+0gvwY1QS1He7Nw==",
"dev": true,
"requires": {
"browser-stdout": "1.3.0",
@@ -1800,6 +1796,7 @@
"escape-string-regexp": "1.0.5",
"glob": "7.1.1",
"growl": "1.9.2",
"he": "1.1.1",
"json3": "3.3.2",
"lodash.create": "3.1.1",
"mkdirp": "0.5.1",
@@ -2234,9 +2231,9 @@
"dev": true
},
"source-map-support": {
"version": "0.4.16",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.16.tgz",
"integrity": "sha512-A6vlydY7H/ljr4L2UOhDSajQdZQ6dMD7cLH0pzwcmwLyc9u8PNI4WGtnfDDzX7uzGL6c/T+ORL97Zlh+S4iOrg==",
"version": "0.4.18",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
"integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
"dev": true,
"requires": {
"source-map": "0.5.7"
@@ -2336,29 +2333,12 @@
"safe-buffer": "5.1.1"
}
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"requires": {
"is-fullwidth-code-point": "2.0.0",
"strip-ansi": "4.0.0"
}
},
"stringstream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
"integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=",
"dev": true
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"requires": {
"ansi-regex": "3.0.0"
}
},
"strip-bom": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
@@ -2707,10 +2687,10 @@
"gulp-symdest": "1.1.0",
"gulp-untar": "0.0.6",
"gulp-vinyl-zip": "1.4.0",
"mocha": "3.5.0",
"mocha": "3.5.2",
"request": "2.81.0",
"semver": "5.4.1",
"source-map-support": "0.4.16",
"source-map-support": "0.4.18",
"url-parse": "1.1.9",
"vinyl-source-stream": "1.1.0"
}

View File

@@ -1,13 +1,13 @@
{
"name": "gitlens",
"version": "5.0.0-alpha",
"version": "5.2.0",
"author": {
"name": "Eric Amodio",
"email": "eamodio@gmail.com"
},
"publisher": "eamodio",
"engines": {
"vscode": "^1.15.0"
"vscode": "^1.16.0"
},
"license": "SEE LICENSE IN LICENSE",
"displayName": "Git Lens \u2014 git blame annotations, code lens, and more",
@@ -15,8 +15,8 @@
"badges": [
{
"url": "https://img.shields.io/badge/chat-on%20slack-brightgreen.svg",
"href": "https://join.slack.com/t/vscode-gitlens/shared_invite/MjIxOTgxNDE3NzM0LTE1MDE2Nzk1MTgtMjkwMmZjMzcxNQ",
"description": "Chat at https://vscode-gitlens.slack.com/"
"href": "https://join.slack.com/t/vscode-dev-community/shared_invite/enQtMjIxOTgxNDE3NzM0LWU5M2ZiZDU1YjBlMzdlZjA2YjBjYzRhYTM5NTgzMTAxMjdiNWU0ZmQzYWI3MWU5N2Q1YjBiYmQ4MzY0NDE1MzY",
"description": "Chat at https://vscode-dev-community.slack.com/"
}
],
"categories": [
@@ -132,11 +132,6 @@
"default": true,
"description": "Specifies whether or not to provide a changes (diff) hover annotation"
},
"gitlens.annotations.file.recentChanges.hover.wholeLine": {
"type": "boolean",
"default": true,
"description": "Specifies whether or not to trigger hover annotations over the whole line"
},
"gitlens.annotations.line.hover.details": {
"type": "boolean",
"default": true,
@@ -172,6 +167,11 @@
"default": false,
"description": "Specifies whether or not to trigger hover annotations over the whole line"
},
"gitlens.blame.ignoreWhitespace": {
"type": "boolean",
"default": false,
"description": "Specifies whether or not to ignore whitespace when comparing revisions during blame operations"
},
"gitlens.blame.file.annotationType": {
"type": "string",
"default": "gutter",
@@ -303,7 +303,7 @@
"minItems": 1,
"maxItems": 4,
"uniqueItems": true,
"description": "Specifies where Git code lens will be shown in the document\n `document` - adds code lens at the top of the document\n `containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc)\n `blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines\n `custom` - adds code lens at the start of symbols contained in `gitlens.codeLens.locationCustomSymbols`"
"description": "Specifies where Git code lens will be shown in the document\n `document` - adds code lens at the top of the document\n `containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc)\n `blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines\n `custom` - adds code lens at the start of symbols contained in `gitlens.codeLens.locationCustomSymbols`"
},
"gitlens.codeLens.customLocationSymbols": {
"type": "array",
@@ -388,7 +388,7 @@
"minItems": 1,
"maxItems": 4,
"uniqueItems": true,
"description": "Specifies where Git code lens will be shown in the document for the specified language\n `document` - adds code lens at the top of the document\n `containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc)\n `blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines\n `custom` - adds code lens at the start of symbols contained in `customSymbols`"
"description": "Specifies where Git code lens will be shown in the document for the specified language\n `document` - adds code lens at the top of the document\n `containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc)\n `blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines\n `custom` - adds code lens at the start of symbols contained in `customSymbols`"
},
"customSymbols": {
"type": "array",
@@ -413,18 +413,9 @@
"default": null,
"description": "Specifies how all absolute dates will be formatted by default\nSee https://momentjs.com/docs/#/displaying/format/ for valid formats"
},
"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.gitExplorer.commitFormat": {
"type": "string",
"default": "${message} \u00a0\u2022\u00a0 ${authorAgo} \u00a0\u2022\u00a0 ${id}",
"default": "${message} \u00a0\u2022\u00a0 ${authorAgo} \u00a0 (${id})",
"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.gitExplorer.commitFileFormat": {
@@ -432,6 +423,21 @@
"default": "${filePath}",
"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.enabled": {
"type": "boolean",
"default": true,
"description": "Specifies whether or not to show the `GitLens` custom view"
},
"gitlens.gitExplorer.includeWorkingTree": {
"type": "boolean",
"default": true,
"description": "Specifies whether or not to include working tree files inside the `Repository Status` node of the `GitLens` custom view"
},
"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}",
@@ -442,6 +448,50 @@
"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.statusFileFormat": {
"type": "string",
"default": "${working}${filePath}",
"description": "Specifies the format of the status of a working or committed file in the `GitLens` custom view\nAvailable tokens\n ${file} - file name\n ${filePath} - file name and path\n ${path} - file path\n ${working} - optional indicator if the file is uncommitted"
},
"gitlens.gitExplorer.view": {
"type": "string",
"default": "auto",
"enum": [
"auto",
"history",
"repository"
],
"description": "Specifies the starting view (mode) of the `GitLens` custom view\n `auto` - shows the last selected view, defaults to `repository`\n `history` - shows the commit history of the active file\n `repository` - shows a repository explorer"
},
"gitlens.remotes": {
"type": "array",
"default": null,
"items": {
"type": "object",
"required": [
"type",
"domain"
],
"properties": {
"type": {
"type": "string",
"enum": [
"Bitbucket",
"BitbucketServer",
"GitHub",
"GitLab"
],
"description": "Specifies the type of the custom remote service\n `Bitbucket`, `BitbucketServer`, `GitHub`, or `GitLab`"
},
"domain": {
"type": "string",
"description": "Specifies the domain name of the custom remote service"
}
}
},
"uniqueItems": true,
"description": "Specifies any custom domains for remote (code-hosting) services"
},
"gitlens.statusBar.enabled": {
"type": "boolean",
"default": true,
@@ -612,16 +662,16 @@
},
"editorTitle": {
"blame": true,
"fileDiff": true,
"history": true,
"remote": true,
"status": true
"fileDiff": false,
"history": false,
"remote": false,
"status": false
},
"editorTitleContext": {
"blame": true,
"fileDiff": true,
"history": true,
"remote": true
"blame": false,
"fileDiff": false,
"history": false,
"remote": false
},
"explorerContext": {
"fileDiff": true,
@@ -779,6 +829,11 @@
"title": "Directory Compare",
"category": "GitLens"
},
{
"command": "gitlens.diffWith",
"title": "Compare File Revisions",
"category": "GitLens"
},
{
"command": "gitlens.diffWithBranch",
"title": "Compare File with Branch...",
@@ -949,6 +1004,11 @@
"title": "Open Changed Files",
"category": "GitLens"
},
{
"command": "gitlens.openBranchesInRemote",
"title": "Open Branches in Remote",
"category": "GitLens"
},
{
"command": "gitlens.openBranchInRemote",
"title": "Open Branch in Remote",
@@ -1050,10 +1110,25 @@
"title": "Open Files",
"category": "GitLens"
},
{
"command": "gitlens.gitExplorer.openChangedFileChanges",
"title": "Open All Changes",
"category": "GitLens"
},
{
"command": "gitlens.gitExplorer.openChangedFileChangesWithWorking",
"title": "Open All Changes with Working Tree",
"category": "GitLens"
},
{
"command": "gitlens.gitExplorer.openChangedFileRevisions",
"title": "Open Revisions",
"category": "GitLens"
},
{
"command": "gitlens.gitExplorer.applyChanges",
"title": "Apply Changes",
"category": "GitLens"
}
],
"menus": {
@@ -1062,6 +1137,10 @@
"command": "gitlens.diffDirectory",
"when": "gitlens:enabled"
},
{
"command": "gitlens.diffWith",
"when": "false"
},
{
"command": "gitlens.diffWithBranch",
"when": "gitlens:isTracked"
@@ -1178,6 +1257,10 @@
"command": "gitlens.openChangedFiles",
"when": "gitlens:enabled"
},
{
"command": "gitlens.openBranchesInRemote",
"when": "gitlens:hasRemotes"
},
{
"command": "gitlens.openBranchInRemote",
"when": "gitlens:hasRemotes"
@@ -1242,9 +1325,21 @@
"command": "gitlens.gitExplorer.openChangedFiles",
"when": "false"
},
{
"command": "gitlens.gitExplorer.openChangedFileChanges",
"when": "false"
},
{
"command": "gitlens.gitExplorer.openChangedFileChangesWithWorking",
"when": "false"
},
{
"command": "gitlens.gitExplorer.openChangedFileRevisions",
"when": "false"
},
{
"command": "gitlens.gitExplorer.applyChanges",
"when": "false"
}
],
"editor/context": [
@@ -1318,12 +1413,12 @@
},
{
"command": "gitlens.openFileInRemote",
"when": "gitlens:enabled && config.gitlens.advanced.menus.editorTitleContext.remote",
"when": "gitlens:enabled && gitlens:hasRemotes && config.gitlens.advanced.menus.editorTitle.remote",
"group": "1_gitlens"
},
{
"command": "gitlens.openRepoInRemote",
"when": "gitlens:enabled && config.gitlens.advanced.menus.editorTitleContext.remote",
"when": "gitlens:enabled && gitlens:hasRemotes && config.gitlens.advanced.menus.editorTitle.remote",
"group": "1_gitlens"
},
{
@@ -1355,7 +1450,7 @@
"editor/title/context": [
{
"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"
},
{
@@ -1382,7 +1477,7 @@
"explorer/context": [
{
"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"
},
{
@@ -1411,6 +1506,11 @@
"command": "gitlens.closeUnchangedFiles",
"when": "gitlens:enabled",
"group": "1_gitlens@2"
},
{
"command": "gitlens.stashSave",
"when": "gitlens:enabled",
"group": "2_gitlens@1"
}
],
"scm/resourceState/context": [
@@ -1428,160 +1528,280 @@
"command": "gitlens.showQuickFileHistory",
"when": "gitlens:enabled",
"group": "1_gitlens_1@1"
},
{
"command": "gitlens.stashSave",
"when": "gitlens:enabled",
"group": "2_gitlens@1"
}
],
"view/title": [
{
"command": "gitlens.showCommitSearch",
"when": "gitlens:enabled && view == gitlens.gitExplorer",
"when": "view == gitlens.gitExplorer",
"group": "navigation@1"
},
{
"command": "gitlens.gitExplorer.switchToHistoryView",
"when": "gitlens:enabled && view == gitlens.gitExplorer && gitlens:gitExplorer:view == repository",
"when": "view == gitlens.gitExplorer && gitlens:gitExplorer:view == repository",
"group": "navigation@2"
},
{
"command": "gitlens.gitExplorer.switchToRepositoryView",
"when": "gitlens:enabled && view == gitlens.gitExplorer && gitlens:gitExplorer:view == history",
"when": "view == gitlens.gitExplorer && gitlens:gitExplorer:view == history",
"group": "navigation@3"
},
{
"command": "gitlens.gitExplorer.refresh",
"when": "gitlens:enabled && view == gitlens.gitExplorer",
"when": "view == gitlens.gitExplorer",
"group": "navigation@4"
}
],
"view/item/context": [
{
"command": "gitlens.openBranchesInRemote",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:branches:remote",
"group": "1_gitlens@1"
},
{
"command": "gitlens.openBranchInRemote",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:branch-history",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:branch-history:remote",
"group": "1_gitlens@1"
},
{
"command": "gitlens.openCommitInRemote",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "1_gitlens@1"
},
{
"command": "gitlens.copyShaToClipboard",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
"command": "gitlens.gitExplorer.openChangedFileChanges",
"when": "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.openChangedFileChangesWithWorking",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "2_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openChangedFiles",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "3_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openChangedFileRevisions",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "3_gitlens@2"
},
{
"command": "gitlens.showQuickCommitDetails",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
"command": "gitlens.copyShaToClipboard",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "4_gitlens@1"
},
{
"command": "gitlens.copyMessageToClipboard",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "4_gitlens@2"
},
{
"command": "gitlens.showQuickCommitDetails",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "5_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openChanges",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"when": "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",
"when": "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",
"when": "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",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "2_gitlens@2"
},
{
"command": "gitlens.openFileInRemote",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "3_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openFileRevisionInRemote",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "3_gitlens@2"
},
{
"command": "gitlens.showQuickFileHistory",
"when": "gitlens:isTracked && view == gitlens.gitExplorer && viewItem == gitlens:commit-file && gitlens:gitExplorer:view == repository",
"command": "gitlens.gitExplorer.applyChanges",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "4_gitlens@1"
},
{
"command": "gitlens.showQuickFileHistory",
"when": "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": "4_gitlens@2"
"when": "view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "5_gitlens@2"
},
{
"command": "gitlens.gitExplorer.openFile",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:file-history",
"group": "1_gitlens@1"
},
{
"command": "gitlens.openFileInRemote",
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:file-history",
"group": "1_gitlens@2"
},
{
"command": "gitlens.openBranchesInRemote",
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:remote",
"group": "1_gitlens@1"
},
{
"command": "gitlens.openRepoInRemote",
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:remote",
"group": "1_gitlens@2"
},
{
"command": "gitlens.stashSave",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:stashes",
"group": "1_gitlens@1"
},
{
"command": "gitlens.stashApply",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:stash",
"group": "1_gitlens@1"
},
{
"command": "gitlens.stashDelete",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:stash",
"group": "1_gitlens@2"
},
{
"command": "gitlens.copyMessageToClipboard",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash",
"command": "gitlens.gitExplorer.openChangedFileChanges",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:stash",
"group": "2_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openChangedFileChangesWithWorking",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:stash",
"group": "2_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openChangedFiles",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:stash",
"group": "3_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openChangedFileRevisions",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:stash",
"group": "3_gitlens@2"
},
{
"command": "gitlens.copyMessageToClipboard",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:stash",
"group": "4_gitlens@1"
},
{
"command": "gitlens.gitExplorer.applyChanges",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "1_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openChanges",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "2_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openChangesWithWorking",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "2_gitlens@2"
},
{
"command": "gitlens.gitExplorer.openFile",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "3_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openFileRevision",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "3_gitlens@2"
},
{
"command": "gitlens.openFileInRemote",
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "4_gitlens@1"
},
{
"command": "gitlens.showQuickFileHistory",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "5_gitlens@1"
},
{
"command": "gitlens.openRepoInRemote",
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:status",
"group": "1_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openChanges",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file",
"group": "1_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openChangesWithWorking",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file",
"group": "1_gitlens@2"
},
{
"command": "gitlens.gitExplorer.openFile",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file",
"group": "2_gitlens@1"
},
{
"command": "gitlens.gitExplorer.openFileRevision",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file",
"group": "2_gitlens@2"
},
{
"command": "gitlens.openFileInRemote",
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:status-file",
"group": "3_gitlens@1"
},
{
"command": "gitlens.showQuickFileHistory",
"when": "gitlens:isTracked && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "4_gitlens@1"
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file && gitlens:gitExplorer:view == repository",
"group": "5_gitlens@1"
},
{
"command": "gitlens.showQuickCommitFileDetails",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file",
"group": "5_gitlens@2"
},
{
"command": "gitlens.gitExplorer.openFile",
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file-commits",
"group": "1_gitlens@1"
},
{
"command": "gitlens.openFileInRemote",
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:status-file-commits",
"group": "1_gitlens@2"
},
{
"command": "gitlens.gitExplorer.refresh",
"when": "view == gitlens.gitExplorer && viewItem != gitlens:commit-file && viewItem != gitlens:stash-file && viewItem != gitlens:status-file",
"group": "9_gitlens@1"
}
]
},
@@ -1682,7 +1902,7 @@
{
"id": "gitlens.gitExplorer",
"name": "GitLens",
"when": "gitlens:enabled"
"when": "gitlens:enabled && config.gitlens.gitExplorer.enabled"
}
]
}
@@ -1704,7 +1924,7 @@
"dependencies": {
"applicationinsights": "0.21.0",
"copy-paste": "1.3.0",
"iconv-lite": "0.4.18",
"iconv-lite": "0.4.19",
"ignore": "3.3.5",
"lodash.debounce": "4.0.8",
"lodash.escaperegexp": "4.1.2",
@@ -1712,16 +1932,15 @@
"lodash.once": "4.1.1",
"moment": "2.18.1",
"spawn-rx": "2.0.11",
"string-width": "2.1.1",
"tmp": "0.0.33"
},
"devDependencies": {
"@types/copy-paste": "1.1.30",
"@types/iconv-lite": "0.0.1",
"@types/mocha": "2.2.42",
"@types/node": "8.0.26",
"@types/mocha": "2.2.43",
"@types/node": "8.0.28",
"@types/tmp": "0.0.33",
"mocha": "3.5.0",
"mocha": "3.5.2",
"tslint": "5.7.0",
"typescript": "2.5.2",
"vscode": "1.1.5"

View File

@@ -222,7 +222,7 @@ export class AnnotationController extends Disposable {
}
getProvider(editor: TextEditor | undefined): AnnotationProviderBase | undefined {
if (!editor || !editor.document || !this.git.isEditorBlameable(editor)) return undefined;
if (editor === undefined || editor.document === undefined || !this.git.isEditorBlameable(editor)) return undefined;
return this._annotationProviders.get(editor.viewColumn || -1);
}
@@ -233,7 +233,7 @@ export class AnnotationController extends Disposable {
if (!editor || !editor.document || !this.git.isEditorBlameable(editor)) return false;
const currentProvider = this._annotationProviders.get(editor.viewColumn || -1);
if (currentProvider && TextEditorComparer.equals(currentProvider.editor, editor)) {
if (currentProvider !== undefined && TextEditorComparer.equals(currentProvider.editor, editor)) {
await currentProvider.selection(shaOrLine);
return true;
}

View File

@@ -1,9 +1,9 @@
import { Strings } from '../system';
import { DecorationInstanceRenderOptions, DecorationOptions, ThemableDecorationRenderOptions } from 'vscode';
import { Dates, Objects, Strings } from '../system';
import { DecorationInstanceRenderOptions, DecorationOptions, MarkdownString, ThemableDecorationRenderOptions } from 'vscode';
import { DiffWithCommand, OpenCommitInRemoteCommand, ShowQuickCommitDetailsCommand } from '../commands';
import { IThemeConfig, themeDefaults } from '../configuration';
import { GlyphChars } from '../constants';
import { CommitFormatter, GitCommit, GitDiffChunkLine, GitService, GitUri, ICommitFormatOptions } from '../gitService';
import * as moment from 'moment';
interface IHeatmapConfig {
enabled: boolean;
@@ -27,13 +27,13 @@ const escapeMarkdownRegEx = /[`\>\#\*\_\-\+\.]/g;
export class Annotations {
static applyHeatmap(decoration: DecorationOptions, date: Date, now: moment.Moment) {
static applyHeatmap(decoration: DecorationOptions, date: Date, now: number) {
const color = this._getHeatmapColor(now, date);
(decoration.renderOptions!.before! as any).borderColor = color;
}
private static _getHeatmapColor(now: moment.Moment, date: Date) {
const days = now.diff(moment(date), 'days');
private static _getHeatmapColor(now: number, date: Date) {
const days = Dates.dateDaysFromNow(date, now);
if (days <= 2) return '#ffeca7';
if (days <= 7) return '#ffdd8c';
@@ -47,7 +47,7 @@ export class Annotations {
return '#793738';
}
static getHoverMessage(commit: GitCommit, dateFormat: string | null): string | string[] {
static getHoverMessage(commit: GitCommit, dateFormat: string | null, hasRemotes: boolean): MarkdownString {
if (dateFormat === null) {
dateFormat = 'MMMM Do, YYYY h:MMa';
}
@@ -63,16 +63,25 @@ export class Annotations {
.replace(/\n/g, ' \n');
message = `\n\n> ${message}`;
}
return `\`${commit.shortSha}\` &nbsp; __${commit.author}__, ${moment(commit.date).fromNow()} &nbsp; _(${moment(commit.date).format(dateFormat)})_${message}`;
const openInRemoteCommand = hasRemotes
? `${'&nbsp;'.repeat(3)} [\`${GlyphChars.ArrowUpRight}\`](${OpenCommitInRemoteCommand.getMarkdownCommandArgs(commit.sha)} "Open in Remote")`
: '';
const markdown = new MarkdownString(`[\`${commit.shortSha}\`](${ShowQuickCommitDetailsCommand.getMarkdownCommandArgs(commit.sha)} "Show Commit Details") &nbsp; __${commit.author}__, ${commit.fromNow()} &nbsp; _(${commit.formatDate(dateFormat)})_ ${openInRemoteCommand} &nbsp; ${message}`);
markdown.isTrusted = true;
return markdown;
}
static getHoverDiffMessage(commit: GitCommit, chunkLine: GitDiffChunkLine | undefined): string | undefined {
static getHoverDiffMessage(commit: GitCommit, chunkLine: GitDiffChunkLine | undefined): MarkdownString | undefined {
if (chunkLine === undefined) return undefined;
const codeDiff = this._getCodeDiff(chunkLine);
return commit.isUncommitted
? `\`Changes\` &nbsp; ${GlyphChars.Dash} &nbsp; _uncommitted_\n${codeDiff}`
: `\`Changes\` &nbsp; ${GlyphChars.Dash} &nbsp; \`${commit.previousShortSha}\` ${GlyphChars.ArrowLeftRight} \`${commit.shortSha}\`\n${codeDiff}`;
const markdown = new MarkdownString(commit.isUncommitted
? `[\`Changes\`](${DiffWithCommand.getMarkdownCommandArgs(commit)} "Open Changes") &nbsp; ${GlyphChars.Dash} &nbsp; _uncommitted_\n${codeDiff}`
: `[\`Changes\`](${DiffWithCommand.getMarkdownCommandArgs(commit)} "Open Changes") &nbsp; ${GlyphChars.Dash} &nbsp; [\`${commit.previousShortSha}\`](${ShowQuickCommitDetailsCommand.getMarkdownCommandArgs(commit.previousSha!)} "Show Commit Details") ${GlyphChars.ArrowLeftRight} [\`${commit.shortSha}\`](${ShowQuickCommitDetailsCommand.getMarkdownCommandArgs(commit.sha)} "Show Commit Details")\n${codeDiff}`);
markdown.isTrusted = true;
return markdown;
}
private static _getCodeDiff(chunkLine: GitDiffChunkLine): string {
@@ -92,22 +101,22 @@ export class Annotations {
} as DecorationOptions;
}
static detailsHover(commit: GitCommit, dateFormat: string | null): DecorationOptions {
const message = this.getHoverMessage(commit, dateFormat);
static detailsHover(commit: GitCommit, dateFormat: string | null, hasRemotes: boolean): DecorationOptions {
const message = this.getHoverMessage(commit, dateFormat, hasRemotes);
return {
hoverMessage: message
} as DecorationOptions;
}
static gutter(commit: GitCommit, format: string, dateFormatOrFormatOptions: string | null | ICommitFormatOptions, renderOptions: IRenderOptions): DecorationOptions {
const content = Strings.pad(CommitFormatter.fromTemplate(format, commit, dateFormatOrFormatOptions), 1, 1);
const message = CommitFormatter.fromTemplate(format, commit, dateFormatOrFormatOptions);
return {
renderOptions: {
before: {
...renderOptions.before,
...{
contentText: content
contentText: Strings.pad(message.replace(/ /g, GlyphChars.Space), 1, 1)
}
},
dark: {
@@ -124,9 +133,23 @@ export class Annotations {
} as DecorationOptions;
}
static gutterRenderOptions(cfgTheme: IThemeConfig, heatmap: IHeatmapConfig): IRenderOptions {
static gutterRenderOptions(cfgTheme: IThemeConfig, heatmap: IHeatmapConfig, options: ICommitFormatOptions): IRenderOptions {
const cfgFileTheme = cfgTheme.annotations.file.gutter;
// Try to get the width of the string, if there is a cap
let width = 4; // Start with a padding
for (const token of Objects.values<Strings.ITokenOptions | undefined>(options.tokenOptions)) {
if (token === undefined) continue;
// If any token is uncapped, kick out and set no max
if (token.truncateTo == null) {
width = 0;
break;
}
width += token.truncateTo;
}
let borderStyle = undefined;
let borderWidth = undefined;
if (heatmap.enabled) {
@@ -143,7 +166,8 @@ export class Annotations {
borderStyle: borderStyle,
borderWidth: borderWidth,
height: '100%',
margin: '0 26px -1px 0'
margin: '0 26px -1px 0',
width: (width > 4) ? `${width}ch` : undefined
},
dark: {
backgroundColor: cfgFileTheme.dark.backgroundColor || undefined,
@@ -158,11 +182,12 @@ export class Annotations {
} as IRenderOptions;
}
static hover(commit: GitCommit, renderOptions: IRenderOptions, heatmap: boolean, dateFormat: string | null): DecorationOptions {
return {
hoverMessage: this.getHoverMessage(commit, dateFormat),
renderOptions: heatmap ? { before: { ...renderOptions.before } } : undefined
static hover(commit: GitCommit, renderOptions: IRenderOptions, now: number): DecorationOptions {
const decoration = {
renderOptions: { before: { ...renderOptions.before } }
} as DecorationOptions;
this.applyHeatmap(decoration, commit.date, now);
return decoration;
}
static hoverRenderOptions(cfgTheme: IThemeConfig, heatmap: IHeatmapConfig): IRenderOptions {
@@ -181,11 +206,15 @@ export class Annotations {
}
static trailing(commit: GitCommit, format: string, dateFormat: string | null, cfgTheme: IThemeConfig): DecorationOptions {
const message = CommitFormatter.fromTemplate(format, commit, dateFormat);
const message = CommitFormatter.fromTemplate(format, commit, {
truncateMessageAtNewLine: true,
dateFormat: dateFormat
} as ICommitFormatOptions);
return {
renderOptions: {
after: {
contentText: Strings.pad(message, 1, 1)
contentText: Strings.pad(message.replace(/ /g, GlyphChars.Space), 1, 1)
},
dark: {
after: {

View File

@@ -1,13 +1,15 @@
'use strict';
import { Iterables } from '../system';
import { ExtensionContext, Range, TextEditor, TextEditorDecorationType } from 'vscode';
import { CancellationToken, Disposable, ExtensionContext, Hover, HoverProvider, languages, Position, Range, TextDocument, TextEditor, TextEditorDecorationType } from 'vscode';
import { AnnotationProviderBase } from './annotationProvider';
import { GitBlame, GitService, GitUri } from '../gitService';
import { Annotations, endOfLineIndex } from './annotations';
import { GitBlame, GitCommit, GitService, GitUri } from '../gitService';
import { WhitespaceController } from './whitespaceController';
export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase {
export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase implements HoverProvider {
protected _blame: Promise<GitBlame | undefined>;
protected _hoverProviderDisposable: Disposable;
constructor(context: ExtensionContext, editor: TextEditor, decoration: TextEditorDecorationType | undefined, highlightDecoration: TextEditorDecorationType | undefined, whitespaceController: WhitespaceController | undefined, protected git: GitService, protected uri: GitUri) {
super(context, editor, decoration, highlightDecoration, whitespaceController);
@@ -15,6 +17,11 @@ export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase
this._blame = this.git.getBlameForFile(this.uri);
}
async clear() {
this._hoverProviderDisposable && this._hoverProviderDisposable.dispose();
super.clear();
}
async selection(shaOrLine?: string | number, blame?: GitBlame) {
if (!this.highlightDecoration) return;
@@ -56,6 +63,7 @@ export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase
const blame = await this._blame;
return blame !== undefined && blame.lines.length !== 0;
}
protected async getBlame(requiresWhitespaceHack: boolean): Promise<GitBlame | undefined> {
let whitespacePromise: Promise<void> | undefined;
// HACK: Until https://github.com/Microsoft/vscode/issues/11485 is fixed -- override whitespace (turn off)
@@ -64,18 +72,47 @@ export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase
}
let blame: GitBlame | undefined;
if (whitespacePromise) {
if (whitespacePromise !== undefined) {
[blame] = await Promise.all([this._blame, whitespacePromise]);
}
else {
blame = await this._blame;
}
if (blame === undefined || !blame.lines.length) {
if (blame === undefined || blame.lines.length === 0) {
this.whitespaceController && await this.whitespaceController.restore();
return undefined;
}
return blame;
}
registerHoverProvider() {
this._hoverProviderDisposable = languages.registerHoverProvider({ pattern: this.uri.fsPath }, this);
}
async provideHover(document: TextDocument, position: Position, token: CancellationToken): Promise<Hover | undefined> {
// Avoid double annotations if we are showing the whole-file hover blame annotations
if (this._config.blame.line.enabled && this.editor.selection.start.line === position.line) return undefined;
const cfg = this._config.annotations.file.gutter;
if (!cfg.hover.wholeLine && position.character !== 0) return undefined;
const blame = await this.getBlame(true);
if (blame === undefined) return undefined;
const line = blame.lines[position.line - this.uri.offset];
const commit = blame.commits.get(line.sha);
if (commit === undefined) return undefined;
// Get the full commit message -- since blame only returns the summary
let logCommit: GitCommit | undefined = undefined;
if (!commit.isUncommitted) {
logCommit = await this.git.getLogCommit(commit.repoPath, commit.uri.fsPath, commit.sha);
}
const message = Annotations.getHoverMessage(logCommit || commit, this._config.defaultDateFormat, this.git.hasRemotes(commit.repoPath));
return new Hover(message, document.validateRange(new Range(position.line, 0, position.line, endOfLineIndex)));
}
}

View File

@@ -2,11 +2,11 @@
import { Strings } from '../system';
import { DecorationOptions, Range } from 'vscode';
import { FileAnnotationType } from './annotationController';
import { Annotations, endOfLineIndex } from './annotations';
import { Annotations } from './annotations';
import { BlameAnnotationProviderBase } from './blameAnnotationProvider';
import { GlyphChars } from '../constants';
import { GitBlameCommit, ICommitFormatOptions } from '../gitService';
import * as moment from 'moment';
import { Logger } from '../logger';
export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
@@ -16,7 +16,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
const blame = await this.getBlame(true);
if (blame === undefined) return false;
// console.time('Computing blame annotations...');
const start = process.hrtime();
const cfg = this._config.annotations.file.gutter;
@@ -32,59 +32,53 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
tokenOptions: tokenOptions
};
const now = moment();
const now = Date.now();
const offset = this.uri.offset;
const renderOptions = Annotations.gutterRenderOptions(this._config.theme, cfg.heatmap);
const dateFormat = this._config.defaultDateFormat;
const renderOptions = Annotations.gutterRenderOptions(this._config.theme, cfg.heatmap, options);
const separateLines = this._config.theme.annotations.file.gutter.separateLines;
const decorations: DecorationOptions[] = [];
const document = this.document;
const decorationsMap: { [sha: string]: DecorationOptions | undefined } = Object.create(null);
let commit: GitBlameCommit | undefined;
let compacted = false;
let details: DecorationOptions | undefined;
let gutter: DecorationOptions | undefined;
let previousSha: string | undefined;
for (const l of blame.lines) {
commit = blame.commits.get(l.sha);
if (commit === undefined) continue;
const line = l.line + offset;
if (previousSha === l.sha) {
// Use a shallow copy of the previous decoration options
gutter = { ...gutter } as DecorationOptions;
if (cfg.compact && !compacted) {
// Since we are wiping out the contextText make sure to copy the objects
gutter.renderOptions = { ...gutter.renderOptions };
gutter.renderOptions.before = {
...gutter.renderOptions.before,
...{ contentText: GlyphChars.Space.repeat(Strings.getWidth(gutter.renderOptions!.before!.contentText!)) }
gutter.renderOptions = {
...gutter.renderOptions,
before: {
...gutter.renderOptions!.before,
contentText: GlyphChars.Space.repeat(Strings.width(gutter.renderOptions!.before!.contentText!))
}
};
if (separateLines) {
gutter.renderOptions.dark = { ...gutter.renderOptions.dark };
gutter.renderOptions.dark.before = { ...gutter.renderOptions.dark.before, ...{ textDecoration: 'none' } };
gutter.renderOptions.light = { ...gutter.renderOptions.light };
gutter.renderOptions.light.before = { ...gutter.renderOptions.light.before, ...{ textDecoration: 'none' } };
gutter.renderOptions.dark = {
...gutter.renderOptions.dark,
before: { ...gutter.renderOptions.dark!.before, textDecoration: 'none' }
};
gutter.renderOptions.light = {
...gutter.renderOptions.light,
before: { ...gutter.renderOptions.light!.before, textDecoration: 'none' }
};
}
compacted = true;
}
const endIndex = document.lineAt(line).firstNonWhitespaceCharacterIndex;
gutter.range = new Range(line, 0, line, endIndex);
decorations.push(gutter);
gutter.range = new Range(line, 0, line, 0);
if (details !== undefined) {
details = { ...details } as DecorationOptions;
details.range = cfg.hover.wholeLine
? document.validateRange(new Range(line, 0, line, endOfLineIndex))
: gutter.range;
decorations.push(details);
}
decorations.push(gutter);
continue;
}
@@ -92,30 +86,43 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
compacted = false;
previousSha = l.sha;
gutter = decorationsMap[l.sha];
if (gutter !== undefined) {
gutter = {
...gutter,
range: new Range(line, 0, line, 0)
} as DecorationOptions;
decorations.push(gutter);
continue;
}
commit = blame.commits.get(l.sha);
if (commit === undefined) continue;
gutter = Annotations.gutter(commit, cfg.format, options, renderOptions);
if (cfg.heatmap.enabled) {
Annotations.applyHeatmap(gutter, commit.date, now);
}
const endIndex = document.lineAt(line).firstNonWhitespaceCharacterIndex;
gutter.range = new Range(line, 0, line, endIndex);
decorations.push(gutter);
gutter.range = new Range(line, 0, line, 0);
if (cfg.hover.details) {
details = Annotations.detailsHover(commit, dateFormat);
details.range = cfg.hover.wholeLine
? document.validateRange(new Range(line, 0, line, endOfLineIndex))
: gutter.range;
decorations.push(details);
}
decorations.push(gutter);
decorationsMap[l.sha] = gutter;
}
if (decorations.length) {
this.editor.setDecorations(this.decoration!, decorations);
}
// console.timeEnd('Computing blame annotations...');
const duration = process.hrtime(start);
Logger.log(`${(duration[0] * 1000) + Math.floor(duration[1] / 1000000)} ms to compute gutter blame annotations`);
if (cfg.hover.details) {
this.registerHoverProvider();
}
this.selection(shaOrLine, blame);
return true;

View File

@@ -1,63 +1,70 @@
'use strict';
import { DecorationOptions, Range } from 'vscode';
import { FileAnnotationType } from './annotationController';
import { Annotations, endOfLineIndex } from './annotations';
import { Annotations } from './annotations';
import { BlameAnnotationProviderBase } from './blameAnnotationProvider';
import { GitBlameCommit } from '../gitService';
import * as moment from 'moment';
import { Logger } from '../logger';
export class HoverBlameAnnotationProvider extends BlameAnnotationProviderBase {
async provideAnnotation(shaOrLine?: string | number): Promise<boolean> {
this.annotationType = FileAnnotationType.Hover;
const blame = await this.getBlame(this._config.annotations.file.hover.heatmap.enabled);
if (blame === undefined) return false;
// console.time('Computing blame annotations...');
const cfg = this._config.annotations.file.hover;
const now = moment();
const offset = this.uri.offset;
const renderOptions = Annotations.hoverRenderOptions(this._config.theme, cfg.heatmap);
const dateFormat = this._config.defaultDateFormat;
const blame = await this.getBlame(cfg.heatmap.enabled);
if (blame === undefined) return false;
const decorations: DecorationOptions[] = [];
const document = this.document;
if (cfg.heatmap.enabled) {
const start = process.hrtime();
let commit: GitBlameCommit | undefined;
let hover: DecorationOptions | undefined;
const now = Date.now();
const offset = this.uri.offset;
const renderOptions = Annotations.hoverRenderOptions(this._config.theme, cfg.heatmap);
for (const l of blame.lines) {
commit = blame.commits.get(l.sha);
if (commit === undefined) continue;
const decorations: DecorationOptions[] = [];
const decorationsMap: { [sha: string]: DecorationOptions } = Object.create(null);
const line = l.line + offset;
let commit: GitBlameCommit | undefined;
let hover: DecorationOptions | undefined;
hover = Annotations.hover(commit, renderOptions, cfg.heatmap.enabled, dateFormat);
for (const l of blame.lines) {
const line = l.line + offset;
hover = decorationsMap[l.sha];
if (hover !== undefined) {
hover = {
...hover,
range: new Range(line, 0, line, 0)
} as DecorationOptions;
decorations.push(hover);
continue;
}
commit = blame.commits.get(l.sha);
if (commit === undefined) continue;
hover = Annotations.hover(commit, renderOptions, now);
hover.range = new Range(line, 0, line, 0);
decorations.push(hover);
decorationsMap[l.sha] = hover;
if (cfg.wholeLine) {
hover.range = document.validateRange(new Range(line, 0, line, endOfLineIndex));
}
else {
const endIndex = document.lineAt(line).firstNonWhitespaceCharacterIndex;
hover.range = new Range(line, 0, line, endIndex);
}
if (cfg.heatmap.enabled) {
Annotations.applyHeatmap(hover, commit.date, now);
if (decorations.length) {
this.editor.setDecorations(this.decoration!, decorations);
}
decorations.push(hover);
const duration = process.hrtime(start);
Logger.log(`${(duration[0] * 1000) + Math.floor(duration[1] / 1000000)} ms to compute hover blame annotations`);
}
if (decorations.length) {
this.editor.setDecorations(this.decoration!, decorations);
}
// console.timeEnd('Computing blame annotations...');
this.registerHoverProvider();
this.selection(shaOrLine, blame);
return true;
}

View File

@@ -1,9 +1,10 @@
'use strict';
import { DecorationOptions, ExtensionContext, Position, Range, TextEditor, TextEditorDecorationType } from 'vscode';
import { DecorationOptions, ExtensionContext, MarkdownString, Position, Range, TextEditor, TextEditorDecorationType } from 'vscode';
import { Annotations, endOfLineIndex } from './annotations';
import { FileAnnotationType } from './annotationController';
import { AnnotationProviderBase } from './annotationProvider';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
export class RecentChangesAnnotationProvider extends AnnotationProviderBase {
@@ -20,6 +21,8 @@ export class RecentChangesAnnotationProvider extends AnnotationProviderBase {
const diff = await this.git.getDiffForFile(this.uri, commit.previousSha);
if (diff === undefined) return false;
const start = process.hrtime();
const cfg = this._config.annotations.file.recentChanges;
const dateFormat = this._config.defaultDateFormat;
@@ -34,21 +37,16 @@ export class RecentChangesAnnotationProvider extends AnnotationProviderBase {
if (line.state === 'unchanged') continue;
let endingIndex = 0;
if (cfg.hover.details || cfg.hover.changes) {
endingIndex = cfg.hover.wholeLine ? endOfLineIndex : this.editor.document.lineAt(count).firstNonWhitespaceCharacterIndex;
}
const range = this.editor.document.validateRange(new Range(new Position(count, 0), new Position(count, endingIndex)));
const range = this.editor.document.validateRange(new Range(new Position(count, 0), new Position(count, endOfLineIndex)));
if (cfg.hover.details) {
decorators.push({
hoverMessage: Annotations.getHoverMessage(commit, dateFormat),
hoverMessage: Annotations.getHoverMessage(commit, dateFormat, this.git.hasRemotes(commit.repoPath)),
range: range
} as DecorationOptions);
}
let message: string | undefined = undefined;
let message: MarkdownString | undefined = undefined;
if (cfg.hover.changes) {
message = Annotations.getHoverDiffMessage(commit, line);
}
@@ -62,6 +60,9 @@ export class RecentChangesAnnotationProvider extends AnnotationProviderBase {
this.editor.setDecorations(this.highlightDecoration!, decorators);
const duration = process.hrtime(start);
Logger.log(`${(duration[0] * 1000) + Math.floor(duration[1] / 1000000)} ms to compute recent changes annotations`);
return true;
}

View File

@@ -8,12 +8,14 @@ export * from './commands/copyShaToClipboard';
export * from './commands/diffDirectory';
export * from './commands/diffLineWithPrevious';
export * from './commands/diffLineWithWorking';
export * from './commands/diffWith';
export * from './commands/diffWithBranch';
export * from './commands/diffWithNext';
export * from './commands/diffWithPrevious';
export * from './commands/diffWithRevision';
export * from './commands/diffWithWorking';
export * from './commands/openChangedFiles';
export * from './commands/openBranchesInRemote';
export * from './commands/openBranchInRemote';
export * from './commands/openCommitInRemote';
export * from './commands/openFileInRemote';

View File

@@ -1,7 +1,7 @@
'use strict';
import { commands, Disposable, SourceControlResourceGroup, SourceControlResourceState, TextDocumentShowOptions, TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
import { ExplorerNode } from '../views/explorerNodes';
import { GitBranch, GitCommit } from '../gitService';
import { GitBranch, GitCommit, GitRemote } from '../gitService';
import { Logger } from '../logger';
import { Telemetry } from '../telemetry';
@@ -11,6 +11,7 @@ export type Commands =
'gitlens.copyMessageToClipboard' |
'gitlens.copyShaToClipboard' |
'gitlens.diffDirectory' |
'gitlens.diffWith' |
'gitlens.diffWithBranch' |
'gitlens.diffWithNext' |
'gitlens.diffWithPrevious' |
@@ -19,6 +20,7 @@ export type Commands =
'gitlens.diffWithWorking' |
'gitlens.diffLineWithWorking' |
'gitlens.openChangedFiles' |
'gitlens.openBranchesInRemote' |
'gitlens.openBranchInRemote' |
'gitlens.openCommitInRemote' |
'gitlens.openFileInRemote' |
@@ -51,6 +53,7 @@ export const Commands = {
CopyMessageToClipboard: 'gitlens.copyMessageToClipboard' as Commands,
CopyShaToClipboard: 'gitlens.copyShaToClipboard' as Commands,
DiffDirectory: 'gitlens.diffDirectory' as Commands,
DiffWith: 'gitlens.diffWith' as Commands,
DiffWithBranch: 'gitlens.diffWithBranch' as Commands,
DiffWithNext: 'gitlens.diffWithNext' as Commands,
DiffWithPrevious: 'gitlens.diffWithPrevious' as Commands,
@@ -59,6 +62,7 @@ export const Commands = {
DiffWithWorking: 'gitlens.diffWithWorking' as Commands,
DiffLineWithWorking: 'gitlens.diffLineWithWorking' as Commands,
OpenChangedFiles: 'gitlens.openChangedFiles' as Commands,
OpenBranchesInRemote: 'gitlens.openBranchesInRemote' as Commands,
OpenBranchInRemote: 'gitlens.openBranchInRemote' as Commands,
OpenCommitInRemote: 'gitlens.openCommitInRemote' as Commands,
OpenFileInRemote: 'gitlens.openFileInRemote' as Commands,
@@ -138,6 +142,10 @@ export function isCommandViewContextWithCommit<T extends GitCommit>(context: Com
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;
function isScmResourceGroup(group: any): group is SourceControlResourceGroup {
@@ -160,6 +168,10 @@ function isTextEditor(editor: any): editor is TextEditor {
export abstract class Command extends Disposable {
static getMarkdownCommandArgsCore<T>(command: Commands, args: T): string {
return `command:${command}?${encodeURIComponent(JSON.stringify(args))}`;
}
protected readonly contextParsingOptions: CommandContextParsingOptions = { editor: false, uri: false };
private _disposable: Disposable;

View File

@@ -1,16 +1,14 @@
'use strict';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { commands, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands, GlyphChars } from '../constants';
import { DiffWithPreviousCommandArgs } from './diffWithPrevious';
import { DiffWithWorkingCommandArgs } from './diffWithWorking';
import { DiffWithCommandArgs } from './diffWith';
import { GitCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import * as path from 'path';
export interface DiffLineWithPreviousCommandArgs {
commit?: GitCommit;
line?: number;
showOptions?: TextDocumentShowOptions;
}
@@ -43,56 +41,26 @@ export class DiffLineWithPreviousCommand extends ActiveEditorCommand {
if (blame === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to open compare');
args.commit = blame.commit;
// If we don't have a sha or the current commit matches the blame, show the previous
if (gitUri.sha === undefined || gitUri.sha === args.commit.sha) {
return commands.executeCommand(Commands.DiffWithPrevious, new GitUri(uri, args.commit), {
line: args.line,
showOptions: args.showOptions
} as DiffWithPreviousCommandArgs);
}
// If the line is uncommitted, find the previous commit and treat it as a DiffWithWorking
if (args.commit.isUncommitted) {
uri = args.commit.uri;
args.commit = new GitCommit(args.commit.type, args.commit.repoPath, args.commit.previousSha!, args.commit.previousFileName!, args.commit.author, args.commit.date, args.commit.message);
args.line = (blame.line.line + 1) + gitUri.offset;
return commands.executeCommand(Commands.DiffWithWorking, uri, {
commit: args.commit,
line: args.line,
showOptions: args.showOptions
} as DiffWithWorkingCommandArgs);
}
}
catch (ex) {
Logger.error(ex, 'DiffWithPreviousLineCommand', `getBlameForLine(${blameline})`);
Logger.error(ex, 'DiffLineWithPreviousCommand', `getBlameForLine(${blameline})`);
return window.showErrorMessage(`Unable to open compare. See output channel for more details`);
}
}
try {
const [rhs, lhs] = await Promise.all([
this.git.getVersionedFile(gitUri.repoPath, gitUri.fsPath, gitUri.sha!),
this.git.getVersionedFile(args.commit.repoPath, args.commit.uri.fsPath, args.commit.sha)
]);
if (args.line !== undefined && args.line !== 0) {
if (args.showOptions === undefined) {
args.showOptions = {};
}
args.showOptions.selection = new Range(args.line, 0, args.line, 0);
}
await commands.executeCommand(BuiltInCommands.Diff,
Uri.file(lhs),
Uri.file(rhs),
`${path.basename(args.commit.uri.fsPath)} (${args.commit.shortSha}) ${GlyphChars.ArrowLeftRight} ${path.basename(gitUri.fsPath)} (${gitUri.shortSha})`,
args.showOptions);
}
catch (ex) {
Logger.error(ex, 'DiffWithPreviousLineCommand', 'getVersionedFile');
return window.showErrorMessage(`Unable to open compare. See output channel for more details`);
}
const diffArgs: DiffWithCommandArgs = {
repoPath: args.commit.repoPath,
lhs: {
sha: args.commit.previousSha !== undefined ? args.commit.previousSha : GitService.fakeSha,
uri: args.commit.previousUri
},
rhs: {
sha: args.commit.sha,
uri: args.commit.uri
},
line: args.line,
showOptions: args.showOptions
};
return commands.executeCommand(Commands.DiffWith, diffArgs);
}
}

View File

@@ -1,13 +1,14 @@
'use strict';
import { commands, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { DiffWithWorkingCommandArgs } from './diffWithWorking';
import { DiffWithCommandArgs } from './diffWith';
import { GitCommit, GitService, GitUri } from '../gitService';
import { Messages } from '../messages';
import { Logger } from '../logger';
export interface DiffLineWithWorkingCommandArgs {
commit?: GitCommit;
line?: number;
showOptions?: TextDocumentShowOptions;
}
@@ -52,6 +53,19 @@ export class DiffLineWithWorkingCommand extends ActiveEditorCommand {
}
}
return commands.executeCommand(Commands.DiffWithWorking, uri, args as DiffWithWorkingCommandArgs);
const diffArgs: DiffWithCommandArgs = {
repoPath: args.commit.repoPath,
lhs: {
sha: args.commit.sha,
uri: args.commit.uri
},
rhs: {
sha: '',
uri: args.commit.uri
},
line: args.line,
showOptions: args.showOptions
};
return commands.executeCommand(Commands.DiffWith, diffArgs);
}
}

142
src/commands/diffWith.ts Normal file
View File

@@ -0,0 +1,142 @@
'use strict';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './common';
import { BuiltInCommands, GlyphChars } from '../constants';
import { GitCommit, GitService } from '../gitService';
import { Logger } from '../logger';
import * as path from 'path';
export interface DiffWithCommandArgsRevision {
sha: string;
uri: Uri;
title?: string;
}
export interface DiffWithCommandArgs {
lhs?: DiffWithCommandArgsRevision;
rhs?: DiffWithCommandArgsRevision;
repoPath?: string;
line?: number;
showOptions?: TextDocumentShowOptions;
}
export class DiffWithCommand extends ActiveEditorCommand {
static getMarkdownCommandArgs(args: DiffWithCommandArgs): string;
static getMarkdownCommandArgs(commit1: GitCommit, commit2: GitCommit): string;
static getMarkdownCommandArgs(argsOrCommit1: DiffWithCommandArgs | GitCommit, commit2?: GitCommit): string {
let args = argsOrCommit1;
if (argsOrCommit1 instanceof GitCommit) {
const commit1 = argsOrCommit1;
if (commit2 === undefined) {
if (commit1.isUncommitted) {
args = {
repoPath: commit1.repoPath,
lhs: {
sha: 'HEAD',
uri: commit1.uri
},
rhs: {
sha: '',
uri: commit1.uri
}
};
}
else {
args = {
repoPath: commit1.repoPath,
lhs: {
sha: commit1.previousSha!,
uri: commit1.previousUri!
},
rhs: {
sha: commit1.sha,
uri: commit1.uri
}
};
}
}
else {
args = {
repoPath: commit1.repoPath,
lhs: {
sha: commit1.sha,
uri: commit1.uri
},
rhs: {
sha: commit2.sha,
uri: commit2.uri
}
};
}
}
return super.getMarkdownCommandArgsCore<DiffWithCommandArgs>(Commands.DiffWith, args);
}
constructor(private git: GitService) {
super(Commands.DiffWith);
}
async execute(editor?: TextEditor, uri?: Uri, args: DiffWithCommandArgs = {}): Promise<any> {
args = { ...args };
if (args.repoPath === undefined || args.lhs === undefined || args.rhs === undefined) return undefined;
try {
const [lhs, rhs] = await Promise.all([
args.lhs.sha !== '' && !GitService.isUncommitted(args.lhs.sha)
? this.git.getVersionedFile(args.repoPath, args.lhs.uri.fsPath, args.lhs.sha)
: args.lhs.uri.fsPath,
args.rhs.sha !== '' && !GitService.isUncommitted(args.rhs.sha)
? this.git.getVersionedFile(args.repoPath, args.rhs.uri.fsPath, args.rhs.sha)
: args.rhs.uri.fsPath
]);
if (args.line !== undefined && args.line !== 0) {
if (args.showOptions === undefined) {
args.showOptions = {};
}
args.showOptions.selection = new Range(args.line, 0, args.line, 0);
}
let rhsPrefix = '';
if (rhs === undefined) {
rhsPrefix = 'deleted in ';
}
else if (lhs === undefined || args.lhs.sha === GitService.fakeSha) {
rhsPrefix = 'added in ';
}
if (args.lhs.title === undefined && lhs !== undefined && args.lhs.sha !== GitService.fakeSha) {
args.lhs.title = (args.lhs.sha === '' || GitService.isUncommitted(args.lhs.sha))
? `${path.basename(args.lhs.uri.fsPath)}`
: `${path.basename(args.lhs.uri.fsPath)} (${GitService.shortenSha(args.lhs.sha)})`;
}
if (args.rhs.title === undefined && args.rhs.sha !== GitService.fakeSha) {
args.rhs.title = (args.rhs.sha === '' || GitService.isUncommitted(args.rhs.sha))
? `${path.basename(args.rhs.uri.fsPath)}`
: `${path.basename(args.rhs.uri.fsPath)} (${rhsPrefix}${GitService.shortenSha(args.rhs.sha)})`;
}
const title = (args.lhs.title !== undefined && args.rhs.title !== undefined)
? `${args.lhs.title} ${GlyphChars.ArrowLeftRight} ${args.rhs.title}`
: args.lhs.title || args.rhs.title;
return await commands.executeCommand(BuiltInCommands.Diff,
lhs === undefined
? GitService.toGitContentUri(GitService.fakeSha, args.lhs.uri.fsPath, args.repoPath)
: Uri.file(lhs),
rhs === undefined
? GitService.toGitContentUri(GitService.fakeSha, args.rhs.uri.fsPath, args.repoPath)
: Uri.file(rhs),
title,
args.showOptions);
}
catch (ex) {
Logger.error(ex, 'DiffWithCommand', 'getVersionedFile');
return window.showErrorMessage(`Unable to open compare. See output channel for more details`);
}
}
}

View File

@@ -1,9 +1,9 @@
'use strict';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { commands, TextDocumentShowOptions, TextEditor, Uri } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands, GlyphChars } from '../constants';
import { GlyphChars } from '../constants';
import { DiffWithCommandArgs } from './diffWith';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { BranchesQuickPick, CommandQuickPickItem } from '../quickPicks';
import * as path from 'path';
@@ -42,25 +42,20 @@ export class DiffWithBranchCommand extends ActiveEditorCommand {
const branch = pick.branch.name;
if (branch === undefined) return undefined;
try {
const compare = await this.git.getVersionedFile(gitUri.repoPath, gitUri.fsPath, branch);
if (args.line !== undefined && args.line !== 0) {
if (args.showOptions === undefined) {
args.showOptions = {};
}
args.showOptions.selection = new Range(args.line, 0, args.line, 0);
}
await commands.executeCommand(BuiltInCommands.Diff,
Uri.file(compare),
gitUri.fileUri(),
`${path.basename(gitUri.fsPath)} (${branch}) ${GlyphChars.ArrowLeftRight} ${path.basename(gitUri.fsPath)}`,
args.showOptions);
}
catch (ex) {
Logger.error(ex, 'DiffWithBranchCommand', 'getVersionedFile');
return window.showErrorMessage(`Unable to open branch compare. See output channel for more details`);
}
const diffArgs: DiffWithCommandArgs = {
repoPath: gitUri.repoPath,
lhs: {
sha: pick.branch.remote ? `remotes/${branch}` : branch,
uri: gitUri as Uri,
title: `${path.basename(gitUri.fsPath)} (${branch})`
},
rhs: {
sha: '',
uri: gitUri as Uri
},
line: args.line,
showOptions: args.showOptions
};
return commands.executeCommand(Commands.DiffWith, diffArgs);
}
}

View File

@@ -2,16 +2,16 @@
import { Iterables } from '../system';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands, GlyphChars } from '../constants';
import { DiffWithCommandArgs } from './diffWith';
import { GitLogCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import * as path from 'path';
export interface DiffWithNextCommandArgs {
commit?: GitLogCommit;
line?: number;
range?: Range;
line?: number;
showOptions?: TextDocumentShowOptions;
}
@@ -54,28 +54,19 @@ export class DiffWithNextCommand extends ActiveEditorCommand {
if (args.commit.nextSha === undefined) return commands.executeCommand(Commands.DiffWithWorking, uri);
try {
const [rhs, lhs] = await Promise.all([
this.git.getVersionedFile(args.commit.repoPath, args.commit.nextUri.fsPath, args.commit.nextSha),
this.git.getVersionedFile(args.commit.repoPath, args.commit.uri.fsPath, args.commit.sha)
]);
if (args.line !== undefined && args.line !== 0) {
if (args.showOptions === undefined) {
args.showOptions = {};
}
args.showOptions.selection = new Range(args.line, 0, args.line, 0);
}
await commands.executeCommand(BuiltInCommands.Diff,
Uri.file(lhs),
Uri.file(rhs),
`${path.basename(args.commit.uri.fsPath)} (${args.commit.shortSha}) ${GlyphChars.ArrowLeftRight} ${path.basename(args.commit.nextUri.fsPath)} (${args.commit.nextShortSha})`,
args.showOptions);
}
catch (ex) {
Logger.error(ex, 'DiffWithNextCommand', 'getVersionedFile');
return window.showErrorMessage(`Unable to open compare. See output channel for more details`);
}
const diffArgs: DiffWithCommandArgs = {
repoPath: args.commit.repoPath,
lhs: {
sha: args.commit.sha,
uri: args.commit.uri
},
rhs: {
sha: args.commit.nextSha,
uri: args.commit.nextUri
},
line: args.line,
showOptions: args.showOptions
};
return commands.executeCommand(Commands.DiffWith, diffArgs);
}
}

View File

@@ -2,22 +2,18 @@
import { Iterables } from '../system';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands, FakeSha, GlyphChars } from '../constants';
import { DiffWithCommandArgs } from './diffWith';
import { DiffWithWorkingCommandArgs } from './diffWithWorking';
import { GitCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import * as path from 'path';
export interface DiffWithPreviousCommandArgs {
commit?: GitCommit;
line?: number;
range?: Range;
showOptions?: TextDocumentShowOptions;
allowMissingPrevious?: boolean;
leftTitlePrefix?: string;
rightTitlePrefix?: string;
line?: number;
showOptions?: TextDocumentShowOptions;
}
export class DiffWithPreviousCommand extends ActiveEditorCommand {
@@ -40,6 +36,7 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
try {
const sha = args.commit === undefined ? gitUri.sha : args.commit.sha;
if (sha === GitService.fakeSha) return Messages.showCommitHasNoPreviousCommitWarningMessage();
const log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, sha, { maxCount: 2, range: args.range!, skipMerges: true });
if (log === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to open compare');
@@ -47,7 +44,9 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
args.commit = (sha && log.commits.get(sha)) || Iterables.first(log.commits.values());
// If the sha is missing and the file is uncommitted, then treat it as a DiffWithWorking
if (gitUri.sha === undefined && await this.git.isFileUncommitted(gitUri)) return commands.executeCommand(Commands.DiffWithWorking, uri, { commit: args.commit, showOptions: args.showOptions } as DiffWithWorkingCommandArgs);
if (gitUri.sha === undefined && await this.git.isFileUncommitted(gitUri)) {
return commands.executeCommand(Commands.DiffWithWorking, uri, { commit: args.commit, showOptions: args.showOptions } as DiffWithWorkingCommandArgs);
}
}
catch (ex) {
Logger.error(ex, 'DiffWithPreviousCommand', `getLogForFile(${gitUri.repoPath}, ${gitUri.fsPath})`);
@@ -55,32 +54,19 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
}
}
if (args.commit.previousSha === undefined && !args.allowMissingPrevious) return Messages.showCommitHasNoPreviousCommitWarningMessage(args.commit);
try {
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.previousUri.fsPath, args.commit.previousSha === undefined ? FakeSha : args.commit.previousSha)
]);
if (args.line !== undefined && args.line !== 0) {
if (args.showOptions === undefined) {
args.showOptions = {};
}
args.showOptions.selection = new Range(args.line, 0, args.line, 0);
}
await commands.executeCommand(BuiltInCommands.Diff,
Uri.file(lhs),
Uri.file(rhs),
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);
}
catch (ex) {
Logger.error(ex, 'DiffWithPreviousCommand', 'getVersionedFile');
return window.showErrorMessage(`Unable to open compare. See output channel for more details`);
}
const diffArgs: DiffWithCommandArgs = {
repoPath: args.commit.repoPath,
lhs: {
sha: args.commit.previousSha !== undefined ? args.commit.previousSha : GitService.fakeSha,
uri: args.commit.previousUri
},
rhs: {
sha: args.commit.sha,
uri: args.commit.uri
},
line: args.line,
showOptions: args.showOptions
};
return commands.executeCommand(Commands.DiffWith, diffArgs);
}
}

View File

@@ -1,16 +1,16 @@
'use strict';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { commands, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands, GlyphChars } from '../constants';
import { DiffWithCommandArgs } from './diffWith';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { CommandQuickPickItem, FileHistoryQuickPick } from '../quickPicks';
import * as path from 'path';
export interface DiffWithRevisionCommandArgs {
line?: number;
maxCount?: number;
line?: number;
showOptions?: TextDocumentShowOptions;
}
@@ -46,24 +46,24 @@ export class DiffWithRevisionCommand extends ActiveEditorCommand {
if (pick instanceof CommandQuickPickItem) return pick.execute();
const compare = await this.git.getVersionedFile(gitUri.repoPath, gitUri.fsPath, pick.commit.sha);
if (args.line !== undefined && args.line !== 0) {
if (args.showOptions === undefined) {
args.showOptions = {};
}
args.showOptions.selection = new Range(args.line, 0, args.line, 0);
}
await commands.executeCommand(BuiltInCommands.Diff,
Uri.file(compare),
gitUri.fileUri(),
`${path.basename(gitUri.fsPath)} (${pick.commit.shortSha}) ${GlyphChars.ArrowLeftRight} ${path.basename(gitUri.fsPath)}`,
args.showOptions);
const diffArgs: DiffWithCommandArgs = {
repoPath: gitUri.repoPath,
lhs: {
sha: pick.commit.sha,
uri: gitUri as Uri
},
rhs: {
sha: '',
uri: gitUri as Uri
},
line: args.line,
showOptions: args.showOptions
};
return await commands.executeCommand(Commands.DiffWith, diffArgs);
}
catch (ex) {
Logger.error(ex, 'DiffWithRevisionCommand', 'getVersionedFile');
return window.showErrorMessage(`Unable to open history compare. See output channel for more details`);
Logger.error(ex, 'DiffWithRevisionCommand');
return window.showErrorMessage(`Unable to open compare. See output channel for more details`);
}
}
}

View File

@@ -1,14 +1,14 @@
'use strict';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { commands, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands, GlyphChars } from '../constants';
import { DiffWithCommandArgs } from './diffWith';
import { GitCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import * as path from 'path';
export interface DiffWithWorkingCommandArgs {
commit?: GitCommit;
line?: number;
showOptions?: TextDocumentShowOptions;
}
@@ -48,25 +48,19 @@ export class DiffWithWorkingCommand extends ActiveEditorCommand {
const workingFileName = await this.git.findWorkingFileName(gitUri.repoPath, gitUri.fsPath);
if (workingFileName === undefined) return undefined;
try {
const compare = await this.git.getVersionedFile(args.commit.repoPath, args.commit.uri.fsPath, args.commit.sha);
if (args.line !== undefined && args.line !== 0) {
if (args.showOptions === undefined) {
args.showOptions = {};
}
args.showOptions.selection = new Range(args.line, 0, args.line, 0);
}
await commands.executeCommand(BuiltInCommands.Diff,
Uri.file(compare),
Uri.file(path.resolve(gitUri.repoPath, workingFileName)),
`${path.basename(args.commit.uri.fsPath)} (${args.commit.shortSha}) ${GlyphChars.ArrowLeftRight} ${path.basename(workingFileName)}`,
args.showOptions);
}
catch (ex) {
Logger.error(ex, 'DiffWithWorkingCommand', 'getVersionedFile');
return window.showErrorMessage(`Unable to open compare. See output channel for more details`);
}
const diffArgs: DiffWithCommandArgs = {
repoPath: args.commit.repoPath,
lhs: {
sha: args.commit.sha,
uri: args.commit.uri
},
rhs: {
sha: '',
uri: args.commit.uri
},
line: args.line,
showOptions: args.showOptions
};
return commands.executeCommand(Commands.DiffWith, diffArgs);
}
}

View File

@@ -1,5 +1,4 @@
'use strict';
import { Arrays } from '../system';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithBranch } from './common';
import { GlyphChars } from '../constants';
@@ -10,6 +9,7 @@ import { OpenInRemoteCommandArgs } from './openInRemote';
export interface OpenBranchInRemoteCommandArgs {
branch?: string;
remote?: string;
}
export class OpenBranchInRemoteCommand extends ActiveEditorCommand {
@@ -22,7 +22,7 @@ export class OpenBranchInRemoteCommand extends ActiveEditorCommand {
if (isCommandViewContextWithBranch(context)) {
args = { ...args };
args.branch = context.node.branch.name;
return this.execute(context.editor, context.uri, args);
args.remote = context.node.branch.getRemote();
}
return this.execute(context.editor, context.uri, args);
@@ -51,12 +51,14 @@ export class OpenBranchInRemoteCommand extends ActiveEditorCommand {
if (args.branch === undefined) return undefined;
}
const remotes = Arrays.uniqueBy(await this.git.getRemotes(repoPath), _ => _.url, _ => !!_.provider);
const remotes = (await this.git.getRemotes(repoPath)).filter(r => r.provider !== undefined);
return commands.executeCommand(Commands.OpenInRemote, uri, {
resource: {
type: 'branch',
branch: args.branch
},
remote: args.remote,
remotes
} as OpenInRemoteCommandArgs);
}

View File

@@ -0,0 +1,51 @@
'use strict';
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);
}
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 = (await this.git.getRemotes(repoPath)).filter(r => r.provider !== undefined);
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,5 +1,4 @@
'use strict';
import { Arrays } from '../system';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
import { GitBlameCommit, GitService, GitUri } from '../gitService';
@@ -13,6 +12,15 @@ export interface OpenCommitInRemoteCommandArgs {
export class OpenCommitInRemoteCommand extends ActiveEditorCommand {
static getMarkdownCommandArgs(sha: string): string;
static getMarkdownCommandArgs(args: OpenCommitInRemoteCommandArgs): string;
static getMarkdownCommandArgs(argsOrSha: OpenCommitInRemoteCommandArgs | string): string {
const args = typeof argsOrSha === 'string'
? { sha: argsOrSha }
: argsOrSha;
return super.getMarkdownCommandArgsCore<OpenCommitInRemoteCommandArgs>(Commands.OpenCommitInRemote, args);
}
constructor(private git: GitService) {
super(Commands.OpenCommitInRemote);
}
@@ -53,7 +61,8 @@ export class OpenCommitInRemoteCommand extends ActiveEditorCommand {
args.sha = commit.sha;
}
const remotes = Arrays.uniqueBy(await this.git.getRemotes(gitUri.repoPath), _ => _.url, _ => !!_.provider);
const remotes = (await this.git.getRemotes(gitUri.repoPath)).filter(r => r.provider !== undefined);
return commands.executeCommand(Commands.OpenInRemote, uri, {
resource: {
type: 'commit',

View File

@@ -1,12 +1,12 @@
'use strict';
import { Arrays } from '../system';
import { commands, Range, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithBranch, isCommandViewContextWithCommit } from './common';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { OpenInRemoteCommandArgs } from './openInRemote';
export interface OpenFileInRemoteCommandArgs {
branch?: string;
range?: boolean;
}
@@ -16,10 +16,13 @@ export class OpenFileInRemoteCommand extends ActiveEditorCommand {
super(Commands.OpenFileInRemote);
}
protected async preExecute(context: CommandContext, args: OpenFileInRemoteCommandArgs = {}): Promise<any> {
protected async preExecute(context: CommandContext, args: OpenFileInRemoteCommandArgs = { range: true }): Promise<any> {
if (isCommandViewContextWithCommit(context)) {
args = { ...args };
args.range = false;
if (isCommandViewContextWithBranch(context)) {
args.branch = context.node.branch !== undefined ? context.node.branch.name : undefined;
}
return this.execute(context.editor, context.node.commit.uri, args);
}
@@ -33,18 +36,23 @@ export class OpenFileInRemoteCommand extends ActiveEditorCommand {
const gitUri = await GitUri.fromUri(uri, this.git);
if (!gitUri.repoPath) return undefined;
const branch = await this.git.getBranch(gitUri.repoPath);
if (args.branch === undefined) {
const branch = await this.git.getBranch(gitUri.repoPath);
if (branch !== undefined) {
args.branch = branch.name;
}
}
try {
const remotes = Arrays.uniqueBy(await this.git.getRemotes(gitUri.repoPath), _ => _.url, _ => !!_.provider);
const remotes = (await this.git.getRemotes(gitUri.repoPath)).filter(r => r.provider !== undefined);
const range = (args.range && editor !== undefined)
? new Range(editor.selection.start.with({ line: editor.selection.start.line + 1 }), editor.selection.end.with({ line: editor.selection.end.line + 1 }))
: undefined;
return commands.executeCommand(Commands.OpenInRemote, uri, {
resource: {
type: 'file',
branch: branch === undefined ? 'Current' : branch.name,
type: gitUri.sha === undefined ? 'file' : 'revision',
branch: args.branch === undefined ? 'Current' : args.branch,
fileName: gitUri.getRelativePath(),
range: range,
sha: gitUri.sha

View File

@@ -3,11 +3,12 @@ import { Strings } from '../system';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { GlyphChars } from '../constants';
import { GitLogCommit, GitRemote, RemoteResource } from '../gitService';
import { GitLogCommit, GitRemote, GitService, RemoteResource } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem, OpenRemoteCommandQuickPickItem, RemotesQuickPick } from '../quickPicks';
export interface OpenInRemoteCommandArgs {
remote?: string;
remotes?: GitRemote[];
resource?: RemoteResource;
@@ -26,6 +27,14 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
args = { ...args };
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 {
if (args.remotes.length === 1) {
this.ensureRemoteBranchName(args);
@@ -41,7 +50,7 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
break;
case 'commit':
const shortSha = args.resource.sha.substring(0, 8);
const shortSha = GitService.shortenSha(args.resource.sha);
placeHolder = `open commit ${shortSha} in${GlyphChars.Ellipsis}`;
break;
@@ -61,7 +70,7 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
}
}
else {
const shortFileSha = args.resource.sha === undefined ? '' : args.resource.sha.substring(0, 8);
const shortFileSha = args.resource.sha === undefined ? '' : GitService.shortenSha(args.resource.sha);
const shaSuffix = shortFileSha ? ` ${Strings.pad(GlyphChars.Dot, 1, 1)} ${shortFileSha}` : '';
placeHolder = `open ${args.resource.fileName}${shaSuffix} in${GlyphChars.Ellipsis}`;

View File

@@ -1,18 +1,30 @@
'use strict';
import { Arrays } from '../system';
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 { Logger } from '../logger';
import { OpenInRemoteCommandArgs } from './openInRemote';
export interface OpenRepoInRemoteCommandArgs {
remote?: string;
}
export class OpenRepoInRemoteCommand extends ActiveEditorCommand {
constructor(private git: GitService) {
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);
}
async execute(editor?: TextEditor, uri?: Uri, args: OpenRepoInRemoteCommandArgs = {}) {
uri = getCommandUri(uri, editor);
const gitUri = uri && await GitUri.fromUri(uri, this.git);
@@ -21,11 +33,13 @@ export class OpenRepoInRemoteCommand extends ActiveEditorCommand {
if (!repoPath) return undefined;
try {
const remotes = Arrays.uniqueBy(await this.git.getRemotes(repoPath), _ => _.url, _ => !!_.provider);
const remotes = (await this.git.getRemotes(repoPath)).filter(r => r.provider !== undefined);
return commands.executeCommand(Commands.OpenInRemote, uri, {
resource: {
type: 'repo'
},
remote: args.remote,
remotes
} as OpenInRemoteCommandArgs);
}

View File

@@ -20,20 +20,29 @@ export interface ShowQuickCommitDetailsCommandArgs {
export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
static getMarkdownCommandArgs(sha: string): string;
static getMarkdownCommandArgs(args: ShowQuickCommitDetailsCommandArgs): string;
static getMarkdownCommandArgs(argsOrSha: ShowQuickCommitDetailsCommandArgs | string): string {
const args = typeof argsOrSha === 'string'
? { sha: argsOrSha }
: argsOrSha;
return super.getMarkdownCommandArgsCore<ShowQuickCommitDetailsCommandArgs>(Commands.ShowQuickCommitDetails, args);
}
constructor(private git: GitService) {
super(Commands.ShowQuickCommitDetails);
}
protected async preExecute(context: CommandContext, ...args: any[]): Promise<any> {
protected async preExecute(context: CommandContext, args: ShowQuickCommitDetailsCommandArgs = {}): Promise<any> {
if (context.type === 'view') {
args = { ...args };
args.sha = context.node.uri.sha;
if (isCommandViewContextWithCommit(context)) {
args = [{ sha: context.node.uri.sha, commit: context.node.commit }];
}
else {
args = [{ sha: context.node.uri.sha }];
args.commit = context.node.commit;
}
}
return this.execute(context.editor, context.uri, ...args);
return this.execute(context.editor, context.uri, args);
}
async execute(editor?: TextEditor, uri?: Uri, args: ShowQuickCommitDetailsCommandArgs = {}) {

View File

@@ -20,20 +20,29 @@ export interface ShowQuickCommitFileDetailsCommandArgs {
export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCachedCommand {
static getMarkdownCommandArgs(sha: string): string;
static getMarkdownCommandArgs(args: ShowQuickCommitFileDetailsCommandArgs): string;
static getMarkdownCommandArgs(argsOrSha: ShowQuickCommitFileDetailsCommandArgs | string): string {
const args = typeof argsOrSha === 'string'
? { sha: argsOrSha }
: argsOrSha;
return super.getMarkdownCommandArgsCore<ShowQuickCommitFileDetailsCommandArgs>(Commands.ShowQuickCommitFileDetails, args);
}
constructor(private git: GitService) {
super(Commands.ShowQuickCommitFileDetails);
}
protected async preExecute(context: CommandContext, ...args: any[]): Promise<any> {
protected async preExecute(context: CommandContext, args: ShowQuickCommitFileDetailsCommandArgs = {}): Promise<any> {
if (context.type === 'view') {
args = { ...args };
args.sha = context.node.uri.sha;
if (isCommandViewContextWithCommit(context)) {
args = [{ sha: context.node.uri.sha, commit: context.node.commit }];
}
else {
args = [{ sha: context.node.uri.sha }];
args.commit = context.node.commit;
}
}
return this.execute(context.editor, context.uri, ...args);
return this.execute(context.editor, context.uri, args);
}
async execute(editor?: TextEditor, uri?: Uri, args: ShowQuickCommitFileDetailsCommandArgs = {}) {
@@ -95,7 +104,7 @@ export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCachedCommand
args.commit.workingFileName = workingFileName;
args.commit.workingFileName = await this.git.findWorkingFileName(args.commit);
const shortSha = args.sha!.substring(0, 8);
const shortSha = GitService.shortenSha(args.sha!);
if (args.goBackCommand === undefined) {
// Create a command to get back to the commit details

View File

@@ -28,7 +28,7 @@ export class StashApplyCommand extends Command {
return this.execute(args);
}
return super.preExecute(context, args);
return this.execute(args);
}
async execute(args: StashApplyCommandArgs = { confirm: true, deleteAfter: false }) {

View File

@@ -26,7 +26,7 @@ export class StashDeleteCommand extends Command {
return this.execute(args);
}
return super.preExecute(context, args);
return this.execute(args);
}
async execute(args: StashDeleteCommandArgs = { confirm: true }) {

View File

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

View File

@@ -7,7 +7,8 @@ import { OutputLevel } from './logger';
export { ExtensionKey } from './constants';
export type CodeLensCommand = 'gitlens.toggleFileBlame' |
export type CodeLensCommand =
'gitlens.toggleFileBlame' |
'gitlens.showBlameHistory' |
'gitlens.showFileHistory' |
'gitlens.diffWithPrevious' |
@@ -41,7 +42,19 @@ export const LineHighlightLocations = {
OverviewRuler: 'overviewRuler' as LineHighlightLocations
};
export type StatusBarCommand = 'gitlens.toggleFileBlame' |
export type CustomRemoteType =
'Bitbucket' |
'GitHub' |
'GitLab';
export const CustomRemoteType = {
Bitbucket: 'Bitbucket' as CustomRemoteType,
BitbucketServer: 'BitbucketServer' as CustomRemoteType,
GitHub: 'GitHub' as CustomRemoteType,
GitLab: 'GitLab' as CustomRemoteType
};
export type StatusBarCommand =
'gitlens.toggleFileBlame' |
'gitlens.showBlameHistory' |
'gitlens.showFileHistory' |
'gitlens.toggleCodeLens' |
@@ -119,6 +132,11 @@ export interface ICodeLensLanguageLocation {
customSymbols?: string[];
}
export interface IRemotesConfig {
type: CustomRemoteType;
domain: string;
}
export interface IThemeConfig {
annotations: {
file: {
@@ -233,7 +251,6 @@ export interface IConfig {
hover: {
details: boolean;
changes: boolean;
wholeLine: boolean;
};
};
};
@@ -257,6 +274,8 @@ export interface IConfig {
};
blame: {
ignoreWhitespace: boolean;
file: {
annotationType: FileAnnotationType;
lineHighlight: {
@@ -298,14 +317,20 @@ export interface IConfig {
defaultDateFormat: string | null;
gitExplorer: {
enabled: boolean;
view: GitExplorerView;
includeWorkingTree: boolean;
showTrackingBranch: boolean;
commitFormat: string;
commitFileFormat: string;
stashFormat: string;
stashFileFormat: string;
statusFileFormat: string;
// dateFormat: string | null;
};
remotes: IRemotesConfig[];
statusBar: {
enabled: boolean;
alignment: 'left' | 'right';

View File

@@ -8,8 +8,6 @@ export const QualifiedExtensionId = `eamodio.${ExtensionId}`;
export const ApplicationInsightsKey = 'a9c302f8-6483-4d01-b92c-c159c799c679';
export const FakeSha = 'ffffffffffffffffffffffffffffffffffffffff';
export type BuiltInCommands = 'cursorMove' |
'editor.action.showReferences' |
'editor.action.toggleRenderWhitespace' |
@@ -79,12 +77,16 @@ export type GlyphChars = '\u21a9' |
'\u2937' |
'\u2190' |
'\u2194' |
'\u2192' |
'\u21e8' |
'\u2191' |
'\u2197' |
'\u2217' |
'\u2713' |
'\u2014' |
'\u2022' |
'\u2026' |
'\u270E' |
'\u00a0' |
'\u200b';
export const GlyphChars = {
@@ -93,17 +95,26 @@ export const GlyphChars = {
ArrowDropRight: '\u2937' as GlyphChars,
ArrowLeft: '\u2190' as GlyphChars,
ArrowLeftRight: '\u2194' as GlyphChars,
ArrowRight: '\u2192' as GlyphChars,
ArrowRightHollow: '\u21e8' as GlyphChars,
ArrowUp: '\u2191' as GlyphChars,
ArrowUpRight: '\u2197' as GlyphChars,
Asterisk: '\u2217' as GlyphChars,
Check: '\u2713' as GlyphChars,
Dash: '\u2014' as GlyphChars,
Dot: '\u2022' as GlyphChars,
Ellipsis: '\u2026' as GlyphChars,
Pensil: '\u270E' as GlyphChars,
Space: '\u00a0' as GlyphChars,
ZeroWidthSpace: '\u200b' as GlyphChars
};
export type WorkspaceState = 'gitlensVersion';
export type GlobalState = 'gitlensVersion';
export const GlobalState = {
GitLensVersion: 'gitlensVersion' as GlobalState
};
export type WorkspaceState = 'gitlens:gitExplorer:view';
export const WorkspaceState = {
GitLensVersion: 'gitlensVersion' as WorkspaceState
GitExplorerView: 'gitlens:gitExplorer:view' as WorkspaceState
};

View File

@@ -7,7 +7,7 @@ import { Commands } from './commands';
import { TextEditorComparer } from './comparers';
import { IConfig, StatusBarCommand } from './configuration';
import { DocumentSchemes, ExtensionKey } from './constants';
import { BlameabilityChangeEvent, CommitFormatter, GitCommit, GitCommitLine, GitContextTracker, GitService, GitUri } from './gitService';
import { BlameabilityChangeEvent, CommitFormatter, GitCommit, GitCommitLine, GitContextTracker, GitService, GitUri, ICommitFormatOptions } from './gitService';
import { Logger } from './logger';
const annotationDecoration: TextEditorDecorationType = window.createTextEditorDecorationType({
@@ -295,12 +295,10 @@ export class CurrentLineController extends Disposable {
const decorationOptions: DecorationOptions[] = [];
let showChanges = false;
let showChangesStartIndex = 0;
let showChangesInStartingWhitespace = false;
let showDetails = false;
let showDetailsStartIndex = 0;
let showDetailsInStartingWhitespace = false;
let showAtStart = false;
let showStartIndex = 0;
switch (state.annotationType) {
case LineAnnotationType.Trailing: {
@@ -308,21 +306,7 @@ export class CurrentLineController extends Disposable {
showChanges = cfgAnnotations.hover.changes;
showDetails = cfgAnnotations.hover.details;
if (cfgAnnotations.hover.wholeLine) {
showChangesStartIndex = 0;
showChangesInStartingWhitespace = false;
showDetailsStartIndex = 0;
showDetailsInStartingWhitespace = false;
}
else {
showChangesStartIndex = endOfLineIndex;
showChangesInStartingWhitespace = true;
showDetailsStartIndex = endOfLineIndex;
showDetailsInStartingWhitespace = true;
}
showStartIndex = cfgAnnotations.hover.wholeLine ? 0 : endOfLineIndex;
const decoration = Annotations.trailing(commit, cfgAnnotations.format, cfgAnnotations.dateFormat === null ? this._config.defaultDateFormat : cfgAnnotations.dateFormat, this._config.theme);
decoration.range = editor.document.validateRange(new Range(line, endOfLineIndex, line, endOfLineIndex));
@@ -334,12 +318,8 @@ export class CurrentLineController extends Disposable {
const cfgAnnotations = this._config.annotations.line.hover;
showChanges = cfgAnnotations.changes;
showChangesStartIndex = 0;
showChangesInStartingWhitespace = false;
showDetails = cfgAnnotations.details;
showDetailsStartIndex = 0;
showDetailsInStartingWhitespace = false;
showStartIndex = 0;
break;
}
@@ -348,25 +328,15 @@ export class CurrentLineController extends Disposable {
if (showDetails || showChanges) {
const annotationType = this.annotationController.getAnnotationType(editor);
const firstNonWhitespace = editor.document.lineAt(line).firstNonWhitespaceCharacterIndex;
switch (annotationType) {
case FileAnnotationType.Gutter: {
const cfgHover = this._config.annotations.file.gutter.hover;
if (cfgHover.details) {
showDetailsInStartingWhitespace = false;
if (cfgHover.wholeLine) {
// Avoid double annotations if we are showing the whole-file hover blame annotations
showDetails = false;
showStartIndex = 0;
}
else {
if (showDetailsStartIndex === 0) {
showDetailsStartIndex = firstNonWhitespace === 0 ? 1 : firstNonWhitespace;
}
if (showChangesStartIndex === 0) {
showChangesInStartingWhitespace = true;
showChangesStartIndex = firstNonWhitespace === 0 ? 1 : firstNonWhitespace;
}
else if (showStartIndex !== 0) {
showAtStart = true;
}
}
@@ -374,20 +344,11 @@ export class CurrentLineController extends Disposable {
}
case FileAnnotationType.Hover: {
const cfgHover = this._config.annotations.file.hover;
showDetailsInStartingWhitespace = false;
if (cfgHover.wholeLine) {
// Avoid double annotations if we are showing the whole-file hover blame annotations
showDetails = false;
showChangesStartIndex = 0;
showStartIndex = 0;
}
else {
if (showDetailsStartIndex === 0) {
showDetailsStartIndex = firstNonWhitespace === 0 ? 1 : firstNonWhitespace;
}
if (showChangesStartIndex === 0) {
showChangesInStartingWhitespace = true;
showChangesStartIndex = firstNonWhitespace === 0 ? 1 : firstNonWhitespace;
}
else if (showStartIndex !== 0) {
showAtStart = true;
}
break;
@@ -395,29 +356,21 @@ export class CurrentLineController extends Disposable {
case FileAnnotationType.RecentChanges: {
const cfgChanges = this._config.annotations.file.recentChanges.hover;
if (cfgChanges.details) {
if (cfgChanges.wholeLine) {
// Avoid double annotations if we are showing the whole-file hover blame annotations
showDetails = false;
}
else {
showDetailsInStartingWhitespace = false;
}
// Avoid double annotations if we are showing the whole-file hover blame annotations
showDetails = false;
}
if (cfgChanges.changes) {
if (cfgChanges.wholeLine) {
// Avoid double annotations if we are showing the whole-file hover blame annotations
showChanges = false;
}
else {
showChangesInStartingWhitespace = false;
}
// Avoid double annotations if we are showing the whole-file hover blame annotations
showChanges = false;
}
break;
}
}
const range = editor.document.validateRange(new Range(line, showStartIndex, line, endOfLineIndex));
if (showDetails) {
// Get the full commit message -- since blame only returns the summary
let logCommit: GitCommit | undefined = undefined;
@@ -425,29 +378,22 @@ export class CurrentLineController extends Disposable {
logCommit = await this.git.getLogCommit(this._uri.repoPath, this._uri.fsPath, commit.sha);
}
// I have no idea why I need this protection -- but it happens
if (editor.document === undefined) return;
const decoration = Annotations.detailsHover(logCommit || commit, this._config.defaultDateFormat);
decoration.range = editor.document.validateRange(new Range(line, showDetailsStartIndex, line, endOfLineIndex));
const decoration = Annotations.detailsHover(logCommit || commit, this._config.defaultDateFormat, this.git.hasRemotes((logCommit || commit).repoPath));
decoration.range = range;
decorationOptions.push(decoration);
if (showDetailsInStartingWhitespace && showDetailsStartIndex !== 0) {
decorationOptions.push(Annotations.withRange(decoration, 0, firstNonWhitespace));
if (showAtStart) {
decorationOptions.push(Annotations.withRange(decoration, 0, 0));
}
}
if (showChanges) {
const decoration = await Annotations.changesHover(commit, line, this._uri, this.git);
// I have no idea why I need this protection -- but it happens
if (editor.document === undefined) return;
decoration.range = editor.document.validateRange(new Range(line, showChangesStartIndex, line, endOfLineIndex));
decoration.range = range;
decorationOptions.push(decoration);
if (showChangesInStartingWhitespace && showChangesStartIndex !== 0) {
decorationOptions.push(Annotations.withRange(decoration, 0, firstNonWhitespace));
if (showAtStart) {
decorationOptions.push(Annotations.withRange(decoration, 0, 0));
}
}
}
@@ -462,7 +408,10 @@ export class CurrentLineController extends Disposable {
const cfg = this._config.statusBar;
if (!cfg.enabled || this._statusBarItem === undefined) return;
this._statusBarItem.text = `$(git-commit) ${CommitFormatter.fromTemplate(cfg.format, commit, cfg.dateFormat === null ? this._config.defaultDateFormat : cfg.dateFormat)}`;
this._statusBarItem.text = `$(git-commit) ${CommitFormatter.fromTemplate(cfg.format, commit, {
truncateMessageAtNewLine: true,
dateFormat: cfg.dateFormat === null ? this._config.defaultDateFormat : cfg.dateFormat
} as ICommitFormatOptions)}`;
switch (cfg.command) {
case StatusBarCommand.BlameAnnotate:

View File

@@ -3,9 +3,9 @@
import { commands, ExtensionContext, extensions, languages, window, workspace } from 'vscode';
import { AnnotationController } from './annotations/annotationController';
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 { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithBranchCommand, DiffWithNextCommand, DiffWithPreviousCommand, DiffWithRevisionCommand, DiffWithWorkingCommand } from './commands';
import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithBranchCommand, DiffWithCommand, DiffWithNextCommand, DiffWithPreviousCommand, DiffWithRevisionCommand, DiffWithWorkingCommand } from './commands';
import { ResetSuppressedWarningsCommand } from './commands';
import { ClearFileAnnotationsCommand, ShowFileBlameCommand, ShowLineBlameCommand, ToggleFileBlameCommand, ToggleFileRecentChangesCommand, ToggleLineBlameCommand } from './commands';
import { ShowBlameHistoryCommand, ShowFileHistoryCommand } from './commands';
@@ -16,9 +16,10 @@ import { ShowQuickRepoStatusCommand, ShowQuickStashListCommand } from './command
import { StashApplyCommand, StashDeleteCommand, StashSaveCommand } from './commands';
import { ToggleCodeLensCommand } from './commands';
import { CodeLensLocations, IConfig, LineHighlightLocations } from './configuration';
import { ApplicationInsightsKey, CommandContext, ExtensionKey, QualifiedExtensionId, setCommandContext, WorkspaceState } from './constants';
import { ApplicationInsightsKey, CommandContext, ExtensionKey, GlobalState, QualifiedExtensionId, setCommandContext } from './constants';
import { CodeLensController } from './codeLensController';
import { CurrentLineController, LineAnnotationType } from './currentLineController';
import { RemoteProviderFactory } from './git/remotes/factory';
import { GitContentProvider } from './gitContentProvider';
import { GitExplorer } from './views/gitExplorer';
import { GitRevisionCodeLensProvider } from './gitRevisionCodeLensProvider';
@@ -33,6 +34,7 @@ export async function activate(context: ExtensionContext) {
Logger.configure(context);
Messages.configure(context);
Telemetry.configure(ApplicationInsightsKey);
RemoteProviderFactory.configure(context);
const gitlens = extensions.getExtension(QualifiedExtensionId)!;
const gitlensVersion = gitlens.packageJSON.version;
@@ -69,7 +71,7 @@ export async function activate(context: ExtensionContext) {
notifyOnUnsupportedGitVersion(context, gitVersion);
notifyOnNewGitLensVersion(context, gitlensVersion);
await context.globalState.update(WorkspaceState.GitLensVersion, gitlensVersion);
await context.globalState.update(GlobalState.GitLensVersion, gitlensVersion);
const git = new GitService(repoPath);
context.subscriptions.push(git);
@@ -103,11 +105,13 @@ export async function activate(context: ExtensionContext) {
context.subscriptions.push(new DiffDirectoryCommand(git));
context.subscriptions.push(new DiffLineWithPreviousCommand(git));
context.subscriptions.push(new DiffLineWithWorkingCommand(git));
context.subscriptions.push(new DiffWithCommand(git));
context.subscriptions.push(new DiffWithBranchCommand(git));
context.subscriptions.push(new DiffWithNextCommand(git));
context.subscriptions.push(new DiffWithPreviousCommand(git));
context.subscriptions.push(new DiffWithRevisionCommand(git));
context.subscriptions.push(new DiffWithWorkingCommand(git));
context.subscriptions.push(new OpenBranchesInRemoteCommand(git));
context.subscriptions.push(new OpenBranchInRemoteCommand(git));
context.subscriptions.push(new OpenCommitInRemoteCommand(git));
context.subscriptions.push(new OpenFileInRemoteCommand(git));
@@ -144,7 +148,7 @@ export async function activate(context: ExtensionContext) {
export function deactivate() { }
async function migrateSettings(context: ExtensionContext) {
const previousVersion = context.globalState.get<string>(WorkspaceState.GitLensVersion);
const previousVersion = context.globalState.get<string>(GlobalState.GitLensVersion);
if (previousVersion === undefined) return;
const [major] = previousVersion.split('.');
@@ -270,7 +274,7 @@ async function migrateSettings(context: ExtensionContext) {
async function notifyOnNewGitLensVersion(context: ExtensionContext, version: string) {
if (context.globalState.get(SuppressedKeys.UpdateNotice, false)) return;
const previousVersion = context.globalState.get<string>(WorkspaceState.GitLensVersion);
const previousVersion = context.globalState.get<string>(GlobalState.GitLensVersion);
if (previousVersion === undefined) {
Logger.log(`GitLens first-time install`);

View File

@@ -2,9 +2,11 @@
import { Strings } from '../../system';
import { GitCommit } from '../models/commit';
import { Formatter, IFormatOptions } from './formatter';
import * as moment from 'moment';
import { GlyphChars } from '../../constants';
export interface ICommitFormatOptions extends IFormatOptions {
truncateMessageAtNewLine?: boolean;
tokenOptions?: {
ago?: Strings.ITokenOptions;
author?: Strings.ITokenOptions;
@@ -17,7 +19,7 @@ export interface ICommitFormatOptions extends IFormatOptions {
export class CommitFormatter extends Formatter<GitCommit, ICommitFormatOptions> {
get ago() {
const ago = moment(this._item.date).fromNow();
const ago = this._item.fromNow();
return this._padOrTruncate(ago, this._options.tokenOptions!.ago);
}
@@ -27,21 +29,28 @@ export class CommitFormatter extends Formatter<GitCommit, ICommitFormatOptions>
}
get authorAgo() {
const authorAgo = `${this._item.author}, ${moment(this._item.date).fromNow()}`;
const authorAgo = `${this._item.author}, ${this._item.fromNow()}`;
return this._padOrTruncate(authorAgo, this._options.tokenOptions!.authorAgo);
}
get date() {
const date = moment(this._item.date).format(this._options.dateFormat!);
const date = this._item.formatDate(this._options.dateFormat!);
return this._padOrTruncate(date, this._options.tokenOptions!.date);
}
get id() {
return this._item.shortSha;
return this._item.isUncommitted ? 'index' : this._item.shortSha;
}
get message() {
const message = this._item.isUncommitted ? 'Uncommitted change' : this._item.message;
let message = this._item.isUncommitted ? 'Uncommitted change' : this._item.message;
if (this._options.truncateMessageAtNewLine) {
const index = message.indexOf('\n');
if (index !== -1) {
message = `${message.substring(0, index)}${GlyphChars.Space}${GlyphChars.Ellipsis}`;
}
}
return this._padOrTruncate(message, this._options.tokenOptions!.message);
}

View File

@@ -51,7 +51,7 @@ export abstract class Formatter<TItem = any, TOptions extends IFormatOptions = I
let max = options.truncateTo;
const width = Strings.getWidth(s);
const width = Strings.width(s);
if (max === undefined) {
if (this.collapsableWhitespace === 0) return s;

View File

@@ -1,7 +1,8 @@
'use strict';
import { Strings } from '../../system';
import { GlyphChars } from '../../constants';
import { Formatter, IFormatOptions } from './formatter';
import { GitStatusFile, IGitStatusFile } from '../models/status';
import { GitStatusFile, IGitStatusFile, IGitStatusFileWithCommit } from '../models/status';
import * as path from 'path';
export interface IStatusFormatOptions extends IFormatOptions {
@@ -29,6 +30,11 @@ export class StatusFileFormatter extends Formatter<IGitStatusFile, IStatusFormat
return this._padOrTruncate(directory, this._options.tokenOptions!.file);
}
get working() {
const commit = (this._item as IGitStatusFileWithCommit).commit;
return (commit !== undefined && commit.isUncommitted) ? `${GlyphChars.Pensil} ${GlyphChars.Space}` : '';
}
static fromTemplate(template: string, status: IGitStatusFile, dateFormat: string | null): string;
static fromTemplate(template: string, status: IGitStatusFile, options?: IStatusFormatOptions): string;
static fromTemplate(template: string, status: IGitStatusFile, dateFormatOrOptions?: string | null | IStatusFormatOptions): string;

View File

@@ -1,4 +1,5 @@
'use strict';
import { Strings } from '../system';
import { findGitPath, IGit } from './gitLocator';
import { Logger } from '../logger';
import { spawnPromise } from 'spawn-rx';
@@ -10,17 +11,19 @@ import * as iconv from 'iconv-lite';
export { IGit };
export * from './models/models';
export * from './parsers/blameParser';
export * from './parsers/branchParser';
export * from './parsers/diffParser';
export * from './parsers/logParser';
export * from './parsers/remoteParser';
export * from './parsers/stashParser';
export * from './parsers/statusParser';
export * from './remotes/provider';
let git: IGit;
// `--format=%H -%nauthor %an%nauthor-date %ai%ncommitter %cn%ncommitter-date %ci%nparents %P%nsummary %B%nfilename ?`
const defaultLogParams = [`log`, `--name-status`, `--full-history`, `-M`, `--date=iso8601`, `--format=%H -%nauthor %an%nauthor-date %ai%nparents %P%nsummary %B%nfilename ?`];
const defaultStashParams = [`stash`, `list`, `--name-status`, `--full-history`, `-M`, `--format=%H -%nauthor-date %ai%nreflog-selector %gd%nsummary %B%nfilename ?`];
const defaultBlameParams = [`blame`, `--root`, `--incremental`];
const defaultLogParams = [`log`, `--name-status`, `--full-history`, `-M`, `--format=%H -%nauthor %an%nauthor-date %at%nparents %P%nsummary %B%nfilename ?`];
const defaultStashParams = [`stash`, `list`, `--name-status`, `--full-history`, `-M`, `--format=%H -%nauthor-date %at%nreflog-selector %gd%nsummary %B%nfilename ?`];
let defaultEncoding = 'utf8';
export function setDefaultEncoding(encoding: string) {
@@ -33,36 +36,53 @@ const GitWarnings = [
/no such path/,
/does not have any commits/,
/Path \'.*?\' does not exist in/,
/Path \'.*?\' exists on disk, but not in/
/Path \'.*?\' exists on disk, but not in/,
/no upstream configured for branch/
];
async function gitCommand(options: { cwd: string, encoding?: string }, ...args: any[]) {
interface GitCommandOptions {
cwd: string;
encoding?: string;
overrideErrorHandling?: boolean;
}
async function gitCommand(options: GitCommandOptions, ...args: any[]): Promise<string> {
if (options.overrideErrorHandling) return gitCommandCore(options, ...args);
try {
// 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
args.splice(0, 0, '-c', 'core.quotepath=false');
const opts = { encoding: 'utf8', ...options };
const s = await spawnPromise(git.path, args, { cwd: options.cwd, encoding: (opts.encoding === 'utf8') ? 'utf8' : 'binary' });
Logger.log('git', ...args, ` cwd='${options.cwd}'`);
if (opts.encoding === 'utf8' || opts.encoding === 'binary') return s;
return iconv.decode(Buffer.from(s, 'binary'), opts.encoding);
return await gitCommandCore(options, ...args);
}
catch (ex) {
const msg = ex && ex.toString();
if (msg) {
for (const warning of GitWarnings) {
if (warning.test(msg)) {
Logger.warn('git', ...args, ` cwd='${options.cwd}'`, msg && `\n ${msg.replace(/\r?\n|\r/g, ' ')}`);
return '';
}
return gitCommandDefaultErrorHandler(ex, options, ...args);
}
}
async function gitCommandCore(options: GitCommandOptions, ...args: any[]): Promise<string> {
// 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
args.splice(0, 0, '-c', 'core.quotepath=false');
const opts = { encoding: 'utf8', ...options };
const s = await spawnPromise(git.path, args, { cwd: options.cwd, encoding: (opts.encoding === 'utf8') ? 'utf8' : 'binary' });
Logger.log('git', ...args, ` cwd='${options.cwd}'`);
if (opts.encoding === 'utf8' || opts.encoding === 'binary') return s;
return iconv.decode(Buffer.from(s, 'binary'), opts.encoding);
}
function gitCommandDefaultErrorHandler(ex: Error, options: GitCommandOptions, ...args: any[]): string {
const msg = ex && ex.toString();
if (msg) {
for (const warning of GitWarnings) {
if (warning.test(msg)) {
Logger.warn('git', ...args, ` cwd='${options.cwd}'`, msg && `\n ${msg.replace(/\r?\n|\r/g, ' ')}`);
return '';
}
}
Logger.error(ex, 'git', ...args, ` cwd='${options.cwd}'`, msg && `\n ${msg.replace(/\r?\n|\r/g, ' ')}`);
throw ex;
}
Logger.error(ex, 'git', ...args, ` cwd='${options.cwd}'`, msg && `\n ${msg.replace(/\r?\n|\r/g, ' ')}`);
throw ex;
}
export class Git {
@@ -91,8 +111,9 @@ export class Git {
static async getVersionedFile(repoPath: string | undefined, fileName: string, branchOrSha: string) {
const data = await Git.show(repoPath, fileName, branchOrSha, 'binary');
if (data === undefined) return undefined;
const suffix = Git.isSha(branchOrSha) ? branchOrSha.substring(0, 8) : branchOrSha;
const suffix = Strings.truncate(Strings.sanitizeForFS(Git.isSha(branchOrSha) ? Git.shortenSha(branchOrSha) : branchOrSha), 50, '');
const ext = path.extname(fileName);
return new Promise<string>((resolve, reject) => {
tmp.file({ prefix: `${path.basename(fileName, ext)}-${suffix}__`, postfix: ext },
@@ -127,6 +148,10 @@ export class Git {
return fileName && fileName.replace(/\\/g, '/');
}
static shortenSha(sha: string) {
return sha.substring(0, 8);
}
static splitPath(fileName: string, repoPath: string | undefined, extract: boolean = true): [string, string] {
if (repoPath) {
fileName = this.normalizePath(fileName);
@@ -152,15 +177,17 @@ export class Git {
// Git commands
static blame(repoPath: string | undefined, fileName: string, sha?: string, startLine?: number, endLine?: number) {
static blame(repoPath: string | undefined, fileName: string, sha?: string, options: { ignoreWhitespace?: boolean, startLine?: number, endLine?: number } = {}) {
const [file, root] = Git.splitPath(fileName, repoPath);
const params = [`blame`, `--root`, `--incremental`];
const params = [...defaultBlameParams];
if (startLine != null && endLine != null) {
params.push(`-L ${startLine},${endLine}`);
if (options.ignoreWhitespace) {
params.push('-w');
}
if (options.startLine != null && options.endLine != null) {
params.push(`-L ${options.startLine},${options.endLine}`);
}
if (sha) {
params.push(sha);
}
@@ -168,20 +195,42 @@ export class Git {
return gitCommand({ cwd: root }, ...params, `--`, file);
}
static branch(repoPath: string, all: boolean) {
const params = [`branch`];
if (all) {
static branch(repoPath: string, options: { all: boolean } = { all: false }) {
const params = [`branch`, `-vv`];
if (options.all) {
params.push(`-a`);
}
return gitCommand({ cwd: repoPath }, ...params);
}
static async config_get(key: string, repoPath?: string) {
static async branch_current(repoPath: string) {
const params = [`rev-parse`, `--abbrev-ref`, `--symbolic-full-name`, `@`, `@{u}`];
const opts = { cwd: repoPath, overrideErrorHandling: true };
try {
return await gitCommand({ cwd: repoPath || '' }, `config`, `--get`, key);
return await gitCommand(opts, ...params);
}
catch (ex) {
if (/no upstream configured for branch/.test(ex && ex.toString())) {
return ex.message.split('\n')[0];
}
return gitCommandDefaultErrorHandler(ex, opts, ...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) {
try {
return await gitCommand({ cwd: repoPath || '', overrideErrorHandling: true }, `config`, `--get`, key);
}
catch {
return '';
}
}
@@ -210,6 +259,14 @@ export class Git {
return gitCommand({ cwd: repoPath }, ...params);
}
static diff_shortstat(repoPath: string, sha?: string) {
const params = [`diff`, `--shortstat`, `--no-ext-diff`];
if (sha) {
params.push(sha);
}
return gitCommand({ cwd: repoPath }, ...params);
}
static difftool_dirDiff(repoPath: string, sha1: string, sha2?: string) {
const params = [`difftool`, `--dir-diff`, sha1];
if (sha2) {
@@ -283,11 +340,19 @@ export class Git {
return gitCommand({ cwd: repoPath }, ...params, ...search);
}
static log_shortstat(repoPath: string, sha?: string) {
const params = [`log`, `--shortstat`, `--oneline`];
if (sha) {
params.push(sha);
}
return gitCommand({ cwd: repoPath }, ...params);
}
static async ls_files(repoPath: string, fileName: string): Promise<string> {
try {
return await gitCommand({ cwd: repoPath }, 'ls-files', fileName);
return await gitCommand({ cwd: repoPath, overrideErrorHandling: true }, 'ls-files', fileName);
}
catch (ex) {
catch {
return '';
}
}
@@ -300,12 +365,25 @@ export class Git {
return gitCommand({ cwd: repoPath }, 'remote', 'get-url', remote);
}
static show(repoPath: string | undefined, fileName: string, branchOrSha: string, encoding?: string) {
static async show(repoPath: string | undefined, fileName: string, branchOrSha: string, encoding?: string) {
const [file, root] = Git.splitPath(fileName, repoPath);
branchOrSha = branchOrSha.replace('^', '');
if (Git.isUncommitted(branchOrSha)) return Promise.reject(new Error(`sha=${branchOrSha} is uncommitted`));
return gitCommand({ cwd: root, encoding: encoding || defaultEncoding }, 'show', `${branchOrSha}:./${file}`);
if (Git.isUncommitted(branchOrSha)) throw new Error(`sha=${branchOrSha} is uncommitted`);
const opts = { cwd: root, encoding: encoding || defaultEncoding, overrideErrorHandling: true };
const args = `${branchOrSha}:./${file}`;
try {
return await gitCommand(opts, 'show', args);
}
catch (ex) {
const msg = ex && ex.toString();
if (/Path \'.*?\' does not exist in/.test(msg) || /Path \'.*?\' exists on disk, but not in /.test(msg)) {
return undefined;
}
return gitCommandDefaultErrorHandler(ex, opts, args);
}
}
static stash_apply(repoPath: string, stashName: string, deleteAfter: boolean) {
@@ -322,11 +400,18 @@ export class Git {
return gitCommand({ cwd: repoPath }, ...defaultStashParams);
}
static stash_save(repoPath: string, message?: string, unstagedOnly: boolean = false) {
const params = [`stash`, `save`, `--include-untracked`];
if (unstagedOnly) {
params.push(`--keep-index`);
static stash_push(repoPath: string, pathspecs: string[], message?: string) {
const params = [`stash`, `push`, `-u`];
if (message) {
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) {
params.push(message);
}

View File

@@ -2,7 +2,7 @@
import { Disposable, Event, EventEmitter, TextDocument, TextDocumentChangeEvent, TextEditor, window, workspace } from 'vscode';
import { TextDocumentComparer } from '../comparers';
import { CommandContext, setCommandContext } from '../constants';
import { GitService, GitUri } from '../gitService';
import { GitService, GitUri, RepoChangedReasons } from '../gitService';
import { Logger } from '../logger';
export interface BlameabilityChangeEvent {
@@ -32,13 +32,13 @@ export class GitContextTracker extends Disposable {
subscriptions.push(workspace.onDidChangeConfiguration(this._onConfigurationChanged, this));
subscriptions.push(workspace.onDidSaveTextDocument(this._onTextDocumentSaved, this));
subscriptions.push(this.git.onDidBlameFail(this._onBlameFailed, this));
subscriptions.push(this.git.onDidChangeRepo(this._onRepoChanged, this));
this._disposable = Disposable.from(...subscriptions);
setCommandContext(CommandContext.IsRepository, !!this.git.repoPath);
this._onConfigurationChanged();
this._onActiveTextEditorChanged(window.activeTextEditor);
}
dispose() {
@@ -55,6 +55,13 @@ export class GitContextTracker extends Disposable {
}
}
async _onRepoChanged(reasons: RepoChangedReasons[]) {
if (!reasons.includes(RepoChangedReasons.Remotes)) return;
const gitUri = this._editor === undefined ? undefined : await GitUri.fromUri(this._editor.document.uri, this.git);
this._updateContextHasRemotes(gitUri);
}
private _onActiveTextEditorChanged(editor: TextEditor | undefined) {
this._editor = editor;
this._updateContext(this._gitEnabled ? editor : undefined);

View File

@@ -42,7 +42,7 @@ export class GitUri extends Uri {
}
else {
const commit = commitOrRepoPath;
base._fsPath = path.resolve(commit.repoPath, commit.originalFileName || commit.fileName);
base._fsPath = path.resolve(commit.repoPath, commit.originalFileName || commit.fileName || '');
if (commit.repoPath !== undefined) {
this.repoPath = commit.repoPath;
@@ -56,7 +56,7 @@ export class GitUri extends Uri {
}
get shortSha() {
return this.sha && this.sha.substring(0, 8);
return this.sha && GitService.shortenSha(this.sha);
}
fileUri() {

View File

@@ -5,25 +5,32 @@ export class GitBranch {
current: boolean;
name: string;
remote: boolean;
tracking?: string;
constructor(branch: string) {
branch = branch.trim();
if (branch.startsWith('* ')) {
branch = branch.substring(2);
this.current = true;
}
constructor(public readonly repoPath: string, branch: string, current: boolean = false, tracking?: string) {
if (branch.startsWith('remotes/')) {
branch = branch.substring(8);
this.remote = true;
}
const index = branch.indexOf(' ');
if (index !== -1) {
branch = branch.substring(0, index);
}
this.current = current;
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

@@ -1,5 +1,5 @@
'use strict';
import { Strings } from '../../system';
import { Dates, Strings } from '../../system';
import { Uri } from 'vscode';
import { GlyphChars } from '../../constants';
import { Git } from '../git';
@@ -51,7 +51,7 @@ export class GitCommit {
}
get shortSha() {
return this.sha.substring(0, 8);
return Git.shortenSha(this.sha);
}
get isUncommitted(): boolean {
@@ -62,7 +62,7 @@ export class GitCommit {
}
get previousShortSha() {
return this.previousSha && this.previousSha.substring(0, 8);
return this.previousSha && Git.shortenSha(this.previousSha);
}
get previousUri(): Uri {
@@ -70,7 +70,23 @@ export class GitCommit {
}
get uri(): Uri {
return Uri.file(path.resolve(this.repoPath, this.originalFileName || this.fileName));
return Uri.file(path.resolve(this.repoPath, this.originalFileName || this.fileName || ''));
}
private _dateFormatter?: Dates.IDateFormatter;
formatDate(format: string) {
if (this._dateFormatter === undefined) {
this._dateFormatter = Dates.toFormatter(this.date);
}
return this._dateFormatter.format(format);
}
fromNow() {
if (this._dateFormatter === undefined) {
this._dateFormatter = Dates.toFormatter(this.date);
}
return this._dateFormatter.fromNow();
}
getFormattedPath(separator: string = Strings.pad(GlyphChars.Dot, 2, 2)): string {

View File

@@ -33,4 +33,10 @@ export interface GitDiff {
chunks: GitDiffChunk[];
diff?: string;
}
export interface GitDiffShortStat {
files: number;
insertions: number;
deletions: number;
}

View File

@@ -1,6 +1,7 @@
'use strict';
import { Uri } from 'vscode';
import { GitCommit, GitCommitType } from './commit';
import { Git } from '../git';
import { GitStatusFileStatus, IGitStatusFile } from './status';
import * as path from 'path';
@@ -39,7 +40,12 @@ export class GitLogCommit extends GitCommit {
this.status = fileStatus.status;
}
else {
this.fileStatuses = [{ status: status, fileName: fileName, originalFileName: originalFileName } as IGitStatusFile];
if (fileName === undefined) {
this.fileStatuses = [];
}
else {
this.fileStatuses = [{ status: status, fileName: fileName, originalFileName: originalFileName } as IGitStatusFile];
}
this.status = status;
}
}
@@ -49,7 +55,7 @@ export class GitLogCommit extends GitCommit {
}
get nextShortSha() {
return this.nextSha && this.nextSha.substring(0, 8);
return this.nextSha && Git.shortenSha(this.nextSha);
}
get nextUri(): Uri {

View File

@@ -5,23 +5,9 @@ export type GitRemoteType = 'fetch' | 'push';
export class GitRemote {
name: string;
url: string;
type: GitRemoteType;
provider?: RemoteProvider;
constructor(remote: string) {
remote = remote.trim();
const [name, info] = remote.split('\t');
this.name = name;
const [url, typeInfo] = info.split(' ');
this.url = url;
this.type = typeInfo.substring(1, typeInfo.length - 1) as GitRemoteType;
this.provider = RemoteProviderFactory.getRemoteProvider(this.url);
constructor(public readonly repoPath: string, public readonly name: string, public readonly url: string, public readonly domain: string, public readonly path: string, public readonly types: GitRemoteType[]) {
this.provider = RemoteProviderFactory.getRemoteProvider(this.domain, this.path);
}
}

View File

@@ -3,6 +3,7 @@ import { Strings } from '../../system';
import { Uri } from 'vscode';
import { GlyphChars } from '../../constants';
import { GitUri } from '../gitUri';
import { GitLogCommit } from './logCommit';
import * as path from 'path';
export interface GitStatus {
@@ -19,7 +20,7 @@ export interface GitStatus {
files: GitStatusFile[];
}
export declare type GitStatusFileStatus = '!' | '?' | 'A' | 'C' | 'D' | 'M' | 'R' | 'U';
export declare type GitStatusFileStatus = '!' | '?' | 'A' | 'C' | 'D' | 'M' | 'R' | 'T' | 'U' | 'X' | 'B';
export interface IGitStatusFile {
status: GitStatusFileStatus;
@@ -27,6 +28,10 @@ export interface IGitStatusFile {
originalFileName?: string;
}
export interface IGitStatusFileWithCommit extends IGitStatusFile {
commit: GitLogCommit;
}
export class GitStatusFile implements IGitStatusFile {
originalFileName?: string;
@@ -71,7 +76,10 @@ const statusOcticonsMap = {
D: '$(diff-removed)',
M: '$(diff-modified)',
R: '$(diff-renamed)',
U: '$(question)'
T: '$(diff-modified)',
U: '$(alert)',
X: '$(question)',
B: '$(question)'
};
export function getGitStatusOcticon(status: GitStatusFileStatus, missing: string = GlyphChars.Space.repeat(4)): string {
@@ -86,7 +94,10 @@ const statusIconsMap = {
D: 'icon-status-deleted.svg',
M: 'icon-status-modified.svg',
R: 'icon-status-renamed.svg',
U: 'icon-status-conflict.svg'
T: 'icon-status-modified.svg',
U: 'icon-status-conflict.svg',
X: 'icon-status-unknown.svg',
B: 'icon-status-unknown.svg'
};
export function getGitStatusIcon(status: GitStatusFileStatus): string {

View File

@@ -1,7 +1,6 @@
'use strict';
import { Strings } from '../../system';
import { Git, GitAuthor, GitBlame, GitBlameCommit, GitCommitLine } from './../git';
import * as moment from 'moment';
import * as path from 'path';
interface BlameEntry {
@@ -134,7 +133,7 @@ export class GitBlameParser {
}
}
commit = new GitBlameCommit(repoPath!, entry.sha, fileName!, entry.author, moment(`${entry.authorDate} ${entry.authorTimeZone}`, 'X +-HHmm').toDate(), entry.summary!, []);
commit = new GitBlameCommit(repoPath!, entry.sha, fileName!, entry.author, new Date(entry.authorDate as any * 1000), entry.summary!, []);
if (fileName !== entry.fileName) {
commit.originalFileName = entry.fileName;

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

@@ -1,8 +1,9 @@
'use strict';
import { Iterables, Strings } from '../../system';
import { GitDiff, GitDiffChunk, GitDiffChunkLine, GitDiffLine } from './../git';
import { GitDiff, GitDiffChunk, GitDiffChunkLine, GitDiffLine, GitDiffShortStat } from './../git';
const unifiedDiffRegex = /^@@ -([\d]+),([\d]+) [+]([\d]+),([\d]+) @@([\s\S]*?)(?=^@@)/gm;
const shortStatDiffRegex = /^\s*(\d+)\sfiles? changed(?:,\s+(\d+)\s+insertions?\(\+\))?(?:,\s+(\d+)\s+deletions?\(-\))?/;
export class GitDiffParser {
@@ -116,4 +117,20 @@ export class GitDiffParser {
return chunkLines;
}
static parseShortStat(data: string): GitDiffShortStat | undefined {
if (!data) return undefined;
const match = shortStatDiffRegex.exec(data);
if (match == null) return undefined;
const files = match[1];
const insertions = match[2];
const deletions = match[3];
return {
files: files == null ? 0 : parseInt(files, 10),
insertions: insertions == null ? 0 : parseInt(insertions, 10),
deletions: deletions == null ? 0 : parseInt(deletions, 10)
} as GitDiffShortStat;
}
}

View File

@@ -3,7 +3,6 @@ import { Strings } from '../../system';
import { Range } from 'vscode';
import { Git, GitAuthor, GitCommitType, GitLog, GitLogCommit, GitStatusFileStatus, IGitStatusFile } from './../git';
// import { Logger } from '../../logger';
import * as moment from 'moment';
import * as path from 'path';
interface LogEntry {
@@ -87,7 +86,7 @@ export class GitLogParser {
break;
case 'author-date':
entry.authorDate = `${lineParts[1]}T${lineParts[2]}${lineParts[3]}`;
entry.authorDate = lineParts[1];
break;
case 'parents':
@@ -231,7 +230,7 @@ export class GitLogParser {
}
}
commit = new GitLogCommit(type, repoPath!, entry.sha, relativeFileName, entry.author, moment(entry.authorDate).toDate(), entry.summary!, entry.status, entry.fileStatuses, undefined, entry.originalFileName);
commit = new GitLogCommit(type, repoPath!, entry.sha, relativeFileName, entry.author, new Date(entry.authorDate! as any * 1000), entry.summary!, entry.status, entry.fileStatuses, undefined, entry.originalFileName);
commit.parentShas = entry.parentShas!;
if (relativeFileName !== entry.fileName) {

View File

@@ -0,0 +1,50 @@
'use strict';
import { GitRemote } from './../git';
import { GitRemoteType } from '../models/remote';
const remoteRegex = /^(.*)\t(.*)\s\((.*)\)$/gm;
const urlRegex = /^(?:git:\/\/(.*?)\/|https:\/\/(.*?)\/|http:\/\/(.*?)\/|git@(.*):|ssh:\/\/(?:.*@)?(.*?)(?::.*?)?\/)(.*)$/;
export class GitRemoteParser {
static parse(data: string, repoPath: string): GitRemote[] {
if (!data) return [];
const remotes: GitRemote[] = [];
const groups = Object.create(null);
let match: RegExpExecArray | null = null;
do {
match = remoteRegex.exec(data);
if (match == null) break;
const url = match[2];
const [domain, path] = this.parseGitUrl(url);
let remote: GitRemote | undefined = groups[url];
if (remote === undefined) {
remote = new GitRemote(repoPath, match[1], url, domain, path, [match[3] as GitRemoteType]);
remotes.push(remote);
groups[url] = remote;
}
else {
remote.types.push(match[3] as GitRemoteType);
}
} while (match != null);
if (!remotes.length) return [];
return remotes;
}
static parseGitUrl(url: string): [string, string] {
const match = urlRegex.exec(url);
if (match == null) return ['', ''];
return [
match[1] || match[2] || match[3] || match[4] || match[5],
match[6].replace(/\.git\/?$/, '')
];
}
}

View File

@@ -1,7 +1,6 @@
'use strict';
import { Git, GitStash, GitStashCommit, GitStatusFileStatus, IGitStatusFile } from './../git';
// import { Logger } from '../../logger';
import * as moment from 'moment';
interface StashEntry {
sha: string;
@@ -14,11 +13,33 @@ interface StashEntry {
export class GitStashParser {
static parse(data: string, repoPath: string): GitStash | undefined {
const entries = this._parseEntries(data);
if (entries === undefined) return undefined;
const commits: Map<string, GitStashCommit> = new Map();
for (let i = 0, len = entries.length; i < len; i++) {
const entry = entries[i];
let commit = commits.get(entry.sha);
if (commit === undefined) {
commit = new GitStashCommit(entry.stashName, repoPath, entry.sha, entry.fileNames, new Date(entry.date! as any * 1000), entry.summary, undefined, entry.fileStatuses) as GitStashCommit;
commits.set(entry.sha, commit);
}
}
return {
repoPath: repoPath,
commits: commits
} as GitStash;
}
private static _parseEntries(data: string): StashEntry[] | undefined {
if (!data) return undefined;
const lines = data.split('\n');
if (!lines.length) return undefined;
if (lines.length === 0) return undefined;
const entries: StashEntry[] = [];
@@ -42,7 +63,7 @@ export class GitStashParser {
switch (lineParts[0]) {
case 'author-date':
entry.date = `${lineParts[1]}T${lineParts[2]}${lineParts[3]}`;
entry.date = lineParts[1];
break;
case 'summary':
@@ -66,7 +87,12 @@ export class GitStashParser {
case 'filename':
const nextLine = lines[position + 1];
// If the next line isn't blank, make sure it isn't starting a new commit
if (nextLine && Git.shaRegex.test(nextLine)) continue;
if (nextLine && Git.shaRegex.test(nextLine)) {
entries.push(entry);
entry = undefined;
continue;
}
position++;
@@ -109,28 +135,6 @@ export class GitStashParser {
return entries;
}
static parse(data: string, repoPath: string): GitStash | undefined {
const entries = this._parseEntries(data);
if (entries === undefined) return undefined;
const commits: Map<string, GitStashCommit> = new Map();
for (let i = 0, len = entries.length; i < len; i++) {
const entry = entries[i];
let commit = commits.get(entry.sha);
if (commit === undefined) {
commit = new GitStashCommit(entry.stashName, repoPath, entry.sha, entry.fileNames, moment(entry.date).toDate(), entry.summary, undefined, entry.fileStatuses) as GitStashCommit;
commits.set(entry.sha, commit);
}
}
return {
repoPath: repoPath,
commits: commits
} as GitStash;
}
private static _parseFileName(entry: { fileName?: string, originalFileName?: string }) {
if (entry.fileName === undefined) return;

View File

@@ -17,7 +17,7 @@ export class GitStatusParser {
if (!data) return undefined;
const lines = data.split('\n').filter(_ => !!_);
if (!lines.length) return undefined;
if (lines.length === 0) return undefined;
const status = {
branch: '',

View File

@@ -0,0 +1,47 @@
'use strict';
import { Range } from 'vscode';
import { RemoteProvider } from './provider';
export class BitbucketServerService extends RemoteProvider {
constructor(public domain: string, public path: string, public custom: boolean = false) {
super(domain, path);
}
get name() {
return this.formatName('Bitbucket Server');
}
protected get baseUrl() {
const [project, repo] = super.splitPath();
return `https://${this.domain}/projects/${project}/repos/${repo}`;
}
protected getUrlForBranches(): string {
return `${this.baseUrl}/branches`;
}
protected getUrlForBranch(branch: string): string {
return `${this.baseUrl}/commits?until=${branch}`;
}
protected getUrlForCommit(sha: string): string {
return `${this.baseUrl}/commits/${sha}`;
}
protected getUrlForFile(fileName: string, branch?: string, sha?: string, range?: Range): string {
let line = '';
if (range) {
if (range.start.line === range.end.line) {
line = `#${range.start.line}`;
}
else {
line = `#${range.start.line}-${range.end.line}`;
}
}
if (sha) return `${this.baseUrl}/browse/${fileName}?at=${sha}${line}`;
if (branch) return `${this.baseUrl}/browse/${fileName}?at=${branch}${line}`;
return `${this.baseUrl}/browse/${fileName}${line}`;
}
}

View File

@@ -4,12 +4,16 @@ import { RemoteProvider } from './provider';
export class BitbucketService extends RemoteProvider {
constructor(public domain: string, public path: string) {
constructor(public domain: string, public path: string, public custom: boolean = false) {
super(domain, path);
}
get name() {
return 'Bitbucket';
return this.formatName('Bitbucket');
}
protected getUrlForBranches(): string {
return `${this.baseUrl}/branches`;
}
protected getUrlForBranch(branch: string): string {

View File

@@ -1,38 +1,49 @@
'use strict';
import { RemoteProvider } from './provider';
import { Objects } from '../../system';
import { Event, EventEmitter, ExtensionContext, workspace } from 'vscode';
import { BitbucketService } from './bitbucket';
import { BitbucketServerService } from './bitbucket-server';
import { CustomRemoteType, IConfig, IRemotesConfig } from '../../configuration';
import { ExtensionKey } from '../../constants';
import { GitHubService } from './github';
import { GitLabService } from './gitlab';
import { VisualStudioService } from './visualStudio';
import { Logger } from '../../logger';
import { RemoteProvider } from './provider';
import { VisualStudioService } from './visualStudio';
export { RemoteProvider };
const providerMap = new Map<string, (domain: string, path: string) => RemoteProvider>([
const defaultProviderMap = new Map<string, (domain: string, path: string) => RemoteProvider>([
['bitbucket.org', (domain: string, path: string) => new BitbucketService(domain, path)],
['github.com', (domain: string, path: string) => new GitHubService(domain, path)],
['gitlab.com', (domain: string, path: string) => new GitLabService(domain, path)],
['visualstudio.com', (domain: string, path: string) => new VisualStudioService(domain, path)]
]);
const UrlRegex = /^(?:git:\/\/(.*?)\/|https:\/\/(.*?)\/|http:\/\/(.*?)\/|git@(.*):|ssh:\/\/(?:.*@)?(.*?)(?::.*?)?\/)(.*)$/;
export class RemoteProviderFactory {
static getRemoteProvider(url: string): RemoteProvider | undefined {
private static _providerMap: Map<string, (domain: string, path: string) => RemoteProvider>;
private static _remotesCfg: IRemotesConfig[];
private static _onDidChange = new EventEmitter<void>();
public static get onDidChange(): Event<void> {
return this._onDidChange.event;
}
static configure(context: ExtensionContext) {
context.subscriptions.push(workspace.onDidChangeConfiguration(() => this.onConfigurationChanged()));
this.onConfigurationChanged(true);
}
static getRemoteProvider(domain: string, path: string): RemoteProvider | undefined {
try {
const match = UrlRegex.exec(url);
if (match == null) return undefined;
let key = domain.toLowerCase();
if (key.endsWith('visualstudio.com')) {
key = 'visualstudio.com';
}
const domain = match[1] || match[2] || match[3] || match[4] || match[5];
const path = match[6].replace(/\.git\/?$/, '');
const key = domain.toLowerCase().endsWith('visualstudio.com')
? 'visualstudio.com'
: domain;
const creator = providerMap.get(key.toLowerCase());
if (!creator) return undefined;
const creator = this._providerMap.get(key);
if (creator === undefined) return undefined;
return creator(domain, path);
}
@@ -41,4 +52,37 @@ export class RemoteProviderFactory {
return undefined;
}
}
private static onConfigurationChanged(silent: boolean = false) {
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey);
if (cfg === undefined) return;
if (!Objects.areEquivalent(cfg.remotes, this._remotesCfg)) {
this._providerMap = new Map(defaultProviderMap);
this._remotesCfg = cfg.remotes;
if (this._remotesCfg != null && this._remotesCfg.length > 0) {
for (const svc of this._remotesCfg) {
const provider = this.getCustomProvider(svc.type);
if (provider === undefined) continue;
this._providerMap.set(svc.domain.toLowerCase(), provider);
}
if (!silent) {
this._onDidChange.fire();
}
}
}
}
private static getCustomProvider(type: CustomRemoteType) {
switch (type) {
case CustomRemoteType.Bitbucket: return (domain: string, path: string) => new BitbucketService(domain, path, true);
case CustomRemoteType.BitbucketServer: return (domain: string, path: string) => new BitbucketServerService(domain, path, true);
case CustomRemoteType.GitHub: return (domain: string, path: string) => new GitHubService(domain, path, true);
case CustomRemoteType.GitLab: return (domain: string, path: string) => new GitLabService(domain, path, true);
}
return undefined;
}
}

View File

@@ -4,12 +4,16 @@ import { RemoteProvider } from './provider';
export class GitHubService extends RemoteProvider {
constructor(public domain: string, public path: string) {
constructor(public domain: string, public path: string, public custom: boolean = false) {
super(domain, path);
}
get name() {
return 'GitHub';
return this.formatName('GitHub');
}
protected getUrlForBranches(): string {
return `${this.baseUrl}/branches`;
}
protected getUrlForBranch(branch: string): string {

View File

@@ -1,13 +1,42 @@
'use strict';
import { GitHubService } from './github';
import { Range } from 'vscode';
import { RemoteProvider } from './provider';
export class GitLabService extends GitHubService {
export class GitLabService extends RemoteProvider {
constructor(public domain: string, public path: string) {
constructor(public domain: string, public path: string, public custom: boolean = false) {
super(domain, path);
}
get name() {
return 'GitLab';
return this.formatName('GitLab');
}
protected getUrlForBranches(): string {
return `${this.baseUrl}/branches`;
}
protected getUrlForBranch(branch: string): string {
return `${this.baseUrl}/commits/${branch}`;
}
protected getUrlForCommit(sha: string): string {
return `${this.baseUrl}/commit/${sha}`;
}
protected getUrlForFile(fileName: string, branch?: string, sha?: string, range?: Range): string {
let line = '';
if (range) {
if (range.start.line === range.end.line) {
line = `#L${range.start.line}`;
}
else {
line = `#L${range.start.line}-${range.end.line}`;
}
}
if (sha) return `${this.baseUrl}/blob/${sha}/${fileName}${line}`;
if (branch) return `${this.baseUrl}/blob/${branch}/${fileName}${line}`;
return `${this.baseUrl}?path=${fileName}${line}`;
}
}

View File

@@ -3,9 +3,10 @@ import { commands, Range, Uri } from 'vscode';
import { BuiltInCommands } from '../../constants';
import { GitLogCommit } from '../../gitService';
export type RemoteResourceType = 'branch' | 'commit' | 'file' | 'repo' | 'revision';
export type RemoteResourceType = 'branch' | 'branches' | 'commit' | 'file' | 'repo' | 'revision';
export type RemoteResource =
{ type: 'branch', branch: string } |
{ type: 'branches' } |
{ type: 'commit', sha: string } |
{ type: 'file', branch?: string, fileName: string, range?: Range } |
{ type: 'repo' } |
@@ -14,6 +15,7 @@ export type RemoteResource =
export function getNameFromRemoteResource(resource: RemoteResource) {
switch (resource.type) {
case 'branch': return 'Branch';
case 'branches': return 'Branches';
case 'commit': return 'Commit';
case 'file': return 'File';
case 'repo': return 'Repository';
@@ -24,7 +26,7 @@ export function getNameFromRemoteResource(resource: RemoteResource) {
export abstract class RemoteProvider {
constructor(public domain: string, public path: string) { }
constructor(public domain: string, public path: string, public custom: boolean = false) { }
abstract get name(): string;
@@ -32,6 +34,16 @@ export abstract class RemoteProvider {
return `https://${this.domain}/${this.path}`;
}
protected formatName(name: string) {
return `${name}${this.custom ? ` (${this.domain})` : ''}`;
}
protected splitPath(): [string, string] {
const index = this.path.indexOf('/');
return [ this.path.substring(0, index), this.path.substring(index + 1) ];
}
protected abstract getUrlForBranches(): string;
protected abstract getUrlForBranch(branch: string): string;
protected abstract getUrlForCommit(sha: string): string;
protected abstract getUrlForFile(fileName: string, branch?: string, sha?: string, range?: Range): string;
@@ -45,6 +57,7 @@ export abstract class RemoteProvider {
open(resource: RemoteResource): Promise<{} | undefined> {
switch (resource.type) {
case 'branch': return this.openBranch(resource.branch);
case 'branches': return this.openBranches();
case 'commit': return this.openCommit(resource.sha);
case 'file': return this.openFile(resource.fileName, resource.branch, undefined, resource.range);
case 'repo': return this.openRepo();
@@ -56,6 +69,10 @@ export abstract class RemoteProvider {
return this._openUrl(this.baseUrl);
}
openBranches() {
return this._openUrl(this.getUrlForBranches());
}
openBranch(branch: string) {
return this._openUrl(this.getUrlForBranch(branch));
}

View File

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

View File

@@ -6,7 +6,6 @@ import { BuiltInCommands, DocumentSchemes, ExtensionKey } from './constants';
import { CodeLensCommand, CodeLensLocations, ICodeLensLanguageLocation, IConfig } from './configuration';
import { GitBlame, GitBlameCommit, GitBlameLines, GitService, GitUri } from './gitService';
import { Logger } from './logger';
import * as moment from 'moment';
export class GitRecentChangeCodeLens extends CodeLens {
@@ -254,7 +253,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
if (blame === undefined) return lens;
const recentCommit = Iterables.first(blame.commits.values());
title = `${recentCommit.author}, ${moment(recentCommit.date).fromNow()}`;
title = `${recentCommit.author}, ${recentCommit.fromNow()}`;
if (this._config.codeLens.debug) {
title += ` [${SymbolKind[lens.symbolKind]}(${lens.range.start.character}-${lens.range.end.character}), Lines (${lens.blameRange.start.line + 1}-${lens.blameRange.end.line + 1}), Commit (${recentCommit.shortSha})]`;
}

View File

@@ -15,7 +15,9 @@ export class GitContentProvider implements TextDocumentContentProvider {
const data = GitService.fromGitContentUri(uri);
const fileName = data.originalFileName || data.fileName;
try {
let text = await this.git.getVersionedFileText(data.repoPath, fileName, data.sha);
let text = data.sha !== GitService.fakeSha
? await this.git.getVersionedFileText(data.repoPath, fileName, data.sha)
: '';
if (data.decoration) {
text = `${data.decoration}\n${text}`;
}
@@ -23,7 +25,7 @@ export class GitContentProvider implements TextDocumentContentProvider {
}
catch (ex) {
Logger.error(ex, 'GitContentProvider', 'getVersionedFileText');
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 ${GitService.shortenSha(data.sha)} of '${path.relative(data.repoPath, fileName)}'`);
return undefined;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,6 @@ import { commands, ExtensionContext, Uri, window } from 'vscode';
import { BuiltInCommands } from './constants';
import { GitCommit } from './gitService';
import { Logger } from './logger';
import * as moment from 'moment';
export type SuppressedKeys = 'suppressCommitHasNoPreviousCommitWarning' |
'suppressCommitNotFoundWarning' |
@@ -11,7 +10,8 @@ export type SuppressedKeys = 'suppressCommitHasNoPreviousCommitWarning' |
'suppressGitVersionWarning' |
'suppressLineUncommittedWarning' |
'suppressNoRepositoryWarning' |
'suppressUpdateNotice';
'suppressUpdateNotice' |
'suppressWelcomeNotice';
export const SuppressedKeys = {
CommitHasNoPreviousCommitWarning: 'suppressCommitHasNoPreviousCommitWarning' as SuppressedKeys,
CommitNotFoundWarning: 'suppressCommitNotFoundWarning' as SuppressedKeys,
@@ -19,7 +19,8 @@ export const SuppressedKeys = {
GitVersionWarning: 'suppressGitVersionWarning' as SuppressedKeys,
LineUncommittedWarning: 'suppressLineUncommittedWarning' as SuppressedKeys,
NoRepositoryWarning: 'suppressNoRepositoryWarning' as SuppressedKeys,
UpdateNotice: 'suppressUpdateNotice' as SuppressedKeys
UpdateNotice: 'suppressUpdateNotice' as SuppressedKeys,
WelcomeNotice: 'suppressWelcomeNotice' as SuppressedKeys
};
export class Messages {
@@ -30,8 +31,9 @@ export class Messages {
this.context = context;
}
static showCommitHasNoPreviousCommitWarningMessage(commit: GitCommit): Promise<string | undefined> {
return Messages._showMessage('info', `Commit ${commit.shortSha} (${commit.author}, ${moment(commit.date).fromNow()}) has no previous commit`, SuppressedKeys.CommitHasNoPreviousCommitWarning);
static showCommitHasNoPreviousCommitWarningMessage(commit?: GitCommit): Promise<string | undefined> {
if (commit === undefined) return Messages._showMessage('info', `Commit has no previous commit`, SuppressedKeys.CommitHasNoPreviousCommitWarning);
return Messages._showMessage('info', `Commit ${commit.shortSha} (${commit.author}, ${commit.fromNow()}) has no previous commit`, SuppressedKeys.CommitHasNoPreviousCommitWarning);
}
static showCommitNotFoundWarningMessage(message: string): Promise<string | undefined> {
@@ -65,7 +67,7 @@ export class Messages {
static async showWelcomeMessage(): Promise<string | undefined> {
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) {
commands.executeCommand(BuiltInCommands.Open, Uri.parse('https://marketplace.visualstudio.com/items/eamodio.gitlens'));
}
@@ -73,7 +75,7 @@ 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> {
Logger.log(`ShowMessage(${type}, "${message}", ${suppressionKey}, ${dontShowAgain})`);
Logger.log(`ShowMessage(${type}, '${message}', ${suppressionKey}, ${dontShowAgain})`);
if (Messages.context.globalState.get(suppressionKey, false)) {
Logger.log(`ShowMessage(${type}, ${message}, ${suppressionKey}, ${dontShowAgain}) skipped`);
@@ -100,12 +102,13 @@ export class Messages {
}
if (dontShowAgain === null || result === dontShowAgain) {
Logger.log(`ShowMessage(${type}, ${message}, ${suppressionKey}, ${dontShowAgain}) don't show again requested`);
Logger.log(`ShowMessage(${type}, '${message}', ${suppressionKey}, ${dontShowAgain}) don't show again requested`);
await Messages.context.globalState.update(suppressionKey, true);
return undefined;
if (result === dontShowAgain) return undefined;
}
Logger.log(`ShowMessage(${type}, ${message}, ${suppressionKey}, ${dontShowAgain}) returned ${result}`);
Logger.log(`ShowMessage(${type}, '${message}', ${suppressionKey}, ${dontShowAgain}) returned ${result}`);
return result;
}
}

View File

@@ -1,5 +1,5 @@
'use strict';
import { Arrays, Iterables, Strings } from '../system';
import { Iterables, Strings } from '../system';
import { CancellationTokenSource, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, ShowCommitSearchCommandArgs, ShowQuickBranchHistoryCommandArgs } from '../commands';
import { CommandQuickPickItem, CommitQuickPickItem, getQuickPickIgnoreFocusOut, showQuickPickProgress } from './common';
@@ -35,7 +35,7 @@ export class BranchHistoryQuickPick {
} as ShowQuickBranchHistoryCommandArgs
]);
const remotes = Arrays.uniqueBy(await git.getRemotes((uri && uri.repoPath) || git.repoPath), _ => _.url, _ => !!_.provider);
const remotes = (await git.getRemotes((uri && uri.repoPath) || git.repoPath)).filter(r => r.provider !== undefined);
if (remotes.length) {
items.splice(0, 0, new OpenRemotesCommandQuickPickItem(remotes, {
type: 'branch',

View File

@@ -1,5 +1,5 @@
'use strict';
import { Arrays, Iterables, Strings } from '../system';
import { Iterables, Strings } from '../system';
import { commands, QuickPickOptions, TextDocumentShowOptions, Uri, window } from 'vscode';
import { Commands, CopyMessageToClipboardCommandArgs, CopyShaToClipboardCommandArgs, DiffDirectoryCommandCommandArgs, DiffWithPreviousCommandArgs, ShowQuickCommitDetailsCommandArgs, StashApplyCommandArgs, StashDeleteCommandArgs } from '../commands';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem, QuickPickItem } from './common';
@@ -7,7 +7,6 @@ import { GlyphChars } from '../constants';
import { getGitStatusOcticon, GitCommit, GitLog, GitLogCommit, GitService, GitStashCommit, GitStatusFile, GitStatusFileStatus, GitUri, IGitCommitInfo, IGitStatusFile, RemoteResource } from '../gitService';
import { Keyboard, KeyCommand, KeyNoopCommand, Keys } from '../keyboard';
import { OpenRemotesCommandQuickPickItem } from './remotes';
import * as moment from 'moment';
import * as path from 'path';
export class CommitWithFileStatusQuickPickItem extends OpenFileCommandQuickPickItem {
@@ -24,17 +23,14 @@ export class CommitWithFileStatusQuickPickItem extends OpenFileCommandQuickPickI
const description = GitStatusFile.getFormattedDirectory(status, true);
let sha;
let shortSha;
if (status.status === 'D') {
sha = commit.previousSha!;
shortSha = commit.previousShortSha!;
}
else {
sha = commit.sha;
shortSha = commit.shortSha;
}
super(GitService.toGitContentUri(sha, shortSha, status.fileName, commit.repoPath, status.originalFileName), {
super(GitService.toGitContentUri(sha, status.fileName, commit.repoPath, status.originalFileName), {
label: `${Strings.pad(octicon, 4, 2)} ${path.basename(status.fileName)}`,
description: description
});
@@ -48,7 +44,7 @@ export class CommitWithFileStatusQuickPickItem extends OpenFileCommandQuickPickI
originalFileName: status.originalFileName
} as IGitCommitInfo);
this.sha = sha;
this.shortSha = shortSha;
this.shortSha = GitService.shortenSha(sha)!;
this.status = status.status;
}
@@ -88,7 +84,7 @@ export class OpenCommitFileRevisionsCommandQuickPickItem extends OpenFilesComman
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));
.map(s => GitService.toGitContentUri(commit.sha, s.fileName, commit.repoPath, s.originalFileName));
super(uris, item || {
label: `$(file-symlink-file) Open Changed Revisions`,
@@ -156,7 +152,7 @@ export class CommitDetailsQuickPick {
]));
if (!stash) {
const remotes = Arrays.uniqueBy(await git.getRemotes(commit.repoPath), _ => _.url, _ => !!_.provider);
const remotes = (await git.getRemotes(commit.repoPath)).filter(r => r.provider !== undefined);
if (remotes.length) {
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, {
type: 'commit',
@@ -302,7 +298,7 @@ export class CommitDetailsQuickPick {
const pick = await window.showQuickPick(items, {
matchOnDescription: true,
matchOnDetail: true,
placeHolder: `${commit.shortSha} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.author ? `${commit.author}, ` : ''}${moment(commit.date).fromNow()} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.message}`,
placeHolder: `${commit.shortSha} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.author ? `${commit.author}, ` : ''}${commit.fromNow()} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.message}`,
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
onDidSelectItem: (item: QuickPickItem) => {
scope.setKeyCommand('right', item);

View File

@@ -1,13 +1,12 @@
'use strict';
import { Arrays, Iterables, Strings } from '../system';
import { Iterables, Strings } from '../system';
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, CopyMessageToClipboardCommandArgs, CopyShaToClipboardCommandArgs, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, ShowQuickCommitDetailsCommandArgs, ShowQuickCommitFileDetailsCommandArgs, ShowQuickFileHistoryCommandArgs } from '../commands';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem } from './common';
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 { OpenRemotesCommandQuickPickItem } from './remotes';
import * as moment from 'moment';
import * as path from 'path';
export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
@@ -27,7 +26,7 @@ export class OpenCommitFileRevisionCommandQuickPickItem extends OpenFileCommandQ
let description: string;
let uri: Uri;
if (commit.status === 'D') {
uri = GitService.toGitContentUri(commit.previousSha!, commit.previousShortSha!, commit.previousFileName!, commit.repoPath, undefined);
uri = GitService.toGitContentUri(commit.previousSha!, commit.previousFileName!, commit.repoPath, undefined);
description = `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)} in ${GlyphChars.Space}$(git-commit) ${commit.previousShortSha} (deleted in ${GlyphChars.Space}$(git-commit) ${commit.shortSha})`;
}
else {
@@ -125,14 +124,14 @@ export class CommitFileDetailsQuickPick {
}
items.push(new OpenCommitFileRevisionCommandQuickPickItem(commit));
const remotes = Arrays.uniqueBy(await git.getRemotes(commit.repoPath), _ => _.url, _ => !!_.provider);
const remotes = (await git.getRemotes(commit.repoPath)).filter(r => r.provider !== undefined);
if (remotes.length) {
if (commit.workingFileName && commit.status !== 'D') {
const branch = await git.getBranch(commit.repoPath || git.repoPath) as GitBranch;
const branch = await git.getBranch(commit.repoPath || git.repoPath);
items.push(new OpenRemotesCommandQuickPickItem(remotes, {
type: 'file',
fileName: commit.workingFileName,
branch: branch.name
branch: branch!.name
} as RemoteResource, currentCommand));
}
if (!stash) {
@@ -275,7 +274,7 @@ export class CommitFileDetailsQuickPick {
const pick = await window.showQuickPick(items, {
matchOnDescription: true,
placeHolder: `${commit.getFormattedPath()} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${isUncommitted ? `Uncommitted ${GlyphChars.ArrowRightHollow} ` : '' }${commit.shortSha} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.author}, ${moment(commit.date).fromNow()} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.message}`,
placeHolder: `${commit.getFormattedPath()} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${isUncommitted ? `Uncommitted ${GlyphChars.ArrowRightHollow} ` : '' }${commit.shortSha} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.author}, ${commit.fromNow()} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.message}`,
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
onDidSelectItem: (item: QuickPickItem) => {
scope.setKeyCommand('right', item as KeyCommand);

View File

@@ -7,7 +7,6 @@ import { GlyphChars } from '../constants';
import { GitCommit, GitLogCommit, GitStashCommit } from '../gitService';
import { Keyboard, KeyboardScope, KeyMapping, Keys } from '../keyboard';
// import { Logger } from '../logger';
import * as moment from 'moment';
export function getQuickPickIgnoreFocusOut() {
const cfg = workspace.getConfiguration(ExtensionKey).get<IAdvancedConfig>('advanced')!;
@@ -174,12 +173,12 @@ export class CommitQuickPickItem implements QuickPickItem {
if (commit instanceof GitStashCommit) {
this.label = message;
this.description = '';
this.detail = `${GlyphChars.Space} ${commit.stashName} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${moment(commit.date).fromNow()} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.getDiffStatus()}`;
this.detail = `${GlyphChars.Space} ${commit.stashName} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.fromNow()} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.getDiffStatus()}`;
}
else {
this.label = message;
this.description = `${Strings.pad('$(git-commit)', 1, 1)} ${commit.shortSha}`;
this.detail = `${GlyphChars.Space} ${commit.author}, ${moment(commit.date).fromNow()}${(commit.type === 'branch') ? ` ${Strings.pad(GlyphChars.Dot, 1, 1)} ${(commit as GitLogCommit).getDiffStatus()}` : ''}`;
this.detail = `${GlyphChars.Space} ${commit.author}, ${commit.fromNow()}${(commit.type === 'branch') ? ` ${Strings.pad(GlyphChars.Dot, 1, 1)} ${(commit as GitLogCommit).getDiffStatus()}` : ''}`;
}
}
}

View File

@@ -1,5 +1,5 @@
'use strict';
import { Arrays, Iterables, Strings } from '../system';
import { Iterables, Strings } from '../system';
import { CancellationTokenSource, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, ShowQuickCurrentBranchHistoryCommandArgs, ShowQuickFileHistoryCommandArgs } from '../commands';
import { CommandQuickPickItem, CommitQuickPickItem, getQuickPickIgnoreFocusOut, showQuickPickProgress } from './common';
@@ -136,7 +136,7 @@ export class FileHistoryQuickPick {
]));
}
const remotes = Arrays.uniqueBy(await git.getRemotes(uri.repoPath!), _ => _.url, _ => !!_.provider);
const remotes = (await git.getRemotes(uri.repoPath!)).filter(r => r.provider !== undefined);
if (remotes.length) {
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, {
type: 'revision',

View File

@@ -4,7 +4,7 @@ import { QuickPickOptions, window } from 'vscode';
import { Commands, OpenInRemoteCommandArgs } from '../commands';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './common';
import { GlyphChars } from '../constants';
import { getNameFromRemoteResource, GitLogCommit, GitRemote, RemoteResource } from '../gitService';
import { getNameFromRemoteResource, GitLogCommit, GitRemote, GitService, RemoteResource } from '../gitService';
import * as path from 'path';
export class OpenRemoteCommandQuickPickItem extends CommandQuickPickItem {
@@ -38,8 +38,12 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
description = `$(git-branch) ${resource.branch}`;
break;
case 'branches':
description = `$(git-branch) Branches`;
break;
case 'commit':
const shortSha = resource.sha.substring(0, 8);
const shortSha = GitService.shortenSha(resource.sha);
description = `$(git-commit) ${shortSha}`;
break;
@@ -63,7 +67,7 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
}
}
else {
const shortFileSha = resource.sha === undefined ? '' : resource.sha.substring(0, 8);
const shortFileSha = resource.sha === undefined ? '' : GitService.shortenSha(resource.sha);
description = `$(file-text) ${path.basename(resource.fileName)}${shortFileSha ? ` in ${GlyphChars.Space}$(git-commit) ${shortFileSha}` : ''}`;
}
break;

View File

@@ -4,7 +4,7 @@ import { commands, QuickPickOptions, TextDocumentShowOptions, Uri, window } from
import { Commands, DiffWithWorkingCommandArgs, OpenChangedFilesCommandArgs, ShowQuickBranchHistoryCommandArgs, ShowQuickRepoStatusCommandArgs, ShowQuickStashListCommandArgs } from '../commands';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFileCommandQuickPickItem, QuickPickItem } from './common';
import { GlyphChars } from '../constants';
import { GitStatus, GitStatusFile, GitUri } from '../gitService';
import { GitService, GitStatus, GitStatusFile, GitUri } from '../gitService';
import { Keyboard, Keys } from '../keyboard';
import * as path from 'path';
@@ -156,7 +156,7 @@ export class RepoStatusQuickPick {
}
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`
}, Commands.ShowQuickStashList, [
new GitUri(Uri.file(status.repoPath), { fileName: '', repoPath: status.repoPath }),
@@ -182,7 +182,7 @@ export class RepoStatusQuickPick {
if (status.upstream && status.state.behind) {
items.splice(0, 0, new CommandQuickPickItem({
label: `$(cloud-download)${GlyphChars.Space} ${status.state.behind} Commit${status.state.behind > 1 ? 's' : ''} behind ${GlyphChars.Space}$(git-branch) ${status.upstream}`,
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows commits in ${GlyphChars.Space}$(git-branch) ${status.upstream} but not ${GlyphChars.Space}$(git-branch) ${status.branch}${status.sha ? ` (since ${GlyphChars.Space}$(git-commit) ${status.sha.substring(0, 8)})` : ''}`
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows commits in ${GlyphChars.Space}$(git-branch) ${status.upstream} but not ${GlyphChars.Space}$(git-branch) ${status.branch}${status.sha ? ` (since ${GlyphChars.Space}$(git-commit) ${GitService.shortenSha(status.sha)})` : ''}`
}, Commands.ShowQuickBranchHistory, [
new GitUri(Uri.file(status.repoPath), { fileName: '', repoPath: status.repoPath, sha: `${status.branch}..${status.upstream}` }),
{

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