Quellcode durchsuchen

Refactors commit quick pick commands

Splits showQuickCommitDetails into showQuickCommitDetails and showQuickCommitFileDetails
Adds closeUnchangedFiles command
Adds openChangedFiles command
Adds diffDirectory command
Adds contextual description to the `go back` commands
Fixes #44 by adding a warning message about Git version requirements
Fixes intermittent errors when adding active line annotations
Fixes intermittent errors when opening multiple files via quick picks
Updates dependencies
Preps v2.11.0
main
Eric Amodio vor 8 Jahren
Ursprung
Commit
eaea44872c
30 geänderte Dateien mit 764 neuen und 353 gelöschten Zeilen
  1. +24
    -1
      CHANGELOG.md
  2. +3
    -3
      README.md
  3. +50
    -11
      package.json
  4. +59
    -0
      src/activeEditorTracker.ts
  5. +10
    -1
      src/blameActiveLineController.ts
  6. +5
    -1
      src/commands.ts
  7. +70
    -0
      src/commands/closeUnchangedFiles.ts
  8. +19
    -2
      src/commands/commands.ts
  9. +35
    -0
      src/commands/diffDirectory.ts
  10. +41
    -0
      src/commands/openChangedFiles.ts
  11. +20
    -49
      src/commands/showQuickCommitDetails.ts
  12. +89
    -0
      src/commands/showQuickCommitFileDetails.ts
  13. +12
    -17
      src/commands/showQuickFileHistory.ts
  14. +12
    -30
      src/commands/showQuickRepoHistory.ts
  15. +2
    -14
      src/commands/showQuickRepoStatus.ts
  16. +9
    -1
      src/comparers.ts
  17. +4
    -2
      src/configuration.ts
  18. +15
    -3
      src/extension.ts
  19. +3
    -3
      src/git/enrichers/logParserEnricher.ts
  20. +47
    -34
      src/git/git.ts
  21. +4
    -1
      src/git/gitEnrichment.ts
  22. +13
    -2
      src/gitCodeLensProvider.ts
  23. +24
    -10
      src/gitProvider.ts
  24. +2
    -1
      src/quickPicks.ts
  25. +27
    -119
      src/quickPicks/commitDetails.ts
  26. +111
    -0
      src/quickPicks/commitFileDetails.ts
  27. +13
    -9
      src/quickPicks/fileHistory.ts
  28. +3
    -16
      src/quickPicks/quickPicks.ts
  29. +1
    -3
      src/quickPicks/repoHistory.ts
  30. +37
    -20
      src/quickPicks/repoStatus.ts

+ 24
- 1
CHANGELOG.md Datei anzeigen

@ -1,5 +1,28 @@
## Release Notes ## Release Notes
### 2.11.0
- Adds `gitlens.showQuickCommitFileDetails` command to show a quick pick list of details for a file commit
- Adds `gitlens.showQuickCommitFileDetails` command to CodeLens
- Adds `gitlens.showQuickCommitFileDetails` command to the status bar
- Adds `gitlens.closeUnchangedFiles` command to close any editors that don't have uncommitted changes
- Adds `gitlens.openChangedFiles` command to open all files that have uncommitted changes
- Adds `gitlens.diffDirectory` command to open the configured git difftool to compare directory versions
- Adds `Directory Compare with Previous Commit` command on the `gitlens.showQuickCommitDetails` quick pick
- Adds `Directory Compare with Working Tree` command on the `gitlens.showQuickCommitDetails` quick pick
- Adds a `Changed Files` grouping on the `gitlens.showQuickCommitDetails` quick pick
- Adds a `Close Unchanged Files` command on the `gitlens.showQuickRepoStatus` quick pick
- Adds a contextual description to the `go back` command in quick pick lists
- Changes layout of the `gitlens.showQuickRepoStatus` quick pick for better clarity
- Changes behavior of `gitlens.showQuickCommitDetails` to show commit a quick pick list of details for a commit
- Changes default of `gitlens.codeLens.recentChange.command` to be `gitlens.showQuickCommitFileDetails` (though there is no visible behavior change)
- Renames `Open Files` to `Open Changed Files` on the `gitlens.showQuickCommitDetails` quick pick
- Renames `Open Working Files` to `Open Changed Working Files` on the `gitlens.showQuickCommitDetails` quick pick
- Renames `Show Changed Files` to `Show Commit Details` on the `gitlens.showQuickCommitFileDetails` quick pick
- Renames `Open Files` to `Open Changed Files` on the `gitlens.showQuickRepoStatus` quick pick
- Fixes [#44](https://github.com/eamodio/vscode-gitlens/issues/43) by adding a warning message about Git version requirements
- Fixes intermittent errors when adding active line annotations
- Fixes intermittent errors when opening multiple files via quick picks
### 2.10.1 ### 2.10.1
- Fixes [#43](https://github.com/eamodio/vscode-gitlens/issues/43) - File-level CodeLens isn't using the blame of the whole file as it should - Fixes [#43](https://github.com/eamodio/vscode-gitlens/issues/43) - File-level CodeLens isn't using the blame of the whole file as it should
- Fixes issue with single quotes (') in annotations - Fixes issue with single quotes (') in annotations
@ -108,7 +131,7 @@
- Adds `gitlens.showQuickRepoHistory` and `gitlens.showQuickCommitDetails` commands to the status bar - Adds `gitlens.showQuickRepoHistory` and `gitlens.showQuickCommitDetails` commands to the status bar
- Changes the default command of `gitlens.codeLens.recentChange.command` to `gitlens.showQuickCommitDetails` - Changes the default command of `gitlens.codeLens.recentChange.command` to `gitlens.showQuickCommitDetails`
- Changes the default command of `gitlens.statusBar.command` to `gitlens.showQuickCommitDetails` - Changes the default command of `gitlens.statusBar.command` to `gitlens.showQuickCommitDetails`
- Changes Changes behavior of `gitlens.showQuickCommitDetails` to show commit commands rather than file set (use `Show Changed Files` command to get to the file set)
- Changes behavior of `gitlens.showQuickCommitDetails` to show commit commands rather than file set (use `Show Changed Files` command to get to the file set)
- Changes `gitlens.diffWithPrevious` command to behave as `gitlens.diffWithWorking` if the file has uncommitted changes - Changes `gitlens.diffWithPrevious` command to behave as `gitlens.diffWithWorking` if the file has uncommitted changes
- Renames `gitlens.diffWithPrevious` command from `Diff Commit with Previous` to `Compare with Previous Commit` - Renames `gitlens.diffWithPrevious` command from `Diff Commit with Previous` to `Compare with Previous Commit`
- Renames `gitlens.diffLineWithPrevious` command from `Diff Commit (line) with Previous` to `Compare Line with Previous Commit` - Renames `gitlens.diffLineWithPrevious` command from `Diff Commit (line) with Previous` to `Compare Line with Previous Commit`

+ 3
- 3
README.md Datei anzeigen

@ -46,12 +46,12 @@ Provides Git CodeLens information (most recent commit, # of authors), on-demand
|`gitlens.codeLens.locationCustomSymbols`|Specifies the set of document symbols to render active document CodeLens on. Must be a member of `SymbolKind` |`gitlens.codeLens.locationCustomSymbols`|Specifies the set of document symbols to render active document CodeLens on. Must be a member of `SymbolKind`
|`gitlens.codeLens.languageLocations`|Specifies where CodeLens will be rendered in the active document for the specified languages |`gitlens.codeLens.languageLocations`|Specifies where CodeLens will be rendered in the active document for the specified languages
|`gitlens.codeLens.recentChange.enabled`|Specifies whether the recent change CodeLens is shown |`gitlens.codeLens.recentChange.enabled`|Specifies whether the recent change CodeLens is shown
|`gitlens.codeLens.recentChange.command`|"Specifies the command executed when the recent change CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick
|`gitlens.codeLens.recentChange.command`|"Specifies the command executed when the recent change CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick
|`gitlens.codeLens.authors.enabled`|Specifies whether the authors CodeLens is shown |`gitlens.codeLens.authors.enabled`|Specifies whether the authors CodeLens is shown
|`gitlens.codeLens.authors.command`|Specifies the command executed when the authors CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick
|`gitlens.codeLens.authors.command`|Specifies the command executed when the authors CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick
|`gitlens.menus.diff.enabled`|Specifies whether diff commands will be added to the context menus |`gitlens.menus.diff.enabled`|Specifies whether diff commands will be added to the context menus
|`gitlens.statusBar.enabled`|Specifies whether blame information is shown in the status bar |`gitlens.statusBar.enabled`|Specifies whether blame information is shown in the status bar
|`gitlens.statusBar.command`|"Specifies the command executed when the blame status bar item is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick
|`gitlens.statusBar.command`|"Specifies the command executed when the blame status bar item is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick
|`gitlens.statusBar.date`|Specifies whether and how the commit date will be shown in the blame status bar. `off` - no date. `relative` - relative date (e.g. 1 day ago). `absolute` - date format specified by `gitlens.statusBar.dateFormat` |`gitlens.statusBar.date`|Specifies whether and how the commit date will be shown in the blame status bar. `off` - no date. `relative` - relative date (e.g. 1 day ago). `absolute` - date format specified by `gitlens.statusBar.dateFormat`
|`gitlens.statusBar.dateFormat`|Specifies the date format of how absolute dates will be shown in the blame status bar. See https://momentjs.com/docs/#/displaying/format/ for valid formats |`gitlens.statusBar.dateFormat`|Specifies the date format of how absolute dates will be shown in the blame status bar. See https://momentjs.com/docs/#/displaying/format/ for valid formats

+ 50
- 11
package.json Datei anzeigen

@ -1,6 +1,6 @@
{ {
"name": "gitlens", "name": "gitlens",
"version": "2.10.1",
"version": "2.11.0",
"author": { "author": {
"name": "Eric Amodio", "name": "Eric Amodio",
"email": "eamodio@gmail.com" "email": "eamodio@gmail.com"
@ -214,17 +214,18 @@
}, },
"gitlens.codeLens.recentChange.command": { "gitlens.codeLens.recentChange.command": {
"type": "string", "type": "string",
"default": "gitlens.showQuickCommitDetails",
"default": "gitlens.showQuickCommitFileDetails",
"enum": [ "enum": [
"gitlens.toggleBlame", "gitlens.toggleBlame",
"gitlens.showBlameHistory", "gitlens.showBlameHistory",
"gitlens.showFileHistory", "gitlens.showFileHistory",
"gitlens.diffWithPrevious", "gitlens.diffWithPrevious",
"gitlens.showQuickCommitDetails", "gitlens.showQuickCommitDetails",
"gitlens.showQuickCommitFileDetails",
"gitlens.showQuickFileHistory", "gitlens.showQuickFileHistory",
"gitlens.showQuickRepoHistory" "gitlens.showQuickRepoHistory"
], ],
"description": "Specifies the command executed when the recent change CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick"
"description": "Specifies the command executed when the recent change CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick"
}, },
"gitlens.codeLens.authors.enabled": { "gitlens.codeLens.authors.enabled": {
"type": "boolean", "type": "boolean",
@ -240,10 +241,11 @@
"gitlens.showFileHistory", "gitlens.showFileHistory",
"gitlens.diffWithPrevious", "gitlens.diffWithPrevious",
"gitlens.showQuickCommitDetails", "gitlens.showQuickCommitDetails",
"gitlens.showQuickCommitFileDetails",
"gitlens.showQuickFileHistory", "gitlens.showQuickFileHistory",
"gitlens.showQuickRepoHistory" "gitlens.showQuickRepoHistory"
], ],
"description": "Specifies the command executed when the authors CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick"
"description": "Specifies the command executed when the authors CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick"
}, },
"gitlens.menus.diff.enabled": { "gitlens.menus.diff.enabled": {
"type": "boolean", "type": "boolean",
@ -265,10 +267,11 @@
"gitlens.diffWithPrevious", "gitlens.diffWithPrevious",
"gitlens.toggleCodeLens", "gitlens.toggleCodeLens",
"gitlens.showQuickCommitDetails", "gitlens.showQuickCommitDetails",
"gitlens.showQuickCommitFileDetails",
"gitlens.showQuickFileHistory", "gitlens.showQuickFileHistory",
"gitlens.showQuickRepoHistory" "gitlens.showQuickRepoHistory"
], ],
"description": "Specifies the command executed when the blame status bar item is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick"
"description": "Specifies the command executed when the blame status bar item is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick"
}, },
"gitlens.statusBar.date": { "gitlens.statusBar.date": {
"type": "string", "type": "string",
@ -349,6 +352,11 @@
"category": "GitLens:KeyPress" "category": "GitLens:KeyPress"
}, },
{ {
"command": "gitlens.diffDirectory",
"title": "Directory Compare",
"category": "GitLens"
},
{
"command": "gitlens.diffWithPrevious", "command": "gitlens.diffWithPrevious",
"title": "Compare with Previous Commit", "title": "Compare with Previous Commit",
"category": "GitLens" "category": "GitLens"
@ -403,6 +411,11 @@
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.showQuickCommitFileDetails",
"title": "Show Line Commit Details",
"category": "GitLens"
},
{
"command": "gitlens.showQuickFileHistory", "command": "gitlens.showQuickFileHistory",
"title": "Show File History", "title": "Show File History",
"category": "GitLens" "category": "GitLens"
@ -426,6 +439,16 @@
"command": "gitlens.copyMessageToClipboard", "command": "gitlens.copyMessageToClipboard",
"title": "Copy Commit Message to Clipboard", "title": "Copy Commit Message to Clipboard",
"category": "GitLens" "category": "GitLens"
},
{
"command": "gitlens.closeUnchangedFiles",
"title": "Close Unchanged Files",
"category": "GitLens"
},
{
"command": "gitlens.openChangedFiles",
"title": "Open Changed Files",
"category": "GitLens"
} }
], ],
"menus": { "menus": {
@ -439,6 +462,10 @@
"when": "false" "when": "false"
}, },
{ {
"command": "gitlens.diffDirectory",
"when": "gitlens:enabled"
},
{
"command": "gitlens.diffWithPrevious", "command": "gitlens.diffWithPrevious",
"when": "gitlens:enabled" "when": "gitlens:enabled"
}, },
@ -479,6 +506,10 @@
"when": "gitlens:enabled && gitlens:isBlameable" "when": "gitlens:enabled && gitlens:isBlameable"
}, },
{ {
"command": "gitlens.showQuickCommitFileDetails",
"when": "gitlens:enabled && gitlens:isBlameable"
},
{
"command": "gitlens.showQuickFileHistory", "command": "gitlens.showQuickFileHistory",
"when": "gitlens:enabled" "when": "gitlens:enabled"
}, },
@ -497,6 +528,14 @@
{ {
"command": "gitlens.copyMessageToClipboard", "command": "gitlens.copyMessageToClipboard",
"when": "gitlens:enabled && gitlens:isBlameable" "when": "gitlens:enabled && gitlens:isBlameable"
},
{
"command": "gitlens.closeUnchangedFiles",
"when": "gitlens:enabled"
},
{
"command": "gitlens.openChangedFiles",
"when": "gitlens:enabled"
} }
], ],
"explorer/context": [ "explorer/context": [
@ -572,7 +611,7 @@
"group": "1_gitlens@2" "group": "1_gitlens@2"
}, },
{ {
"command": "gitlens.showQuickCommitDetails",
"command": "gitlens.showQuickCommitFileDetails",
"when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable", "when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable",
"group": "1_gitlens@3" "group": "1_gitlens@3"
}, },
@ -651,7 +690,7 @@
"when": "gitlens:enabled" "when": "gitlens:enabled"
}, },
{ {
"command": "gitlens.showQuickCommitDetails",
"command": "gitlens.showQuickCommitFileDetails",
"key": "alt+c", "key": "alt+c",
"mac": "alt+c", "mac": "alt+c",
"when": "editorTextFocus && gitlens:enabled" "when": "editorTextFocus && gitlens:enabled"
@ -701,17 +740,17 @@
"lodash.isequal": "^4.5.0", "lodash.isequal": "^4.5.0",
"lodash.once": "^4.1.1", "lodash.once": "^4.1.1",
"moment": "^2.17.1", "moment": "^2.17.1",
"spawn-rx": "^2.0.8",
"spawn-rx": "^2.0.10",
"tmp": "^0.0.31" "tmp": "^0.0.31"
}, },
"devDependencies": { "devDependencies": {
"@types/copy-paste": "^1.1.30", "@types/copy-paste": "^1.1.30",
"@types/mocha": "^2.2.39", "@types/mocha": "^2.2.39",
"@types/node": "^7.0.5",
"@types/node": "^7.0.7",
"@types/tmp": "^0.0.32", "@types/tmp": "^0.0.32",
"mocha": "^3.2.0", "mocha": "^3.2.0",
"tslint": "^4.4.2",
"tslint": "^4.5.1",
"typescript": "^2.2.1", "typescript": "^2.2.1",
"vscode": "^1.0.3"
"vscode": "^1.0.5"
} }
} }

