Skip to content

Commit de83305

Browse files
committed
Adds new branch history node under status
1 parent 4e1e52f commit de83305

File tree

5 files changed

+101
-65
lines changed

5 files changed

+101
-65
lines changed

CHANGELOG.md

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

77
## [Unreleased]
88
### Added
9+
- Adds new branch history node under the **Repository Status** node in the *GitLens* explorer
910
- Adds GitLab and Visual Studio Team Services icons to the remote nodes in the *GitLens* explorer — thanks to [PR #421](https://github.com/eamodio/vscode-gitlens/pull/421) by Maxim Pekurin ([@pmaxim25](https://github.com/pmaxim25))
1011

1112
### Fixed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ The repository view provides a full Git repository explorer, which has the follo
185185
- A context menu provides *Open Repository in Remote*, and *Refresh* commands
186186
- **Changed Files** — lists all the "working" changes
187187
- Expands to a file-based view of all changed files in the working tree ([optionally](#gitlens-explorer-settings "Jump to the GitLens explorer settings")) and/or all files in all commits ahead of the upstream
188+
- **History (current-branch)** — lists the revision (commit) history of the current branch
189+
- See the *Branches expand* section under **Branches** below for more details
188190

189191
- **Branches** — lists the local branches
190192
- Indicates which branch is the current branch and [optionally](#gitlens-explorer-settings "Jump to the GitLens explorer settings") shows the remote tracking branch

src/views/branchNode.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export class BranchNode extends ExplorerRefNode {
1616
constructor(
1717
public readonly branch: GitBranch,
1818
uri: GitUri,
19-
private readonly explorer: GitExplorer
19+
protected readonly explorer: GitExplorer
2020
) {
2121
super(uri);
2222
}
@@ -32,6 +32,10 @@ export class BranchNode extends ExplorerRefNode {
3232
return GitBranch.isValid(branchName) && !this.current ? this.branch.getBasename() : branchName;
3333
}
3434

35+
get markCurrent(): boolean {
36+
return true;
37+
}
38+
3539
get ref(): string {
3640
return this.branch.name;
3741
}
@@ -79,7 +83,7 @@ export class BranchNode extends ExplorerRefNode {
7983
}
8084
}
8185

82-
const item = new TreeItem(`${this.current ? `${GlyphChars.Check} ${GlyphChars.Space}` : ''}${name}`, TreeItemCollapsibleState.Collapsed);
86+
const item = new TreeItem(`${this.markCurrent && this.current ? `${GlyphChars.Check} ${GlyphChars.Space}` : ''}${name}`, TreeItemCollapsibleState.Collapsed);
8387
item.tooltip = tooltip;
8488

8589
if (this.branch.remote) {

src/views/branchesNode.ts

Lines changed: 46 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,64 +11,63 @@ import { GitUri, Repository } from '../gitService';
1111

1212
export class BranchesNode extends ExplorerNode {
1313

14-
constructor(
15-
uri: GitUri,
16-
private readonly repo: Repository,
17-
private readonly explorer: GitExplorer,
18-
private readonly active: boolean = false
19-
) {
20-
super(uri);
21-
}
22-
23-
get id(): string {
24-
return `gitlens:repository(${this.repo.path})${this.active ? ':active' : ''}:branches`;
25-
}
14+
constructor(
15+
uri: GitUri,
16+
private readonly repo: Repository,
17+
private readonly explorer: GitExplorer,
18+
private readonly active: boolean = false
19+
) {
20+
super(uri);
21+
}
2622

27-
async getChildren(): Promise<ExplorerNode[]> {
28-
const branches = await this.repo.getBranches();
29-
if (branches === undefined) return [];
23+
get id(): string {
24+
return `gitlens:repository(${this.repo.path})${this.active ? ':active' : ''}:branches`;
25+
}
3026

31-
branches.sort((a, b) => (a.current ? -1 : 1) - (b.current ? -1 : 1) || a.name.localeCompare(b.name));
27+
async getChildren(): Promise<ExplorerNode[]> {
28+
const branches = await this.repo.getBranches();
29+
if (branches === undefined) return [];
3230

33-
// filter local branches
34-
const branchNodes = [...Iterables.filterMap(branches, b => b.remote ? undefined : new BranchNode(b, this.uri, this.explorer))];
35-
if (this.explorer.config.branches.layout === ExplorerBranchesLayout.List) return branchNodes;
31+
branches.sort((a, b) => (a.current ? -1 : 1) - (b.current ? -1 : 1) || a.name.localeCompare(b.name));
3632

37-
// Take out the current branch, since that should always be first and un-nested
38-
const current = (branchNodes.length > 0 && branchNodes[0].current)
39-
? branchNodes.splice(0, 1)[0]
40-
: undefined;
33+
// filter local branches
34+
const branchNodes = [...Iterables.filterMap(branches, b => b.remote ? undefined : new BranchNode(b, this.uri, this.explorer))];
35+
if (this.explorer.config.branches.layout === ExplorerBranchesLayout.List) return branchNodes;
4136

42-
const hierarchy = Arrays.makeHierarchical(branchNodes,
43-
n => n.branch.isValid() ? n.branch.getName().split('/') : [n.branch.name],
44-
(...paths: string[]) => paths.join('/'),
45-
this.explorer.config.files.compact);
37+
// Take out the current branch, since that should always be first and un-nested
38+
const current = (branchNodes.length > 0 && branchNodes[0].current)
39+
? branchNodes.splice(0, 1)[0]
40+
: undefined;
4641

47-
const root = new BranchOrTagFolderNode(this.repo.path, '', undefined, hierarchy, this.explorer);
48-
const children = await root.getChildren() as (BranchOrTagFolderNode | BranchNode)[];
42+
const hierarchy = Arrays.makeHierarchical(branchNodes,
43+
n => n.branch.isValid() ? n.branch.getName().split('/') : [n.branch.name],
44+
(...paths: string[]) => paths.join('/'),
45+
this.explorer.config.files.compact);
4946

50-
// If we found a current branch, insert it at the start
51-
if (current !== undefined) {
52-
children.splice(0, 0, current);
53-
}
47+
const root = new BranchOrTagFolderNode(this.repo.path, '', undefined, hierarchy, this.explorer);
48+
const children = await root.getChildren() as (BranchOrTagFolderNode | BranchNode)[];
5449

55-
return children;
50+
// If we found a current branch, insert it at the start
51+
if (current !== undefined) {
52+
children.splice(0, 0, current);
5653
}
5754

58-
async getTreeItem(): Promise<TreeItem> {
59-
// HACK: Until https://github.com/Microsoft/vscode/issues/30918 is fixed
60-
const item = new TreeItem(`Branches`, this.active ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.Collapsed);
55+
return children;
56+
}
57+
58+
async getTreeItem(): Promise<TreeItem> {
59+
const item = new TreeItem(`Branches`, TreeItemCollapsibleState.Collapsed);
6160

62-
const remotes = await this.repo.getRemotes();
63-
item.contextValue = (remotes !== undefined && remotes.length > 0)
64-
? ResourceType.BranchesWithRemotes
65-
: ResourceType.Branches;
61+
const remotes = await this.repo.getRemotes();
62+
item.contextValue = (remotes !== undefined && remotes.length > 0)
63+
? ResourceType.BranchesWithRemotes
64+
: ResourceType.Branches;
6665

67-
item.iconPath = {
68-
dark: Container.context.asAbsolutePath('images/dark/icon-branch.svg'),
69-
light: Container.context.asAbsolutePath('images/light/icon-branch.svg')
70-
};
66+
item.iconPath = {
67+
dark: Container.context.asAbsolutePath('images/dark/icon-branch.svg'),
68+
light: Container.context.asAbsolutePath('images/light/icon-branch.svg')
69+
};
7170

72-
return item;
73-
}
71+
return item;
7472
}
73+
}

src/views/statusNode.ts

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { Disposable, TreeItem, TreeItemCollapsibleState } from 'vscode';
2+
import { BranchNode } from './branchNode';
23
import { GlyphChars } from '../constants';
34
import { Container } from '../container';
45
import { ExplorerNode, ResourceType } from './explorerNode';
56
import { GitExplorer } from './gitExplorer';
6-
import { GitUri, Repository, RepositoryFileSystemChangeEvent } from '../gitService';
7+
import { GitBranch, GitUri, Repository, RepositoryFileSystemChangeEvent } from '../gitService';
78
import { StatusFilesNode } from './statusFilesNode';
89
import { StatusUpstreamNode } from './statusUpstreamNode';
910

@@ -25,24 +26,33 @@ export class StatusNode extends ExplorerNode {
2526
async getChildren(): Promise<ExplorerNode[]> {
2627
this.resetChildren();
2728

29+
this.children = [];
30+
2831
const status = await this.repo.getStatus();
29-
if (status === undefined) return [];
32+
if (status !== undefined) {
33+
if (status.state.behind) {
34+
this.children.push(new StatusUpstreamNode(status, 'behind', this.explorer, this.active));
35+
}
3036

31-
this.children = [];
37+
if (status.state.ahead) {
38+
this.children.push(new StatusUpstreamNode(status, 'ahead', this.explorer, this.active));
39+
}
3240

33-
if (status.state.behind) {
34-
this.children.push(new StatusUpstreamNode(status, 'behind', this.explorer, this.active));
41+
if (status.state.ahead || (status.files.length !== 0 && this.includeWorkingTree)) {
42+
const range = status.upstream
43+
? `${status.upstream}..${status.branch}`
44+
: undefined;
45+
this.children.push(new StatusFilesNode(status, range, this.explorer, this.active));
46+
}
3547
}
3648

37-
if (status.state.ahead) {
38-
this.children.push(new StatusUpstreamNode(status, 'ahead', this.explorer, this.active));
39-
}
49+
let branch = await this.repo.getBranch();
50+
if (branch !== undefined) {
51+
if (status !== undefined) {
52+
branch = new GitBranch(branch.repoPath, branch.name, branch.current, branch.sha, branch.tracking, status.state.ahead, status.state.behind);
53+
}
4054

41-
if (status.state.ahead || (status.files.length !== 0 && this.includeWorkingTree)) {
42-
const range = status.upstream
43-
? `${status.upstream}..${status.branch}`
44-
: undefined;
45-
this.children.push(new StatusFilesNode(status, range, this.explorer, this.active));
55+
this.children.push(new StatusBranchNode(branch, this.uri, this.explorer));
4656
}
4757

4858
return this.children;
@@ -91,7 +101,6 @@ export class StatusNode extends ExplorerNode {
91101
}
92102
}
93103

94-
label = `${status.branch}${label === '' ? '' : ` ${GlyphChars.Space}${status.upstream ? GlyphChars.ArrowLeftRightLong : GlyphChars.Dash}${label}`}`;
95104
if (hasWorkingChanges) {
96105
tooltip += `\n\nHas uncommitted changes${status.getDiffStatus({ expand: true, prefix: `\n`, separator: '\n' })}`;
97106
}
@@ -102,7 +111,7 @@ export class StatusNode extends ExplorerNode {
102111
state = this.active ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.Collapsed;
103112
}
104113
else {
105-
state = TreeItemCollapsibleState.None;
114+
state = TreeItemCollapsibleState.Collapsed;
106115
}
107116

108117
const item = new TreeItem(`${status.branch}${label}`, state);
@@ -133,4 +142,25 @@ export class StatusNode extends ExplorerNode {
133142
private async onFileSystemChanged(e: RepositoryFileSystemChangeEvent) {
134143
this.explorer.refreshNode(this);
135144
}
136-
}
145+
}
146+
147+
export class StatusBranchNode extends BranchNode {
148+
149+
constructor(
150+
branch: GitBranch,
151+
uri: GitUri,
152+
explorer: GitExplorer
153+
) {
154+
super(branch, uri, explorer);
155+
}
156+
157+
get markCurrent() {
158+
return false;
159+
}
160+
161+
async getTreeItem(): Promise<TreeItem> {
162+
const item = await super.getTreeItem();
163+
item.label = `History (${item.label})`;
164+
return item;
165+
}
166+
}

0 commit comments

Comments
 (0)