Adds shortcut for gitlens.showQuickFileHistory

Adds shortcut for gitlens.showQuickRepoHistory
Adds gitlens.advanced.maxQuickHistory to limit the number of quick history entries to show
Adds gitlens.diffLineWithPrevious as alt context menu item for gitlens.diffWithPrevious
Adds gitlens.diffLineWithWorking as alt context menu item for gitlens.diffWithWorking
Adds gitlens.showFileHistory as alt context menu item for gitlens.showQuickFileHistory
Removes context menu for gitlens.diffLineWithPrevious
Removes context menu for gitlens.diffLineWithWorking
Replaces gitlens.menus.fileDiff.enabled & gitlens.menus.lineDiff.enabled with gitlens.menus.diff.enabled
This commit is contained in:
Eric Amodio
2017-01-02 00:39:26 -05:00
parent 567533622c
commit 680d31d43d
11 changed files with 260 additions and 191 deletions

View File

@@ -1,6 +1,18 @@
---
## Release Notes
### 1.4.0
- Adds `alt+h` shortcut for the `gitlens.showQuickFileHistory` command
- Adds `shift+alt+h` shortcut for the `gitlens.showQuickRepoHistory` command
- Adds `gitlens.advanced.maxQuickHistory` to limit the number of quick history entries to show (for better performance); Defaults to 200
- Adds `gitlens.diffLineWithPrevious` as `alt` context menu item for `gitlens.diffWithPrevious`
- Adds `gitlens.diffLineWithWorking` as `alt` context menu item for `gitlens.diffWithWorking`
- Adds `gitlens.showFileHistory` as `alt` context menu item for `gitlens.showQuickFileHistory`
- Removes context menu for `gitlens.diffLineWithPrevious` -- since it is now the `alt` of `gitlens.diffWithPrevious`
- Removes context menu for `gitlens.diffLineWithWorking` -- since it is now the `alt` of `gitlens.diffWithWorking`
- Replaces `gitlens.menus.fileDiff.enabled` and `gitlens.menus.lineDiff.enabled` with `gitlens.menus.diff.enabled` -- since the switch between file and line diff is now controlled by the `alt` key
### 1.3.1
- Renames `Diff` commands for better clarity

View File