+ 59
- 0
src/activeEditorTracker.ts Datei anzeigen

@ -0,0 +1,59 @@
'use strict';
import { commands, Disposable, TextEditor, window } from 'vscode';
export class ActiveEditorTracker extends Disposable {
private _disposable: Disposable;
private _resolver: (value?: TextEditor | PromiseLike<TextEditor>) => void;
constructor() {
super(() => this.dispose());
this._disposable = window.onDidChangeActiveTextEditor(e => this._resolver && this._resolver(e));
}
dispose() {
this._disposable && this._disposable.dispose();
}
async awaitClose(timeout: number = 500): Promise<TextEditor> {
this.close();
return this.wait(timeout);
}
async awaitNext(timeout: number = 500): Promise<TextEditor> {
this.next();
return this.wait(timeout);
}
async close(): Promise<{}> {
return commands.executeCommand('workbench.action.closeActiveEditor');
}
async next(): Promise<{}> {
return commands.executeCommand('workbench.action.nextEditor');
}
async wait(timeout: number = 500): Promise<TextEditor> {
const editor = await new Promise<TextEditor>((resolve, reject) => {
let timer: any;
this._resolver = (editor: TextEditor) => {
if (timer) {
clearTimeout(timer as any);
timer = 0;
resolve(editor);
}
};
timer = setTimeout(() => {
resolve(window.activeTextEditor);
timer = 0;
}, timeout) as any;
});
this._resolver = undefined;
return editor;
}
}

+ 10
- 1
src/blameActiveLineController.ts Datei anzeigen

