1 Commits

Author SHA1 Message Date
Eric Amodio
821fa1cc3c Fixes #130 - Stops repeated welcome for some users
No idea why the version check fails, but hopefully this will help
2017-09-04 03:26:18 -04:00
51 changed files with 385 additions and 675 deletions

View File

@@ -4,7 +4,7 @@ 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-beta] - 2017-09-11
## [5.0.0-alpha.2] - 2017-09-03
### Added
- Adds an all-new `GitLens` custom view to the Explorer activity
@@ -57,13 +57,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
- Quickly switch between views using the `Switch to Repository View` or `Switch to History View` commands
- Provides toolbar commands to `Search Commits`, `Switch to Repository View` or `Switch to History View`, and `Refresh`
- Adds command-links to the `details` hover annotation
- Clicking the commit id will run the `Show Commit Details` command (`gitlens.showQuickCommitDetails`)
- Adds 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 custom remote services - see [#120](https://github.com/eamodio/vscode-gitlens/issues/120)
- 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!)
@@ -93,9 +86,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Fixed
- 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 [#120](https://github.com/eamodio/vscode-gitlens/issues/120) - Feature Request: "Open in Remote" support for custom repositories
- Fixes an issue where sometimes diffs (via branch name) wouldn't open properly
## [4.4.3] - 2017-08-30
## Fixed
@@ -167,7 +157,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`) - compares the active file with the selected revision of the same file
- Adds `Compare File with Revision...` command (`gitlens.diffWithRevision`) - compare 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

View File

@@ -21,10 +21,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
![Line Blame Annotation](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-line-blame-annotation.png)
- Contains the author, date, and message of the line's most recent commit, by [default](#line-blame-annotation-settings)
- 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`)
![Line Blame Annotations](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/screenshot-line-blame-annotations.png)
@@ -34,7 +31,6 @@ 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
@@ -65,10 +61,7 @@ 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
@@ -350,12 +343,6 @@ GitLens is highly customizable and provides many configuration settings to allow
|`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 Custom Remotes Settings
|Name | Description
|-----|------------
|`gitlens.remotes`|Specifies the custom remote services (code-hosting)"
### Status Bar Settings
|Name | Description

65
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "gitlens",
"version": "5.0.0-beta",
"version": "5.0.0-alpha.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -16,19 +16,19 @@
"integrity": "sha1-qjuL2ivlErGuCgV7lC6GnDcKVWk=",
"dev": true,
"requires": {
"@types/node": "8.0.28"
"@types/node": "8.0.26"
}
},
"@types/mocha": {
"version": "2.2.43",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.43.tgz",
"integrity": "sha512-xNlAmH+lRJdUMXClMTI9Y0pRqIojdxfm7DHsIxoB2iTzu3fnPmSMEN8SsSx0cdwV36d02PWCWaDUoZPDSln+xw==",
"version": "2.2.42",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.42.tgz",
"integrity": "sha512-b6gVDoxEbAQGwbV7gSzeFw/hy3/eEAokztktdzl4bHvGgb9K5zW4mVQDlVYch2w31m8t/J7L2iqhQvz3r5edCQ==",
"dev": true
},
"@types/node": {
"version": "8.0.28",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.28.tgz",
"integrity": "sha512-HupkFXEv3O3KSzcr3Ylfajg0kaerBg1DyaZzRBBQfrU3NN1mTBRE7sCveqHwXLS5Yrjvww8qFzkzYQQakG9FuQ==",
"version": "8.0.26",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.26.tgz",
"integrity": "sha512-wbKN0MB4XsjdnSE04HiCzLoBDirGCM6zXrqavSj44nZnPFYpnrTF64E9O6Xmf0ca/IuKK/BHUcXwMiwk92gW6Q==",
"dev": true
},
"@types/tmp": {
@@ -348,7 +348,7 @@
"resolved": "https://registry.npmjs.org/copy-paste/-/copy-paste-1.3.0.tgz",
"integrity": "sha1-p+bEocKP3t8rCB5yuX3y75X0ce0=",
"requires": {
"iconv-lite": "0.4.19",
"iconv-lite": "0.4.18",
"sync-exec": "0.6.2"
}
},
@@ -1248,12 +1248,6 @@
"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",
@@ -1272,9 +1266,9 @@
}
},
"iconv-lite": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
"integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
"version": "0.4.18",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz",
"integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA=="
},
"ignore": {
"version": "3.3.5",
@@ -1794,9 +1788,9 @@
}
},
"mocha": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.2.tgz",
"integrity": "sha512-iH5zl7afRZl1GvD0pnrRlazgc9Z/o34pXWmTFi8xNIMFKXgNL1SoBTDDb9sJfbV/sJV/j8X+0gvwY1QS1He7Nw==",
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.0.tgz",
"integrity": "sha512-pIU2PJjrPYvYRqVpjXzj76qltO9uBYI7woYAMoxbSefsa+vqAfptjoeevd6bUgwD0mPIO+hv9f7ltvsNreL2PA==",
"dev": true,
"requires": {
"browser-stdout": "1.3.0",
@@ -1806,7 +1800,6 @@
"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",
@@ -2241,9 +2234,9 @@
"dev": true
},
"source-map-support": {
"version": "0.4.18",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
"integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
"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==",
"dev": true,
"requires": {
"source-map": "0.5.7"
@@ -2334,15 +2327,6 @@
"integrity": "sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8=",
"dev": true
},
"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"
}
},
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
@@ -2352,6 +2336,15 @@
"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",
@@ -2714,10 +2707,10 @@
"gulp-symdest": "1.1.0",
"gulp-untar": "0.0.6",
"gulp-vinyl-zip": "1.4.0",
"mocha": "3.5.2",
"mocha": "3.5.0",
"request": "2.81.0",
"semver": "5.4.1",
"source-map-support": "0.4.18",
"source-map-support": "0.4.16",
"url-parse": "1.1.9",
"vinyl-source-stream": "1.1.0"
}

View File

@@ -1,13 +1,13 @@
{
"name": "gitlens",
"version": "5.0.0-beta",
"version": "5.0.0-alpha.2",
"author": {
"name": "Eric Amodio",
"email": "eamodio@gmail.com"
},
"publisher": "eamodio",
"engines": {
"vscode": "^1.16.0"
"vscode": "^1.15.0"
},
"license": "SEE LICENSE IN LICENSE",
"displayName": "Git Lens \u2014 git blame annotations, code lens, and more",
@@ -447,34 +447,6 @@
],
"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.remotes": {
"type": "array",
"default": null,
"items": {
"type": "object",
"required": [
"type",
"domain"
],
"properties": {
"type": {
"type": "string",
"enum": [
"Bitbucket",
"GitHub",
"GitLab"
],
"description": "Specifies the type of the custom remote service\n `Bitbucket`, `GitHub`, or `GitLab`"
},
"domain": {
"type": "string",
"description": "Specifies the domain name of the custom remote service"
}
}
},
"uniqueItems": true,
"description": "Specifies the custom remote services (code-hosting)"
},
"gitlens.statusBar.enabled": {
"type": "boolean",
"default": true,
@@ -812,11 +784,6 @@
"title": "Directory Compare",
"category": "GitLens"
},
{
"command": "gitlens.diffWith",
"title": "Compare File Revisions",
"category": "GitLens"
},
{
"command": "gitlens.diffWithBranch",
"title": "Compare File with Branch...",
@@ -1110,10 +1077,6 @@
"command": "gitlens.diffDirectory",
"when": "gitlens:enabled"
},
{
"command": "gitlens.diffWith",
"when": "false"
},
{
"command": "gitlens.diffWithBranch",
"when": "gitlens:isTracked"
@@ -1824,7 +1787,7 @@
"dependencies": {
"applicationinsights": "0.21.0",
"copy-paste": "1.3.0",
"iconv-lite": "0.4.19",
"iconv-lite": "0.4.18",
"ignore": "3.3.5",
"lodash.debounce": "4.0.8",
"lodash.escaperegexp": "4.1.2",
@@ -1838,10 +1801,10 @@
"devDependencies": {
"@types/copy-paste": "1.1.30",
"@types/iconv-lite": "0.0.1",
"@types/mocha": "2.2.43",
"@types/node": "8.0.28",
"@types/mocha": "2.2.42",
"@types/node": "8.0.26",
"@types/tmp": "0.0.33",
"mocha": "3.5.2",
"mocha": "3.5.0",
"tslint": "5.7.0",
"typescript": "2.5.2",
"vscode": "1.1.5"

View File

@@ -1,6 +1,5 @@
import { Strings } from '../system';
import { DecorationInstanceRenderOptions, DecorationOptions, MarkdownString, ThemableDecorationRenderOptions } from 'vscode';
import { DiffWithCommand, ShowQuickCommitDetailsCommand } from '../commands';
import { DecorationInstanceRenderOptions, DecorationOptions, ThemableDecorationRenderOptions } from 'vscode';
import { IThemeConfig, themeDefaults } from '../configuration';
import { GlyphChars } from '../constants';
import { CommitFormatter, GitCommit, GitDiffChunkLine, GitService, GitUri, ICommitFormatOptions } from '../gitService';
@@ -48,7 +47,7 @@ export class Annotations {
return '#793738';
}
static getHoverMessage(commit: GitCommit, dateFormat: string | null): MarkdownString {
static getHoverMessage(commit: GitCommit, dateFormat: string | null): string | string[] {
if (dateFormat === null) {
dateFormat = 'MMMM Do, YYYY h:MMa';
}
@@ -64,21 +63,16 @@ export class Annotations {
.replace(/\n/g, ' \n');
message = `\n\n> ${message}`;
}
const markdown = new MarkdownString(`[\`${commit.shortSha}\`](${ShowQuickCommitDetailsCommand.getMarkdownCommandArgs(commit.sha)}) &nbsp; __${commit.author}__, ${moment(commit.date).fromNow()} &nbsp; _(${moment(commit.date).format(dateFormat)})_${message}`);
markdown.isTrusted = true;
return markdown;
return `\`${commit.shortSha}\` &nbsp; __${commit.author}__, ${moment(commit.date).fromNow()} &nbsp; _(${moment(commit.date).format(dateFormat)})_${message}`;
}
static getHoverDiffMessage(commit: GitCommit, chunkLine: GitDiffChunkLine | undefined): MarkdownString | undefined {
static getHoverDiffMessage(commit: GitCommit, chunkLine: GitDiffChunkLine | undefined): string | undefined {
if (chunkLine === undefined) return undefined;
const codeDiff = this._getCodeDiff(chunkLine);
const markdown = new MarkdownString(commit.isUncommitted
? `[\`Changes\`](${DiffWithCommand.getMarkdownCommandArgs(commit)}) &nbsp; ${GlyphChars.Dash} &nbsp; _uncommitted_\n${codeDiff}`
: `[\`Changes\`](${DiffWithCommand.getMarkdownCommandArgs(commit)}) &nbsp; ${GlyphChars.Dash} &nbsp; [\`${commit.previousShortSha}\`](${ShowQuickCommitDetailsCommand.getMarkdownCommandArgs(commit.previousSha!)}) ${GlyphChars.ArrowLeftRight} [\`${commit.shortSha}\`](${ShowQuickCommitDetailsCommand.getMarkdownCommandArgs(commit.sha)})\n${codeDiff}`);
markdown.isTrusted = true;
return markdown;
return commit.isUncommitted
? `\`Changes\` &nbsp; ${GlyphChars.Dash} &nbsp; _uncommitted_\n${codeDiff}`
: `\`Changes\` &nbsp; ${GlyphChars.Dash} &nbsp; \`${commit.previousShortSha}\` ${GlyphChars.ArrowLeftRight} \`${commit.shortSha}\`\n${codeDiff}`;
}
private static _getCodeDiff(chunkLine: GitDiffChunkLine): string {
@@ -187,10 +181,7 @@ export class Annotations {
}
static trailing(commit: GitCommit, format: string, dateFormat: string | null, cfgTheme: IThemeConfig): DecorationOptions {
const message = CommitFormatter.fromTemplate(format, commit, {
truncateMessageAtNewLine: true,
dateFormat: dateFormat
} as ICommitFormatOptions);
const message = CommitFormatter.fromTemplate(format, commit, dateFormat);
return {
renderOptions: {
after: {

View File

@@ -1,5 +1,5 @@
'use strict';
import { DecorationOptions, ExtensionContext, MarkdownString, Position, Range, TextEditor, TextEditorDecorationType } from 'vscode';
import { DecorationOptions, ExtensionContext, Position, Range, TextEditor, TextEditorDecorationType } from 'vscode';
import { Annotations, endOfLineIndex } from './annotations';
import { FileAnnotationType } from './annotationController';
import { AnnotationProviderBase } from './annotationProvider';
@@ -48,7 +48,7 @@ export class RecentChangesAnnotationProvider extends AnnotationProviderBase {
} as DecorationOptions);
}
let message: MarkdownString | undefined = undefined;
let message: string | undefined = undefined;
if (cfg.hover.changes) {
message = Annotations.getHoverDiffMessage(commit, line);
}

View File

@@ -8,7 +8,6 @@ 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';

View File

@@ -11,7 +11,6 @@ export type Commands =
'gitlens.copyMessageToClipboard' |
'gitlens.copyShaToClipboard' |
'gitlens.diffDirectory' |
'gitlens.diffWith' |
'gitlens.diffWithBranch' |
'gitlens.diffWithNext' |
'gitlens.diffWithPrevious' |
@@ -53,7 +52,6 @@ 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,
@@ -168,10 +166,6 @@ 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,14 +1,16 @@
'use strict';
import { commands, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { DiffWithCommandArgs } from './diffWith';
import { BuiltInCommands, GlyphChars } from '../constants';
import { DiffWithPreviousCommandArgs } from './diffWithPrevious';
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 DiffLineWithPreviousCommandArgs {
commit?: GitCommit;
line?: number;
showOptions?: TextDocumentShowOptions;
}
@@ -41,26 +43,56 @@ 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, 'DiffLineWithPreviousCommand', `getBlameForLine(${blameline})`);
Logger.error(ex, 'DiffWithPreviousLineCommand', `getBlameForLine(${blameline})`);
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);
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`);
}
}
}

View File

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

View File

@@ -1,150 +0,0 @@
'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 lhsPrefix = '';
if (lhs === undefined) {
lhsPrefix = 'deleted in ';
}
else if (args.rhs.sha === GitService.fakeSha) {
lhsPrefix = 'added in ';
}
let rhsPrefix = '';
if (rhs === undefined) {
rhsPrefix = 'deleted in ';
}
else if (args.lhs.sha === GitService.fakeSha) {
rhsPrefix = 'added in ';
}
if (args.lhs.title === 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)} (${lhsPrefix}${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, TextDocumentShowOptions, TextEditor, Uri } from 'vscode';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { GlyphChars } from '../constants';
import { DiffWithCommandArgs } from './diffWith';
import { BuiltInCommands, GlyphChars } from '../constants';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { BranchesQuickPick, CommandQuickPickItem } from '../quickPicks';
import * as path from 'path';
@@ -42,20 +42,25 @@ export class DiffWithBranchCommand extends ActiveEditorCommand {
const branch = pick.branch.name;
if (branch === undefined) return undefined;
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);
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`);
}
}
}

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 { DiffWithCommandArgs } from './diffWith';
import { BuiltInCommands, GlyphChars } from '../constants';
import { GitLogCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import * as path from 'path';
export interface DiffWithNextCommandArgs {
commit?: GitLogCommit;
range?: Range;
line?: number;
range?: Range;
showOptions?: TextDocumentShowOptions;
}
@@ -54,19 +54,28 @@ export class DiffWithNextCommand extends ActiveEditorCommand {
if (args.commit.nextSha === undefined) return commands.executeCommand(Commands.DiffWithWorking, uri);
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);
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`);
}
}
}

View File

@@ -2,18 +2,22 @@
import { Iterables } from '../system';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { DiffWithCommandArgs } from './diffWith';
import { BuiltInCommands, FakeSha, GlyphChars } from '../constants';
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;
range?: Range;
line?: number;
range?: Range;
showOptions?: TextDocumentShowOptions;
allowMissingPrevious?: boolean;
leftTitlePrefix?: string;
rightTitlePrefix?: string;
}
export class DiffWithPreviousCommand extends ActiveEditorCommand {
@@ -36,7 +40,6 @@ 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');
@@ -44,9 +47,7 @@ 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})`);
@@ -54,19 +55,32 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
}
}
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);
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`);
}
}
}

View File

@@ -1,16 +1,16 @@
'use strict';
import { commands, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { DiffWithCommandArgs } from './diffWith';
import { BuiltInCommands, GlyphChars } from '../constants';
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 {
maxCount?: number;
line?: number;
maxCount?: number;
showOptions?: TextDocumentShowOptions;
}
@@ -46,24 +46,24 @@ export class DiffWithRevisionCommand extends ActiveEditorCommand {
if (pick instanceof CommandQuickPickItem) return pick.execute();
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);
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);
}
catch (ex) {
Logger.error(ex, 'DiffWithRevisionCommand');
return window.showErrorMessage(`Unable to open compare. See output channel for more details`);
Logger.error(ex, 'DiffWithRevisionCommand', 'getVersionedFile');
return window.showErrorMessage(`Unable to open history compare. See output channel for more details`);
}
}
}

View File

@@ -1,14 +1,14 @@
'use strict';
import { commands, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { DiffWithCommandArgs } from './diffWith';
import { BuiltInCommands, GlyphChars } from '../constants';
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,19 +48,25 @@ export class DiffWithWorkingCommand extends ActiveEditorCommand {
const workingFileName = await this.git.findWorkingFileName(gitUri.repoPath, gitUri.fsPath);
if (workingFileName === undefined) return undefined;
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);
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`);
}
}
}

