Skip to content

Commit 71d17bc

Browse files
committed
Closes #139 - adds changed files node to repository status
Reworks commit-file nodes
1 parent a69afdb commit 71d17bc

23 files changed

+365
-79
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
66

77
## [Unreleased]
88
### Added
9+
- Adds new `Changed Files` node to the `Repository Status` node of the `GitLens` custom view's `Repository View` -- closes [#139](https://github.com/eamodio/vscode-gitlens/issues/139)
10+
- Provides a file-based view of all the changed files in the working tree and/or files in commits that haven't yet been pushed upstream
911
- Adds `gitlens.gitExplorer.enabled` setting to specify whether or not to show the `GitLens` custom view - closes [#144](https://github.com/eamodio/vscode-gitlens/issues/144)
1012

1113
## [5.1.0] - 2017-09-15

package.json

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1742,9 +1742,54 @@
17421742
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:status",
17431743
"group": "1_gitlens@1"
17441744
},
1745+
{
1746+
"command": "gitlens.gitExplorer.openChanges",
1747+
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file",
1748+
"group": "1_gitlens@1"
1749+
},
1750+
{
1751+
"command": "gitlens.gitExplorer.openChangesWithWorking",
1752+
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file",
1753+
"group": "1_gitlens@2"
1754+
},
1755+
{
1756+
"command": "gitlens.gitExplorer.openFile",
1757+
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file",
1758+
"group": "2_gitlens@1"
1759+
},
1760+
{
1761+
"command": "gitlens.gitExplorer.openFileRevision",
1762+
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file",
1763+
"group": "2_gitlens@2"
1764+
},
1765+
{
1766+
"command": "gitlens.openFileInRemote",
1767+
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:status-file",
1768+
"group": "3_gitlens@1"
1769+
},
1770+
{
1771+
"command": "gitlens.showQuickFileHistory",
1772+
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file && gitlens:gitExplorer:view == repository",
1773+
"group": "5_gitlens@1"
1774+
},
1775+
{
1776+
"command": "gitlens.showQuickCommitFileDetails",
1777+
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file",
1778+
"group": "5_gitlens@2"
1779+
},
1780+
{
1781+
"command": "gitlens.gitExplorer.openFile",
1782+
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file-commits",
1783+
"group": "1_gitlens@1"
1784+
},
1785+
{
1786+
"command": "gitlens.openFileInRemote",
1787+
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:status-file-commits",
1788+
"group": "1_gitlens@2"
1789+
},
17451790
{
17461791
"command": "gitlens.gitExplorer.refresh",
1747-
"when": "view == gitlens.gitExplorer && viewItem != gitlens:commit-file && viewItem != gitlens:stash-file",
1792+
"when": "view == gitlens.gitExplorer && viewItem != gitlens:commit-file && viewItem != gitlens:stash-file && viewItem != gitlens:status-file",
17481793
"group": "9_gitlens@1"
17491794
}
17501795
]

src/git/formatters/commit.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export class CommitFormatter extends Formatter<GitCommit, ICommitFormatOptions>
3939
}
4040

4141
get id() {
42-
return this._item.shortSha;
42+
return this._item.isUncommitted ? 'index' : this._item.shortSha;
4343
}
4444

