Skip to content

Commit

Permalink
#5 Open diff view from commit details file tree
Browse files Browse the repository at this point in the history
  • Loading branch information
mhutchie committed Feb 17, 2019
1 parent b01e817 commit 9495d3b
Show file tree
Hide file tree
Showing 12 changed files with 315 additions and 159 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Change Log

## Coming Soon
* Open the VSCode diff view for a file selected in the commit details view.
## 1.3.1 - 2019-02-17
* View the Visual Studio Code Diff of a file change in a commit, by clicking on the file in the commit details view.

## 1.3.0 - 2019-02-16
* Commit details view (click on a commit to open it). This shows the full commit details, and a tree view of all file changes in the commit.
Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ View a Git Graph of your repository, and easily perform Git actions from the gra
* Local & Remote Branches
* Local Refs: Heads, Tags & Remotes
* Local Uncommitted Changes
* View commit details and file changes by clicking on a commit
* View the Visual Studio Code Diff of a file change by clicking on it in the commit details view
* Perform Git Actions (available by right clicking on a commit / branch / tag):
* Create, Checkout, Rename & Delete Branches
* Add & Delete Tags
Expand Down Expand Up @@ -38,9 +40,5 @@ This extension contributes the following commands:

* `git-graph.view`: Git Graph: View Git Graph

## Coming Soon

* Open the VSCode diff view for a file selected in the commit details view.