View File

@@ -22,8 +22,10 @@ export class OpenBranchInRemoteCommand extends ActiveEditorCommand {
protected async preExecute(context: CommandContext, args: OpenBranchInRemoteCommandArgs = {}): Promise<any> {
if (isCommandViewContextWithBranch(context)) {
args = { ...args };
args.branch = context.node.branch.name;
args.remote = context.node.branch.getRemote();
return this.execute(context.editor, context.uri, args);
}
return this.execute(context.editor, context.uri, args);

View File

@@ -20,6 +20,7 @@ export class OpenBranchesInRemoteCommand extends ActiveEditorCommand {
if (isCommandViewContextWithRemote(context)) {
args = { ...args };
args.remote = context.node.remote.name;
return this.execute(context.editor, context.uri, args);
}
return this.execute(context.editor, context.uri, args);

View File

@@ -16,7 +16,7 @@ export class OpenFileInRemoteCommand extends ActiveEditorCommand {
super(Commands.OpenFileInRemote);
}
protected async preExecute(context: CommandContext, args: OpenFileInRemoteCommandArgs = { range: true }): Promise<any> {
protected async preExecute(context: CommandContext, args: OpenFileInRemoteCommandArgs = {}): Promise<any> {
if (isCommandViewContextWithCommit(context)) {
args = { ...args };
args.range = false;

View File

@@ -3,7 +3,7 @@ import { Strings } from '../system';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { GlyphChars } from '../constants';
import { GitLogCommit, GitRemote, GitService, RemoteResource } from '../gitService';
import { GitLogCommit, GitRemote, RemoteResource } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem, OpenRemoteCommandQuickPickItem, RemotesQuickPick } from '../quickPicks';
@@ -50,7 +50,7 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
break;
case 'commit':
const shortSha = GitService.shortenSha(args.resource.sha);
const shortSha = args.resource.sha.substring(0, 8);
placeHolder = `open commit ${shortSha} in${GlyphChars.Ellipsis}`;
break;
@@ -70,7 +70,7 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
}
}
else {
const shortFileSha = args.resource.sha === undefined ? '' : GitService.shortenSha(args.resource.sha);
const shortFileSha = args.resource.sha === undefined ? '' : args.resource.sha.substring(0, 8);
const shaSuffix = shortFileSha ? ` ${Strings.pad(GlyphChars.Dot, 1, 1)} ${shortFileSha}` : '';
placeHolder = `open ${args.resource.fileName}${shaSuffix} in${GlyphChars.Ellipsis}`;

View File

@@ -20,6 +20,7 @@ export class OpenRepoInRemoteCommand extends ActiveEditorCommand {
if (isCommandViewContextWithRemote(context)) {
args = { ...args };
args.remote = context.node.remote.name;
return this.execute(context.editor, context.uri, args);
}
return this.execute(context.editor, context.uri, args);

View File

@@ -20,29 +20,20 @@ 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: ShowQuickCommitDetailsCommandArgs = {}): Promise<any> {
protected async preExecute(context: CommandContext, ...args: any[]): Promise<any> {
if (context.type === 'view') {
args = { ...args };
args.sha = context.node.uri.sha;
if (isCommandViewContextWithCommit(context)) {
args.commit = context.node.commit;
args = [{ sha: context.node.uri.sha, commit: context.node.commit }];
}
else {
args = [{ sha: context.node.uri.sha }];
}
}
return this.execute(context.editor, context.uri, args);
return this.execute(context.editor, context.uri, ...args);
}
async execute(editor?: TextEditor, uri?: Uri, args: ShowQuickCommitDetailsCommandArgs = {}) {

View File

@@ -20,29 +20,20 @@ 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: ShowQuickCommitFileDetailsCommandArgs = {}): Promise<any> {
protected async preExecute(context: CommandContext, ...args: any[]): Promise<any> {
if (context.type === 'view') {
args = { ...args };
args.sha = context.node.uri.sha;
if (isCommandViewContextWithCommit(context)) {
args.commit = context.node.commit;
args = [{ sha: context.node.uri.sha, commit: context.node.commit }];
}
else {
args = [{ sha: context.node.uri.sha }];
}
}
return this.execute(context.editor, context.uri, args);
return this.execute(context.editor, context.uri, ...args);
}
async execute(editor?: TextEditor, uri?: Uri, args: ShowQuickCommitFileDetailsCommandArgs = {}) {
@@ -104,7 +95,7 @@ export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCachedCommand
args.commit.workingFileName = workingFileName;
args.commit.workingFileName = await this.git.findWorkingFileName(args.commit);
const shortSha = GitService.shortenSha(args.sha!);
const shortSha = args.sha!.substring(0, 8);
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 this.execute(args);
return super.preExecute(context, 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 this.execute(args);
return super.preExecute(context, args);
}
async execute(args: StashDeleteCommandArgs = { confirm: true }) {

View File

@@ -35,7 +35,7 @@ export class StashSaveCommand extends Command {
return this.execute(args);
}
async execute(args: StashSaveCommandArgs = {}) {
async execute(args: StashSaveCommandArgs = { }) {
if (!this.git.repoPath) return undefined;
try {

View File

@@ -7,8 +7,7 @@ import { OutputLevel } from './logger';
export { ExtensionKey } from './constants';
export type CodeLensCommand =
'gitlens.toggleFileBlame' |
export type CodeLensCommand = 'gitlens.toggleFileBlame' |
'gitlens.showBlameHistory' |
'gitlens.showFileHistory' |
'gitlens.diffWithPrevious' |
@@ -42,18 +41,7 @@ export const LineHighlightLocations = {
OverviewRuler: 'overviewRuler' as LineHighlightLocations
};
export type CustomRemoteType =
'Bitbucket' |
'GitHub' |
'GitLab';
export const CustomRemoteType = {
Bitbucket: 'Bitbucket' as CustomRemoteType,
GitHub: 'GitHub' as CustomRemoteType,
GitLab: 'GitLab' as CustomRemoteType
};
export type StatusBarCommand =
'gitlens.toggleFileBlame' |
export type StatusBarCommand = 'gitlens.toggleFileBlame' |
'gitlens.showBlameHistory' |
'gitlens.showFileHistory' |
'gitlens.toggleCodeLens' |
@@ -131,11 +119,6 @@ export interface ICodeLensLanguageLocation {
customSymbols?: string[];
}
export interface IRemotesConfig {
type: CustomRemoteType;
domain: string;
}
export interface IThemeConfig {
annotations: {
file: {
@@ -324,8 +307,6 @@ export interface IConfig {
// dateFormat: string | null;
};
remotes: IRemotesConfig[];
statusBar: {
enabled: boolean;
alignment: 'left' | 'right';

View File

@@ -8,6 +8,8 @@ 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' |

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, ICommitFormatOptions } from './gitService';
import { BlameabilityChangeEvent, CommitFormatter, GitCommit, GitCommitLine, GitContextTracker, GitService, GitUri } from './gitService';
import { Logger } from './logger';
const annotationDecoration: TextEditorDecorationType = window.createTextEditorDecorationType({
@@ -462,10 +462,7 @@ 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, {
truncateMessageAtNewLine: true,
dateFormat: cfg.dateFormat === null ? this._config.defaultDateFormat : cfg.dateFormat
} as ICommitFormatOptions)}`;
this._statusBarItem.text = `$(git-commit) ${CommitFormatter.fromTemplate(cfg.format, commit, cfg.dateFormat === null ? this._config.defaultDateFormat : cfg.dateFormat)}`;
switch (cfg.command) {
case StatusBarCommand.BlameAnnotate:

View File

@@ -5,7 +5,7 @@ import { AnnotationController } from './annotations/annotationController';
import { CloseUnchangedFilesCommand, OpenChangedFilesCommand } from './commands';
import { OpenBranchesInRemoteCommand, OpenBranchInRemoteCommand, OpenCommitInRemoteCommand, OpenFileInRemoteCommand, OpenInRemoteCommand, OpenRepoInRemoteCommand } from './commands';
import { CopyMessageToClipboardCommand, CopyShaToClipboardCommand } from './commands';
import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithBranchCommand, DiffWithCommand, DiffWithNextCommand, DiffWithPreviousCommand, DiffWithRevisionCommand, DiffWithWorkingCommand } from './commands';
import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithBranchCommand, DiffWithNextCommand, DiffWithPreviousCommand, DiffWithRevisionCommand, DiffWithWorkingCommand } from './commands';
import { ResetSuppressedWarningsCommand } from './commands';
import { ClearFileAnnotationsCommand, ShowFileBlameCommand, ShowLineBlameCommand, ToggleFileBlameCommand, ToggleFileRecentChangesCommand, ToggleLineBlameCommand } from './commands';
import { ShowBlameHistoryCommand, ShowFileHistoryCommand } from './commands';
@@ -19,7 +19,6 @@ import { CodeLensLocations, IConfig, LineHighlightLocations } from './configurat
import { ApplicationInsightsKey, CommandContext, ExtensionKey, QualifiedExtensionId, setCommandContext, WorkspaceState } 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';
@@ -34,7 +33,6 @@ 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;
@@ -105,7 +103,6 @@ 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));

View File

@@ -3,11 +3,8 @@ 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;
@@ -44,14 +41,7 @@ export class CommitFormatter extends Formatter<GitCommit, ICommitFormatOptions>
}
get 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}`;
}
}
const message = this._item.isUncommitted ? 'Uncommitted change' : this._item.message;
return this._padOrTruncate(message, this._options.tokenOptions!.message);
}

View File

@@ -1,5 +1,4 @@
'use strict';
import { Strings } from '../system';
import { findGitPath, IGit } from './gitLocator';
import { Logger } from '../logger';
import { spawnPromise } from 'spawn-rx';
@@ -39,49 +38,38 @@ const GitWarnings = [
/no upstream configured for branch/
];
interface GitCommandOptions {
cwd: string;
encoding?: string;
overrideErrorHandling?: boolean;
}
async function gitCommand(options: GitCommandOptions, ...args: any[]): Promise<string> {
if (options.overrideErrorHandling) return gitCommandCore(options, ...args);
async function gitCommand(options: { cwd: string, encoding?: string, onError?: (ex: Error) => string | undefined }, ...args: any[]) {
try {
return await gitCommandCore(options, ...args);
// 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);
}
catch (ex) {
return gitCommandDefaultErrorHandler(ex, options, ...args);
}
}
if (options.onError !== undefined) {
const result = options.onError(ex);
if (result !== undefined) return result;
}
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 '';
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 {
@@ -110,9 +98,8 @@ 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 = Strings.truncate(Strings.sanitizeForFS(Git.isSha(branchOrSha) ? Git.shortenSha(branchOrSha) : branchOrSha), 50, '');
const suffix = Git.isSha(branchOrSha) ? branchOrSha.substring(0, 8) : branchOrSha;
const ext = path.extname(fileName);
return new Promise<string>((resolve, reject) => {
tmp.file({ prefix: `${path.basename(fileName, ext)}-${suffix}__`, postfix: ext },
@@ -147,10 +134,6 @@ 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);
@@ -201,20 +184,16 @@ export class Git {
return gitCommand({ cwd: repoPath }, ...params);
}
static async branch_current(repoPath: string) {
static branch_current(repoPath: string) {
const params = [`rev-parse`, `--abbrev-ref`, `--symbolic-full-name`, `@`, `@{u}`];
const opts = { cwd: repoPath, overrideErrorHandling: true };
try {
return await gitCommand(opts, ...params);
}
catch (ex) {
const onError = (ex: Error) => {
if (/no upstream configured for branch/.test(ex && ex.toString())) {
return ex.message.split('\n')[0];
}
return gitCommandDefaultErrorHandler(ex, opts, ...params);
}
return undefined;
};
return gitCommand({ cwd: repoPath, onError: onError }, ...params);
}
static checkout(repoPath: string, fileName: string, sha: string) {
@@ -225,9 +204,9 @@ export class Git {
static async config_get(key: string, repoPath?: string) {
try {
return await gitCommand({ cwd: repoPath || '', overrideErrorHandling: true }, `config`, `--get`, key);
return await gitCommand({ cwd: repoPath || '' }, `config`, `--get`, key);
}
catch {
catch (ex) {
return '';
}
}
@@ -331,9 +310,9 @@ export class Git {
static async ls_files(repoPath: string, fileName: string): Promise<string> {
try {
return await gitCommand({ cwd: repoPath, overrideErrorHandling: true }, 'ls-files', fileName);
return await gitCommand({ cwd: repoPath }, 'ls-files', fileName);
}
catch {
catch (ex) {
return '';
}
}
@@ -346,24 +325,12 @@ export class Git {
return gitCommand({ cwd: repoPath }, 'remote', 'get-url', remote);
}
static async show(repoPath: string | undefined, fileName: string, branchOrSha: string, encoding?: string) {
static show(repoPath: string | undefined, fileName: string, branchOrSha: string, encoding?: string) {
const [file, root] = Git.splitPath(fileName, repoPath);
branchOrSha = branchOrSha.replace('^', '');
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) {
if (/Path \'.*?\' does not exist in/.test(ex && ex.toString())) {
return undefined;
}
return gitCommandDefaultErrorHandler(ex, opts, args);
}
if (Git.isUncommitted(branchOrSha)) return Promise.reject(new Error(`sha=${branchOrSha} is uncommitted`));
return gitCommand({ cwd: root, encoding: encoding || defaultEncoding }, 'show', `${branchOrSha}:./${file}`);
}
static stash_apply(repoPath: string, stashName: string, deleteAfter: boolean) {

View File

@@ -56,7 +56,7 @@ export class GitUri extends Uri {
}
get shortSha() {
return this.sha && GitService.shortenSha(this.sha);
return this.sha && this.sha.substring(0, 8);
}
fileUri() {

View File

@@ -13,6 +13,11 @@ export class GitBranch {
this.remote = true;
}
const index = branch.indexOf(' ');
if (index !== -1) {
branch = branch.substring(0, index);
}
this.current = current;
this.name = branch;
this.tracking = tracking;

View File

@@ -51,7 +51,7 @@ export class GitCommit {
}
get shortSha() {
return Git.shortenSha(this.sha);
return this.sha.substring(0, 8);
}
get isUncommitted(): boolean {
@@ -62,7 +62,7 @@ export class GitCommit {
}
get previousShortSha() {
return this.previousSha && Git.shortenSha(this.previousSha);
return this.previousSha && this.previousSha.substring(0, 8);
}
get previousUri(): Uri {

View File

@@ -1,7 +1,6 @@
'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';
@@ -50,7 +49,7 @@ export class GitLogCommit extends GitCommit {
}
get nextShortSha() {
return this.nextSha && Git.shortenSha(this.nextSha);
return this.nextSha && this.nextSha.substring(0, 8);
}
get nextUri(): Uri {

View File

@@ -1,64 +1,24 @@
'use strict';
import { ExtensionContext, workspace } from 'vscode';
import { RemoteProvider } from './provider';
import { BitbucketService } from './bitbucket';
import { CustomRemoteType, IConfig, IRemotesConfig } from '../../configuration';
import { ExtensionKey } from '../../constants';
import { GitHubService } from './github';
import { GitLabService } from './gitlab';
import { Logger } from '../../logger';
import { RemoteProvider } from './provider';
import { VisualStudioService } from './visualStudio';
import { Objects } from '../../system';
import { Logger } from '../../logger';
export { RemoteProvider };
const UrlRegex = /^(?:git:\/\/(.*?)\/|https:\/\/(.*?)\/|http:\/\/(.*?)\/|git@(.*):|ssh:\/\/(?:.*@)?(.*?)(?::.*?)?\/)(.*)$/;
function getProviderKey(type: CustomRemoteType) {
switch (type) {
case CustomRemoteType.Bitbucket: return 'bitbucket.org';
case CustomRemoteType.GitHub: return 'github.com';
case CustomRemoteType.GitLab: return 'gitlab.com';
}
return undefined;
}
const defaultProviderMap = new Map<string, (domain: string, path: string) => RemoteProvider>([
const providerMap = 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)]
]);
let providerMap: Map<string, (domain: string, path: string) => RemoteProvider>;
let remotesCfg: IRemotesConfig[];
function onConfigurationChanged() {
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey);
if (cfg === undefined) return;
if (!Objects.areEquivalent(cfg.remotes, remotesCfg)) {
providerMap = new Map(defaultProviderMap);
remotesCfg = cfg.remotes;
if (remotesCfg != null && remotesCfg.length > 0) {
for (const svc of remotesCfg) {
const key = getProviderKey(svc.type);
if (key === undefined) continue;
providerMap.set(svc.domain.toLowerCase(), providerMap.get(key)!);
}
}
}
}
const UrlRegex = /^(?:git:\/\/(.*?)\/|https:\/\/(.*?)\/|http:\/\/(.*?)\/|git@(.*):|ssh:\/\/(?:.*@)?(.*?)(?::.*?)?\/)(.*)$/;
export class RemoteProviderFactory {
static configure(context: ExtensionContext) {
context.subscriptions.push(workspace.onDidChangeConfiguration(onConfigurationChanged));
onConfigurationChanged();
}
static getRemoteProvider(url: string): RemoteProvider | undefined {
try {
const match = UrlRegex.exec(url);
@@ -72,7 +32,7 @@ export class RemoteProviderFactory {
: domain;
const creator = providerMap.get(key.toLowerCase());
if (creator === undefined) return undefined;
if (!creator) return undefined;
return creator(domain, path);
}

View File

@@ -15,9 +15,7 @@ export class GitContentProvider implements TextDocumentContentProvider {
const data = GitService.fromGitContentUri(uri);
const fileName = data.originalFileName || data.fileName;
try {
let text = data.sha !== GitService.fakeSha
? await this.git.getVersionedFileText(data.repoPath, fileName, data.sha)
: '';
let text = await this.git.getVersionedFileText(data.repoPath, fileName, data.sha);
if (data.decoration) {
text = `${data.decoration}\n${text}`;
}
@@ -25,7 +23,7 @@ export class GitContentProvider implements TextDocumentContentProvider {
}
catch (ex) {
Logger.error(ex, 'GitContentProvider', 'getVersionedFileText');
window.showErrorMessage(`Unable to show Git revision ${GitService.shortenSha(data.sha)} of '${path.relative(data.repoPath, fileName)}'`);
window.showErrorMessage(`Unable to show Git revision ${data.sha.substring(0, 8)} of '${path.relative(data.repoPath, fileName)}'`);
return undefined;
}
}

View File

@@ -72,8 +72,6 @@ export const RepoChangedReasons = {
export class GitService extends Disposable {
static fakeSha = 'ffffffffffffffffffffffffffffffffffffffff';
private _onDidBlameFail = new EventEmitter<string>();
get onDidBlameFail(): Event<string> {
return this._onDidBlameFail.event;
@@ -98,6 +96,7 @@ export class GitService extends Disposable {
private _disposable: Disposable | undefined;
private _gitignore: Promise<ignore.Ignore | undefined>;
private _repoWatcher: FileSystemWatcher | undefined;
private _stashWatcher: FileSystemWatcher | undefined;
static EmptyPromise: Promise<GitBlame | GitDiff | GitLog | undefined> = Promise.resolve(undefined);
@@ -126,6 +125,9 @@ export class GitService extends Disposable {
this._repoWatcher && this._repoWatcher.dispose();
this._repoWatcher = undefined;
this._stashWatcher && this._stashWatcher.dispose();
this._stashWatcher = undefined;
this._gitCache.clear();
this._remotesCache.clear();
this._uriCache.clear();
@@ -145,7 +147,8 @@ export class GitService extends Disposable {
if (cfg.advanced.caching.enabled) {
this._cacheDisposable && this._cacheDisposable.dispose();
this._repoWatcher = this._repoWatcher || workspace.createFileSystemWatcher('**/.git/{index,HEAD,refs/stash,refs/heads/**,refs/remotes/**}');
this._repoWatcher = this._repoWatcher || workspace.createFileSystemWatcher('**/.git/index', true, false, true);
this._stashWatcher = this._stashWatcher || workspace.createFileSystemWatcher('**/.git/refs/stash', true, false, true);
const disposables: Disposable[] = [];
@@ -153,6 +156,7 @@ export class GitService extends Disposable {
disposables.push(workspace.onDidChangeTextDocument(this._onTextDocumentChanged, this));
disposables.push(workspace.onDidSaveTextDocument(d => this._removeCachedEntry(d, RemoveCacheReason.DocumentSaved)));
disposables.push(this._repoWatcher.onDidChange(this._onRepoChanged, this));
disposables.push(this._stashWatcher.onDidChange(this._onStashChanged, this));
this._cacheDisposable = Disposable.from(...disposables);
}
@@ -163,6 +167,9 @@ export class GitService extends Disposable {
this._repoWatcher && this._repoWatcher.dispose();
this._repoWatcher = undefined;
this._stashWatcher && this._stashWatcher.dispose();
this._stashWatcher = undefined;
this._gitCache.clear();
this._remotesCache.clear();
}
@@ -209,19 +216,17 @@ export class GitService extends Disposable {
}, 1);
}
private _onRepoChanged(uri: Uri) {
if (uri !== undefined && uri.path.endsWith('ref/stash')) {
this._fireRepoChange('stash');
return;
}
private _onRepoChanged() {
this._gitCache.clear();
this._fireRepoChange();
this._fireGitCacheChange();
}
private _onStashChanged() {
this._fireRepoChange('stash');
}
private _fireGitCacheChangeDebounced: (() => void) | undefined = undefined;
private _fireGitCacheChange() {
@@ -956,8 +961,6 @@ export class GitService extends Disposable {
Logger.log(`getVersionedFile('${repoPath}', '${fileName}', ${sha})`);
const file = await Git.getVersionedFile(repoPath, fileName, sha);
if (file === undefined) return undefined;
const cacheKey = this.getCacheEntryKey(file);
const entry = new UriCacheEntry(new GitUri(Uri.file(fileName), { sha, repoPath: repoPath!, fileName }));
this._uriCache.set(cacheKey, entry);
@@ -1065,17 +1068,11 @@ export class GitService extends Disposable {
return Git.normalizePath(fileName, repoPath);
}
static shortenSha(sha: string | undefined) {
if (sha === undefined) return undefined;
return Git.shortenSha(sha);
}
static toGitContentUri(sha: string, fileName: string, repoPath: string, originalFileName?: string): Uri;
static toGitContentUri(sha: string, shortSha: string, fileName: string, repoPath: string, originalFileName?: string): Uri;
static toGitContentUri(commit: GitCommit): Uri;
static toGitContentUri(uri: GitUri): Uri;
static toGitContentUri(shaOrcommitOrUri: string | GitCommit | GitUri, fileName?: string, repoPath?: string, originalFileName?: string): Uri {
static toGitContentUri(shaOrcommitOrUri: string | GitCommit | GitUri, shortSha?: string, fileName?: string, repoPath?: string, originalFileName?: string): Uri {
let data: IGitUriData;
let shortSha: string | undefined;
if (typeof shaOrcommitOrUri === 'string') {
data = GitService._toGitUriData({
sha: shaOrcommitOrUri,
@@ -1083,7 +1080,6 @@ export class GitService extends Disposable {
repoPath: repoPath!,
originalFileName: originalFileName
});
shortSha = GitService.shortenSha(shaOrcommitOrUri);
}
else if (shaOrcommitOrUri instanceof GitCommit) {
data = GitService._toGitUriData(shaOrcommitOrUri, undefined, shaOrcommitOrUri.originalFileName);

View File

@@ -32,8 +32,7 @@ export class Messages {
this.context = context;
}
static showCommitHasNoPreviousCommitWarningMessage(commit?: GitCommit): Promise<string | undefined> {
if (commit === undefined) return Messages._showMessage('info', `Commit has no previous commit`, SuppressedKeys.CommitHasNoPreviousCommitWarning);
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);
}

View File

@@ -24,14 +24,17 @@ 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, status.fileName, commit.repoPath, status.originalFileName), {
super(GitService.toGitContentUri(sha, shortSha, status.fileName, commit.repoPath, status.originalFileName), {
label: `${Strings.pad(octicon, 4, 2)} ${path.basename(status.fileName)}`,
description: description
});
@@ -45,7 +48,7 @@ export class CommitWithFileStatusQuickPickItem extends OpenFileCommandQuickPickI
originalFileName: status.originalFileName
} as IGitCommitInfo);
this.sha = sha;
this.shortSha = GitService.shortenSha(sha)!;
this.shortSha = shortSha;
this.status = status.status;
}
@@ -85,7 +88,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, s.fileName, commit.repoPath, s.originalFileName));
.map(s => GitService.toGitContentUri(commit.sha, commit.shortSha, s.fileName, commit.repoPath, s.originalFileName));
super(uris, item || {
label: `$(file-symlink-file) Open Changed Revisions`,

View File

@@ -27,7 +27,7 @@ export class OpenCommitFileRevisionCommandQuickPickItem extends OpenFileCommandQ
let description: string;
let uri: Uri;
if (commit.status === 'D') {
uri = GitService.toGitContentUri(commit.previousSha!, commit.previousFileName!, commit.repoPath, undefined);
uri = GitService.toGitContentUri(commit.previousSha!, commit.previousShortSha!, 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 {

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, GitService, RemoteResource } from '../gitService';
import { getNameFromRemoteResource, GitLogCommit, GitRemote, RemoteResource } from '../gitService';
import * as path from 'path';
export class OpenRemoteCommandQuickPickItem extends CommandQuickPickItem {
@@ -43,7 +43,7 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
break;
case 'commit':
const shortSha = GitService.shortenSha(resource.sha);
const shortSha = resource.sha.substring(0, 8);
description = `$(git-commit) ${shortSha}`;
break;
@@ -67,7 +67,7 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
}
}
else {
const shortFileSha = resource.sha === undefined ? '' : GitService.shortenSha(resource.sha);
const shortFileSha = resource.sha === undefined ? '' : resource.sha.substring(0, 8);
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 { GitService, GitStatus, GitStatusFile, GitUri } from '../gitService';
import { GitStatus, GitStatusFile, GitUri } from '../gitService';
import { Keyboard, Keys } from '../keyboard';
import * as path from 'path';
@@ -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) ${GitService.shortenSha(status.sha)})` : ''}`
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)})` : ''}`
}, Commands.ShowQuickBranchHistory, [
new GitUri(Uri.file(status.repoPath), { fileName: '', repoPath: status.repoPath, sha: `${status.branch}..${status.upstream}` }),
{

View File

@@ -101,20 +101,12 @@ export namespace Strings {
return s;
}
// Removes \ / : * ? " < > | and C0 and C1 control codes
const illegalCharsForFSRegEx = /[\\/:*?"<>|\x00-\x1f\x80-\x9f]/g;
export function sanitizeForFS(s: string, replacement: string = '_') {
if (!s) return s;
return s.replace(illegalCharsForFSRegEx, replacement);
}
export function truncate(s: string, truncateTo: number, ellipsis: string = '\u2026') {
if (!s) return s;
export function truncate(s: string, truncateTo?: number) {
if (!s || truncateTo === undefined) return s;
const len = getWidth(s);
if (len <= truncateTo) return s;
if (len === s.length) return `${s.substring(0, truncateTo - 1)}${ellipsis}`;
if (len === s.length) return `${s.substring(0, truncateTo - 1)}\u2026`;
// Skip ahead to start as far as we can by assuming all the double-width characters won't be truncated
let chars = Math.floor(truncateTo / (len / s.length));
@@ -127,6 +119,6 @@ export namespace Strings {
chars--;
}
return `${s.substring(0, chars)}${ellipsis}`;
return `${s.substring(0, chars)}\u2026`;
}
}

View File

@@ -15,7 +15,7 @@ export class BranchHistoryNode extends ExplorerNode {
}
async getChildren(): Promise<ExplorerNode[]> {
const log = await this.git.getLogForRepo(this.uri.repoPath!, this.branch.name, 0);
const log = await this.git.getLogForRepo(this.uri.repoPath!, this.branch.name);
if (log === undefined) return [];
return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.template, this.context, this.git))];

View File

@@ -40,6 +40,16 @@ export class CommitFileNode extends ExplorerNode {
}
getCommand(): Command | undefined {
let allowMissingPrevious = false;
let prefix = undefined;
if (this.status.status === 'A') {
allowMissingPrevious = true;
prefix = 'added in ';
}
else if (this.status.status === 'D') {
prefix = 'deleted in ';
}
return {
title: 'Compare File with Previous Revision',
command: Commands.DiffWithPrevious,
@@ -51,7 +61,9 @@ export class CommitFileNode extends ExplorerNode {
showOptions: {
preserveFocus: true,
preview: true
}
},
allowMissingPrevious: allowMissingPrevious,
rightTitlePrefix: prefix
} as DiffWithPreviousCommandArgs
]
};

View File

@@ -4,7 +4,7 @@ import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'v
import { Commands, DiffWithPreviousCommandArgs } from '../commands';
import { CommitFileNode } from './commitFileNode';
import { ExplorerNode, ResourceType } from './explorerNode';
import { CommitFormatter, getGitStatusIcon, GitLogCommit, GitService, GitUri, ICommitFormatOptions } from '../gitService';
import { CommitFormatter, getGitStatusIcon, GitLogCommit, GitService, GitUri } from '../gitService';
import * as path from 'path';
export class CommitNode extends ExplorerNode {
@@ -28,11 +28,7 @@ export class CommitNode extends ExplorerNode {
}
getTreeItem(): TreeItem {
const item = new TreeItem(CommitFormatter.fromTemplate(this.template, this.commit, {
truncateMessageAtNewLine: true,
dataFormat: this.git.config.defaultDateFormat
} as ICommitFormatOptions));
const item = new TreeItem(CommitFormatter.fromTemplate(this.template, this.commit, this.git.config.defaultDateFormat));
if (this.commit.type === 'file') {
item.collapsibleState = TreeItemCollapsibleState.None;
item.command = this.getCommand();
@@ -59,6 +55,17 @@ export class CommitNode extends ExplorerNode {
}
getCommand(): Command | undefined {
let allowMissingPrevious = false;
let prefix = undefined;
const status = this.commit.fileStatuses[0];
if (status.status === 'A') {
allowMissingPrevious = true;
prefix = 'added in ';
}
else if (status.status === 'D') {
prefix = 'deleted in ';
}
return {
title: 'Compare File with Previous Revision',
command: Commands.DiffWithPrevious,
@@ -70,7 +77,9 @@ export class CommitNode extends ExplorerNode {
showOptions: {
preserveFocus: true,
preview: true
}
},
allowMissingPrevious: allowMissingPrevious,
rightTitlePrefix: prefix
} as DiffWithPreviousCommandArgs
]
};

View File

@@ -47,11 +47,10 @@ export class GitExplorer implements TreeDataProvider<ExplorerNode> {
commands.registerCommand('gitlens.gitExplorer.openChangedFileRevisions', this.openChangedFileRevisions, this);
commands.registerCommand('gitlens.gitExplorer.applyChanges', this.applyChanges, this);
const repoChangedFn = Functions.debounce(this.onRepoChanged, 250);
context.subscriptions.push(this.git.onDidChangeRepo(repoChangedFn, this));
context.subscriptions.push(this.git.onDidChangeRepo(this.onRepoChanged, this));
const editorChangedFn = Functions.debounce(this.onActiveEditorChanged, 500);
context.subscriptions.push(window.onDidChangeActiveTextEditor(editorChangedFn, this));
const fn = Functions.debounce(this.onActiveEditorChanged, 500);
context.subscriptions.push(window.onDidChangeActiveTextEditor(fn, this));
context.subscriptions.push(workspace.onDidChangeConfiguration(this.onConfigurationChanged, this));
this.onConfigurationChanged();
@@ -188,7 +187,7 @@ export class GitExplorer implements TreeDataProvider<ExplorerNode> {
private async openChangedFileRevisions(node: CommitNode | StashNode, options: TextDocumentShowOptions = { preserveFocus: false, preview: false }) {
const uris = node.commit.fileStatuses
.filter(s => s.status !== 'D')
.map(s => GitService.toGitContentUri(node.commit.sha, s.fileName, node.commit.repoPath, s.originalFileName));
.map(s => GitService.toGitContentUri(node.commit.sha, node.commit.shortSha, s.fileName, node.commit.repoPath, s.originalFileName));
for (const uri of uris) {
await openEditor(uri, options);
}

View File

@@ -1,7 +1,7 @@
'use strict';
import { Event, EventEmitter, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ExplorerNode, ResourceType } from './explorerNode';
import { CommitFormatter, GitService, GitStashCommit, GitUri, ICommitFormatOptions } from '../gitService';
import { CommitFormatter, GitService, GitStashCommit, GitUri } from '../gitService';
import { StashFileNode } from './stashFileNode';
export class StashNode extends ExplorerNode {
@@ -22,10 +22,9 @@ export class StashNode extends ExplorerNode {
}
getTreeItem(): TreeItem {
const item = new TreeItem(CommitFormatter.fromTemplate(this.git.config.gitExplorer.stashFormat, this.commit, {
truncateMessageAtNewLine: true,
dataFormat: this.git.config.defaultDateFormat
} as ICommitFormatOptions), TreeItemCollapsibleState.Collapsed);
const label = CommitFormatter.fromTemplate(this.git.config.gitExplorer.stashFormat, this.commit, this.git.config.defaultDateFormat);
const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
return item;
}

View File

@@ -17,7 +17,7 @@ export class StatusUpstreamNode extends ExplorerNode {
const range = this.direction === 'ahead'
? `${this.status.upstream}..${this.status.branch}`
: `${this.status.branch}..${this.status.upstream}`;
let log = await this.git.getLogForRepo(this.uri.repoPath!, range, 0);
let log = await this.git.getLogForRepo(this.uri.repoPath!, range);
if (log === undefined) return [];
if (this.direction !== 'ahead') return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.template, this.context, this.git))];