Fixes some issues with uncommited blames

Automatically turns off blame only when required now
This commit is contained in:
Eric Amodio
2016-09-07 15:45:07 -04:00
parent 26ce5f7d53
commit 19c0e46729
6 changed files with 92 additions and 30 deletions

View File

@@ -22,17 +22,19 @@ None yet.
## Known Issues ## Known Issues
- Content in the **Blame explorer** disappears after a bit: [Open vscode issue](https://github.com/Microsoft/vscode/issues/11360) - Content in the **Blame explorer** disappears after a bit: [vscode issue](https://github.com/Microsoft/vscode/issues/11360)
- Highlighted lines disappear in **Blame explorer** after changing selection and returning to a previous selection: [Open vscode issue](https://github.com/Microsoft/vscode/issues/11360) - Highlighted lines disappear in **Blame explorer** after changing selection and returning to a previous selection: [vscode issue](https://github.com/Microsoft/vscode/issues/11360)
- CodeLens aren't updated properly after a file is saved: [Open vscode issue](https://github.com/Microsoft/vscode/issues/11546) - 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 fails randomly): [Open vscode issue](https://github.com/Microsoft/vscode/issues/11485) - Visible whitespace causes issue with blame overlay (currently fixed with a hack, but fails randomly): [vscode issue](https://github.com/Microsoft/vscode/issues/11485)
## Release Notes ## Release Notes
### 0.0.5 ### 0.0.5
Fixes issues where filename changes in history would cause diffs to fails - Fixes issues where filename changes in history would cause diffs to fails
Removes CodeLens from fields and single-line properties to reduce visual noise - Fixes some issues with uncommited blames
- Removes CodeLens from fields and single-line properties to reduce visual noise
- Automatically turns off blame only when required now
### 0.0.4 ### 0.0.4

View File

@@ -13,13 +13,16 @@
"Other" "Other"
], ],
"keywords": [ "keywords": [
"git", "gitblame", "blame", "codelens" "git", "blame", "history", "log", "codelens"
], ],
"galleryBanner": { "galleryBanner": {
"color": "#0000FF", "color": "#0000FF",
"theme": "dark" "theme": "dark"
}, },
"preview": true, "preview": true,
"bugs": {
"url": "https://github.com/eamodio/vscode-gitlens/issues"
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/eamodio/vscode-gitlens.git" "url": "https://github.com/eamodio/vscode-gitlens.git"
@@ -28,22 +31,22 @@
"contributes": { "contributes": {
"commands": [{ "commands": [{
"command": "gitlens.diffWithPrevious", "command": "gitlens.diffWithPrevious",
"title": "GitLens: Open Diff with Previous Commit", "title": "Git: Open Diff with Previous Commit",
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.diffWithWorking", "command": "gitlens.diffWithWorking",
"title": "GitLens: Open Diff with Working Tree", "title": "Git: Open Diff with Working Tree",
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.showBlame", "command": "gitlens.showBlame",
"title": "GitLens: Show Git Blame", "title": "Git: Show Blame",
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.toggleBlame", "command": "gitlens.toggleBlame",
"title": "GitLens: Toggle Git Blame", "title": "Git: Toggle Blame",
"category": "GitLens" "category": "GitLens"
}], }],
"menus": { "menus": {

View File

@@ -36,6 +36,8 @@ abstract class EditorCommand extends Disposable {
abstract execute(editor: TextEditor, edit: TextEditorEdit, ...args): any; abstract execute(editor: TextEditor, edit: TextEditorEdit, ...args): any;
} }
const UncommitedRegex = /^[0]+$/;
export class DiffWithPreviousCommand extends EditorCommand { export class DiffWithPreviousCommand extends EditorCommand {
constructor(private git: GitProvider) { constructor(private git: GitProvider) {
super(Commands.DiffWithPrevious); super(Commands.DiffWithPrevious);
@@ -45,7 +47,14 @@ export class DiffWithPreviousCommand extends EditorCommand {
line = line || editor.selection.active.line; line = line || editor.selection.active.line;
if (!sha) { if (!sha) {
return this.git.getBlameForLine(uri.fsPath, line) return this.git.getBlameForLine(uri.fsPath, line)
.then(blame => commands.executeCommand(Commands.DiffWithPrevious, uri, blame.commit.sha, blame.commit.toUri(), blame.commit.previousSha, blame.commit.toPreviousUri(), line)); .then(blame => {
if (!blame) return;
if (UncommitedRegex.test(blame.commit.sha)) {
return commands.executeCommand(Commands.DiffWithWorking, uri, blame.commit.previousSha, blame.commit.toPreviousUri(), line);
}
return commands.executeCommand(Commands.DiffWithPrevious, uri, blame.commit.sha, blame.commit.toUri(), blame.commit.previousSha, blame.commit.toPreviousUri(), line);
});
} }
if (!compareWithSha) { if (!compareWithSha) {
@@ -69,7 +78,14 @@ export class DiffWithWorkingCommand extends EditorCommand {
line = line || editor.selection.active.line; line = line || editor.selection.active.line;
if (!sha) { if (!sha) {
return this.git.getBlameForLine(uri.fsPath, line) return this.git.getBlameForLine(uri.fsPath, line)
.then(blame => commands.executeCommand(Commands.DiffWithWorking, uri, blame.commit.sha, blame.commit.toUri(), line)); .then(blame => {
if (!blame) return;
if (UncommitedRegex.test(blame.commit.sha)) {
return commands.executeCommand(Commands.DiffWithWorking, uri, blame.commit.previousSha, blame.commit.toPreviousUri(), line);
}
return commands.executeCommand(Commands.DiffWithWorking, uri, blame.commit.sha, blame.commit.toUri(), line)
});
}; };
// TODO: Moving doesn't always seem to work -- or more accurately it seems like it moves down that number of lines from the current line // TODO: Moving doesn't always seem to work -- or more accurately it seems like it moves down that number of lines from the current line
@@ -92,7 +108,7 @@ export class ShowBlameCommand extends EditorCommand {
const activeLine = editor.selection.active.line; const activeLine = editor.selection.active.line;
return this.git.getBlameForLine(editor.document.fileName, activeLine) return this.git.getBlameForLine(editor.document.fileName, activeLine)
.then(blame => this.blameController.showBlame(editor, blame.commit.sha)); .then(blame => this.blameController.showBlame(editor, blame && blame.commit.sha));
} }
} }
@@ -132,6 +148,6 @@ export class ToggleBlameCommand extends EditorCommand {
const activeLine = editor.selection.active.line; const activeLine = editor.selection.active.line;
return this.git.getBlameForLine(editor.document.fileName, activeLine) return this.git.getBlameForLine(editor.document.fileName, activeLine)
.then(blame => this.blameController.toggleBlame(editor, blame.commit.sha)); .then(blame => this.blameController.toggleBlame(editor, blame && blame.commit.sha));
} }
} }

View File

@@ -1,5 +1,5 @@
'use strict' 'use strict'
import {commands, DecorationOptions, Diagnostic, DiagnosticCollection, DiagnosticSeverity, Disposable, ExtensionContext, languages, OverviewRulerLane, Position, Range, TextEditor, TextEditorDecorationType, Uri, window, workspace} from 'vscode'; import {commands, DecorationInstanceRenderOptions, DecorationOptions, Diagnostic, DiagnosticCollection, DiagnosticSeverity, Disposable, ExtensionContext, languages, OverviewRulerLane, Position, Range, TextEditor, TextEditorDecorationType, Uri, window, workspace} from 'vscode';
import {BuiltInCommands, Commands, DocumentSchemes} from './constants'; import {BuiltInCommands, Commands, DocumentSchemes} from './constants';
import GitProvider, {IGitBlame} from './gitProvider'; import GitProvider, {IGitBlame} from './gitProvider';
import GitCodeActionsProvider from './gitCodeActionProvider'; import GitCodeActionsProvider from './gitCodeActionProvider';
@@ -8,7 +8,6 @@ import * as moment from 'moment';
const blameDecoration: TextEditorDecorationType = window.createTextEditorDecorationType({ const blameDecoration: TextEditorDecorationType = window.createTextEditorDecorationType({
before: { before: {
color: '#5a5a5a',
margin: '0 1.75em 0 0', margin: '0 1.75em 0 0',
width: '5em' width: '5em'
}, },
@@ -46,10 +45,15 @@ export default class GitBlameController extends Disposable {
const subscriptions: Disposable[] = []; const subscriptions: Disposable[] = [];
subscriptions.push(window.onDidChangeActiveTextEditor(e => { // subscriptions.push(window.onDidChangeActiveTextEditor(e => {
if (!this._controller || this._controller.editor === e) return; // if (!e || !this._controller || this._controller.editor === e) return;
// this.clear();
// }));
workspace.onDidCloseTextDocument(d => {
if (!this._controller || this._controller.uri.fsPath !== d.uri.fsPath) return;
this.clear(); this.clear();
})); })
this._disposable = Disposable.from(...subscriptions); this._disposable = Disposable.from(...subscriptions);
} }
@@ -64,7 +68,7 @@ export default class GitBlameController extends Disposable {
this._controller = null; this._controller = null;
} }
showBlame(editor: TextEditor, sha: string) { showBlame(editor: TextEditor, sha?: string) {
if (!editor) { if (!editor) {
this.clear(); this.clear();
return; return;
@@ -76,7 +80,7 @@ export default class GitBlameController extends Disposable {
} }
} }
toggleBlame(editor: TextEditor, sha: string) { toggleBlame(editor: TextEditor, sha?: string) {
if (!editor || this._controller) { if (!editor || this._controller) {
this.clear(); this.clear();
return; return;
@@ -87,6 +91,7 @@ export default class GitBlameController extends Disposable {
} }
class GitBlameEditorController extends Disposable { class GitBlameEditorController extends Disposable {
public uri: Uri;
private _disposable: Disposable; private _disposable: Disposable;
private _blame: Promise<IGitBlame>; private _blame: Promise<IGitBlame>;
private _diagnostics: DiagnosticCollection; private _diagnostics: DiagnosticCollection;
@@ -95,7 +100,8 @@ class GitBlameEditorController extends Disposable {
constructor(private context: ExtensionContext, private git: GitProvider, public editor: TextEditor) { constructor(private context: ExtensionContext, private git: GitProvider, public editor: TextEditor) {
super(() => this.dispose()); super(() => this.dispose());
const fileName = this.editor.document.uri.fsPath; this.uri = this.editor.document.uri;
const fileName = this.uri.fsPath;
this._blame = this.git.getBlameForFile(fileName); this._blame = this.git.getBlameForFile(fileName);
const subscriptions: Disposable[] = []; const subscriptions: Disposable[] = [];
@@ -112,6 +118,8 @@ class GitBlameEditorController extends Disposable {
this.git.getBlameForLine(e.textEditor.document.fileName, activeLine) this.git.getBlameForLine(e.textEditor.document.fileName, activeLine)
.then(blame => { .then(blame => {
if (!blame) return;
// Add the bogus diagnostics to provide code actions for this sha // Add the bogus diagnostics to provide code actions for this sha
this._diagnostics.set(editor.document.uri, [this._getDiagnostic(editor, activeLine, blame.commit.sha)]); this._diagnostics.set(editor.document.uri, [this._getDiagnostic(editor, activeLine, blame.commit.sha)]);
@@ -124,6 +132,7 @@ class GitBlameEditorController extends Disposable {
dispose() { dispose() {
if (this.editor) { if (this.editor) {
// HACK: This only works when switching to another editor - diffs handle whitespace toggle differently
if (this._toggleWhitespace) { if (this._toggleWhitespace) {
commands.executeCommand(BuiltInCommands.ToggleRenderWhitespace); commands.executeCommand(BuiltInCommands.ToggleRenderWhitespace);
} }
@@ -142,7 +151,7 @@ class GitBlameEditorController extends Disposable {
return diag; return diag;
} }
applyBlame(sha: string) { applyBlame(sha?: string) {
return this._blame.then(blame => { return this._blame.then(blame => {
if (!blame.lines.length) return; if (!blame.lines.length) return;
@@ -152,12 +161,39 @@ class GitBlameEditorController extends Disposable {
commands.executeCommand(BuiltInCommands.ToggleRenderWhitespace); commands.executeCommand(BuiltInCommands.ToggleRenderWhitespace);
} }
let lastSha;
const blameDecorationOptions: DecorationOptions[] = blame.lines.map(l => { const blameDecorationOptions: DecorationOptions[] = blame.lines.map(l => {
let color = '#6b6b6b';
const c = blame.commits.get(l.sha); const c = blame.commits.get(l.sha);
return { if (c.previousSha) {
color = '#999999';
}
let gutter = '';
if (lastSha !== l.sha || true) { // TODO: Add a config option
gutter = l.sha.substring(0, 8);
if (gutter === '00000000') {
if (c.previousSha) {
const pc = blame.commits.get(c.previousSha);
if (pc && pc.lines.find(_ => _.line === l.line)) {
gutter = c.previousSha.substring(0, 8);
color = 'rgba(0, 188, 242, 0.6)';
}
else {
color = 'rgba(127, 186, 0, 0.6)';
}
} else {
color = 'rgba(127, 186, 0, 0.6)';
}
}
}
lastSha = l.sha;
return <DecorationOptions>{
range: this.editor.document.validateRange(new Range(l.line, 0, l.line, 0)), range: this.editor.document.validateRange(new Range(l.line, 0, l.line, 0)),
hoverMessage: `${c.message}\n${c.author}, ${moment(c.date).format('MMMM Do, YYYY hh:MM a')}`, hoverMessage: `${c.message}\n${c.author}, ${moment(c.date).format('MMMM Do, YYYY hh:MM a')}`,
renderOptions: { before: { contentText: `${l.sha.substring(0, 8)}`, } } renderOptions: { before: { color: color, contentText: gutter } }
}; };
}); });

View File

@@ -17,6 +17,8 @@ export default class GitCodeActionProvider implements CodeActionProvider {
return this.git.getBlameForLine(document.fileName, range.start.line) return this.git.getBlameForLine(document.fileName, range.start.line)
.then(blame => { .then(blame => {
const actions: Command[] = []; const actions: Command[] = [];
if (!blame) return actions;
if (blame.commit.sha) { if (blame.commit.sha) {
actions.push({ actions.push({
title: `GitLens: Diff ${blame.commit.sha} with working tree`, title: `GitLens: Diff ${blame.commit.sha} with working tree`,

View File

@@ -150,6 +150,8 @@ export default class GitProvider extends Disposable {
getBlameForLine(fileName: string, line: number): Promise<IGitBlameLine> { getBlameForLine(fileName: string, line: number): Promise<IGitBlameLine> {
return this.getBlameForFile(fileName).then(blame => { return this.getBlameForFile(fileName).then(blame => {
const blameLine = blame.lines[line]; const blameLine = blame.lines[line];
if (!blameLine) return undefined;
const commit = blame.commits.get(blameLine.sha); const commit = blame.commits.get(blameLine.sha);
return { return {
author: Object.assign({}, blame.authors.get(commit.author), { lineCount: commit.lines.length }), author: Object.assign({}, blame.authors.get(commit.author), { lineCount: commit.lines.length }),
@@ -176,7 +178,7 @@ export default class GitProvider extends Disposable {
blame.commits.forEach(c => { blame.commits.forEach(c => {
if (!shas.has(c.sha)) return; if (!shas.has(c.sha)) return;
const commit: IGitCommit = Object.assign({}, c, { lines: c.lines.filter(l => l.line >= range.start.line && l.line <= range.end.line) }); const commit: IGitCommit = new GitCommit(this.repoPath, c.sha, c.fileName, c.author, c.date, c.message, c.lines.filter(l => l.line >= range.start.line && l.line <= range.end.line));
commits.set(c.sha, commit); commits.set(c.sha, commit);
let author = authors.get(commit.author); let author = authors.get(commit.author);
@@ -208,7 +210,8 @@ export default class GitProvider extends Disposable {
getBlameForShaRange(fileName: string, sha: string, range: Range): Promise<IGitBlameCommitLines> { getBlameForShaRange(fileName: string, sha: string, range: Range): Promise<IGitBlameCommitLines> {
return this.getBlameForFile(fileName).then(blame => { return this.getBlameForFile(fileName).then(blame => {
const lines = blame.lines.slice(range.start.line, range.end.line + 1).filter(l => l.sha === sha); const lines = blame.lines.slice(range.start.line, range.end.line + 1).filter(l => l.sha === sha);
const commit = Object.assign({}, blame.commits.get(sha), { lines: lines }); let commit = blame.commits.get(sha);
commit = new GitCommit(this.repoPath, commit.sha, commit.fileName, commit.author, commit.date, commit.message, lines);
return { return {
author: Object.assign({}, blame.authors.get(commit.author), { lineCount: commit.lines.length }), author: Object.assign({}, blame.authors.get(commit.author), { lineCount: commit.lines.length }),
commit: commit, commit: commit,
@@ -345,8 +348,8 @@ class GitCommit implements IGitCommit {
previousSha?: string; previousSha?: string;
previousFileName?: string; previousFileName?: string;
constructor(private repoPath: string, public sha: string, public fileName: string, public author: string, public date: Date, public message: string) { constructor(private repoPath: string, public sha: string, public fileName: string, public author: string, public date: Date, public message: string, lines?: IGitCommitLine[]) {
this.lines = []; this.lines = lines || [];
} }
toPreviousUri(): Uri { toPreviousUri(): Uri {