@ -224,6 +224,9 @@ export class BlameActiveLineController extends Disposable {
} }
async show(commit: GitCommit, blameLine: IGitCommitLine, editor: TextEditor) { async show(commit: GitCommit, blameLine: IGitCommitLine, editor: TextEditor) {
// I have no idea why I need this protection -- but it happens
if (!editor.document) return;
if (this._config.statusBar.enabled) { if (this._config.statusBar.enabled) {
switch (this._config.statusBar.date) { switch (this._config.statusBar.date) {
case 'off': case 'off':
@ -260,9 +263,12 @@ export class BlameActiveLineController extends Disposable {
case StatusBarCommand.ToggleCodeLens: case StatusBarCommand.ToggleCodeLens:
this._statusBarItem.tooltip = 'Toggle Git CodeLens'; this._statusBarItem.tooltip = 'Toggle Git CodeLens';
break; break;
case StatusBarCommand.ShowQuickFileHistory:
case StatusBarCommand.ShowQuickCommitDetails:
this._statusBarItem.tooltip = 'Show Commit Details'; this._statusBarItem.tooltip = 'Show Commit Details';
break; break;
case StatusBarCommand.ShowQuickCommitFileDetails:
this._statusBarItem.tooltip = 'Show Line Commit Details';
break;
case StatusBarCommand.ShowQuickFileHistory: case StatusBarCommand.ShowQuickFileHistory:
this._statusBarItem.tooltip = 'Show File History'; this._statusBarItem.tooltip = 'Show File History';
break; break;
@ -296,6 +302,9 @@ export class BlameActiveLineController extends Disposable {
logCommit = log && log.commits.get(commit.sha); logCommit = log && log.commits.get(commit.sha);
} }
// I have no idea why I need this protection -- but it happens
if (!editor.document) return;
let hoverMessage: string | string[]; let hoverMessage: string | string[];
if (activeLine !== 'inline') { if (activeLine !== 'inline') {
// If the messages match (or we couldn't find the log), then this is a possible duplicate annotation // If the messages match (or we couldn't find the log), then this is a possible duplicate annotation

+ 5
- 1
src/commands.ts Datei anzeigen

@ -1,17 +1,21 @@
'use strict'; 'use strict';
export { Keyboard } from './commands/keyboard'; export { Keyboard } from './commands/keyboard';
export { ActiveEditorCommand, Command, Commands, EditorCommand } from './commands/commands';
export { ActiveEditorCommand, Command, Commands, EditorCommand, openEditor } from './commands/commands';
export { CloseUnchangedFilesCommand } from './commands/closeUnchangedFiles';
export { CopyMessageToClipboardCommand } from './commands/copyMessageToClipboard'; export { CopyMessageToClipboardCommand } from './commands/copyMessageToClipboard';
export { CopyShaToClipboardCommand } from './commands/copyShaToClipboard'; export { CopyShaToClipboardCommand } from './commands/copyShaToClipboard';
export { DiffDirectoryCommand } from './commands/diffDirectory';
export { DiffLineWithPreviousCommand } from './commands/diffLineWithPrevious'; export { DiffLineWithPreviousCommand } from './commands/diffLineWithPrevious';
export { DiffLineWithWorkingCommand } from './commands/diffLineWithWorking'; export { DiffLineWithWorkingCommand } from './commands/diffLineWithWorking';
export { DiffWithPreviousCommand } from './commands/diffWithPrevious'; export { DiffWithPreviousCommand } from './commands/diffWithPrevious';
export { DiffWithWorkingCommand } from './commands/diffWithWorking'; export { DiffWithWorkingCommand } from './commands/diffWithWorking';
export { OpenChangedFilesCommand } from './commands/openChangedFiles';
export { ShowBlameCommand } from './commands/showBlame'; export { ShowBlameCommand } from './commands/showBlame';
export { ShowBlameHistoryCommand } from './commands/showBlameHistory'; export { ShowBlameHistoryCommand } from './commands/showBlameHistory';
export { ShowFileHistoryCommand } from './commands/showFileHistory'; export { ShowFileHistoryCommand } from './commands/showFileHistory';
export { ShowQuickCommitDetailsCommand } from './commands/showQuickCommitDetails'; export { ShowQuickCommitDetailsCommand } from './commands/showQuickCommitDetails';
export { ShowQuickCommitFileDetailsCommand } from './commands/showQuickCommitFileDetails';
export { ShowQuickFileHistoryCommand } from './commands/showQuickFileHistory'; export { ShowQuickFileHistoryCommand } from './commands/showQuickFileHistory';
export { ShowQuickRepoHistoryCommand } from './commands/showQuickRepoHistory'; export { ShowQuickRepoHistoryCommand } from './commands/showQuickRepoHistory';
export { ShowQuickRepoStatusCommand } from './commands/showQuickRepoStatus'; export { ShowQuickRepoStatusCommand } from './commands/showQuickRepoStatus';

+ 70
- 0
src/commands/closeUnchangedFiles.ts Datei anzeigen

@ -0,0 +1,70 @@
'use strict';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorTracker } from '../activeEditorTracker';
import { ActiveEditorCommand, Commands } from './commands';
import { TextEditorComparer, UriComparer } from '../comparers';
import { GitProvider } from '../gitProvider';
import { Logger } from '../logger';
import * as path from 'path';
export class CloseUnchangedFilesCommand extends ActiveEditorCommand {
constructor(private git: GitProvider, public repoPath: string) {
super(Commands.CloseUnchangedFiles);
}
async execute(editor: TextEditor, uri?: Uri, uris?: Uri[]) {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
try {
if (!uris) {
const repoPath = await this.git.getRepoPathFromUri(uri, this.repoPath);
if (!repoPath) return window.showWarningMessage(`Unable to close unchanged files`);
const statuses = await this.git.getStatusesForRepo(repoPath);
if (!statuses) return window.showWarningMessage(`Unable to close unchanged files`);
uris = statuses.map(_ => Uri.file(path.resolve(repoPath, _.fileName)));
}
const editorTracker = new ActiveEditorTracker();
let active = window.activeTextEditor;
let editor = active;
do {
if (editor) {
if ((editor.document && editor.document.isDirty) ||
uris.some(_ => UriComparer.equals(_, editor.document && editor.document.uri))) {
// If we didn't start with a valid editor, set one once we find it
if (!active) {
active = editor;
}
editor = await editorTracker.awaitNext(500);
}
else {
if (active === editor) {
active = undefined;
}
editor = await editorTracker.awaitClose(500);
}
}
else {
if (active === editor) {
active = undefined;
}
editor = await editorTracker.awaitClose(500);
}
} while ((!active && !editor) || !TextEditorComparer.equals(active, editor, { useId: true, usePosition: true }));
editorTracker.dispose();
return undefined;
}
catch (ex) {
Logger.error('[GitLens.CloseUnchangedFilesCommand]', ex);
return window.showErrorMessage(`Unable to close unchanged files. See output channel for more details`);
}
}
}

+ 19
- 2
src/commands/commands.ts Datei anzeigen

@ -1,18 +1,23 @@
'use strict'; 'use strict';
import { commands, Disposable, TextEditor, TextEditorEdit, window } from 'vscode';
import { commands, Disposable, TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
import { BuiltInCommands } from '../constants';
export type Commands = 'gitlens.copyMessageToClipboard' | 'gitlens.copyShaToClipboard' | 'gitlens.diffWithPrevious' | 'gitlens.diffLineWithPrevious' | 'gitlens.diffWithWorking' | 'gitlens.diffLineWithWorking' | 'gitlens.showBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.showQuickCommitDetails' | 'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory' | 'gitlens.showQuickRepoStatus' | 'gitlens.toggleBlame' | 'gitlens.toggleCodeLens';
export type Commands = 'gitlens.closeUnchangedFiles' | 'gitlens.copyMessageToClipboard' | 'gitlens.copyShaToClipboard' | 'gitlens.diffDirectory' | 'gitlens.diffWithPrevious' | 'gitlens.diffLineWithPrevious' | 'gitlens.diffWithWorking' | 'gitlens.diffLineWithWorking' | 'gitlens.openChangedFiles' | 'gitlens.showBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.showQuickCommitDetails' | 'gitlens.showQuickCommitFileDetails' | 'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory' | 'gitlens.showQuickRepoStatus' | 'gitlens.toggleBlame' | 'gitlens.toggleCodeLens';
export const Commands = { export const Commands = {
CloseUnchangedFiles: 'gitlens.closeUnchangedFiles' as Commands,
CopyMessageToClipboard: 'gitlens.copyMessageToClipboard' as Commands, CopyMessageToClipboard: 'gitlens.copyMessageToClipboard' as Commands,
CopyShaToClipboard: 'gitlens.copyShaToClipboard' as Commands, CopyShaToClipboard: 'gitlens.copyShaToClipboard' as Commands,
DiffDirectory: 'gitlens.diffDirectory' as Commands,
DiffWithPrevious: 'gitlens.diffWithPrevious' as Commands, DiffWithPrevious: 'gitlens.diffWithPrevious' as Commands,
DiffLineWithPrevious: 'gitlens.diffLineWithPrevious' as Commands, DiffLineWithPrevious: 'gitlens.diffLineWithPrevious' as Commands,
DiffWithWorking: 'gitlens.diffWithWorking' as Commands, DiffWithWorking: 'gitlens.diffWithWorking' as Commands,
DiffLineWithWorking: 'gitlens.diffLineWithWorking' as Commands, DiffLineWithWorking: 'gitlens.diffLineWithWorking' as Commands,
OpenChangedFiles: 'gitlens.openChangedFiles' as Commands,
ShowBlame: 'gitlens.showBlame' as Commands, ShowBlame: 'gitlens.showBlame' as Commands,
ShowBlameHistory: 'gitlens.showBlameHistory' as Commands, ShowBlameHistory: 'gitlens.showBlameHistory' as Commands,
ShowFileHistory: 'gitlens.showFileHistory' as Commands, ShowFileHistory: 'gitlens.showFileHistory' as Commands,
ShowQuickCommitDetails: 'gitlens.showQuickCommitDetails' as Commands, ShowQuickCommitDetails: 'gitlens.showQuickCommitDetails' as Commands,
ShowQuickCommitFileDetails: 'gitlens.showQuickCommitFileDetails' as Commands,
ShowQuickFileHistory: 'gitlens.showQuickFileHistory' as Commands, ShowQuickFileHistory: 'gitlens.showQuickFileHistory' as Commands,
ShowQuickRepoHistory: 'gitlens.showQuickRepoHistory' as Commands, ShowQuickRepoHistory: 'gitlens.showQuickRepoHistory' as Commands,
ShowQuickRepoStatus: 'gitlens.showQuickRepoStatus' as Commands, ShowQuickRepoStatus: 'gitlens.showQuickRepoStatus' as Commands,
@ -68,4 +73,16 @@ export abstract class ActiveEditorCommand extends Disposable {
} }
abstract execute(editor: TextEditor, ...args: any[]): any; abstract execute(editor: TextEditor, ...args: any[]): any;
}
export async function openEditor(uri: Uri, pinned: boolean = false) {
try {
if (!pinned) return await commands.executeCommand(BuiltInCommands.Open, uri);
const document = await workspace.openTextDocument(uri);
return window.showTextDocument(document, (window.activeTextEditor && window.activeTextEditor.viewColumn) || 1, true);
}
catch (ex) {
return undefined;
}
} }

+ 35
- 0
src/commands/diffDirectory.ts Datei anzeigen

@ -0,0 +1,35 @@
'use strict';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './commands';
import { GitProvider } from '../gitProvider';
import { Logger } from '../logger';
export class DiffDirectoryCommand extends ActiveEditorCommand {
constructor(private git: GitProvider, public repoPath: string) {
super(Commands.DiffDirectory);
}
async execute(editor: TextEditor, uri?: Uri, shaOrBranch1?: string, shaOrBranch2?: string): Promise<any> {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
try {
const repoPath = await this.git.getRepoPathFromUri(uri, this.repoPath);
if (!repoPath) return window.showWarningMessage(`Unable to open directory diff`);
if (!shaOrBranch1) {
//window.showQuickPick()
return undefined;
}
this.git.openDirectoryDiff(repoPath, shaOrBranch1, shaOrBranch2);
return undefined;
}
catch (ex) {
Logger.error('GitLens.DiffDirectoryCommand', ex);
return window.showErrorMessage(`Unable to open directory diff. See output channel for more details`);
}
}
}

+ 41
- 0
src/commands/openChangedFiles.ts Datei anzeigen

@ -0,0 +1,41 @@
'use strict';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, openEditor } from './commands';
import { GitProvider } from '../gitProvider';
import { Logger } from '../logger';
import * as path from 'path';
export class OpenChangedFilesCommand extends ActiveEditorCommand {
constructor(private git: GitProvider, public repoPath: string) {
super(Commands.OpenChangedFiles);
}
async execute(editor: TextEditor, uri?: Uri, uris?: Uri[]) {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
try {
if (!uris) {
const repoPath = await this.git.getRepoPathFromUri(uri, this.repoPath);
if (!repoPath) return window.showWarningMessage(`Unable to open changed files`);
const statuses = await this.git.getStatusesForRepo(repoPath);
if (!statuses) return window.showWarningMessage(`Unable to open changed files`);
uris = statuses.filter(_ => _.status !== 'D').map(_ => Uri.file(path.resolve(repoPath, _.fileName)));
}
for (const uri of uris) {
await openEditor(uri, true);
}
return undefined;
}
catch (ex) {
Logger.error('[GitLens.OpenChangedFilesCommand]', ex);
return window.showErrorMessage(`Unable to open changed files. See output channel for more details`);
}
}
}

+ 20
- 49
src/commands/showQuickCommitDetails.ts Datei anzeigen

@ -4,7 +4,7 @@ import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './commands'; import { ActiveEditorCommand, Commands } from './commands';
import { GitCommit, GitLogCommit, GitProvider, GitUri } from '../gitProvider'; import { GitCommit, GitLogCommit, GitProvider, GitUri } from '../gitProvider';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { CommandQuickPickItem, CommitFileDetailsQuickPick, CommitDetailsQuickPick, CommitWithFileStatusQuickPickItem } from '../quickPicks';
import { CommandQuickPickItem, CommitDetailsQuickPick, CommitWithFileStatusQuickPickItem } from '../quickPicks';
export class ShowQuickCommitDetailsCommand extends ActiveEditorCommand { export class ShowQuickCommitDetailsCommand extends ActiveEditorCommand {
@ -12,7 +12,7 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCommand {
super(Commands.ShowQuickCommitDetails); super(Commands.ShowQuickCommitDetails);
} }
async execute(editor: TextEditor, uri?: Uri, sha?: string, commit?: GitCommit, goBackCommand?: CommandQuickPickItem, options: { showFileHistory?: boolean } = { showFileHistory: true }) {
async execute(editor: TextEditor, uri?: Uri, sha?: string, commit?: GitCommit | GitLogCommit, goBackCommand?: CommandQuickPickItem) {
if (!(uri instanceof Uri)) { if (!(uri instanceof Uri)) {
if (!editor || !editor.document) return undefined; if (!editor || !editor.document) return undefined;
uri = editor.document.uri; uri = editor.document.uri;
@ -35,72 +35,43 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCommand {
sha = blame.commit.isUncommitted ? blame.commit.previousSha : blame.commit.sha; sha = blame.commit.isUncommitted ? blame.commit.previousSha : blame.commit.sha;
repoPath = blame.commit.repoPath; repoPath = blame.commit.repoPath;
return commands.executeCommand(Commands.ShowQuickFileHistory, uri, undefined, blame.commit);
commit = blame.commit;
} }
catch (ex) { catch (ex) {
Logger.error('[GitLens.ShowQuickCommitDetails]', `getBlameForLine(${blameline})`, ex);
Logger.error('[GitLens.ShowQuickCommitDetailsCommand]', `getBlameForLine(${blameline})`, ex);
return window.showErrorMessage(`Unable to show commit details. See output channel for more details`); return window.showErrorMessage(`Unable to show commit details. See output channel for more details`);
} }
} }
try { try {
let pick: CommitWithFileStatusQuickPickItem | CommandQuickPickItem;
let alreadyPickedCommit = !!commit;
let workingFileName: string;
if (!alreadyPickedCommit) {
let log = await this.git.getLogForRepo(repoPath, sha, 0);
if (!commit || !(commit instanceof GitLogCommit) || commit.type !== 'repo') {
let log = await this.git.getLogForRepo(repoPath, sha, 2);
if (!log) return window.showWarningMessage(`Unable to show commit details`); if (!log) return window.showWarningMessage(`Unable to show commit details`);
commit = Iterables.first(log.commits.values()); commit = Iterables.first(log.commits.values());
pick = await CommitDetailsQuickPick.show(commit as GitLogCommit, uri, goBackCommand);
if (!pick) return undefined;
if (!(pick instanceof CommitWithFileStatusQuickPickItem)) {
return pick.execute();
}
// Attempt to the most recent commit -- so that we can find the real working filename if there was a rename
const workingCommit = await this.git.findMostRecentCommitForFile(pick.uri.fsPath, pick.sha);
// TODO: Leave this at undefined until findMostRecentCommitForFile actually works
workingFileName = !workingCommit ? pick.fileName : undefined;
log = await this.git.getLogForFile(pick.gitUri.fsPath, pick.sha, undefined, undefined, 2);
if (!log) return window.showWarningMessage(`Unable to show commit details`);
commit = Iterables.find(log.commits.values(), c => c.sha === commit.sha);
uri = pick.gitUri || uri;
}
else {
// Attempt to the most recent commit -- so that we can find the real working filename if there was a rename
const workingCommit = await this.git.findMostRecentCommitForFile(commit.uri.fsPath, commit.sha);
// TODO: Leave this at undefined until findMostRecentCommitForFile actually works
workingFileName = !workingCommit ? commit.fileName : undefined;
} }
pick = await CommitFileDetailsQuickPick.show(this.git, commit, workingFileName, uri,
// Create a command to get back to where we are right now
new CommandQuickPickItem({
if (!goBackCommand) {
// Create a command to get back to the commit details
goBackCommand = new CommandQuickPickItem({
label: `go back \u21A9`, label: `go back \u21A9`,
description: null
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), sha, commit, goBackCommand, options]),
// If we have already picked a commit, just jump back to the previous (since we skipped a quickpick menu)
// Otherwise setup a normal back command
alreadyPickedCommit
? goBackCommand
: new CommandQuickPickItem({
label: `go back \u21A9`,
description: null
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), sha, undefined, goBackCommand, options]),
{ showFileHistory: options.showFileHistory });
description: `\u00a0 \u2014 \u00a0\u00a0 to repository history`
}, Commands.ShowQuickRepoHistory, [new GitUri(commit.uri, commit)]);
}
const pick = await CommitDetailsQuickPick.show(commit as GitLogCommit, uri, goBackCommand);
if (!pick) return undefined; if (!pick) return undefined;
if (pick instanceof CommandQuickPickItem) {
if (!(pick instanceof CommitWithFileStatusQuickPickItem)) {
return pick.execute(); return pick.execute();
} }
return undefined;
return commands.executeCommand(Commands.ShowQuickCommitFileDetails, pick.gitUri, pick.sha, undefined,
// Create a command to get back to where we are right now
new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to details of \u00a0$(git-commit) ${pick.sha}`
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), sha, commit, goBackCommand]));
} }
catch (ex) { catch (ex) {
Logger.error('[GitLens.ShowQuickCommitDetailsCommand]', ex); Logger.error('[GitLens.ShowQuickCommitDetailsCommand]', ex);

+ 89
- 0
src/commands/showQuickCommitFileDetails.ts Datei anzeigen

@ -0,0 +1,89 @@
'use strict';
import { Iterables } from '../system';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './commands';
import { GitCommit, GitLogCommit, GitProvider, GitUri } from '../gitProvider';
import { Logger } from '../logger';
import { CommandQuickPickItem, CommitFileDetailsQuickPick } from '../quickPicks';
import * as path from 'path';
export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCommand {
constructor(private git: GitProvider) {
super(Commands.ShowQuickCommitFileDetails);
}
async execute(editor: TextEditor, uri?: Uri, sha?: string, commit?: GitCommit | GitLogCommit, goBackCommand?: CommandQuickPickItem, options: { showFileHistory?: boolean } = { showFileHistory: true }) {
if (!(uri instanceof Uri)) {
if (!editor || !editor.document) return undefined;
uri = editor.document.uri;
}
const gitUri = await GitUri.fromUri(uri, this.git);
let repoPath = gitUri.repoPath;
if (!sha) {
if (!editor) return undefined;
const blameline = editor.selection.active.line - gitUri.offset;
if (blameline < 0) return undefined;
try {
const blame = await this.git.getBlameForLine(gitUri.fsPath, blameline, gitUri.sha, gitUri.repoPath);
if (!blame) return window.showWarningMessage(`Unable to show commit file details. File is probably not under source control`);
sha = blame.commit.isUncommitted ? blame.commit.previousSha : blame.commit.sha;
repoPath = blame.commit.repoPath;
commit = blame.commit;
}
catch (ex) {
Logger.error('[GitLens.ShowQuickCommitFileDetailsCommand]', `getBlameForLine(${blameline})`, ex);
return window.showErrorMessage(`Unable to show commit file details. See output channel for more details`);
}
}
try {
if (!commit || ((commit instanceof GitLogCommit) && commit.type !== 'file')) {
let log = await this.git.getLogForFile(uri.fsPath, sha, undefined, undefined, 2);
if (!log) return window.showWarningMessage(`Unable to show commit file details`);
commit = Iterables.find(log.commits.values(), c => c.sha === sha);
}
// Attempt to the most recent commit -- so that we can find the real working filename if there was a rename
const workingCommit = await this.git.findMostRecentCommitForFile(commit.uri.fsPath, commit.sha);
// TODO: Leave this at undefined until findMostRecentCommitForFile actually works
const workingFileName = !workingCommit ? commit.fileName : undefined;
if (!goBackCommand) {
// Create a command to get back to the commit details
goBackCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to details of \u00a0$(git-commit) ${sha}`
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), sha, commit]);
}
const pick = await CommitFileDetailsQuickPick.show(this.git, commit, workingFileName, uri, goBackCommand,
// Create a command to get back to where we are right now
new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to details of \u00a0$(file-text) ${path.basename(commit.fileName)} in \u00a0$(git-commit) ${sha}`
}, Commands.ShowQuickCommitFileDetails, [new GitUri(commit.uri, commit), sha, commit, goBackCommand, options]),
{ showFileHistory: options.showFileHistory });
if (!pick) return undefined;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
return undefined;
}
catch (ex) {
Logger.error('[GitLens.ShowQuickCommitFileDetailsCommand]', ex);
return window.showErrorMessage(`Unable to show commit file details. See output channel for more details`);
}
}
}

+ 12
- 17
src/commands/showQuickFileHistory.ts Datei anzeigen

@ -1,9 +1,10 @@
'use strict'; 'use strict';
import { commands, TextEditor, Uri, window } from 'vscode'; import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './commands'; import { ActiveEditorCommand, Commands } from './commands';
import { GitCommit, GitProvider, GitUri } from '../gitProvider';
import { GitProvider, GitUri } from '../gitProvider';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { CommandQuickPickItem, FileHistoryQuickPick } from '../quickPicks'; import { CommandQuickPickItem, FileHistoryQuickPick } from '../quickPicks';
import * as path from 'path';
export class ShowQuickFileHistoryCommand extends ActiveEditorCommand { export class ShowQuickFileHistoryCommand extends ActiveEditorCommand {
@ -11,7 +12,7 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCommand {
super(Commands.ShowQuickFileHistory); super(Commands.ShowQuickFileHistory);
} }
async execute(editor: TextEditor, uri?: Uri, maxCount?: number, commit?: GitCommit, goBackCommand?: CommandQuickPickItem) {
async execute(editor: TextEditor, uri?: Uri, maxCount?: number, goBackCommand?: CommandQuickPickItem) {
if (!(uri instanceof Uri)) { if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri; uri = editor && editor.document && editor.document.uri;
} }
@ -27,27 +28,21 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCommand {
} }
try { try {
if (!commit) {
const log = await this.git.getLogForFile(gitUri.fsPath, gitUri.sha, gitUri.repoPath, undefined, maxCount);
if (!log) return window.showWarningMessage(`Unable to show file history. File is probably not under source control`);
const log = await this.git.getLogForFile(gitUri.fsPath, gitUri.sha, gitUri.repoPath, undefined, maxCount);
if (!log) return window.showWarningMessage(`Unable to show file history. File is probably not under source control`);
let pick = await FileHistoryQuickPick.show(log, uri, maxCount, this.git.config.advanced.maxQuickHistory, goBackCommand);
if (!pick) return undefined;
let pick = await FileHistoryQuickPick.show(log, uri, gitUri.sha, maxCount, this.git.config.advanced.maxQuickHistory, goBackCommand);
if (!pick) return undefined;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
commit = pick.commit;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
} }
return commands.executeCommand(Commands.ShowQuickCommitDetails,
new GitUri(commit.uri, commit),
commit.sha, commit,
return commands.executeCommand(Commands.ShowQuickCommitFileDetails, new GitUri(pick.commit.uri, pick.commit), pick.commit.sha, pick.commit,
new CommandQuickPickItem({ new CommandQuickPickItem({
label: `go back \u21A9`, label: `go back \u21A9`,
description: null
}, Commands.ShowQuickFileHistory, [uri, maxCount, undefined, goBackCommand]),
description: `\u00a0 \u2014 \u00a0\u00a0 to history of \u00a0$(file-text) ${path.basename(pick.commit.fileName)}`
}, Commands.ShowQuickFileHistory, [uri, maxCount, goBackCommand]),
{ showFileHistory: false }); { showFileHistory: false });
} }
catch (ex) { catch (ex) {

+ 12
- 30
src/commands/showQuickRepoHistory.ts Datei anzeigen

@ -1,7 +1,7 @@
'use strict'; 'use strict';
import { commands, TextEditor, Uri, window } from 'vscode'; import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './commands'; import { ActiveEditorCommand, Commands } from './commands';
import { GitCommit, GitProvider, GitUri } from '../gitProvider';
import { GitProvider, GitUri } from '../gitProvider';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { CommandQuickPickItem, RepoHistoryQuickPick } from '../quickPicks'; import { CommandQuickPickItem, RepoHistoryQuickPick } from '../quickPicks';
@ -11,7 +11,7 @@ export class ShowQuickRepoHistoryCommand extends ActiveEditorCommand {
super(Commands.ShowQuickRepoHistory); super(Commands.ShowQuickRepoHistory);
} }
async execute(editor: TextEditor, uri?: Uri, maxCount?: number, commit?: GitCommit, goBackCommand?: CommandQuickPickItem) {
async execute(editor: TextEditor, uri?: Uri, maxCount?: number, goBackCommand?: CommandQuickPickItem) {
if (!(uri instanceof Uri)) { if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri; uri = editor && editor.document && editor.document.uri;
} }
@ -21,42 +21,24 @@ export class ShowQuickRepoHistoryCommand extends ActiveEditorCommand {
} }
try { try {
let repoPath: string;
if (uri instanceof Uri) {
const gitUri = await GitUri.fromUri(uri, this.git);
repoPath = gitUri.repoPath;
if (!repoPath) {
repoPath = await this.git.getRepoPathFromFile(gitUri.fsPath);
}
}
if (!repoPath) {
repoPath = this.repoPath;
}
const repoPath = await this.git.getRepoPathFromUri(uri, this.repoPath);
if (!repoPath) return window.showWarningMessage(`Unable to show repository history`); if (!repoPath) return window.showWarningMessage(`Unable to show repository history`);
if (!commit) {
const log = await this.git.getLogForRepo(repoPath, undefined, maxCount);
if (!log) return window.showWarningMessage(`Unable to show repository history`);
const pick = await RepoHistoryQuickPick.show(log, uri, maxCount, this.git.config.advanced.maxQuickHistory, goBackCommand);
if (!pick) return undefined;
const log = await this.git.getLogForRepo(repoPath, undefined, maxCount);
if (!log) return window.showWarningMessage(`Unable to show repository history`);
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
const pick = await RepoHistoryQuickPick.show(log, uri, maxCount, this.git.config.advanced.maxQuickHistory, goBackCommand);
if (!pick) return undefined;
commit = pick.commit;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
} }
return commands.executeCommand(Commands.ShowQuickCommitDetails,
new GitUri(commit.uri, commit),
commit.sha, undefined,
return commands.executeCommand(Commands.ShowQuickCommitDetails, new GitUri(pick.commit.uri, pick.commit), pick.commit.sha, pick.commit,
new CommandQuickPickItem({ new CommandQuickPickItem({
label: `go back \u21A9`, label: `go back \u21A9`,
description: null
}, Commands.ShowQuickRepoHistory, [uri, maxCount, undefined, goBackCommand]));
description: `\u00a0 \u2014 \u00a0\u00a0 to repository history`
}, Commands.ShowQuickRepoHistory, [uri, maxCount, goBackCommand]));
} }
catch (ex) { catch (ex) {
Logger.error('[GitLens.ShowQuickRepoHistoryCommand]', ex); Logger.error('[GitLens.ShowQuickRepoHistoryCommand]', ex);

+ 2
- 14
src/commands/showQuickRepoStatus.ts Datei anzeigen

@ -1,7 +1,7 @@
'use strict'; 'use strict';
import { TextEditor, Uri, window } from 'vscode'; import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './commands'; import { ActiveEditorCommand, Commands } from './commands';
import { GitProvider, GitUri } from '../gitProvider';
import { GitProvider } from '../gitProvider';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { CommandQuickPickItem, RepoStatusQuickPick } from '../quickPicks'; import { CommandQuickPickItem, RepoStatusQuickPick } from '../quickPicks';
@ -17,19 +17,7 @@ export class ShowQuickRepoStatusCommand extends ActiveEditorCommand {
} }
try { try {
let repoPath: string;
if (uri instanceof Uri) {
const gitUri = await GitUri.fromUri(uri, this.git);
repoPath = gitUri.repoPath;
if (!repoPath) {
repoPath = await this.git.getRepoPathFromFile(gitUri.fsPath);
}
}
if (!repoPath) {
repoPath = this.repoPath;
}
const repoPath = await this.git.getRepoPathFromUri(uri, this.repoPath);
if (!repoPath) return window.showWarningMessage(`Unable to show repository status`); if (!repoPath) return window.showWarningMessage(`Unable to show repository status`);
const statuses = await this.git.getStatusesForRepo(repoPath); const statuses = await this.git.getStatusesForRepo(repoPath);

+ 9
- 1
src/comparers.ts Datei anzeigen

@ -27,10 +27,18 @@ class TextDocumentComparer extends Comparer {
class TextEditorComparer extends Comparer<TextEditor> { class TextEditorComparer extends Comparer<TextEditor> {
equals(lhs: TextEditor, rhs: TextEditor) {
equals(lhs: TextEditor, rhs: TextEditor, options: { useId: boolean, usePosition: boolean } = { useId: false, usePosition: false }) {
if (!lhs && !rhs) return true; if (!lhs && !rhs) return true;
if ((lhs && !rhs) || (!lhs && rhs)) return false; if ((lhs && !rhs) || (!lhs && rhs)) return false;
if (options.usePosition && (lhs.viewColumn !== rhs.viewColumn)) return false;
if (options.useId && (!lhs.document || !rhs.document)) {
if ((lhs as any)._id !== (rhs as any)._id) return false;
return true;
}
return textDocumentComparer.equals(lhs.document, rhs.document); return textDocumentComparer.equals(lhs.document, rhs.document);
} }
} }

+ 4
- 2
src/configuration.ts Datei anzeigen

@ -28,13 +28,14 @@ export interface IBlameConfig {
}; };
} }
export type CodeLensCommand = 'gitlens.toggleBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.diffWithPrevious' | 'gitlens.showQuickCommitDetails' | 'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory';
export type CodeLensCommand = 'gitlens.toggleBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.diffWithPrevious' | 'gitlens.showQuickCommitDetails' | 'gitlens.showQuickCommitFileDetails' | 'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory';
export const CodeLensCommand = { export const CodeLensCommand = {
BlameAnnotate: Commands.ToggleBlame as CodeLensCommand, BlameAnnotate: Commands.ToggleBlame as CodeLensCommand,
ShowBlameHistory: Commands.ShowBlameHistory as CodeLensCommand, ShowBlameHistory: Commands.ShowBlameHistory as CodeLensCommand,
ShowFileHistory: Commands.ShowFileHistory as CodeLensCommand, ShowFileHistory: Commands.ShowFileHistory as CodeLensCommand,
DiffWithPrevious: Commands.DiffWithPrevious as CodeLensCommand, DiffWithPrevious: Commands.DiffWithPrevious as CodeLensCommand,
ShowQuickCommitDetails: Commands.ShowQuickCommitDetails as CodeLensCommand, ShowQuickCommitDetails: Commands.ShowQuickCommitDetails as CodeLensCommand,
ShowQuickCommitFileDetails: Commands.ShowQuickCommitFileDetails as CodeLensCommand,
ShowQuickFileHistory: Commands.ShowQuickFileHistory as CodeLensCommand, ShowQuickFileHistory: Commands.ShowQuickFileHistory as CodeLensCommand,
ShowQuickRepoHistory: Commands.ShowQuickRepoHistory as CodeLensCommand ShowQuickRepoHistory: Commands.ShowQuickRepoHistory as CodeLensCommand
}; };
@ -75,7 +76,7 @@ export interface ICodeLensesConfig {
authors: ICodeLensConfig; authors: ICodeLensConfig;
} }
export type StatusBarCommand = 'gitlens.toggleBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.toggleCodeLens' | 'gitlens.diffWithPrevious' | 'gitlens.showQuickCommitDetails' | 'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory';
export type StatusBarCommand = 'gitlens.toggleBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.toggleCodeLens' | 'gitlens.diffWithPrevious' | 'gitlens.showQuickCommitDetails' | 'gitlens.showQuickCommitFileDetails' | 'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory';
export const StatusBarCommand = { export const StatusBarCommand = {
BlameAnnotate: Commands.ToggleBlame as StatusBarCommand, BlameAnnotate: Commands.ToggleBlame as StatusBarCommand,
ShowBlameHistory: Commands.ShowBlameHistory as StatusBarCommand, ShowBlameHistory: Commands.ShowBlameHistory as StatusBarCommand,
@ -83,6 +84,7 @@ export const StatusBarCommand = {
DiffWithPrevious: Commands.DiffWithPrevious as StatusBarCommand, DiffWithPrevious: Commands.DiffWithPrevious as StatusBarCommand,
ToggleCodeLens: Commands.ToggleCodeLens as StatusBarCommand, ToggleCodeLens: Commands.ToggleCodeLens as StatusBarCommand,
ShowQuickCommitDetails: Commands.ShowQuickCommitDetails as StatusBarCommand, ShowQuickCommitDetails: Commands.ShowQuickCommitDetails as StatusBarCommand,
ShowQuickCommitFileDetails: Commands.ShowQuickCommitFileDetails as StatusBarCommand,
ShowQuickFileHistory: Commands.ShowQuickFileHistory as StatusBarCommand, ShowQuickFileHistory: Commands.ShowQuickFileHistory as StatusBarCommand,
ShowQuickRepoHistory: Commands.ShowQuickRepoHistory as StatusBarCommand ShowQuickRepoHistory: Commands.ShowQuickRepoHistory as StatusBarCommand
}; };

+ 15
- 3
src/extension.ts Datei anzeigen

@ -4,11 +4,12 @@ import { BlameabilityTracker } from './blameabilityTracker';
import { BlameActiveLineController } from './blameActiveLineController'; import { BlameActiveLineController } from './blameActiveLineController';
import { BlameAnnotationController } from './blameAnnotationController'; import { BlameAnnotationController } from './blameAnnotationController';
import { configureCssCharacters } from './blameAnnotationFormatter'; import { configureCssCharacters } from './blameAnnotationFormatter';
import { CloseUnchangedFilesCommand, OpenChangedFilesCommand } from './commands';
import { CopyMessageToClipboardCommand, CopyShaToClipboardCommand } from './commands'; import { CopyMessageToClipboardCommand, CopyShaToClipboardCommand } from './commands';
import { DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithPreviousCommand, DiffWithWorkingCommand} from './commands';
import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithPreviousCommand, DiffWithWorkingCommand} from './commands';
import { ShowBlameCommand, ToggleBlameCommand } from './commands'; import { ShowBlameCommand, ToggleBlameCommand } from './commands';
import { ShowBlameHistoryCommand, ShowFileHistoryCommand } from './commands'; import { ShowBlameHistoryCommand, ShowFileHistoryCommand } from './commands';
import { ShowQuickCommitDetailsCommand, ShowQuickFileHistoryCommand, ShowQuickRepoHistoryCommand, ShowQuickRepoStatusCommand} from './commands';
import { ShowQuickCommitDetailsCommand, ShowQuickCommitFileDetailsCommand, ShowQuickFileHistoryCommand, ShowQuickRepoHistoryCommand, ShowQuickRepoStatusCommand} from './commands';
import { ToggleCodeLensCommand } from './commands'; import { ToggleCodeLensCommand } from './commands';
import { Keyboard } from './commands'; import { Keyboard } from './commands';
import { IAdvancedConfig, IBlameConfig } from './configuration'; import { IAdvancedConfig, IBlameConfig } from './configuration';
@ -44,12 +45,19 @@ export async function activate(context: ExtensionContext) {
catch (ex) { catch (ex) {
Logger.error(ex); Logger.error(ex);
if (ex.message.includes('Unable to find git')) { if (ex.message.includes('Unable to find git')) {
await window.showErrorMessage(`GitLens: Unable to find Git. Please make sure Git is installed. Also ensure that Git is either in the PATH, or that 'gitlens.advanced.git' is pointed to its installed location.`);
await window.showErrorMessage(`GitLens was unable to find Git. Please make sure Git is installed. Also ensure that Git is either in the PATH, or that 'gitlens.advanced.git' is pointed to its installed location.`);
} }
commands.executeCommand(BuiltInCommands.SetContext, 'gitlens:enabled', false); commands.executeCommand(BuiltInCommands.SetContext, 'gitlens:enabled', false);
return; return;
} }
const version = Git.gitInfo().version;
const [major, minor] = version.split('.');
// If git is less than v2.2.0
if (parseInt(major, 10) < 2 || parseInt(minor, 10) < 2) {
await window.showErrorMessage(`GitLens requires a newer version of Git (>= 2.2.0) than is currently installed (${version}). Please install a more recent version of Git.`);
}
let gitEnabled = workspace.getConfiguration('git').get<boolean>('enabled'); let gitEnabled = workspace.getConfiguration('git').get<boolean>('enabled');
commands.executeCommand(BuiltInCommands.SetContext, 'gitlens:enabled', gitEnabled); commands.executeCommand(BuiltInCommands.SetContext, 'gitlens:enabled', gitEnabled);
context.subscriptions.push(workspace.onDidChangeConfiguration(() => { context.subscriptions.push(workspace.onDidChangeConfiguration(() => {
@ -79,8 +87,11 @@ export async function activate(context: ExtensionContext) {
context.subscriptions.push(new Keyboard(context)); context.subscriptions.push(new Keyboard(context));
context.subscriptions.push(new CloseUnchangedFilesCommand(git, repoPath));
context.subscriptions.push(new OpenChangedFilesCommand(git, repoPath));
context.subscriptions.push(new CopyMessageToClipboardCommand(git, repoPath)); context.subscriptions.push(new CopyMessageToClipboardCommand(git, repoPath));
context.subscriptions.push(new CopyShaToClipboardCommand(git, repoPath)); context.subscriptions.push(new CopyShaToClipboardCommand(git, repoPath));
context.subscriptions.push(new DiffDirectoryCommand(git, repoPath));
context.subscriptions.push(new DiffWithWorkingCommand(git)); context.subscriptions.push(new DiffWithWorkingCommand(git));
context.subscriptions.push(new DiffLineWithWorkingCommand(git)); context.subscriptions.push(new DiffLineWithWorkingCommand(git));
context.subscriptions.push(new DiffWithPreviousCommand(git)); context.subscriptions.push(new DiffWithPreviousCommand(git));
@ -90,6 +101,7 @@ export async function activate(context: ExtensionContext) {
context.subscriptions.push(new ShowBlameHistoryCommand(git)); context.subscriptions.push(new ShowBlameHistoryCommand(git));
context.subscriptions.push(new ShowFileHistoryCommand(git)); context.subscriptions.push(new ShowFileHistoryCommand(git));
context.subscriptions.push(new ShowQuickCommitDetailsCommand(git)); context.subscriptions.push(new ShowQuickCommitDetailsCommand(git));
context.subscriptions.push(new ShowQuickCommitFileDetailsCommand(git));
context.subscriptions.push(new ShowQuickFileHistoryCommand(git)); context.subscriptions.push(new ShowQuickFileHistoryCommand(git));
context.subscriptions.push(new ShowQuickRepoHistoryCommand(git, repoPath)); context.subscriptions.push(new ShowQuickRepoHistoryCommand(git, repoPath));
context.subscriptions.push(new ShowQuickRepoStatusCommand(git, repoPath)); context.subscriptions.push(new ShowQuickRepoStatusCommand(git, repoPath));

+ 3
- 3
src/git/enrichers/logParserEnricher.ts Datei anzeigen

@ -1,5 +1,5 @@
'use strict'; 'use strict';
import Git, { GitFileStatus, GitLogCommit, IGitAuthor, IGitEnricher, IGitLog } from './../git';
import Git, { GitFileStatus, GitLogCommit, GitLogType, IGitAuthor, IGitEnricher, IGitLog } from './../git';
import * as moment from 'moment'; import * as moment from 'moment';
import * as path from 'path'; import * as path from 'path';
@ -149,7 +149,7 @@ export class GitLogParserEnricher implements IGitEnricher {
return entries; return entries;
} }
enrich(data: string, type: 'file' | 'repo', fileNameOrRepoPath: string, isRepoPath: boolean = false): IGitLog {
enrich(data: string, type: GitLogType, fileNameOrRepoPath: string, isRepoPath: boolean = false): IGitLog {
const entries = this._parseEntries(data, isRepoPath); const entries = this._parseEntries(data, isRepoPath);
if (!entries) return undefined; if (!entries) return undefined;
@ -189,7 +189,7 @@ export class GitLogParserEnricher implements IGitEnricher {
authors.set(entry.author, author); authors.set(entry.author, author);
} }
commit = new GitLogCommit(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, moment(entry.authorDate).toDate(), entry.summary, entry.status, entry.fileStatuses, undefined, entry.originalFileName);
if (relativeFileName !== entry.fileName) { if (relativeFileName !== entry.fileName) {
commit.originalFileName = entry.fileName; commit.originalFileName = entry.fileName;

+ 47
- 34
src/git/git.ts Datei anzeigen

@ -91,8 +91,17 @@ export default class Git {
return gitCommand(root, ...params, `--`, file); return gitCommand(root, ...params, `--`, file);
} }
static diffDir(repoPath: string, sha1: string, sha2?: string) {
const params = [`difftool`, `--dir-diff`, sha1];
if (sha2) {
params.push(sha2);
}
return gitCommand(repoPath, ...params);
}
static diffStatus(repoPath: string, sha1?: string, sha2?: string) { static diffStatus(repoPath: string, sha1?: string, sha2?: string) {
const params = [`diff`, `--name- status`, `-M`];
const params = [`diff`, `--name-status`, `-M`];
if (sha1) { if (sha1) {
params.push(sha1); params.push(sha1);
} }
@ -103,6 +112,43 @@ export default class Git {
return gitCommand(repoPath, ...params); return gitCommand(repoPath, ...params);
} }
static async getVersionedFile(fileName: string, repoPath: string, sha: string) {
const data = await Git.getVersionedFileText(fileName, repoPath, sha);
const ext = path.extname(fileName);
return new Promise<string>((resolve, reject) => {
tmp.file({ prefix: `${path.basename(fileName, ext)}-${sha}__`, postfix: ext },
(err, destination, fd, cleanupCallback) => {
if (err) {
reject(err);
return;
}
Logger.log(`getVersionedFile(${fileName}, ${repoPath}, ${sha}); destination=${destination}`);
fs.appendFile(destination, data, err => {
if (err) {
reject(err);
return;
}
resolve(destination);
});
});
});
}
static getVersionedFileText(fileName: string, repoPath: string, sha: string) {
const [file, root] = Git.splitPath(Git.normalizePath(fileName), repoPath);
sha = sha.replace('^', '');
if (Git.isUncommitted(sha)) return Promise.reject(new Error(`sha=${sha} is uncommitted`));
return gitCommand(root, 'show', `${sha}:./${file}`);
}
static gitInfo(): IGit {
return git;
}
static log(fileName: string, sha?: string, repoPath?: string, maxCount?: number, reverse: boolean = false) { static log(fileName: string, sha?: string, repoPath?: string, maxCount?: number, reverse: boolean = false) {
const [file, root]: [string, string] = Git.splitPath(Git.normalizePath(fileName), repoPath); const [file, root]: [string, string] = Git.splitPath(Git.normalizePath(fileName), repoPath);
@ -150,39 +196,6 @@ export default class Git {
return gitCommand(repoPath, ...params); return gitCommand(repoPath, ...params);
} }
static async getVersionedFile(fileName: string, repoPath: string, sha: string) {
const data = await Git.getVersionedFileText(fileName, repoPath, sha);
const ext = path.extname(fileName);
return new Promise<string>((resolve, reject) => {
tmp.file({ prefix: `${path.basename(fileName, ext)}-${sha}__`, postfix: ext },
(err, destination, fd, cleanupCallback) => {
if (err) {
reject(err);
return;
}
Logger.log(`getVersionedFile(${fileName}, ${repoPath}, ${sha}); destination=${destination}`);
fs.appendFile(destination, data, err => {
if (err) {
reject(err);
return;
}
resolve(destination);
});
});
});
}
static getVersionedFileText(fileName: string, repoPath: string, sha: string) {
const [file, root] = Git.splitPath(Git.normalizePath(fileName), repoPath);
sha = sha.replace('^', '');
if (Git.isUncommitted(sha)) return Promise.reject(new Error(`sha=${sha} is uncommitted`));
return gitCommand(root, 'show', `${sha}:./${file}`);
}
static statusFile(fileName: string, repoPath: string): Promise<string> { static statusFile(fileName: string, repoPath: string): Promise<string> {
const [file, root]: [string, string] = Git.splitPath(Git.normalizePath(fileName), repoPath); const [file, root]: [string, string] = Git.splitPath(Git.normalizePath(fileName), repoPath);

+ 4
- 1
src/git/gitEnrichment.ts Datei anzeigen

@ -103,12 +103,15 @@ export class GitCommit implements IGitCommit {
} }
} }
export type GitLogType = 'file' | 'repo';
export class GitLogCommit extends GitCommit { export class GitLogCommit extends GitCommit {
fileStatuses: { status: GitFileStatus, fileName: string }[]; fileStatuses: { status: GitFileStatus, fileName: string }[];
status: GitFileStatus; status: GitFileStatus;
constructor( constructor(
public type: GitLogType,
repoPath: string, repoPath: string,
sha: string, sha: string,
fileName: string, fileName: string,
@ -145,7 +148,7 @@ export interface IGitCommitLine {
export interface IGitLog { export interface IGitLog {
repoPath: string; repoPath: string;
authors: Map<string, IGitAuthor>; authors: Map<string, IGitAuthor>;
commits: Map<string, GitCommit>;
commits: Map<string, GitLogCommit>;
} }
export declare type GitFileStatus = '?' | 'A' | 'C' | 'D' | 'M' | 'R' | 'U'; export declare type GitFileStatus = '?' | 'A' | 'C' | 'D' | 'M' | 'R' | 'U';

+ 13
- 2
src/gitCodeLensProvider.ts Datei anzeigen

@ -271,6 +271,7 @@ export default class GitCodeLensProvider implements CodeLensProvider {
case CodeLensCommand.ShowFileHistory: return this._applyShowFileHistoryCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit); case CodeLensCommand.ShowFileHistory: return this._applyShowFileHistoryCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
case CodeLensCommand.DiffWithPrevious: return this._applyDiffWithPreviousCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit); case CodeLensCommand.DiffWithPrevious: return this._applyDiffWithPreviousCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
case CodeLensCommand.ShowQuickCommitDetails: return this._applyShowQuickCommitDetailsCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit); case CodeLensCommand.ShowQuickCommitDetails: return this._applyShowQuickCommitDetailsCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
case CodeLensCommand.ShowQuickCommitFileDetails: return this._applyShowQuickCommitFileDetailsCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
case CodeLensCommand.ShowQuickFileHistory: return this._applyShowQuickFileHistoryCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit); case CodeLensCommand.ShowQuickFileHistory: return this._applyShowQuickFileHistoryCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
case CodeLensCommand.ShowQuickRepoHistory: return this._applyShowQuickRepoHistoryCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit); case CodeLensCommand.ShowQuickRepoHistory: return this._applyShowQuickRepoHistoryCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
default: return lens; default: return lens;
@ -291,6 +292,7 @@ export default class GitCodeLensProvider implements CodeLensProvider {
case CodeLensCommand.ShowFileHistory: return this._applyShowFileHistoryCommand<GitAuthorsCodeLens>(title, lens, blame); case CodeLensCommand.ShowFileHistory: return this._applyShowFileHistoryCommand<GitAuthorsCodeLens>(title, lens, blame);
case CodeLensCommand.DiffWithPrevious: return this._applyDiffWithPreviousCommand<GitAuthorsCodeLens>(title, lens, blame); case CodeLensCommand.DiffWithPrevious: return this._applyDiffWithPreviousCommand<GitAuthorsCodeLens>(title, lens, blame);
case CodeLensCommand.ShowQuickCommitDetails: return this._applyShowQuickCommitDetailsCommand<GitAuthorsCodeLens>(title, lens, blame); case CodeLensCommand.ShowQuickCommitDetails: return this._applyShowQuickCommitDetailsCommand<GitAuthorsCodeLens>(title, lens, blame);
case CodeLensCommand.ShowQuickCommitFileDetails: return this._applyShowQuickCommitFileDetailsCommand<GitAuthorsCodeLens>(title, lens, blame);
case CodeLensCommand.ShowQuickFileHistory: return this._applyShowQuickFileHistoryCommand<GitAuthorsCodeLens>(title, lens, blame); case CodeLensCommand.ShowQuickFileHistory: return this._applyShowQuickFileHistoryCommand<GitAuthorsCodeLens>(title, lens, blame);
case CodeLensCommand.ShowQuickRepoHistory: return this._applyShowQuickRepoHistoryCommand<GitAuthorsCodeLens>(title, lens, blame); case CodeLensCommand.ShowQuickRepoHistory: return this._applyShowQuickRepoHistoryCommand<GitAuthorsCodeLens>(title, lens, blame);
default: return lens; default: return lens;
@ -363,8 +365,17 @@ export default class GitCodeLensProvider implements CodeLensProvider {
_applyShowQuickCommitDetailsCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines, commit?: GitCommit): T { _applyShowQuickCommitDetailsCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines, commit?: GitCommit): T {
lens.command = { lens.command = {
title: title, title: title,
command: CodeLensCommand.ShowQuickFileHistory,
arguments: [Uri.file(lens.uri.fsPath), undefined, commit]
command: CodeLensCommand.ShowQuickCommitDetails,
arguments: [Uri.file(lens.uri.fsPath), commit.sha, commit]
};
return lens;
}
_applyShowQuickCommitFileDetailsCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines, commit?: GitCommit): T {
lens.command = {
title: title,
command: CodeLensCommand.ShowQuickCommitFileDetails,
arguments: [Uri.file(lens.uri.fsPath), commit.sha, commit]
}; };
return lens; return lens;
} }

+ 24
- 10
src/gitProvider.ts Datei anzeigen

@ -293,13 +293,22 @@ export class GitProvider extends Disposable {
return Git.repoPath(cwd); return Git.repoPath(cwd);
} }
async getRepoPathFromUri(uri?: Uri, fallbackRepoPath?: string): Promise<string | undefined> {
if (!(uri instanceof Uri)) return fallbackRepoPath;
const gitUri = await GitUri.fromUri(uri, this);
if (gitUri.repoPath) return gitUri.repoPath;
return (await this.getRepoPathFromFile(gitUri.fsPath)) || fallbackRepoPath;
}
async getRepoPathFromFile(fileName: string): Promise<string | undefined> { async getRepoPathFromFile(fileName: string): Promise<string | undefined> {
const log = await this.getLogForFile(fileName, undefined, undefined, undefined, 1); const log = await this.getLogForFile(fileName, undefined, undefined, undefined, 1);
return log && log.repoPath; return log && log.repoPath;
} }
getBlameForFile(fileName: string, sha?: string, repoPath?: string): Promise<IGitBlame | undefined> { getBlameForFile(fileName: string, sha?: string, repoPath?: string): Promise<IGitBlame | undefined> {
Logger.log(`getBlameForFile('${fileName}', ${sha}, ${repoPath})`);
Logger.log(`getBlameForFile('${fileName}', ${sha}, '${repoPath}')`);
fileName = Git.normalizePath(fileName); fileName = Git.normalizePath(fileName);
const useCaching = this.UseGitCaching && !sha; const useCaching = this.UseGitCaching && !sha;
@ -362,7 +371,7 @@ export class GitProvider extends Disposable {
} }
async getBlameForLine(fileName: string, line: number, sha?: string, repoPath?: string): Promise<IGitBlameLine | undefined> { async getBlameForLine(fileName: string, line: number, sha?: string, repoPath?: string): Promise<IGitBlameLine | undefined> {
Logger.log(`getBlameForLine('${fileName}', ${line}, ${sha}, ${repoPath})`);
Logger.log(`getBlameForLine('${fileName}', ${line}, ${sha}, '${repoPath}')`);
if (this.UseGitCaching && !sha) { if (this.UseGitCaching && !sha) {
const blame = await this.getBlameForFile(fileName); const blame = await this.getBlameForFile(fileName);
@ -400,7 +409,7 @@ export class GitProvider extends Disposable {
} }
async getBlameForRange(fileName: string, range: Range, sha?: string, repoPath?: string): Promise<IGitBlameLines | undefined> { async getBlameForRange(fileName: string, range: Range, sha?: string, repoPath?: string): Promise<IGitBlameLines | undefined> {
Logger.log(`getBlameForRange('${fileName}', [${range.start.line}, ${range.end.line}], ${sha}, ${repoPath})`);
Logger.log(`getBlameForRange('${fileName}', [${range.start.line}, ${range.end.line}], ${sha}, '${repoPath}')`);
const blame = await this.getBlameForFile(fileName, sha, repoPath); const blame = await this.getBlameForFile(fileName, sha, repoPath);
if (!blame) return undefined; if (!blame) return undefined;
@ -408,7 +417,7 @@ export class GitProvider extends Disposable {
} }
getBlameForRangeSync(blame: IGitBlame, fileName: string, range: Range, sha?: string, repoPath?: string): IGitBlameLines | undefined { getBlameForRangeSync(blame: IGitBlame, fileName: string, range: Range, sha?: string, repoPath?: string): IGitBlameLines | undefined {
Logger.log(`getBlameForRangeSync('${fileName}', [${range.start.line}, ${range.end.line}], ${sha}, ${repoPath})`);
Logger.log(`getBlameForRangeSync('${fileName}', [${range.start.line}, ${range.end.line}], ${sha}, '${repoPath}')`);
if (!blame.lines.length) return Object.assign({ allLines: blame.lines }, blame); if (!blame.lines.length) return Object.assign({ allLines: blame.lines }, blame);
@ -455,7 +464,7 @@ export class GitProvider extends Disposable {
} }
async getBlameLocations(fileName: string, range: Range, sha?: string, repoPath?: string, selectedSha?: string, line?: number): Promise<Location[] | undefined> { async getBlameLocations(fileName: string, range: Range, sha?: string, repoPath?: string, selectedSha?: string, line?: number): Promise<Location[] | undefined> {
Logger.log(`getBlameLocations('${fileName}', [${range.start.line}, ${range.end.line}], ${sha}, ${repoPath})`);
Logger.log(`getBlameLocations('${fileName}', [${range.start.line}, ${range.end.line}], ${sha}, '${repoPath}')`);
const blame = await this.getBlameForRange(fileName, range, sha, repoPath); const blame = await this.getBlameForRange(fileName, range, sha, repoPath);
if (!blame) return undefined; if (!blame) return undefined;
@ -494,7 +503,7 @@ export class GitProvider extends Disposable {
} }
getLogForFile(fileName: string, sha?: string, repoPath?: string, range?: Range, maxCount?: number, reverse: boolean = false): Promise<IGitLog | undefined> { getLogForFile(fileName: string, sha?: string, repoPath?: string, range?: Range, maxCount?: number, reverse: boolean = false): Promise<IGitLog | undefined> {
Logger.log(`getLogForFile('${fileName}', ${sha}, ${repoPath}, ${range && `[${range.start.line}, ${range.end.line}]`}, ${maxCount})`);
Logger.log(`getLogForFile('${fileName}', ${sha}, '${repoPath}', ${range && `[${range.start.line}, ${range.end.line}]`}, ${maxCount})`);
fileName = Git.normalizePath(fileName); fileName = Git.normalizePath(fileName);
const useCaching = this.UseGitCaching && !sha && !range && !maxCount; const useCaching = this.UseGitCaching && !sha && !range && !maxCount;
@ -555,7 +564,7 @@ export class GitProvider extends Disposable {
} }
async getLogLocations(fileName: string, sha?: string, repoPath?: string, selectedSha?: string, line?: number): Promise<Location[] | undefined> { async getLogLocations(fileName: string, sha?: string, repoPath?: string, selectedSha?: string, line?: number): Promise<Location[] | undefined> {
Logger.log(`getLogLocations('${fileName}', ${sha}, ${repoPath}, ${selectedSha}, ${line})`);
Logger.log(`getLogLocations('${fileName}', ${sha}, '${repoPath}', ${selectedSha}, ${line})`);
const log = await this.getLogForFile(fileName, sha, repoPath); const log = await this.getLogForFile(fileName, sha, repoPath);
if (!log) return undefined; if (!log) return undefined;
@ -590,13 +599,13 @@ export class GitProvider extends Disposable {
} }
async isFileUncommitted(fileName: string, repoPath: string): Promise<boolean> { async isFileUncommitted(fileName: string, repoPath: string): Promise<boolean> {
Logger.log(`isFileUncommitted('${fileName}', ${repoPath})`);
Logger.log(`isFileUncommitted('${fileName}', '${repoPath}')`);
const status = await this.getStatusForFile(fileName, repoPath); const status = await this.getStatusForFile(fileName, repoPath);
return !!status; return !!status;
} }
async getVersionedFile(fileName: string, repoPath: string, sha: string) { async getVersionedFile(fileName: string, repoPath: string, sha: string) {
Logger.log(`getVersionedFile('${fileName}', ${repoPath}, ${sha})`);
Logger.log(`getVersionedFile('${fileName}', '${repoPath}', ${sha})`);
const file = await Git.getVersionedFile(fileName, repoPath, sha); const file = await Git.getVersionedFile(fileName, repoPath, sha);
if (this.UseUriCaching) { if (this.UseUriCaching) {
@ -608,7 +617,7 @@ export class GitProvider extends Disposable {
} }
getVersionedFileText(fileName: string, repoPath: string, sha: string) { getVersionedFileText(fileName: string, repoPath: string, sha: string) {
Logger.log(`getVersionedFileText('${fileName}', ${repoPath}, ${sha})`);
Logger.log(`getVersionedFileText('${fileName}', '${repoPath}', ${sha})`);
return Git.getVersionedFileText(fileName, repoPath, sha); return Git.getVersionedFileText(fileName, repoPath, sha);
} }
@ -618,6 +627,11 @@ export class GitProvider extends Disposable {
this.hasGitUriForFile(editor)); this.hasGitUriForFile(editor));
} }
openDirectoryDiff(repoPath: string, sha1: string, sha2?: string) {
Logger.log(`openDirectoryDiff('${repoPath}', ${sha1}, ${sha2})`);
return Git.diffDir(repoPath, sha1, sha2);
}
toggleCodeLens(editor: TextEditor) { toggleCodeLens(editor: TextEditor) {
if (this.config.codeLens.visibility !== CodeLensVisibility.OnDemand || if (this.config.codeLens.visibility !== CodeLensVisibility.OnDemand ||
(!this.config.codeLens.recentChange.enabled && !this.config.codeLens.authors.enabled)) return; (!this.config.codeLens.recentChange.enabled && !this.config.codeLens.authors.enabled)) return;

+ 2
- 1
src/quickPicks.ts Datei anzeigen

@ -1,7 +1,8 @@
'use strict'; 'use strict';
export { CommandQuickPickItem, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem } from './quickPicks/quickPicks'; export { CommandQuickPickItem, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem } from './quickPicks/quickPicks';
export { CommitQuickPickItem, CommitWithFileStatusQuickPickItem } from './quickPicks/gitQuickPicks'; export { CommitQuickPickItem, CommitWithFileStatusQuickPickItem } from './quickPicks/gitQuickPicks';
export { OpenCommitFileCommandQuickPickItem, OpenCommitWorkingTreeFileCommandQuickPickItem, OpenCommitFilesCommandQuickPickItem, OpenCommitWorkingTreeFilesCommandQuickPickItem, CommitDetailsQuickPick, CommitFileDetailsQuickPick } from './quickPicks/commitDetails';
export { OpenCommitFilesCommandQuickPickItem, OpenCommitWorkingTreeFilesCommandQuickPickItem, CommitDetailsQuickPick } from './quickPicks/commitDetails';
export { OpenCommitFileCommandQuickPickItem, OpenCommitWorkingTreeFileCommandQuickPickItem, CommitFileDetailsQuickPick } from './quickPicks/commitFileDetails';
export { FileHistoryQuickPick } from './quickPicks/fileHistory'; export { FileHistoryQuickPick } from './quickPicks/fileHistory';
export { RepoHistoryQuickPick } from './quickPicks/repoHistory'; export { RepoHistoryQuickPick } from './quickPicks/repoHistory';
export { OpenStatusFileCommandQuickPickItem, OpenStatusFilesCommandQuickPickItem, RepoStatusQuickPick } from './quickPicks/repoStatus'; export { OpenStatusFileCommandQuickPickItem, OpenStatusFilesCommandQuickPickItem, RepoStatusQuickPick } from './quickPicks/repoStatus';

+ 27
- 119
src/quickPicks/commitDetails.ts Datei anzeigen

@ -1,45 +1,20 @@
'use strict'; 'use strict';
import { Iterables } from '../system';
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode'; import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, Keyboard } from '../commands'; import { Commands, Keyboard } from '../commands';
import { GitCommit, GitLogCommit, GitProvider, GitUri } from '../gitProvider';
import { GitLogCommit, GitProvider } from '../gitProvider';
import { CommitWithFileStatusQuickPickItem } from './gitQuickPicks'; import { CommitWithFileStatusQuickPickItem } from './gitQuickPicks';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem } from './quickPicks';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFilesCommandQuickPickItem } from './quickPicks';
import * as moment from 'moment'; import * as moment from 'moment';
import * as path from 'path'; import * as path from 'path';
export { CommandQuickPickItem, CommitWithFileStatusQuickPickItem };
export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
constructor(commit: GitCommit, item?: QuickPickItem) {
const uri = GitProvider.toGitContentUri(commit);
super(uri, item || {
label: `$(file-symlink-file) Open File`,
description: `\u00a0 \u2014 \u00a0\u00a0 as of \u00a0 $(git-commit) \u00a0 ${commit.sha} \u00a0\u2022\u00a0 ${commit.getFormattedPath()}`
});
}
}
export class OpenCommitWorkingTreeFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
constructor(commit: GitCommit, item?: QuickPickItem) {
const uri = Uri.file(path.resolve(commit.repoPath, commit.fileName));
super(uri, item || {
label: `$(file-symlink-file) Open Working File`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.getFormattedPath()}`
});
}
}
export class OpenCommitFilesCommandQuickPickItem extends OpenFilesCommandQuickPickItem { export class OpenCommitFilesCommandQuickPickItem extends OpenFilesCommandQuickPickItem {
constructor(commit: GitLogCommit, item?: QuickPickItem) { constructor(commit: GitLogCommit, item?: QuickPickItem) {
const repoPath = commit.repoPath; const repoPath = commit.repoPath;
const uris = commit.fileStatuses.map(_ => GitProvider.toGitContentUri(commit.sha, _.fileName, repoPath, commit.originalFileName)); const uris = commit.fileStatuses.map(_ => GitProvider.toGitContentUri(commit.sha, _.fileName, repoPath, commit.originalFileName));
super(uris, item || { super(uris, item || {
label: `$(file-symlink-file) Open Files`,
description: `\u00a0 \u2014 \u00a0\u00a0 as of \u00a0 $(git-commit) \u00a0 ${commit.sha}`
label: `$(file-symlink-file) Open Changed Files`,
description: `\u00a0 \u2014 \u00a0\u00a0 in \u00a0$(git-commit) ${commit.sha}`
//detail: `Opens all of the changed files in $(git-commit) ${commit.sha}` //detail: `Opens all of the changed files in $(git-commit) ${commit.sha}`
}); });
} }
@ -51,7 +26,7 @@ export class OpenCommitWorkingTreeFilesCommandQuickPickItem extends OpenFilesCom
const repoPath = commit.repoPath; const repoPath = commit.repoPath;
const uris = commit.fileStatuses.map(_ => Uri.file(path.resolve(repoPath, _.fileName))); const uris = commit.fileStatuses.map(_ => Uri.file(path.resolve(repoPath, _.fileName)));
super(uris, item || { super(uris, item || {
label: `$(file-symlink-file) Open Working Files`,
label: `$(file-symlink-file) Open Changed Working Files`,
description: undefined description: undefined
//detail: `Opens all of the changed file in the working tree` //detail: `Opens all of the changed file in the working tree`
}); });
@ -63,103 +38,35 @@ export class CommitDetailsQuickPick {
static async show(commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem): Promise<CommitWithFileStatusQuickPickItem | CommandQuickPickItem | undefined> { static async show(commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem): Promise<CommitWithFileStatusQuickPickItem | CommandQuickPickItem | undefined> {
const items: (CommitWithFileStatusQuickPickItem | CommandQuickPickItem)[] = commit.fileStatuses.map(fs => new CommitWithFileStatusQuickPickItem(commit, fs.fileName, fs.status)); const items: (CommitWithFileStatusQuickPickItem | CommandQuickPickItem)[] = commit.fileStatuses.map(fs => new CommitWithFileStatusQuickPickItem(commit, fs.fileName, fs.status));
items.splice(0, 0, new CommandQuickPickItem({
label: `$(clippy) Copy Commit Sha to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.sha}`
}, Commands.CopyShaToClipboard, [uri, commit.sha]));
items.splice(1, 0, new CommandQuickPickItem({
label: `$(clippy) Copy Commit Message to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.message}`
}, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message]));
items.splice(2, 0, new OpenCommitWorkingTreeFilesCommandQuickPickItem(commit));
items.splice(3, 0, new OpenCommitFilesCommandQuickPickItem(commit));
if (goBackCommand) {
items.splice(0, 0, goBackCommand);
}
await Keyboard.instance.enterScope(['left', goBackCommand]);
const pick = await window.showQuickPick(items, {
matchOnDescription: true,
matchOnDetail: true,
placeHolder: `${commit.sha} \u2022 ${commit.author}, ${moment(commit.date).fromNow()} \u2022 ${commit.message}`,
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
onDidSelectItem: (item: QuickPickItem) => {
Keyboard.instance.setKeyCommand('right', item);
}
} as QuickPickOptions);
await Keyboard.instance.exitScope();
return pick;
}
}
export class CommitFileDetailsQuickPick {
static async show(git: GitProvider, commit: GitCommit, workingFileName: string, uri: Uri, currentCommand?: CommandQuickPickItem, goBackCommand?: CommandQuickPickItem, options: { showFileHistory?: boolean } = {}): Promise<CommandQuickPickItem | undefined> {
const items: CommandQuickPickItem[] = [];
let index = 0;
const workingName = (workingFileName && path.basename(workingFileName)) || path.basename(commit.fileName);
const isUncommitted = commit.isUncommitted;
if (isUncommitted) {
// Since we can't trust the previous sha on an uncommitted commit, find the last commit for this file
const log = await git.getLogForFile(commit.uri.fsPath, undefined, undefined, undefined, 2);
if (!log) return undefined;
commit = Iterables.first(log.commits.values());
}
if (commit.previousSha) {
items.push(new CommandQuickPickItem({
label: `$(git-compare) Compare with Previous Commit`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.previousSha} \u00a0 $(git-compare) \u00a0 $(git-commit) ${commit.sha}`
}, Commands.DiffWithPrevious, [commit.uri, commit]));
}
items.push(new CommandQuickPickItem({
label: `$(git-compare) Compare with Working Tree`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.sha} \u00a0 $(git-compare) \u00a0 $(file-text) ${workingName}`
}, Commands.DiffWithWorking, [uri, commit]));
items.push(new CommandQuickPickItem({
label: `$(diff) Show Changed Files`,
description: undefined, //`\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.sha}`,
detail: `Shows all of the changed files in commit $(git-commit) ${commit.sha}`
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), commit.sha, undefined, currentCommand]));
items.push(new CommandQuickPickItem({
items.splice(index++, 0, new CommandQuickPickItem({
label: `$(clippy) Copy Commit Sha to Clipboard`, label: `$(clippy) Copy Commit Sha to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.sha}`
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.sha}`
}, Commands.CopyShaToClipboard, [uri, commit.sha])); }, Commands.CopyShaToClipboard, [uri, commit.sha]));
items.push(new CommandQuickPickItem({
items.splice(index++, 0, new CommandQuickPickItem({
label: `$(clippy) Copy Commit Message to Clipboard`, label: `$(clippy) Copy Commit Message to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.message}`
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
}, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message])); }, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message]));
items.push(new OpenCommitWorkingTreeFileCommandQuickPickItem(commit));
items.push(new OpenCommitFileCommandQuickPickItem(commit));
items.splice(index++, 0, new CommandQuickPickItem({
label: `$(git-compare) Directory Compare with Previous Commit`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.previousSha || `${commit.sha}^`} \u00a0 $(git-compare) \u00a0 $(git-commit) ${commit.sha}`
}, Commands.DiffDirectory, [commit.uri, commit.previousSha || `${commit.sha}^`, commit.sha]));
if (options.showFileHistory) {
if (workingFileName) {
items.push(new CommandQuickPickItem({
label: `$(history) Show File History`,
description: undefined, //`\u00a0 \u2014 \u00a0\u00a0 ${path.basename(commit.fileName)}`,
detail: `Shows the commit history of the file, starting at the most recent commit`
}, Commands.ShowQuickFileHistory, [commit.uri, undefined, undefined, currentCommand]));
}
items.splice(index++, 0, new CommandQuickPickItem({
label: `$(git-compare) Directory Compare with Working Tree`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.sha} \u00a0 $(git-compare) \u00a0 $(file-directory) Working Tree`
}, Commands.DiffDirectory, [uri, commit.sha]));
items.push(new CommandQuickPickItem({
label: `$(history) Show Previous File History`,
description: undefined, //`\u00a0 \u2014 \u00a0\u00a0 ${path.basename(commit.fileName)}`,
detail: `Shows the previous commit history of the file, starting at $(git-commit) ${commit.sha}`
}, Commands.ShowQuickFileHistory, [new GitUri(commit.uri, commit), undefined, undefined, currentCommand]));
}
items.splice(index++, 0, new CommandQuickPickItem({
label: `Changed Files`,
description: null
}, Commands.ShowQuickCommitDetails, [uri, commit.sha, commit, goBackCommand]));
items.push(new OpenCommitFilesCommandQuickPickItem(commit));
items.push(new OpenCommitWorkingTreeFilesCommandQuickPickItem(commit));
if (goBackCommand) { if (goBackCommand) {
items.splice(0, 0, goBackCommand); items.splice(0, 0, goBackCommand);
@ -169,7 +76,8 @@ export class CommitFileDetailsQuickPick {
const pick = await window.showQuickPick(items, { const pick = await window.showQuickPick(items, {
matchOnDescription: true, matchOnDescription: true,
placeHolder: `${commit.getFormattedPath()} \u2022 ${isUncommitted ? 'Uncommitted \u21E8 ' : '' }${commit.sha} \u2022 ${commit.author}, ${moment(commit.date).fromNow()} \u2022 ${commit.message}`,
matchOnDetail: true,
placeHolder: `${commit.sha} \u2022 ${commit.author}, ${moment(commit.date).fromNow()} \u2022 ${commit.message}`,
ignoreFocusOut: getQuickPickIgnoreFocusOut(), ignoreFocusOut: getQuickPickIgnoreFocusOut(),
onDidSelectItem: (item: QuickPickItem) => { onDidSelectItem: (item: QuickPickItem) => {
Keyboard.instance.setKeyCommand('right', item); Keyboard.instance.setKeyCommand('right', item);

+ 111
- 0
src/quickPicks/commitFileDetails.ts Datei anzeigen

@ -0,0 +1,111 @@
'use strict';
import { Iterables } from '../system';
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, Keyboard } from '../commands';
import { GitCommit, GitLogCommit, GitProvider, GitUri } from '../gitProvider';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFileCommandQuickPickItem } from './quickPicks';
import * as moment from 'moment';
import * as path from 'path';
export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
constructor(commit: GitCommit, item?: QuickPickItem) {
const uri = GitProvider.toGitContentUri(commit);
super(uri, item || {
label: `$(file-symlink-file) Open File`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${path.basename(commit.fileName)} in \u00a0$(git-commit) ${commit.sha}`
});
}
}
export class OpenCommitWorkingTreeFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
constructor(commit: GitCommit, item?: QuickPickItem) {
const uri = Uri.file(path.resolve(commit.repoPath, commit.fileName));
super(uri, item || {
label: `$(file-symlink-file) Open Working File`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${path.basename(commit.fileName)}`
});
}
}
export class CommitFileDetailsQuickPick {
static async show(git: GitProvider, commit: GitCommit | GitLogCommit, workingFileName: string, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, options: { showFileHistory?: boolean } = {}): Promise<CommandQuickPickItem | undefined> {
const items: CommandQuickPickItem[] = [];
const workingName = (workingFileName && path.basename(workingFileName)) || path.basename(commit.fileName);
const isUncommitted = commit.isUncommitted;
if (isUncommitted) {
// Since we can't trust the previous sha on an uncommitted commit, find the last commit for this file
const log = await git.getLogForFile(commit.uri.fsPath, undefined, undefined, undefined, 2);
if (!log) return undefined;
commit = Iterables.first(log.commits.values());
}
if (!options.showFileHistory) {
items.push(new CommandQuickPickItem({
label: `$(git-commit) Show Commit Details`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.sha}`
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), commit.sha, commit, currentCommand]));
}
if (commit.previousSha) {
items.push(new CommandQuickPickItem({
label: `$(git-compare) Compare with Previous Commit`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.previousSha} \u00a0 $(git-compare) \u00a0 $(git-commit) ${commit.sha}`
}, Commands.DiffWithPrevious, [commit.uri, commit]));
}
items.push(new CommandQuickPickItem({
label: `$(git-compare) Compare with Working Tree`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.sha} \u00a0 $(git-compare) \u00a0 $(file-text) ${workingName}`
}, Commands.DiffWithWorking, [uri, commit]));
items.push(new CommandQuickPickItem({
label: `$(clippy) Copy Commit Sha to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.sha}`
}, Commands.CopyShaToClipboard, [uri, commit.sha]));
items.push(new CommandQuickPickItem({
label: `$(clippy) Copy Commit Message to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
}, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message]));
items.push(new OpenCommitFileCommandQuickPickItem(commit));
items.push(new OpenCommitWorkingTreeFileCommandQuickPickItem(commit));
if (workingFileName && options.showFileHistory) {
items.push(new CommandQuickPickItem({
label: `$(history) Show File History`,
description: `\u00a0 \u2014 \u00a0\u00a0 of ${path.basename(commit.fileName)}`
}, Commands.ShowQuickFileHistory, [commit.uri, undefined, currentCommand]));
}
items.push(new CommandQuickPickItem({
label: `$(history) Show ${workingFileName && options.showFileHistory ? 'Previous ' : ''}File History`,
description: `\u00a0 \u2014 \u00a0\u00a0 of ${path.basename(commit.fileName)} \u00a0\u2022\u00a0 starting from \u00a0$(git-commit) ${commit.sha}`
}, Commands.ShowQuickFileHistory, [new GitUri(commit.uri, commit), undefined, currentCommand]));
if (goBackCommand) {
items.splice(0, 0, goBackCommand);
}
await Keyboard.instance.enterScope(['left', goBackCommand]);
const pick = await window.showQuickPick(items, {
matchOnDescription: true,
placeHolder: `${commit.getFormattedPath()} \u2022 ${isUncommitted ? 'Uncommitted \u21E8 ' : '' }${commit.sha} \u2022 ${commit.author}, ${moment(commit.date).fromNow()} \u2022 ${commit.message}`,
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
onDidSelectItem: (item: QuickPickItem) => {
Keyboard.instance.setKeyCommand('right', item);
}
} as QuickPickOptions);
await Keyboard.instance.exitScope();
return pick;
}
}

+ 13
- 9
src/quickPicks/fileHistory.ts Datei anzeigen

@ -5,12 +5,11 @@ import { Commands, Keyboard } from '../commands';
import { IGitLog } from '../gitProvider'; import { IGitLog } from '../gitProvider';
import { CommitQuickPickItem } from './gitQuickPicks'; import { CommitQuickPickItem } from './gitQuickPicks';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './quickPicks'; import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './quickPicks';
export { CommandQuickPickItem, CommitQuickPickItem };
import * as path from 'path';
export class FileHistoryQuickPick { export class FileHistoryQuickPick {
static async show(log: IGitLog, uri: Uri, maxCount: number, defaultMaxCount: number, goBackCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
static async show(log: IGitLog, uri: Uri, sha: string, maxCount: number, defaultMaxCount: number, goBackCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
const items = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))) as (CommitQuickPickItem | CommandQuickPickItem)[]; const items = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))) as (CommitQuickPickItem | CommandQuickPickItem)[];
if (maxCount !== 0 && items.length >= defaultMaxCount) { if (maxCount !== 0 && items.length >= defaultMaxCount) {
@ -18,7 +17,7 @@ export class FileHistoryQuickPick {
label: `$(sync) Show All Commits`, label: `$(sync) Show All Commits`,
description: `\u00a0 \u2014 \u00a0\u00a0 Currently only showing the first ${defaultMaxCount} commits`, description: `\u00a0 \u2014 \u00a0\u00a0 Currently only showing the first ${defaultMaxCount} commits`,
detail: `This may take a while` detail: `This may take a while`
}, Commands.ShowQuickFileHistory, [uri, 0, undefined, goBackCommand]));
}, Commands.ShowQuickFileHistory, [uri, 0, goBackCommand]));
} }
// Only show the full repo option if we are the root // Only show the full repo option if we are the root
@ -27,10 +26,15 @@ export class FileHistoryQuickPick {
label: `$(repo) Show Repository History`, label: `$(repo) Show Repository History`,
description: null, description: null,
detail: 'Shows the commit history of the repository' detail: 'Shows the commit history of the repository'
}, Commands.ShowQuickRepoHistory, [undefined, undefined, undefined, new CommandQuickPickItem({
label: `go back \u21A9`,
description: null
}, Commands.ShowQuickFileHistory, [uri, maxCount])]));
}, Commands.ShowQuickRepoHistory,
[
undefined,
undefined,
new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to history of \u00a0$(file-text) ${path.basename(uri.fsPath)}`
}, Commands.ShowQuickFileHistory, [uri, maxCount])
]));
} }
if (goBackCommand) { if (goBackCommand) {
@ -44,7 +48,7 @@ export class FileHistoryQuickPick {
const pick = await window.showQuickPick(items, { const pick = await window.showQuickPick(items, {
matchOnDescription: true, matchOnDescription: true,
matchOnDetail: true, matchOnDetail: true,
placeHolder: commit.getFormattedPath(),
placeHolder: `${commit.getFormattedPath()}${sha ? ` \u00a0\u2022\u00a0 ${sha}` : ''}`,
ignoreFocusOut: getQuickPickIgnoreFocusOut(), ignoreFocusOut: getQuickPickIgnoreFocusOut(),
onDidSelectItem: (item: QuickPickItem) => { onDidSelectItem: (item: QuickPickItem) => {
Keyboard.instance.setKeyCommand('right', item); Keyboard.instance.setKeyCommand('right', item);

+ 3
- 16
src/quickPicks/quickPicks.ts Datei anzeigen

@ -1,25 +1,12 @@
'use strict'; 'use strict';
import { commands, QuickPickItem, TextEditor, Uri, window, workspace } from 'vscode';
import { Commands } from '../commands';
import { commands, QuickPickItem, TextEditor, Uri, workspace } from 'vscode';
import { Commands, openEditor } from '../commands';
import { IAdvancedConfig } from '../configuration'; import { IAdvancedConfig } from '../configuration';
import { BuiltInCommands } from '../constants';
export function getQuickPickIgnoreFocusOut() { export function getQuickPickIgnoreFocusOut() {
return !workspace.getConfiguration('gitlens').get<IAdvancedConfig>('advanced').quickPick.closeOnFocusOut; return !workspace.getConfiguration('gitlens').get<IAdvancedConfig>('advanced').quickPick.closeOnFocusOut;
} }
export async function openEditor(uri: Uri, pinned: boolean = false) {
try {
if (!pinned) return await commands.executeCommand(BuiltInCommands.Open, uri);
const document = await workspace.openTextDocument(uri);
return window.showTextDocument(document, (window.activeTextEditor && window.activeTextEditor.viewColumn) || 1, true);
}
catch (ex) {
return undefined;
}
}
export class CommandQuickPickItem implements QuickPickItem { export class CommandQuickPickItem implements QuickPickItem {
label: string; label: string;
@ -58,7 +45,7 @@ export class OpenFilesCommandQuickPickItem extends CommandQuickPickItem {
async execute(): Promise<{}> { async execute(): Promise<{}> {
for (const uri of this.uris) { for (const uri of this.uris) {
openEditor(uri, true);
await openEditor(uri, true);
} }
return undefined; return undefined;
} }

+ 1
- 3
src/quickPicks/repoHistory.ts Datei anzeigen

@ -6,8 +6,6 @@ import { IGitLog } from '../gitProvider';
import { CommitQuickPickItem } from './gitQuickPicks'; import { CommitQuickPickItem } from './gitQuickPicks';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './quickPicks'; import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './quickPicks';
export { CommandQuickPickItem, CommitQuickPickItem };
export class RepoHistoryQuickPick { export class RepoHistoryQuickPick {
static async show(log: IGitLog, uri: Uri, maxCount: number, defaultMaxCount: number, goBackCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> { static async show(log: IGitLog, uri: Uri, maxCount: number, defaultMaxCount: number, goBackCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
@ -18,7 +16,7 @@ export class RepoHistoryQuickPick {
label: `$(sync) Show All Commits`, label: `$(sync) Show All Commits`,
description: `\u00a0 \u2014 \u00a0\u00a0 Currently only showing the first ${defaultMaxCount} commits`, description: `\u00a0 \u2014 \u00a0\u00a0 Currently only showing the first ${defaultMaxCount} commits`,
detail: `This may take a while` detail: `This may take a while`
}, Commands.ShowQuickRepoHistory, [uri, 0, undefined, goBackCommand]));
}, Commands.ShowQuickRepoHistory, [uri, 0, goBackCommand]));
} }
if (goBackCommand) { if (goBackCommand) {

+ 37
- 20
src/quickPicks/repoStatus.ts Datei anzeigen

@ -1,13 +1,11 @@
'use strict'; 'use strict';
import { Iterables } from '../system'; import { Iterables } from '../system';
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode'; import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
import { Keyboard } from '../commands';
import { Commands, Keyboard } from '../commands';
import { getGitStatusIcon, GitFileStatusItem } from '../gitProvider'; import { getGitStatusIcon, GitFileStatusItem } from '../gitProvider';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem } from './quickPicks';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFileCommandQuickPickItem } from './quickPicks';
import * as path from 'path'; import * as path from 'path';
export { CommandQuickPickItem };
export class OpenStatusFileCommandQuickPickItem extends OpenFileCommandQuickPickItem { export class OpenStatusFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
constructor(status: GitFileStatusItem, item?: QuickPickItem) { constructor(status: GitFileStatusItem, item?: QuickPickItem) {
@ -26,17 +24,17 @@ export class OpenStatusFileCommandQuickPickItem extends OpenFileCommandQuickPick
} }
} }
export class OpenStatusFilesCommandQuickPickItem extends OpenFilesCommandQuickPickItem {
export class OpenStatusFilesCommandQuickPickItem extends CommandQuickPickItem {
constructor(statuses: GitFileStatusItem[], item?: QuickPickItem) { constructor(statuses: GitFileStatusItem[], item?: QuickPickItem) {
const repoPath = statuses.length && statuses[0].repoPath; const repoPath = statuses.length && statuses[0].repoPath;
const uris = statuses.map(_ => Uri.file(path.resolve(repoPath, _.fileName))); const uris = statuses.map(_ => Uri.file(path.resolve(repoPath, _.fileName)));
super(uris, item || {
label: `$(file-symlink-file) Open Files`,
description: undefined,
detail: `Opens all of the changed files in the repository`
});
super(item || {
label: `$(file-symlink-file) Open Changed Files`,
description: undefined
//detail: `Opens all of the changed files in the repository`
}, Commands.OpenChangedFiles, [undefined, uris]);
} }
} }
@ -49,23 +47,42 @@ export class RepoStatusQuickPick {
const items = Array.from(Iterables.map(statuses, s => new OpenStatusFileCommandQuickPickItem(s))) as (OpenStatusFileCommandQuickPickItem | OpenStatusFilesCommandQuickPickItem | CommandQuickPickItem)[]; const items = Array.from(Iterables.map(statuses, s => new OpenStatusFileCommandQuickPickItem(s))) as (OpenStatusFileCommandQuickPickItem | OpenStatusFilesCommandQuickPickItem | CommandQuickPickItem)[];
if (statuses.some(_ => _.staged)) { if (statuses.some(_ => _.staged)) {
const index = statuses.findIndex(_ => !_.staged);
if (index > -1) {
items.splice(index, 0, new OpenStatusFilesCommandQuickPickItem(statuses.filter(_ => _.status !== 'D' && !_.staged), {
label: `$(file-symlink-file) Open Unstaged Files`,
description: undefined,
detail: `Opens all of the unstaged files in the repository`
let index = 0;
const unstagedIndex = statuses.findIndex(_ => !_.staged);
if (unstagedIndex > -1) {
items.splice(unstagedIndex, 0, new CommandQuickPickItem({
label: `Unstaged Files`,
description: undefined
}, Commands.ShowQuickRepoStatus, [goBackCommand]));
items.splice(index++, 0, new OpenStatusFilesCommandQuickPickItem(statuses.filter(_ => _.status !== 'D' && _.staged), {
label: `$(file-symlink-file) Open Staged Files`,
description: undefined
})); }));
items.splice(0, 0, new OpenStatusFilesCommandQuickPickItem(statuses.filter(_ => _.status !== 'D' && _.staged), {
label: `$(file-symlink-file) Open Staged Files`,
description: undefined,
detail: `Opens all of the staged files in the repository`
items.splice(index++, 0, new OpenStatusFilesCommandQuickPickItem(statuses.filter(_ => _.status !== 'D' && !_.staged), {
label: `$(file-symlink-file) Open Unstaged Files`,
description: undefined
})); }));
} }
items.splice(index++, 0, new CommandQuickPickItem({
label: `Staged Files`,
description: undefined
}, Commands.ShowQuickRepoStatus, [goBackCommand]));
}
else if (statuses.some(_ => !_.staged)) {
items.splice(0, 0, new CommandQuickPickItem({
label: `Unstaged Files`,
description: undefined
}, Commands.ShowQuickRepoStatus, [goBackCommand]));
} }
if (statuses.length) { if (statuses.length) {
items.splice(0, 0, new CommandQuickPickItem({
label: '$(x) Close Unchanged Files',
description: null
}, Commands.CloseUnchangedFiles));
items.splice(0, 0, new OpenStatusFilesCommandQuickPickItem(statuses.filter(_ => _.status !== 'D'))); items.splice(0, 0, new OpenStatusFilesCommandQuickPickItem(statuses.filter(_ => _.status !== 'D')));
} }

Laden…
Abbrechen
Speichern