@@ -38,13 +38,11 @@ Provides Git CodeLens information (most recent commit, # of authors), on-demand
|`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.showQuickFileHistory` - shows a file history picker
|`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.showQuickFileHistory` - shows a file history picker
|`gitlens.menus.fileDiff.enabled`|Specifies whether file-based diff commands will be added to the context menus
|`gitlens.menus.lineDiff.enabled`|Specifies whether line-based 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.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.showQuickFileHistory` - shows a file history picker
## Known Issues
- Content in the **history explorers** disappears after a bit: [vscode issue](https://github.com/Microsoft/vscode/issues/16098)
- CodeLens aren't updated properly after a file is saved: [vscode issue](https://github.com/Microsoft/vscode/issues/11546)
- Visible whitespace causes issue with blame overlay (currently fixed with a hack, but infrequently and randomly fails): [vscode issue](https://github.com/Microsoft/vscode/issues/11485)

View File

@@ -1,6 +1,6 @@
{
"name": "gitlens",
"version": "1.3.1",
"version": "1.4.0",
"author": {
"name": "Eric Amodio",
"email": "eamodio@gmail.com"
@@ -201,15 +201,10 @@
],
"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.showQuickFileHistory` - shows a file history picker"
},
"gitlens.menus.fileDiff.enabled": {
"gitlens.menus.diff.enabled": {
"type": "boolean",
"default": true,
"description": "Specifies whether file-based diff commands will be added to the context menus"
},
"gitlens.menus.lineDiff.enabled": {
"type": "boolean",
"default": false,
"description": "Specifies whether line-based diff commands will be added to the context menus"
"description": "Specifies whether diff commands will be added to the context menus"
},
"gitlens.advanced.caching.enabled": {
"type": "boolean",
@@ -231,6 +226,11 @@
"default": null,
"description": "Specifies a git path to use"
},
"gitlens.advanced.maxQuickHistory": {
"type": "number",
"default": 200,
"description": "Specifies the maximum number of QuickPick history entries to show"
},
"gitlens.advanced.output.level": {
"type": "string",
"default": "silent",
@@ -304,12 +304,12 @@
"explorer/context": [
{
"command": "gitlens.diffWithWorking",
"when": "config.gitlens.menus.fileDiff.enabled && config.git.enabled",
"when": "config.gitlens.menus.diff.enabled && config.git.enabled",
"group": "2_gitlens-file"
},
{
"command": "gitlens.diffWithPrevious",
"when": "config.gitlens.menus.fileDiff.enabled && config.git.enabled",
"when": "config.gitlens.menus.diff.enabled && config.git.enabled",
"group": "2_gitlens-file"
},
{
@@ -321,12 +321,12 @@
"editor/title": [
{
"command": "gitlens.diffWithWorking",
"when": "config.gitlens.menus.fileDiff.enabled && config.git.enabled",
"when": "config.gitlens.menus.diff.enabled && config.git.enabled",
"group": "2_gitlens"
},
{
"command": "gitlens.diffWithPrevious",
"when": "config.gitlens.menus.fileDiff.enabled && config.git.enabled",
"when": "config.gitlens.menus.diff.enabled && config.git.enabled",
"group": "2_gitlens"
},
{
@@ -344,27 +344,18 @@
{
"command": "gitlens.diffWithWorking",
"alt": "gitlens.diffLineWithWorking",
"when": "editorTextFocus && config.gitlens.menus.fileDiff.enabled && config.git.enabled",
"when": "editorTextFocus && config.gitlens.menus.diff.enabled && config.git.enabled",
"group": "2_gitlens@1.0"
},
{
"command": "gitlens.diffLineWithWorking",
"when": "editorTextFocus && config.gitlens.menus.lineDiff.enabled && config.git.enabled",
"group": "2_gitlens@1.1"
},
{
"command": "gitlens.diffWithPrevious",
"alt": "gitlens.diffLineWithPrevious",
"when": "editorTextFocus && config.gitlens.menus.fileDiff.enabled && config.git.enabled",
"group": "2_gitlens@1.2"
},
{
"command": "gitlens.diffLineWithPrevious",
"when": "editorTextFocus && config.gitlens.menus.lineDiff.enabled && config.git.enabled",
"group": "2_gitlens@1.3"
"when": "editorTextFocus && config.gitlens.menus.diff.enabled && config.git.enabled",
"group": "2_gitlens@1.1"
},
{
"command": "gitlens.showQuickFileHistory",
"alt": "gitlens.showFileHistory",
"when": "config.git.enabled",
"group": "3_gitlens"
},
@@ -382,6 +373,17 @@
"mac": "alt+b",
"when": "editorTextFocus"
},
{
"command": "gitlens.showQuickFileHistory",
"key": "alt+h",
"mac": "alt+h",
"when": "editorTextFocus"
},
{
"command": "gitlens.showQuickRepoHistory",
"key": "shift+alt+h",
"mac": "shift+alt+h"
},
{
"command": "gitlens.toggleCodeLens",
"key": "alt+shift+b",
@@ -407,17 +409,17 @@
"lodash.escaperegexp": "^4.1.2",
"lodash.isequal": "^4.4.0",
"lodash.once": "^4.1.1",
"moment": "^2.17.0",
"spawn-rx": "^2.0.6",
"moment": "^2.17.1",
"spawn-rx": "^2.0.7",
"tmp": "^0.0.31"
},
"devDependencies": {
"@types/node": "^0.0.2",
"@types/mocha": "^2.2.33",
"@types/node": "^6.0.55",
"@types/mocha": "^2.2.35",
"@types/tmp": "^0.0.32",
"mocha": "^3.2.0",
"tslint": "^4.0.2",
"typescript": "^2.0.10",
"tslint": "^4.2.0",
"typescript": "^2.1.4",
"vscode": "^1.0.3"
}
}

View File

@@ -32,3 +32,15 @@ export class FileQuickPickItem implements QuickPickItem {
this.uri = GitUri.fromUri(Uri.file(path.resolve(commit.repoPath, fileName)));
}
}
export class ShowAllCommitsQuickPickItem implements QuickPickItem {
label: string;
description: string;
detail: string;
constructor(maxItems: number) {
this.label = `Show All Commits`;
this.description = `\u2014 Currently only showing the first ${maxItems} commits`;
this.detail = `This may take a while`;
}
}

View File

@@ -14,7 +14,9 @@ export default class ShowBlameHistoryCommand extends EditorCommand {
if (!(uri instanceof Uri)) {
if (!editor.document) return undefined;
uri = editor.document.uri;
}
if (range == null || position == null) {
// If the command is executed manually -- treat it as a click on the root lens (i.e. show blame for the whole file)
range = editor.document.validateRange(new Range(0, 0, 1000000, 1000000));
position = editor.document.validateRange(new Range(0, 0, 0, 1000000)).start;

View File

@@ -14,7 +14,9 @@ export default class ShowFileHistoryCommand extends EditorCommand {
if (!(uri instanceof Uri)) {
if (!editor.document) return undefined;
uri = editor.document.uri;
}
if (position == null) {
// If the command is executed manually -- treat it as a click on the root lens (i.e. show blame for the whole file)
position = editor.document.validateRange(new Range(0, 0, 0, 1000000)).start;
}

View File

@@ -1,11 +1,11 @@
'use strict';
import { Iterables } from '../system';
import { commands, QuickPickOptions, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
import { commands, QuickPickItem, QuickPickOptions, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
import { EditorCommand } from './commands';
import { Commands } from '../constants';
import GitProvider, { GitUri } from '../gitProvider';
import { Logger } from '../logger';
import { CommitQuickPickItem, CompareQuickPickItem } from './quickPickItems';
import { CommitQuickPickItem, CompareQuickPickItem, ShowAllCommitsQuickPickItem } from './quickPickItems';
import * as moment from 'moment';
export default class ShowQuickFileHistoryCommand extends EditorCommand {
@@ -13,7 +13,7 @@ export default class ShowQuickFileHistoryCommand extends EditorCommand {
super(Commands.ShowQuickFileHistory);
}
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri) {
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, maxCount?: number) {
if (!(uri instanceof Uri)) {
if (!editor.document) return undefined;
uri = editor.document.uri;
@@ -21,48 +21,62 @@ export default class ShowQuickFileHistoryCommand extends EditorCommand {
const gitUri = GitUri.fromUri(uri, this.git);
if (maxCount == null) {
maxCount = this.git.config.advanced.maxQuickHistory;
}
try {
const log = await this.git.getLogForFile(gitUri.fsPath, gitUri.sha, gitUri.repoPath);
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 items = Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c));
const commitPick = await window.showQuickPick(Array.from(items), <QuickPickOptions>{
const commits = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))) as QuickPickItem[];
let placeHolderSuffix = '';
if (maxCount !== 0 && commits.length === this.git.config.advanced.maxQuickHistory) {
placeHolderSuffix = ` \u2014 Only showing the first ${this.git.config.advanced.maxQuickHistory} commits`;
commits.push(new ShowAllCommitsQuickPickItem(this.git.config.advanced.maxQuickHistory));
}
const pick = await window.showQuickPick(commits, {
matchOnDescription: true,
matchOnDetail: true,
placeHolder: Iterables.first(log.commits.values()).fileName
});
placeHolder: `${Iterables.first(log.commits.values()).fileName}${placeHolderSuffix}`
} as QuickPickOptions);
if (commitPick) {
const commit = commitPick.commit;
if (!pick) return undefined;
if (pick instanceof ShowAllCommitsQuickPickItem) {
return commands.executeCommand(Commands.ShowQuickFileHistory, uri, 0);
}
let command: Commands | undefined = Commands.DiffWithWorking;
if (commit.previousSha) {
const items: CompareQuickPickItem[] = [
{
label: `Compare with Working Tree`,
description: `\u2022 ${commit.sha} $(git-compare) ${commit.fileName}`,
detail: null,
command: Commands.DiffWithWorking
},
{
label: `Compare with Previous Commit`,
description: `\u2022 ${commit.previousSha} $(git-compare) ${commit.sha}`,
detail: null,
command: Commands.DiffWithPrevious
}
];
const commitPick = pick as CommitQuickPickItem;
const commit = commitPick.commit;
const comparePick = await window.showQuickPick(items, <QuickPickOptions>{
matchOnDescription: true,
placeHolder: `${commit.fileName} \u2022 ${commit.sha} \u2022 ${commit.author}, ${moment(commit.date).fromNow()}`
});
let command: Commands | undefined = Commands.DiffWithWorking;
if (commit.previousSha) {
const items: CompareQuickPickItem[] = [
{
label: `Compare with Working Tree`,
description: `\u2022 ${commit.sha} $(git-compare) ${commit.fileName}`,
detail: null,
command: Commands.DiffWithWorking
},
{
label: `Compare with Previous Commit`,
description: `\u2022 ${commit.previousSha} $(git-compare) ${commit.sha}`,
detail: null,
command: Commands.DiffWithPrevious
}
];
command = comparePick ? comparePick.command : undefined;
}
const comparePick = await window.showQuickPick(items, {
matchOnDescription: true,
placeHolder: `${commit.fileName} \u2022 ${commit.sha} \u2022 ${commit.author}, ${moment(commit.date).fromNow()}`
} as QuickPickOptions);
if (command) {
return commands.executeCommand(command, commit.uri, commit);
}
command = comparePick ? comparePick.command : undefined;
}
if (command) {
return commands.executeCommand(command, commit.uri, commit);
}
}
catch (ex) {

View File

@@ -1,18 +1,19 @@
'use strict';
import { Iterables } from '../system';
import { commands, QuickPickOptions, Uri, window } from 'vscode';
import { commands, QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
import { Command } from './commands';
import { Commands } from '../constants';
import GitProvider, { GitUri } from '../gitProvider';
import { Logger } from '../logger';
import { CommitQuickPickItem, CompareQuickPickItem, FileQuickPickItem } from './quickPickItems';
import { CommitQuickPickItem, CompareQuickPickItem, FileQuickPickItem, ShowAllCommitsQuickPickItem } from './quickPickItems';
import * as moment from 'moment';
export default class ShowQuickRepoHistoryCommand extends Command {
constructor(private git: GitProvider, public repoPath: string) {
super(Commands.ShowQuickRepoHistory);
}
async execute(uri?: Uri) {
async execute(uri?: Uri, maxCount?: number) {
if (!(uri instanceof Uri)) {
const document = window.activeTextEditor && window.activeTextEditor.document;
if (document) {
@@ -20,6 +21,10 @@ export default class ShowQuickRepoHistoryCommand extends Command {
}
}
if (maxCount == null) {
maxCount = this.git.config.advanced.maxQuickHistory;
}
try {
let repoPath: string;
if (uri instanceof Uri) {
@@ -37,57 +42,68 @@ export default class ShowQuickRepoHistoryCommand extends Command {
if (!repoPath) return window.showWarningMessage(`Unable to show repository history`);
const log = await this.git.getLogForRepo(repoPath);
const log = await this.git.getLogForRepo(repoPath, maxCount);
if (!log) return window.showWarningMessage(`Unable to show repository history`);
const items = Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c, ` \u2014 ${c.fileName}`));
const commitPick = await window.showQuickPick(Array.from(items), <QuickPickOptions>{
const commits = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c, ` \u2014 ${c.fileName}`))) as QuickPickItem[];
let placeHolder = '';
if (maxCount !== 0 && commits.length === this.git.config.advanced.maxQuickHistory) {
placeHolder = `Only showing the first ${this.git.config.advanced.maxQuickHistory} commits`;
commits.push(new ShowAllCommitsQuickPickItem(this.git.config.advanced.maxQuickHistory));
}
const pick = await window.showQuickPick(commits, {
matchOnDescription: true,
matchOnDetail: true
});
matchOnDetail: true,
placeHolder: placeHolder
} as QuickPickOptions);
if (commitPick) {
const items = commitPick.commit.fileName.split(', ').map(f => new FileQuickPickItem(commitPick.commit, f));
const filePick = await window.showQuickPick(items, <QuickPickOptions>{
matchOnDescription: true,
matchOnDetail: true,
placeHolder: `${commitPick.commit.sha} \u2022 ${commitPick.commit.author}, ${moment(commitPick.commit.date).fromNow()}`
});
if (!pick) return undefined;
if (pick instanceof ShowAllCommitsQuickPickItem) {
return commands.executeCommand(Commands.ShowQuickRepoHistory, uri, 0);
}
if (filePick) {
const log = await this.git.getLogForFile(filePick.uri.fsPath);
if (!log) return window.showWarningMessage(`Unable to open diff`);
const commitPick = pick as CommitQuickPickItem;
const files = commitPick.commit.fileName.split(', ').map(f => new FileQuickPickItem(commitPick.commit, f));
const filePick = await window.showQuickPick(files, {
matchOnDescription: true,
matchOnDetail: true,
placeHolder: `${commitPick.commit.sha} \u2022 ${commitPick.commit.author}, ${moment(commitPick.commit.date).fromNow()}`
} as QuickPickOptions);
const commit = Iterables.find(log.commits.values(), c => c.sha === commitPick.commit.sha);
if (filePick) {
const log = await this.git.getLogForFile(filePick.uri.fsPath);
if (!log) return window.showWarningMessage(`Unable to open diff`);
let command: Commands | undefined = Commands.DiffWithWorking;
if (commit.previousSha) {
const items: CompareQuickPickItem[] = [
{
label: `Compare with Working Tree`,
description: `\u2022 ${commit.sha} $(git-compare) ${commit.fileName}`,
detail: null,
command: Commands.DiffWithWorking
},
{
label: `Compare with Previous Commit`,
description: `\u2022 ${commit.previousSha} $(git-compare) ${commit.sha}`,
detail: null,
command: Commands.DiffWithPrevious
}
];
const commit = Iterables.find(log.commits.values(), c => c.sha === commitPick.commit.sha);
const comparePick = await window.showQuickPick(items, <QuickPickOptions>{
matchOnDescription: true,
placeHolder: `${commit.fileName} \u2022 ${commit.sha} \u2022 ${commit.author}, ${moment(commit.date).fromNow()}`
});
let command: Commands | undefined = Commands.DiffWithWorking;
if (commit.previousSha) {
const items: CompareQuickPickItem[] = [
{
label: `Compare with Working Tree`,
description: `\u2022 ${commit.sha} $(git-compare) ${commit.fileName}`,
detail: null,
command: Commands.DiffWithWorking
},
{
label: `Compare with Previous Commit`,
description: `\u2022 ${commit.previousSha} $(git-compare) ${commit.sha}`,
detail: null,
command: Commands.DiffWithPrevious
}
];
command = comparePick ? comparePick.command : undefined;
}
const comparePick = await window.showQuickPick(items, {
matchOnDescription: true,
placeHolder: `${commit.fileName} \u2022 ${commit.sha} \u2022 ${commit.author}, ${moment(commit.date).fromNow()}`
} as QuickPickOptions);
if (command) {
return commands.executeCommand(command, commit.uri, commit);
}
command = comparePick ? comparePick.command : undefined;
}
if (command) {
return commands.executeCommand(command, commit.uri, commit);
}
}

View File

@@ -92,6 +92,7 @@ export interface IAdvancedConfig {
};
debug: boolean;
git: string;
maxQuickHistory: number;
output: {
level: OutputLevel;
};

View File

@@ -13,6 +13,8 @@ export * from './enrichers/logParserEnricher';
let git: IGit;
const UncommittedRegex = /^[0]+$/;
const DefaultLogParams = [`log`, `--name-only`, `--no-merges`, `--date=iso8601-strict`, `--format=%H -%nauthor %an%nauthor-date %ai%ncommitter %cn%ncommitter-date %ci%nsummary %s%nfilename ?`];
async function gitCommand(cwd: string, ...args: any[]) {
try {
const s = await spawnPromise(git.path, args, { cwd: cwd });
@@ -65,68 +67,84 @@ export default class Git {
static blame(format: GitBlameFormat, fileName: string, sha?: string, repoPath?: string) {
const [file, root]: [string, string] = Git.splitPath(Git.normalizePath(fileName), repoPath);
const params = [`blame`, `--root`, format];
if (sha) {
return gitCommand(root, 'blame', format, '--root', `${sha}^!`, '--', file);
params.push(`${sha}^!`);
}
return gitCommand(root, 'blame', format, '--root', '--', file);
return gitCommand(root, ...params, `--`, file);
}
static blameLines(format: GitBlameFormat, fileName: string, startLine: number, endLine: number, sha?: string, repoPath?: string) {
const [file, root]: [string, string] = Git.splitPath(Git.normalizePath(fileName), repoPath);
const params = [`blame`, `--root`, format, `-L ${startLine},${endLine}`];
if (sha) {
return gitCommand(root, 'blame', `-L ${startLine},${endLine}`, format, '--root', `${sha}^!`, '--', file);
params.push(`${sha}^!`);
}
return gitCommand(root, 'blame', `-L ${startLine},${endLine}`, format, '--root', '--', file);
return gitCommand(root, ...params, `--`, file);
}
static log(fileName: string, sha?: string, repoPath?: string) {
static log(fileName: string, sha?: string, repoPath?: string, maxCount?: number) {
const [file, root]: [string, string] = Git.splitPath(Git.normalizePath(fileName), repoPath);
if (sha) {
return gitCommand(root, 'log', `--follow`, `--name-only`, `--no-merges`, `--date=iso8601-strict`, `--format=%H -%nauthor %an%nauthor-date %ai%ncommitter %cn%ncommitter-date %ci%nsummary %s%nfilename ?`, `origin..${sha}`, '--', file);
const params = [...DefaultLogParams, `--follow`];
if (maxCount) {
params.push(`-n${maxCount}`);
}
return gitCommand(root, 'log', `--follow`, `--name-only`, `--no-merges`, `--date=iso8601-strict`, `--format=%H -%nauthor %an%nauthor-date %ai%ncommitter %cn%ncommitter-date %ci%nsummary %s%nfilename ?`, file);
if (sha) {
params.push(`origin..${sha}`);
params.push(`--`);
}
return gitCommand(root, ...params, file);
}
static logMostRecent(fileName: string, repoPath?: string) {
const [file, root]: [string, string] = Git.splitPath(Git.normalizePath(fileName, repoPath));
return gitCommand(root, 'log', `-n1`, `--follow`, `--name-only`, `--no-merges`, `--date=iso8601-strict`, `--format=%H -%nauthor %an%nauthor-date %ai%ncommitter %cn%ncommitter-date %ci%nsummary %s%nfilename ?`, file);
}
static logRange(fileName: string, start: number, end: number, sha?: string, repoPath?: string) {
static logRange(fileName: string, start: number, end: number, sha?: string, repoPath?: string, maxCount?: number) {
const [file, root]: [string, string] = Git.splitPath(Git.normalizePath(fileName), repoPath);
if (sha) {
return gitCommand(root, 'log', `--follow`, `--name-only`, `--no-merges`, `--date=iso8601-strict`, `--format=%H -%nauthor %an%nauthor-date %ai%ncommitter %cn%ncommitter-date %ci%nsummary %s%nfilename ?`, `origin..${sha}`, `-L ${start},${end}:${file}`);
const params = [...DefaultLogParams];
if (maxCount) {
params.push(`-n${maxCount}`);
}
return gitCommand(root, 'log', `--name-only`, `--no-merges`, `--date=iso8601-strict`, `--format=%H -%nauthor %an%nauthor-date %ai%ncommitter %cn%ncommitter-date %ci%nsummary %s%nfilename ?`, `-L ${start},${end}:${file}`);
if (sha) {
params.push(`--follow`);
params.push(`origin..${sha}`);
}
params.push(`-L ${start},${end}:${file}`);
return gitCommand(root, ...params);
}
static logRepo(repoPath: string) {
return gitCommand(repoPath, 'log', `--name-only`, `--no-merges`, `--date=iso8601-strict`, `--format=%H -%nauthor %an%nauthor-date %ai%ncommitter %cn%ncommitter-date %ci%nsummary %s%nfilename ?`);
static logRepo(repoPath: string, maxCount?: number) {
const params = [...DefaultLogParams];
if (maxCount) {
params.push(`-n${maxCount}`);
}
return gitCommand(repoPath, ...params);
}
static getVersionedFile(fileName: string, repoPath: string, sha: string) {
return new Promise<string>((resolve, reject) => {
Git.getVersionedFileText(fileName, repoPath, sha).then(data => {
const ext = path.extname(fileName);
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 => {
tmp.file({ prefix: `${path.basename(fileName, ext)}-${sha}__`, postfix: ext },
(err, destination, fd, cleanupCallback) => {
if (err) {
reject(err);
return;
}
resolve(destination);
Logger.log(`getVersionedFile(${fileName}, ${repoPath}, ${sha}); destination=${destination}`);
fs.appendFile(destination, data, err => {
if (err) {
reject(err);
return;
}
resolve(destination);
});
});
});
});
});
}
@@ -135,7 +153,7 @@ export default class Git {
const [file, root] = Git.splitPath(Git.normalizePath(fileName), repoPath);
sha = sha.replace('^', '');
if (Git.isUncommitted(sha)) return new Promise<string>((resolve, reject) => reject(new Error(`sha=${sha} is uncommitted`)));
if (Git.isUncommitted(sha)) return Promise.reject(new Error(`sha=${sha} is uncommitted`));
return gitCommand(root, 'show', `${sha}:./${file}`);
}

View File

@@ -47,7 +47,7 @@ export default class GitProvider extends Disposable {
private _cacheDisposable: Disposable | undefined;
private _uriCache: Map<string, UriCacheEntry> | undefined;
private _config: IConfig;
config: IConfig;
private _disposable: Disposable;
private _codeLensProviderDisposable: Disposable | undefined;
private _gitignore: Promise<ignore.Ignore>;
@@ -105,8 +105,8 @@ export default class GitProvider extends Disposable {
private _onConfigure() {
const config = workspace.getConfiguration().get<IConfig>('gitlens');
const codeLensChanged = !Objects.areEquivalent(config.codeLens, this._config && this._config.codeLens);
const advancedChanged = !Objects.areEquivalent(config.advanced, this._config && this._config.advanced);
const codeLensChanged = !Objects.areEquivalent(config.codeLens, this.config && this.config.codeLens);
const advancedChanged = !Objects.areEquivalent(config.advanced, this.config && this.config.advanced);
if (codeLensChanged || advancedChanged) {
Logger.log('CodeLens config changed; resetting CodeLens provider');
@@ -148,7 +148,7 @@ export default class GitProvider extends Disposable {
}
}
this._config = config;
this.config = config;
}
private _getCacheEntryKey(fileName: string) {
@@ -216,7 +216,7 @@ export default class GitProvider extends Disposable {
}
async getRepoPathFromFile(fileName: string): Promise<string | undefined> {
const log = await this.getMostRecentLogForFile(fileName);
const log = await this.getLogForFile(fileName, undefined, undefined, undefined, 1);
return log && log.repoPath;
}
@@ -241,7 +241,7 @@ export default class GitProvider extends Disposable {
const promise = this._gitignore.then(ignore => {
if (ignore && !ignore.filter([fileName]).length) {
Logger.log(`Skipping blame; '${fileName}' is gitignored`);
return <Promise<IGitBlame>>GitProvider.EmptyPromise;
return GitProvider.EmptyPromise as Promise<IGitBlame>;
}
return Git.blame(GitProvider.BlameFormat, fileName, sha, repoPath)
@@ -252,14 +252,14 @@ export default class GitProvider extends Disposable {
const msg = ex && ex.toString();
Logger.log(`Replace blame cache with empty promise for '${cacheKey}'`);
entry.blame = <ICachedBlame>{
entry.blame = {
//date: new Date(),
item: GitProvider.EmptyPromise,
errorMessage: msg
};
} as ICachedBlame;
this._gitCache.set(cacheKey, entry);
return <Promise<IGitBlame>>GitProvider.EmptyPromise;
return GitProvider.EmptyPromise as Promise<IGitBlame>;
}
return undefined;
});
@@ -268,10 +268,10 @@ export default class GitProvider extends Disposable {
if (useCaching) {
Logger.log(`Add blame cache for '${cacheKey}'`);
entry.blame = <ICachedBlame>{
entry.blame = {
//date: new Date(),
item: promise
};
} as ICachedBlame;
this._gitCache.set(cacheKey, entry);
}
@@ -288,11 +288,11 @@ export default class GitProvider extends Disposable {
if (!blameLine) return undefined;
const commit = blame.commits.get(blameLine.sha);
return <IGitBlameLine>{
return {
author: Object.assign({}, blame.authors.get(commit.author), { lineCount: commit.lines.length }),
commit: commit,
line: blameLine
};
} as IGitBlameLine;
}
fileName = Git.normalizePath(fileName);
@@ -306,11 +306,11 @@ export default class GitProvider extends Disposable {
if (repoPath) {
commit.repoPath = repoPath;
}
return <IGitBlameLine>{
return {
author: Iterables.first(blame.authors.values()),
commit: commit,
line: blame.lines[line]
};
} as IGitBlameLine;
}
catch (ex) {
return undefined;
@@ -364,12 +364,12 @@ export default class GitProvider extends Disposable {
.sort((a, b) => b.lineCount - a.lineCount)
.forEach(a => sortedAuthors.set(a.name, a));
return <IGitBlameLines>{
return {
authors: sortedAuthors,
commits: commits,
lines: lines,
allLines: blame.lines
};
} as IGitBlameLines;
}
async getBlameLocations(fileName: string, range: Range, sha?: string, repoPath?: string, selectedSha?: string, line?: number): Promise<Location[] | undefined> {
@@ -395,10 +395,15 @@ export default class GitProvider extends Disposable {
return locations;
}
async getLogForRepo(repoPath: string): Promise<IGitLog | undefined> {
Logger.log(`getLogForRepo('${repoPath}')`);
async getLogForRepo(repoPath: string, maxCount?: number): Promise<IGitLog | undefined> {
Logger.log(`getLogForRepo('${repoPath}', ${maxCount})`);
if (maxCount == null) {
maxCount = this.config.advanced.maxQuickHistory || 0;
}
try {
const data = await Git.logRepo(repoPath);
const data = await Git.logRepo(repoPath, maxCount);
return new GitLogParserEnricher().enrich(data, repoPath, true);
}
catch (ex) {
@@ -406,24 +411,11 @@ export default class GitProvider extends Disposable {
}
}
async getMostRecentLogForFile(fileName: string): Promise<IGitLog | undefined> {
Logger.log(`getMostRecentLogForFile('${fileName}')`);
getLogForFile(fileName: string, sha?: string, repoPath?: string, range?: Range, maxCount?: number): Promise<IGitLog | undefined> {
Logger.log(`getLogForFile('${fileName}', ${sha}, ${repoPath}, ${range && `[${range.start.line}, ${range.end.line}]`}, ${maxCount})`);
fileName = Git.normalizePath(fileName);
try {
const data = await Git.logMostRecent(fileName);
return new GitLogParserEnricher().enrich(data, fileName);
}
catch (ex) {
return undefined;
}
}
getLogForFile(fileName: string, sha?: string, repoPath?: string, range?: Range): Promise<IGitLog | undefined> {
Logger.log(`getLogForFile('${fileName}', ${sha}, ${repoPath}, ${range && `[${range.start.line}, ${range.end.line}]`})`);
fileName = Git.normalizePath(fileName);
const useCaching = this.UseGitCaching && !range;
const useCaching = this.UseGitCaching && !range && !maxCount;
let cacheKey: string;
let entry: GitCacheEntry;
@@ -440,12 +432,12 @@ export default class GitProvider extends Disposable {
const promise = this._gitignore.then(ignore => {
if (ignore && !ignore.filter([fileName]).length) {
Logger.log(`Skipping log; '${fileName}' is gitignored`);
return <Promise<IGitLog>>GitProvider.EmptyPromise;
return GitProvider.EmptyPromise as Promise<IGitLog>;
}
return (range
? Git.logRange(fileName, range.start.line + 1, range.end.line + 1, sha, repoPath)
: Git.log(fileName, sha, repoPath))
? Git.logRange(fileName, range.start.line + 1, range.end.line + 1, sha, repoPath, maxCount)
: Git.log(fileName, sha, repoPath, maxCount))
.then(data => new GitLogParserEnricher().enrich(data, fileName))
.catch(ex => {
// Trap and cache expected log errors
@@ -453,14 +445,14 @@ export default class GitProvider extends Disposable {
const msg = ex && ex.toString();
Logger.log(`Replace log cache with empty promise for '${cacheKey}'`);
entry.log = <ICachedLog>{
entry.log = {
//date: new Date(),
item: GitProvider.EmptyPromise,
errorMessage: msg
};
} as ICachedLog;
this._gitCache.set(cacheKey, entry);
return <Promise<IGitLog>>GitProvider.EmptyPromise;
return GitProvider.EmptyPromise as Promise<IGitLog>;
}
return undefined;
});
@@ -469,10 +461,10 @@ export default class GitProvider extends Disposable {
if (useCaching) {
Logger.log(`Add log cache for '${cacheKey}'`);
entry.log = <ICachedLog>{
entry.log = {
//date: new Date(),
item: promise
};
} as ICachedLog;
this._gitCache.set(cacheKey, entry);
}
@@ -521,8 +513,8 @@ export default class GitProvider extends Disposable {
}
toggleCodeLens(editor: TextEditor) {
if (this._config.codeLens.visibility !== CodeLensVisibility.OnDemand ||
(!this._config.codeLens.recentChange.enabled && !this._config.codeLens.authors.enabled)) return;
if (this.config.codeLens.visibility !== CodeLensVisibility.OnDemand ||
(!this.config.codeLens.recentChange.enabled && !this.config.codeLens.authors.enabled)) return;
Logger.log(`toggleCodeLens(${editor})`);
@@ -595,7 +587,7 @@ export class GitUri extends Uri {
super();
if (!uri) return;
const base = <any>this;
const base = this as any;
base._scheme = uri.scheme;
base._authority = uri.authority;
base._path = uri.path;