## Release Notes
Detailed Release Notes are available [here](CHANGELOG.md).
13 changes: 10 additions & 3 deletions media/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -191,19 +191,26 @@ svg.openFolderIcon, svg.closedFolderIcon, svg.fileIcon{
.gitFile.D{
color:var(--vscode-gitDecoration-deletedResourceForeground);
}
.gitFileAddDel{
.gitFile.gitDiffPossible{
cursor:pointer;
}
.gitFileAddDel, .gitFileRename{
color:var(--vscode-editor-foreground);
margin-left:12px;
margin-left:8px;
}
.gitFileAdditions, .gitFileDeletions{
padding:0 3px;
cursor:help;
}
.gitFileAdditions{
color:var(--vscode-gitDecoration-addedResourceForeground);
}
.gitFileDeletions{
color:var(--vscode-gitDecoration-deletedResourceForeground);
}
.gitFileRename{
cursor:help;
}

/* Ref labels */
.gitRef {
Expand Down Expand Up @@ -334,7 +341,7 @@ svg.openFolderIcon, svg.closedFolderIcon, svg.fileIcon{
text-align:center;
max-width:360px;
z-index:10;
box-shadow:0 0 50px 15px var(--vscode-widget-shadow);
box-shadow:0 0 30px 5px var(--vscode-widget-shadow);
}
#dialog label{
margin-top:10px;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "git-graph",
"displayName": "Git Graph",
"version": "1.3.0",
"version": "1.3.1",
"publisher": "mhutchie",
"author": {
"name": "Michael Hutchison",
Expand Down
41 changes: 25 additions & 16 deletions src/dataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,18 +90,18 @@ export class DataSource {
}
}

if(unsavedChanges !== null){
if (unsavedChanges !== null) {
commitNodes[0].current = true;
}else if(currentBranchHash !== null && typeof commitLookup[currentBranchHash] === 'number'){
} else if (currentBranchHash !== null && typeof commitLookup[currentBranchHash] === 'number') {
commitNodes[commitLookup[currentBranchHash]].current = true;
}

return { commits: commitNodes, moreCommitsAvailable: moreCommitsAvailable };
}

public commitDetails(commitHash: string){
public commitDetails(commitHash: string) {
try {
let lines = cp.execSync('git show --quiet '+commitHash+' --format="'+gitCommitDetailsFormat+'"', { cwd: this.workspaceDir }).toString().split(eolRegex);
let lines = cp.execSync('git show --quiet ' + commitHash + ' --format="' + gitCommitDetailsFormat + '"', { cwd: this.workspaceDir }).toString().split(eolRegex);
let commitInfo = lines[0].split(gitLogSeparator);
let details: GitCommitDetails = {
hash: commitInfo[0],
Expand All @@ -114,29 +114,38 @@ export class DataSource {
fileChanges: []
};

let fileLookup:{[file:string]:number} = {};
lines = cp.execSync('git diff-tree --name-status -r -m --root '+commitHash, { cwd: this.workspaceDir }).toString().split(eolRegex);
let fileLookup: { [file: string]: number } = {};
lines = cp.execSync('git diff-tree --name-status -r -m --root --find-renames --diff-filter=AMDR ' + commitHash, { cwd: this.workspaceDir }).toString().split(eolRegex);
for (let i = 1; i < lines.length - 1; i++) {
let line = lines[i].split('\t');
if(line.length !== 2) break;
fileLookup[line[1]] = details.fileChanges.length;
details.fileChanges.push({fileName: line[1], type: <GitFileChangeType>line[0], additions:null, deletions:null});
if (line.length < 2) break;
let oldFilePath = line[1].replace(/\\/g, '/'), newFilePath = line[line.length-1].replace(/\\/g, '/');
fileLookup[newFilePath] = details.fileChanges.length;
details.fileChanges.push({ oldFilePath: oldFilePath, newFilePath: newFilePath, type: <GitFileChangeType>line[0][0], additions: null, deletions: null });
}
lines = cp.execSync('git diff-tree --numstat -r -m --root '+commitHash, { cwd: this.workspaceDir }).toString().split(eolRegex);
lines = cp.execSync('git diff-tree --numstat -r -m --root --find-renames --diff-filter=AMDR ' + commitHash, { cwd: this.workspaceDir }).toString().split(eolRegex);
for (let i = 1; i < lines.length - 1; i++) {
let line = lines[i].split('\t');
if(line.length !== 3) break;
if(typeof fileLookup[line[2]] === 'number'){
details.fileChanges[fileLookup[line[2]]].additions = parseInt(line[0]);
details.fileChanges[fileLookup[line[2]]].deletions = parseInt(line[1]);
if (line.length !== 3) break;
let fileName = line[2].replace(/(.*){.* => (.*)}/, '$1$2').replace(/.* => (.*)/, '$1');
if (typeof fileLookup[fileName] === 'number') {
details.fileChanges[fileLookup[fileName]].additions = parseInt(line[0]);
details.fileChanges[fileLookup[fileName]].deletions = parseInt(line[1]);
}
}
return details;
} catch (e) {
} catch (e) {
return null;
}
}

public getFile(commitHash: string, filePath: string) {
try {
return cp.execSync('git show "' + commitHash + '":"' + filePath + '"', { cwd: this.workspaceDir }).toString();
} catch (e) {
return '';
}
}

public addTag(tagName: string, commitHash: string): GitCommandStatus {
return this.runGitCommand('git tag -a ' + escapeRefName(tagName) + ' -m "" ' + commitHash);
Expand Down Expand Up @@ -226,6 +235,6 @@ export class DataSource {
}
}

function escapeRefName(str: string){
function escapeRefName(str: string) {
return str.replace(/'/g, '\'');
}
57 changes: 57 additions & 0 deletions src/diffDocProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as vscode from 'vscode';
import { DataSource } from './dataSource';

export class DiffDocProvider implements vscode.TextDocumentContentProvider {
static scheme = 'git-graph';
private dataSource: DataSource | null;
private onDidChangeEventEmitter = new vscode.EventEmitter<vscode.Uri>();
private docs = new Map<string, DiffDocument>();
private subscriptions: vscode.Disposable;

constructor(dataSource: DataSource | null) {
this.dataSource = dataSource;
this.subscriptions = vscode.workspace.onDidCloseTextDocument(doc => this.docs.delete(doc.uri.toString()));
}

dispose() {
this.subscriptions.dispose();
this.docs.clear();
this.onDidChangeEventEmitter.dispose();
}

get onDidChange() {
return this.onDidChangeEventEmitter.event;
}

public provideTextDocumentContent(uri: vscode.Uri): string | Thenable<string> {
let document = this.docs.get(uri.toString());
if (document) {
return document.value;
}

let request = decodeDiffDocUri(uri);
document = new DiffDocument(this.dataSource !== null ? this.dataSource.getFile(request.commit, request.filePath) : '');
this.docs.set(uri.toString(), document);
return document.value;
}
}

class DiffDocument {
private body: string;

constructor(body: string) {
this.body = body;
}

get value() {
return this.body;
}
}

export function encodeDiffDocUri(path: string, commit: string): vscode.Uri {
return vscode.Uri.parse(DiffDocProvider.scheme + ':'+path.replace(/\\/g, '/')+'?'+commit);
}

export function decodeDiffDocUri(uri: vscode.Uri) {
return {filePath: uri.path, commit:uri.query};
}
11 changes: 10 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import * as vscode from 'vscode';
import { Config } from './config';
import { DataSource } from './dataSource';
import { DiffDocProvider } from './diffDocProvider';
import { GitGraphView } from './gitGraphView';

export function activate(context: vscode.ExtensionContext) {
let workspaceFolders = vscode.workspace.workspaceFolders;
const dataSource = workspaceFolders !== undefined && workspaceFolders.length > 0 ? new DataSource(workspaceFolders[0].uri.fsPath) : null;

context.subscriptions.push(vscode.commands.registerCommand('git-graph.view', () => {
GitGraphView.createOrShow(context.extensionPath);
GitGraphView.createOrShow(context.extensionPath, dataSource);
}));

context.subscriptions.push(vscode.Disposable.from(
vscode.workspace.registerTextDocumentContentProvider(DiffDocProvider.scheme, new DiffDocProvider(dataSource))
));

let statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 1);
statusBarItem.text = 'Git Graph';
statusBarItem.tooltip = 'View Git Graph';
Expand Down
32 changes: 21 additions & 11 deletions src/gitGraphView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import * as path from 'path';
import * as vscode from 'vscode';
import { Config } from './config';
import { DataSource } from './dataSource';
import { GitGraphViewSettings, RequestMessage, ResponseMessage } from './types';
import { encodeDiffDocUri } from './diffDocProvider';
import { GitFileChangeType, GitGraphViewSettings, RequestMessage, ResponseMessage } from './types';
import { abbrevCommit } from './utils';

export class GitGraphView {
public static currentPanel: GitGraphView | undefined;
Expand All @@ -12,7 +14,7 @@ export class GitGraphView {
private readonly dataSource: DataSource | null;
private disposables: vscode.Disposable[] = [];

public static createOrShow(extensionPath: string) {
public static createOrShow(extensionPath: string, dataSource: DataSource | null) {
const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined;

if (GitGraphView.currentPanel) {
Expand All @@ -27,14 +29,13 @@ export class GitGraphView {
]
});

GitGraphView.currentPanel = new GitGraphView(panel, extensionPath);
GitGraphView.currentPanel = new GitGraphView(panel, extensionPath, dataSource);
}

private constructor(panel: vscode.WebviewPanel, extensionPath: string) {
private constructor(panel: vscode.WebviewPanel, extensionPath: string, dataSource: DataSource | null) {
this.panel = panel;
this.extensionPath = extensionPath;
let workspaceFolders = vscode.workspace.workspaceFolders;
this.dataSource = workspaceFolders !== undefined && workspaceFolders.length > 0 ? new DataSource(workspaceFolders[0].uri.fsPath) : null;
this.dataSource = dataSource;

this.update();
this.panel.onDidDispose(() => this.dispose(), null, this.disposables);
Expand Down Expand Up @@ -110,6 +111,9 @@ export class GitGraphView {
data: this.dataSource.commitDetails(message.data)
});
return;
case 'viewDiff':
this.viewDiff(message.data.commitHash, message.data.oldFilePath, message.data.newFilePath, message.data.type);
return;
}
}, null, this.disposables);
}
Expand Down Expand Up @@ -188,11 +192,17 @@ export class GitGraphView {
}

private copyCommitHashToClipboard(str: string) {
vscode.env.clipboard.writeText(str)
.then(
() => this.sendMessage({ command: 'copyCommitHashToClipboard', data: true }),
() => this.sendMessage({ command: 'copyCommitHashToClipboard', data: false })
);
vscode.env.clipboard.writeText(str).then(
() => this.sendMessage({ command: 'copyCommitHashToClipboard', data: true }),
() => this.sendMessage({ command: 'copyCommitHashToClipboard', data: false })
);
}

private viewDiff(commitHash: string, oldFilePath: string, newFilePath: string, type: GitFileChangeType) {
let abbrevHash = abbrevCommit(commitHash);
let pathComponents = newFilePath.split('/');
let title = pathComponents[pathComponents.length - 1] + ' (' + (type === 'A' ? 'Added in ' + abbrevHash : type === 'D' ? 'Deleted in ' + abbrevHash : abbrevCommit(commitHash) + '^ ↔ ' + abbrevCommit(commitHash)) + ')';
vscode.commands.executeCommand('vscode.diff', encodeDiffDocUri(oldFilePath, commitHash + '^'), encodeDiffDocUri(newFilePath, commitHash), title, { preview: true });
}
}

Expand Down
25 changes: 21 additions & 4 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ export interface GitGraphViewSettings {
}

export interface GitFileChange{
fileName: string;
oldFilePath: string;
newFilePath: string;
type: GitFileChangeType;
additions: number | null;
deletions: number | null;
Expand Down Expand Up @@ -187,6 +188,20 @@ export interface ResponseCommitDetails {
data: GitCommitDetails | null;
}

export interface RequestViewDiff {
command: 'viewDiff';
data: {
commitHash: string,
oldFilePath: string,
newFilePath: string,
type: GitFileChangeType
};
}
export interface ResponseViewDiff {
command: 'viewDiff';
data: boolean;
}


/* Types */

Expand All @@ -200,7 +215,8 @@ export type RequestMessage = RequestLoadBranchesMessage
| RequestDeleteBranch
| RequestRenameBranch
| RequestResetToCommit
| RequestCommitDetails;
| RequestCommitDetails
| RequestViewDiff;
export type ResponseMessage = ResponseLoadBranchesMessage
| ResponseLoadCommitsMessage
| ResponseAddTag
Expand All @@ -211,9 +227,10 @@ export type ResponseMessage = ResponseLoadBranchesMessage
| ResponseDeleteBranch
| ResponseRenameBranch
| ResponseResetToCommit
| ResponseCommitDetails;
| ResponseCommitDetails
| ResponseViewDiff;
export type DateFormat = 'Date & Time' | 'Date Only' | 'Relative';
export type GraphStyle = 'rounded' | 'angular';
export type GitCommandStatus = string | null;
export type GitResetMode = 'soft' | 'mixed' | 'hard';
export type GitFileChangeType = 'A' | 'M' | 'D' | 'R' | 'C';
export type GitFileChangeType = 'A' | 'M' | 'D' | 'R';
3 changes: 3 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function abbrevCommit(commitHash: string) {
return commitHash.substring(0, 8);
}
Loading

0 comments on commit 9495d3b

Please sign in to comment.