Skip to content

Commit

Permalink
Adds working tree status to custom view (insiders)
Browse files Browse the repository at this point in the history
Hides working changed files behind insiders flag
Unhides Changed Files node from behind insiders flag
Adds changed file count to Changed Files node label
Adds icon to Changed Files node
Adds upstream branch to upstream status nodes
Sorts files in the Changed Files node
  • Loading branch information
eamodio committed Sep 20, 2017
1 parent 712544f commit f7df845
Show file tree
Hide file tree
Showing 14 changed files with 81 additions and 27 deletions.
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p

## [Unreleased]

## [5.2.0-beta]
## [5.2.0-beta] - 2017-09-20
### Added
- Adds working tree status (enabled via `"gitlens.insiders": true`) to the `Repository Status` node in the `GitLens` custom view
- 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)
- Provides a file-based view of all the changed files in the working tree (enabled via `"gitlens.insiders": true`) and/or files in commits that haven't yet been pushed upstream
- Provides a at-a-glance view of all "working" changes
- Expands to a file-based view of all changed files in the working tree (enabled via `"gitlens.insiders": true`) and/or all files in all commits ahead of the upstream
- 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)
- Adds `gitlens.gitExplorer.statusFileFormat` setting to the format of the status of a working or committed file in the `GitLens` custom view

### Changed
- Changes the sorting (now alphabetical) of files shown in the `GitLens` custom view
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,17 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
![GitLens Repository view](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-git-custom-view-repository.png)