4545
get message() {

src/gitService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export const RepoChangedReasons = {
7474
export class GitService extends Disposable {
7575

7676
static fakeSha = 'ffffffffffffffffffffffffffffffffffffffff';
77+
static uncommittedSha = '0000000000000000000000000000000000000000';
7778

7879
private _onDidBlameFail = new EventEmitter<string>();
7980
get onDidBlameFail(): Event<string> {

src/system/array.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22

33
export namespace Arrays {
4-
export function groupBy<T>(array: T[], accessor: (item: T) => any): T[] {
4+
export function groupBy<T>(array: T[], accessor: (item: T) => string): { [key: string]: T[] } {
55
return array.reduce((previous, current) => {
66
const value = accessor(current);
77
previous[value] = previous[value] || [];

src/views/branchHistoryNode.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Iterables } from '../system';
33
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
44
import { CommitNode } from './commitNode';
55
import { GlyphChars } from '../constants';
6-
import { ExplorerNode, ResourceType, ShowAllCommitsNode } from './explorerNode';
6+
import { ExplorerNode, ResourceType, ShowAllNode } from './explorerNode';
77
import { GitBranch, GitService, GitUri } from '../gitService';
88

99
export class BranchHistoryNode extends ExplorerNode {
@@ -12,18 +12,24 @@ export class BranchHistoryNode extends ExplorerNode {
1212

1313
maxCount: number | undefined = undefined;
1414

15-
constructor(public readonly branch: GitBranch, uri: GitUri, private readonly template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
15+
constructor(
16+
public readonly branch: GitBranch,
17+
uri: GitUri,
18+
protected readonly context: ExtensionContext,
19+
protected readonly git: GitService
20+
) {
1621
super(uri);
1722
}
1823

1924
async getChildren(): Promise<ExplorerNode[]> {
2025
const log = await this.git.getLogForRepo(this.uri.repoPath!, this.branch.name, this.maxCount);
2126
if (log === undefined) return [];
2227

23-
const children = Iterables.map(log.commits.values(), c => new CommitNode(c, this.template, this.context, this.git, this.branch));
24-
if (!log.truncated) return [...children];
25-
26-
return [...children, new ShowAllCommitsNode(this, this.context)];
28+
const children: (CommitNode | ShowAllNode)[] = [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.context, this.git, this.branch))];
29+
if (log.truncated) {
30+
children.push(new ShowAllNode('Show All Commits', this, this.context));
31+
}
32+
return children;
2733
}
2834

2935
async getTreeItem(): Promise<TreeItem> {

src/views/branchesNode.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ export class BranchesNode extends ExplorerNode {
99

1010
readonly resourceType: ResourceType = 'gitlens:branches';
1111

12-
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
12+
constructor(
13+
uri: GitUri,
14+
protected readonly context: ExtensionContext,
15+
protected readonly git: GitService
16+
) {
1317
super(uri);
1418
}
1519

@@ -18,7 +22,7 @@ export class BranchesNode extends ExplorerNode {
1822
if (branches === undefined) return [];
1923

2024
branches.sort((a, b) => (a.current ? -1 : 1) - (b.current ? -1 : 1) || a.name.localeCompare(b.name));
21-
return [...Iterables.filterMap(branches, b => b.remote ? undefined : new BranchHistoryNode(b, this.uri, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
25+
return [...Iterables.filterMap(branches, b => b.remote ? undefined : new BranchHistoryNode(b, this.uri, this.context, this.git))];
2226
}
2327

2428
async getTreeItem(): Promise<TreeItem> {

src/views/commitFileNode.ts

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,36 @@
22
import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
33
import { Commands, DiffWithPreviousCommandArgs } from '../commands';
44
import { ExplorerNode, ResourceType } from './explorerNode';
5-
import { getGitStatusIcon, GitBranch, GitCommit, GitService, GitUri, IGitStatusFile, StatusFileFormatter } from '../gitService';
5+
import { CommitFormatter, getGitStatusIcon, GitBranch, GitCommit, GitService, GitUri, ICommitFormatOptions, IGitStatusFile, StatusFileFormatter } from '../gitService';
66
import * as path from 'path';
77

8+
export enum CommitFileNodeDisplayAs {
9+
CommitLabel = 1 << 0,
10+
CommitIcon = 1 << 1,
11+
FileLabel = 1 << 2,
12+
StatusIcon = 1 << 3,
13+
14+
Commit = CommitLabel | CommitIcon,
15+
File = FileLabel | StatusIcon
16+
}
17+
818
export class CommitFileNode extends ExplorerNode {
919

1020
readonly resourceType: ResourceType = 'gitlens:commit-file';
1121

12-
constructor(public readonly status: IGitStatusFile, public commit: GitCommit, protected readonly context: ExtensionContext, protected readonly git: GitService, public readonly branch?: GitBranch) {
22+
constructor(
23+
public readonly status: IGitStatusFile,
24+
public commit: GitCommit,
25+
protected readonly context: ExtensionContext,
26+
protected readonly git: GitService,
27+
private displayAs: CommitFileNodeDisplayAs = CommitFileNodeDisplayAs.Commit,
28+
public readonly branch?: GitBranch
29+
) {
1330
super(new GitUri(Uri.file(path.resolve(commit.repoPath, status.fileName)), { repoPath: commit.repoPath, fileName: status.fileName, sha: commit.sha }));
1431
}
1532

16-
getChildren(): Promise<ExplorerNode[]> {
17-
return Promise.resolve([]);
33+
async getChildren(): Promise<ExplorerNode[]> {
34+
return [];
1835
}
1936

2037
async getTreeItem(): Promise<TreeItem> {
@@ -25,10 +42,20 @@ export class CommitFileNode extends ExplorerNode {
2542
}
2643
}
2744

28-
const item = new TreeItem(StatusFileFormatter.fromTemplate(this.git.config.gitExplorer.commitFileFormat, this.status), TreeItemCollapsibleState.None);
45+
const label = (this.displayAs & CommitFileNodeDisplayAs.CommitLabel)
46+
? CommitFormatter.fromTemplate(this.getCommitTemplate(), this.commit, {
47+
truncateMessageAtNewLine: true,
48+
dataFormat: this.git.config.defaultDateFormat
49+
} as ICommitFormatOptions)
50+
: StatusFileFormatter.fromTemplate(this.getCommitFileTemplate(), this.status);
51+
52+
const item = new TreeItem(label, TreeItemCollapsibleState.None);
2953
item.contextValue = this.resourceType;
3054

31-
const icon = getGitStatusIcon(this.status.status);
55+
const icon = (this.displayAs & CommitFileNodeDisplayAs.CommitIcon)
56+
? 'icon-commit.svg'
57+
: getGitStatusIcon(this.status.status);
58+
3259
item.iconPath = {
3360
dark: this.context.asAbsolutePath(path.join('images', 'dark', icon)),
3461
light: this.context.asAbsolutePath(path.join('images', 'light', icon))
@@ -39,6 +66,14 @@ export class CommitFileNode extends ExplorerNode {
3966
return item;
4067
}
4168

69+
protected getCommitTemplate() {
70+
return this.git.config.gitExplorer.commitFormat;
71+
}
72+
73+
protected getCommitFileTemplate() {
74+
return this.git.config.gitExplorer.commitFileFormat;
75+
}
76+
4277
getCommand(): Command | undefined {
4378
return {
4479
title: 'Compare File with Previous Revision',

src/views/commitNode.ts

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,58 +2,44 @@
22
import { Iterables } from '../system';
33
import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
44
import { Commands, DiffWithPreviousCommandArgs } from '../commands';
5-
import { CommitFileNode } from './commitFileNode';
5+
import { CommitFileNode, CommitFileNodeDisplayAs } from './commitFileNode';
66
import { ExplorerNode, ResourceType } from './explorerNode';
7-
import { CommitFormatter, getGitStatusIcon, GitBranch, GitLogCommit, GitService, GitUri, ICommitFormatOptions } from '../gitService';
8-
import * as path from 'path';
7+
import { CommitFormatter, GitBranch, GitLogCommit, GitService, GitUri, ICommitFormatOptions } from '../gitService';
98

109
export class CommitNode extends ExplorerNode {
1110

1211
readonly resourceType: ResourceType = 'gitlens:commit';
1312

14-
constructor(public readonly commit: GitLogCommit, private readonly template: string, protected readonly context: ExtensionContext, protected readonly git: GitService, public readonly branch?: GitBranch) {
13+
constructor(
14+
public readonly commit: GitLogCommit,
15+
protected readonly context: ExtensionContext,
16+
protected readonly git: GitService,
17+
public readonly branch?: GitBranch
18+
) {
1519
super(new GitUri(commit.uri, commit));
1620
}
1721

1822
async getChildren(): Promise<ExplorerNode[]> {
19-
if (this.commit.type === 'file') Promise.resolve([]);
20-
2123
const log = await this.git.getLogForRepo(this.commit.repoPath, this.commit.sha, 1);
2224
if (log === undefined) return [];
2325

2426
const commit = Iterables.first(log.commits.values());
2527
if (commit === undefined) return [];
2628

27-
return [...Iterables.map(commit.fileStatuses, s => new CommitFileNode(s, commit, this.context, this.git, this.branch))];
29+
return [...Iterables.map(commit.fileStatuses, s => new CommitFileNode(s, commit, this.context, this.git, CommitFileNodeDisplayAs.File, this.branch))];
2830
}
2931

3032
getTreeItem(): TreeItem {
31-
const item = new TreeItem(CommitFormatter.fromTemplate(this.template, this.commit, {
33+
const item = new TreeItem(CommitFormatter.fromTemplate(this.git.config.gitExplorer.commitFormat, this.commit, {
3234
truncateMessageAtNewLine: true,
3335
dataFormat: this.git.config.defaultDateFormat
34-
} as ICommitFormatOptions));
35-
36-
if (this.commit.type === 'file') {
37-
item.collapsibleState = TreeItemCollapsibleState.None;
38-
item.command = this.getCommand();
39-
const resourceType: ResourceType = 'gitlens:commit-file';
40-
item.contextValue = resourceType;
36+
} as ICommitFormatOptions), TreeItemCollapsibleState.Collapsed);
4137

42-
const icon = getGitStatusIcon(this.commit.status!);
43-
item.iconPath = {
44-
dark: this.context.asAbsolutePath(path.join('images', 'dark', icon)),
45-
light: this.context.asAbsolutePath(path.join('images', 'light', icon))
46-
};
47-
}
48-
else {
49-
item.collapsibleState = TreeItemCollapsibleState.Collapsed;
50-
item.contextValue = this.resourceType;
51-
52-
item.iconPath = {
53-
dark: this.context.asAbsolutePath('images/dark/icon-commit.svg'),
54-
light: this.context.asAbsolutePath('images/light/icon-commit.svg')
55-
};
56-
}
38+
item.contextValue = this.resourceType;
39+
item.iconPath = {
40+
dark: this.context.asAbsolutePath('images/dark/icon-commit.svg'),
41+
light: this.context.asAbsolutePath('images/light/icon-commit.svg')
42+
};
5743

5844
return item;
5945
}

src/views/explorerNode.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ export declare type ResourceType =
2020
'gitlens:stash-file' |
2121
'gitlens:stashes' |
2222
'gitlens:status' |
23+
'gitlens:status-file' |
24+
'gitlens:status-files' |
25+
'gitlens:status-file-commits' |
2326
'gitlens:status-upstream';
2427

2528
export abstract class ExplorerNode {
@@ -88,11 +91,11 @@ export class PagerNode extends ExplorerNode {
8891
}
8992
}
9093

91-
export class ShowAllCommitsNode extends PagerNode {
94+
export class ShowAllNode extends PagerNode {
9295

9396
args: RefreshNodeCommandArgs = { maxCount: 0 };
9497

95-
constructor(node: ExplorerNode, context: ExtensionContext) {
96-
super(`Show All Commits ${GlyphChars.Space}${GlyphChars.Dash}${GlyphChars.Space} this may take a while`, node, context);
98+
constructor(message: string, node: ExplorerNode, context: ExtensionContext) {
99+
super(`${message} ${GlyphChars.Space}${GlyphChars.Dash}${GlyphChars.Space} this may take a while`, node, context);
97100
}
98101
}

0 commit comments

Comments
 (0)