- `Repository Status` node — provides the status of the repository
- Provides the name of the current branch, its upstream tracking branch (if available), and its upstream status (if available)
- Provides the name of the current branch, its working tree status (enabled via `"gitlens.insiders": true`), and its upstream tracking branch and status (if available)
- Provides indicator dots on the repository icon which denote the following:
- `None` - up-to-date with the upstream
- `Green` - ahead of the upstream
- `Red` - behind the upstream
- `Yellow` - both ahead of and behind the upstream
- Provides additional nodes, if the current branch is not synchronized with the upstream, to quickly see and explore the specific commits ahead and/or behind the upstream
- Provides additional upstream status nodes, if the current branch is tracking a remote branch and
- is behind the upstream — quickly see and explore the specific commits behind the upstream (i.e. commits that haven't been pulled)
- is ahead of the upstream — quickly see and explore the specific commits ahead of the upstream (i.e. commits that haven't been pushed)
- `Changed Files` node — provides a at-a-glance view of all "working" changes
- Expands to a file-based view of all changed files in the working tree (enabled via `"gitlens.insiders": true`) and/or all files in all commits ahead of the upstream
- Provides a context menu with `Open Repository in Remote`, and `Refresh` commands

- `Branches` node — provides a list of the local branches
Expand Down Expand Up @@ -357,6 +361,7 @@ GitLens is highly customizable and provides many configuration settings to allow
|`gitlens.gitExplorer.commitFileFormat`|Specifies the format of a committed file in the `GitLens` custom view<br />Available tokens<br /> ${file} - file name<br /> ${filePath} - file name and path<br /> ${path} - file path
|`gitlens.gitExplorer.stashFormat`|Specifies the format of stashed changes in the `GitLens` custom view<br />Available tokens<br /> ${id} - commit id<br /> ${author} - commit author<br /> ${message} - commit message<br /> ${ago} - relative commit date (e.g. 1 day ago)<br /> ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)<br /> ${authorAgo} - commit author, relative commit date<br />See https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting
|`gitlens.gitExplorer.stashFileFormat`|Specifies the format of a stashed file in the `GitLens` custom view<br />Available tokens<br /> ${file} - file name<br /> ${filePath} - file name and path<br /> ${path} - file path
|`gitlens.gitExplorer.statusFileFormat`|Specifies the format of the status of a working or committed file in the `GitLens` custom view<br />Available tokens<br /> ${file} - file name<br /> ${filePath} - file name and path<br /> ${path} - file path<br />${working} - optional indicator if the file is uncommitted

### Custom Remotes Settings

Expand Down
4 changes: 4 additions & 0 deletions images/dark/icon-diff.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions images/light/icon-diff.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,11 @@
"default": "${filePath}",
"description": "Specifies the format of a stashed file in the `GitLens` custom view\nAvailable tokens\n ${file} - file name\n ${filePath} - file name and path\n ${path} - file path"
},
"gitlens.gitExplorer.statusFileFormat": {
"type": "string",
"default": "${working}${filePath}",
"description": "Specifies the format of the status of a working or committed file in the `GitLens` custom view\nAvailable tokens\n ${file} - file name\n ${filePath} - file name and path\n ${path} - file path\n ${working} - optional indicator if the file is uncommitted"
},
"gitlens.gitExplorer.view": {
"type": "string",
"default": "repository",
Expand Down
1 change: 1 addition & 0 deletions src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ export interface IConfig {
commitFileFormat: string;
stashFormat: string;
stashFileFormat: string;
statusFileFormat: string;
// dateFormat: string | null;
};

Expand Down
4 changes: 4 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,12 @@ export type GlyphChars = '\u21a9' |
'\u21e8' |
'\u2191' |
'\u2197' |
'\u2217' |
'\u2713' |
'\u2014' |
'\u2022' |
'\u2026' |
'\u270E' |
'\u00a0' |
'\u200b';
export const GlyphChars = {
Expand All @@ -97,10 +99,12 @@ export const GlyphChars = {
ArrowRightHollow: '\u21e8' as GlyphChars,
ArrowUp: '\u2191' as GlyphChars,
ArrowUpRight: '\u2197' as GlyphChars,
Asterisk: '\u2217' as GlyphChars,
Check: '\u2713' as GlyphChars,
Dash: '\u2014' as GlyphChars,
Dot: '\u2022' as GlyphChars,
Ellipsis: '\u2026' as GlyphChars,
Pensil: '\u270E' as GlyphChars,
Space: '\u00a0' as GlyphChars,
ZeroWidthSpace: '\u200b' as GlyphChars
};
Expand Down
8 changes: 7 additions & 1 deletion src/git/formatters/status.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use strict';
import { Strings } from '../../system';
import { GlyphChars } from '../../constants';
import { Formatter, IFormatOptions } from './formatter';
import { GitStatusFile, IGitStatusFile } from '../models/status';
import { GitStatusFile, IGitStatusFile, IGitStatusFileWithCommit } from '../models/status';
import * as path from 'path';

export interface IStatusFormatOptions extends IFormatOptions {
Expand Down Expand Up @@ -29,6 +30,11 @@ export class StatusFileFormatter extends Formatter<IGitStatusFile, IStatusFormat
return this._padOrTruncate(directory, this._options.tokenOptions!.file);
}

get working() {
const commit = (this._item as IGitStatusFileWithCommit).commit;
return (commit !== undefined && commit.isUncommitted) ? `${GlyphChars.Pensil} ${GlyphChars.Space}` : '';
}

static fromTemplate(template: string, status: IGitStatusFile, dateFormat: string | null): string;
static fromTemplate(template: string, status: IGitStatusFile, options?: IStatusFormatOptions): string;
static fromTemplate(template: string, status: IGitStatusFile, dateFormatOrOptions?: string | null | IStatusFormatOptions): string;
Expand Down
5 changes: 5 additions & 0 deletions src/git/models/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Strings } from '../../system';
import { Uri } from 'vscode';
import { GlyphChars } from '../../constants';
import { GitUri } from '../gitUri';
import { GitLogCommit } from './logCommit';
import * as path from 'path';

export interface GitStatus {
Expand All @@ -27,6 +28,10 @@ export interface IGitStatusFile {
originalFileName?: string;
}

export interface IGitStatusFileWithCommit extends IGitStatusFile {
commit: GitLogCommit;
}

export class GitStatusFile implements IGitStatusFile {

originalFileName?: string;
Expand Down
3 changes: 2 additions & 1 deletion src/views/gitExplorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ export class GitExplorer implements TreeDataProvider<ExplorerNode> {
private onConfigurationChanged() {
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;

if (!Objects.areEquivalent(cfg.gitExplorer, this._config && this._config.gitExplorer)) {
if (!Objects.areEquivalent(cfg.gitExplorer, this._config && this._config.gitExplorer) ||
!Objects.areEquivalent(cfg.insiders, this._config && this._config.insiders)) {
setTimeout(() => {
this._root = this.getRootNode(window.activeTextEditor);
this.refresh();
Expand Down
15 changes: 13 additions & 2 deletions src/views/statusFileCommitsNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState, Uri } fr
import { Commands, DiffWithPreviousCommandArgs } from '../commands';
import { CommitFileNode, CommitFileNodeDisplayAs } from './commitFileNode';
import { ExplorerNode, ResourceType } from './explorerNode';
import { getGitStatusIcon, GitBranch, GitLogCommit, GitService, GitUri, IGitStatusFile, StatusFileFormatter } from '../gitService';
import { getGitStatusIcon, GitBranch, GitLogCommit, GitService, GitUri, IGitStatusFile, IGitStatusFileWithCommit, StatusFileFormatter } from '../gitService';
import * as path from 'path';

export class StatusFileCommitsNode extends ExplorerNode {
Expand All @@ -26,7 +26,7 @@ export class StatusFileCommitsNode extends ExplorerNode {
}

async getTreeItem(): Promise<TreeItem> {
const item = new TreeItem(StatusFileFormatter.fromTemplate(this.git.config.gitExplorer.commitFileFormat, this.status), TreeItemCollapsibleState.Collapsed);
const item = new TreeItem(this.label, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;

const icon = getGitStatusIcon(this.status.status);
Expand All @@ -41,9 +41,20 @@ export class StatusFileCommitsNode extends ExplorerNode {
item.command = this.getCommand();
}

// Only cache the label for a single refresh
this._label = undefined;

return item;
}

private _label: string | undefined;
get label() {
if (this._label === undefined) {
this._label = StatusFileFormatter.fromTemplate(this.git.config.gitExplorer.statusFileFormat, { ...this.status, commit: this.commit } as IGitStatusFileWithCommit);
}
return this._label;
}

get commit() {
return this.commits[0];
}
Expand Down
21 changes: 14 additions & 7 deletions src/views/statusFilesNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@
import { Arrays, Iterables, Objects } from '../system';
import { ExtensionContext, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
import { ExplorerNode, ResourceType, ShowAllNode } from './explorerNode';
import { GitBranch, GitLog, GitLogCommit, GitService, GitStatus, GitUri, IGitStatusFile } from '../gitService';
import { GitBranch, GitLog, GitLogCommit, GitService, GitStatus, GitUri, IGitStatusFileWithCommit } from '../gitService';
import { StatusFileCommitsNode } from './statusFileCommitsNode';

interface IGitStatusFileWithCommit extends IGitStatusFile {
commit: GitLogCommit;
}

export class StatusFilesNode extends ExplorerNode {

readonly resourceType: ResourceType = 'gitlens:status-files';
Expand Down Expand Up @@ -42,7 +38,7 @@ export class StatusFilesNode extends ExplorerNode {
statuses = [];
}

if (this.status.files.length !== 0) {
if (this.status.files.length !== 0 && this.git.config.insiders) {
statuses.splice(0, 0, ...this.status.files.map(s => {
return { ...s, commit: new GitLogCommit('file', this.status.repoPath, GitService.uncommittedSha, s.fileName, 'You', new Date(), '', s.status, [s], s.originalFileName, 'HEAD', s.fileName) } as IGitStatusFileWithCommit;
}));
Expand All @@ -56,15 +52,26 @@ export class StatusFilesNode extends ExplorerNode {
statuses => new StatusFileCommitsNode(this.uri.repoPath!, statuses[statuses.length - 1], statuses.map(s => s.commit), this.context, this.git, this.branch))
];

children.sort((a: StatusFileCommitsNode, b: StatusFileCommitsNode) => (a.commit.isUncommitted ? -1 : 1) - (b.commit.isUncommitted ? -1 : 1) || a.label!.localeCompare(b.label!));

if (log !== undefined && log.truncated) {
children.push(new ShowAllNode('Show All Changes', this, this.context));
}
return children;
}

async getTreeItem(): Promise<TreeItem> {
const item = new TreeItem(`Changed Files`, TreeItemCollapsibleState.Collapsed);
const stats = await this.git.getChangedFilesCount(this.status.repoPath, this.git.config.insiders ? this.status.upstream : this.range);
const files = (stats === undefined) ? 0 : stats.files;

const label = `${files} file${files > 1 ? 's' : ''} changed`; // ${this.status.upstream === undefined ? '' : ` (ahead of ${this.status.upstream})`}`;
const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
item.iconPath = {
dark: this.context.asAbsolutePath(`images/dark/icon-diff.svg`),
light: this.context.asAbsolutePath(`images/light/icon-diff.svg`)
};

return item;
}
}
18 changes: 8 additions & 10 deletions src/views/statusNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ export class StatusNode extends ExplorerNode {
children.push(new StatusUpstreamNode(status, 'ahead', this.context, this.git));
}

if (status.files.length !== 0 || status.state.ahead && this.git.config.insiders) {
if (status.state.ahead || (status.files.length !== 0 && this.git.config.insiders)) {
const range = status.state.ahead
? `${status.upstream}..${status.branch}`
: undefined;
children.splice(0, 0, new StatusFilesNode(status, range, this.context, this.git));
children.push(new StatusFilesNode(status, range, this.context, this.git));
}

return children;
Expand All @@ -45,14 +45,16 @@ export class StatusNode extends ExplorerNode {
if (status === undefined) return new TreeItem('No repo status');

let hasChildren = false;
const hasWorkingChanges = status.files.length !== 0 && this.git.config.insiders;
let label = '';
let iconSuffix = '';
if (status.upstream) {
if (!status.state.ahead && !status.state.behind) {
label = `${status.branch} is up-to-date with ${status.upstream}`;
label = `${status.branch}${hasWorkingChanges ? ' has uncommitted changes and' : ''} is up-to-date with ${status.upstream}`;
}
else {
label = `${status.branch} is not up-to-date with ${status.upstream}`;
label = `${status.branch}${hasWorkingChanges ? ' has uncommitted changes and' : ''} is not up-to-date with ${status.upstream}`;

hasChildren = true;
if (status.state.ahead && status.state.behind) {
iconSuffix = '-yellow';
Expand All @@ -66,14 +68,10 @@ export class StatusNode extends ExplorerNode {
}
}
else {
label = `${status.branch} is up-to-date`;
}

if (this.git.config.insiders) {
hasChildren = hasChildren || status.files.length !== 0;
label = `${status.branch} ${hasWorkingChanges ? 'has uncommitted changes' : 'is clean'}`;
}

const item = new TreeItem(label, hasChildren ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.None);
const item = new TreeItem(label, (hasChildren || hasWorkingChanges) ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.None);
item.contextValue = this.resourceType;

item.iconPath = {
Expand Down
4 changes: 2 additions & 2 deletions src/views/statusUpstreamNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ export class StatusUpstreamNode extends ExplorerNode {

async getTreeItem(): Promise<TreeItem> {
const label = this.direction === 'ahead'
? `${this.status.state.ahead} commit${this.status.state.ahead > 1 ? 's' : ''} ahead (local changes)` // of ${this.status.upstream}`
: `${this.status.state.behind} commit${this.status.state.behind > 1 ? 's' : ''} behind (remote changes)`; // ${this.status.upstream}`;
? `${this.status.state.ahead} commit${this.status.state.ahead > 1 ? 's' : ''} (ahead of ${this.status.upstream})`
: `${this.status.state.behind} commit${this.status.state.behind > 1 ? 's' : ''} (behind ${this.status.upstream})`;

const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
Expand Down

0 comments on commit f7df845

Please sign in to comment.