Skip to content

Commit 393ec35

Browse files
committed
Adds gitlens.gitExplorer.includeWorkingTree setting
Adds auto-update for working trree Fixes issues with working tree status
1 parent 99d6da9 commit 393ec35

File tree

9 files changed

+122
-32
lines changed

9 files changed

+122
-32
lines changed

CHANGELOG.md

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

99
## [5.2.0-beta] - 2017-09-20
1010
### Added
11-
- Adds working tree status (enabled via `"gitlens.insiders": true`) to the `Repository Status` node in the `GitLens` custom view
1211
- 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)
1312
- Provides a at-a-glance view of all "working" changes
1413
- 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
14+
- Adds optional (on by default) working tree status information to the `Repository Status` node in the `GitLens` custom view
15+
- Adds `auto` value to `gitlens.gitExplorer.view` setting - closes [#150](https://github.com/eamodio/vscode-gitlens/issues/150)
1516
- 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)
17+
- Adds `gitlens.gitExplorer.includeWorkingTree` setting to specify whether or not to include working tree files inside the `Repository Status` node of the `GitLens` custom view
1618
- Adds `gitlens.gitExplorer.statusFileFormat` setting to the format of the status of a working or committed file in the `GitLens` custom view
17-
- Adds `auto` value to `gitlens.gitExplorer.view` setting - closes [#150](https://github.com/eamodio/vscode-gitlens/issues/150)
1819

1920
### Changed
2021
- Changes the sorting (now alphabetical) of files shown in the `GitLens` custom view

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
126126
![GitLens Repository view](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-git-custom-view-repository.png)
127127

128128
- `Repository Status` node — provides the status of the repository
129-
- 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)
129+
- Provides the name of the current branch, [optionally](#gitlens-custom-view-settings) its working tree status, and its upstream tracking branch and status (if available)
130130
- Provides indicator dots on the repository icon which denote the following:
131131
- `None` - up-to-date with the upstream
132132
- `Green` - ahead of the upstream
@@ -136,7 +136,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
136136
- is behind the upstream — quickly see and explore the specific commits behind the upstream (i.e. commits that haven't been pulled)
137137
- is ahead of the upstream — quickly see and explore the specific commits ahead of the upstream (i.e. commits that haven't been pushed)
138138
- `Changed Files` node — provides a at-a-glance view of all "working" changes
139-
- 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
139+
- Expands to a file-based view of all changed files in the working tree ([optionally](#gitlens-custom-view-settings)) and/or all files in all commits ahead of the upstream
140140
- Provides a context menu with `Open Repository in Remote`, and `Refresh` commands
141141

142142
- `Branches` node — provides a list of the local branches
@@ -356,6 +356,7 @@ GitLens is highly customizable and provides many configuration settings to allow
356356
|-----|------------
357357
|`gitlens.gitExplorer.enabled`|Specifies whether or not to show the `GitLens` custom view"
358358
|`gitlens.gitExplorer.view`|Specifies the starting view (mode) of the `GitLens` custom view<br /> `auto` - shows the last selected view, defaults to `repository`<br />`history` - shows the commit history of the active file<br />`repository` - shows a repository explorer"
359+
|`gitlens.gitExplorer.includeWorkingTree`|Specifies whether or not to include working tree files inside the `Repository Status` node of the `GitLens` custom view
359360
|`gitlens.gitExplorer.showTrackingBranch`|Specifies whether or not to show the tracking branch when displaying local branches in the `GitLens` custom view"
360361
|`gitlens.gitExplorer.commitFormat`|Specifies the format of committed 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
361362
|`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

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,11 @@
428428
"default": true,
429429
"description": "Specifies whether or not to show the `GitLens` custom view"
430430
},
431+
"gitlens.gitExplorer.includeWorkingTree": {
432+
"type": "boolean",
433+
"default": true,
434+
"description": "Specifies whether or not to include working tree files inside the `Repository Status` node of the `GitLens` custom view"
435+
},
431436
"gitlens.gitExplorer.showTrackingBranch": {
432437
"type": "boolean",
433438
"default": true,

src/configuration.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ export interface IConfig {
319319
gitExplorer: {
320320
enabled: boolean;
321321
view: GitExplorerView;
322+
includeWorkingTree: boolean;
322323
showTrackingBranch: boolean;
323324
commitFormat: string;
324325
commitFileFormat: string;

src/git/git.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,14 @@ export class Git {
340340
return gitCommand({ cwd: repoPath }, ...params, ...search);
341341
}
342342

343+
static log_shortstat(repoPath: string, sha?: string) {
344+
const params = [`log`, `--shortstat`, `--oneline`];
345+
if (sha) {
346+
params.push(sha);
347+
}
348+
return gitCommand({ cwd: repoPath }, ...params);
349+
}
350+
343351
static async ls_files(repoPath: string, fileName: string): Promise<string> {
344352
try {
345353
return await gitCommand({ cwd: repoPath, overrideErrorHandling: true }, 'ls-files', fileName);

src/gitService.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ export class GitService extends Disposable {
8686
return this._onDidChangeGitCache.event;
8787
}
8888

89+
private _onDidChangeFileSystem = new EventEmitter<Uri>();
90+
get onDidChangeFileSystem(): Event<Uri> {
91+
return this._onDidChangeFileSystem.event;
92+
}
93+
8994
private _onDidChangeRepo = new EventEmitter<RepoChangedReasons[]>();
9095
get onDidChangeRepo(): Event<RepoChangedReasons[]> {
9196
return this._onDidChangeRepo.event;
@@ -121,14 +126,16 @@ export class GitService extends Disposable {
121126
}
122127

123128
dispose() {
129+
this.stopWatchingFileSystem();
130+
131+
this._repoWatcher && this._repoWatcher.dispose();
132+
this._repoWatcher = undefined;
133+
124134
this._disposable && this._disposable.dispose();
125135

126136
this._cacheDisposable && this._cacheDisposable.dispose();
127137
this._cacheDisposable = undefined;
128138

129-
this._repoWatcher && this._repoWatcher.dispose();
130-
this._repoWatcher = undefined;
131-
132139
this._gitCache.clear();
133140
this._remotesCache.clear();
134141
this._uriCache.clear();
@@ -602,7 +609,8 @@ export class GitService extends Disposable {
602609
}
603610

604611
async getChangedFilesCount(repoPath: string, sha?: string): Promise<GitDiffShortStat | undefined> {
605-
return GitDiffParser.parseShortStat(await Git.diff_shortstat(repoPath, sha));
612+
const data = await Git.diff_shortstat(repoPath, sha);
613+
return GitDiffParser.parseShortStat(data);
606614
}
607615

608616
async getConfig(key: string, repoPath?: string): Promise<string> {
@@ -1034,6 +1042,33 @@ export class GitService extends Disposable {
10341042
return Git.difftool_dirDiff(repoPath, sha1, sha2);
10351043
}
10361044

1045+
private _fsWatcherDisposable: Disposable | undefined;
1046+
1047+
startWatchingFileSystem() {
1048+
if (this._fsWatcherDisposable !== undefined) return;
1049+
1050+
const debouncedFn = Functions.debounce((uri: Uri) => this._onDidChangeFileSystem.fire(uri), 2500);
1051+
const fn = (uri: Uri) => {
1052+
// Ignore .git changes
1053+
if (/\.git/.test(uri.fsPath)) return;
1054+
1055+
debouncedFn(uri);
1056+
};
1057+
1058+
const watcher = workspace.createFileSystemWatcher(`**`);
1059+
this._fsWatcherDisposable = Disposable.from(
1060+
watcher,
1061+
watcher.onDidChange(fn),
1062+
watcher.onDidCreate(fn),
1063+
watcher.onDidDelete(fn)
1064+
);
1065+
}
1066+
1067+
stopWatchingFileSystem() {
1068+
this._fsWatcherDisposable && this._fsWatcherDisposable.dispose();
1069+
this._fsWatcherDisposable = undefined;
1070+
}
1071+
10371072
stashApply(repoPath: string, stashName: string, deleteAfter: boolean = false) {
10381073
Logger.log(`stashApply('${repoPath}', ${stashName}, ${deleteAfter})`);
10391074

src/views/gitExplorer.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,14 @@ export class GitExplorer implements TreeDataProvider<ExplorerNode> {
8181
}
8282

8383
private getRootNode(editor?: TextEditor): ExplorerNode | undefined {
84-
const uri = new GitUri(Uri.file(this.git.repoPath), { repoPath: this.git.repoPath, fileName: this.git.repoPath });
85-
8684
switch (this._view) {
87-
case GitExplorerView.History: return this.getHistoryNode(editor || window.activeTextEditor);
88-
case GitExplorerView.Repository: return new RepositoryNode(uri, this.context, this.git);
89-
}
85+
case GitExplorerView.History:
86+
return this.getHistoryNode(editor || window.activeTextEditor);
9087

91-
return undefined;
88+
default:
89+
const uri = new GitUri(Uri.file(this.git.repoPath), { repoPath: this.git.repoPath, fileName: this.git.repoPath });
90+
return new RepositoryNode(uri, this.context, this.git);
91+
}
9292
}
9393

9494
private getHistoryNode(editor: TextEditor | undefined): ExplorerNode | undefined {
@@ -114,11 +114,7 @@ export class GitExplorer implements TreeDataProvider<ExplorerNode> {
114114
private onConfigurationChanged() {
115115
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
116116

117-
let changed = false;
118-
if (!Objects.areEquivalent(cfg.gitExplorer, this._config && this._config.gitExplorer) ||
119-
!Objects.areEquivalent(cfg.insiders, this._config && this._config.insiders)) {
120-
changed = true;
121-
}
117+
const changed = !Objects.areEquivalent(cfg.gitExplorer, this._config && this._config.gitExplorer);
122118

123119
this._config = cfg;
124120

@@ -165,6 +161,10 @@ export class GitExplorer implements TreeDataProvider<ExplorerNode> {
165161

166162
this._view = view;
167163
setCommandContext(CommandContext.GitExplorerView, this._view);
164+
165+
if (view !== GitExplorerView.Repository) {
166+
this.git.stopWatchingFileSystem();
167+
}
168168
}
169169

170170
switchTo(view: GitExplorerView) {

src/views/statusFilesNode.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export class StatusFilesNode extends ExplorerNode {
3838
statuses = [];
3939
}
4040

41-
if (this.status.files.length !== 0 && this.git.config.insiders) {
41+
if (this.status.files.length !== 0 && this.includeWorkingTree) {
4242
statuses.splice(0, 0, ...this.status.files.map(s => {
4343
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;
4444
}));
@@ -61,12 +61,13 @@ export class StatusFilesNode extends ExplorerNode {
6161
}
6262

6363
async getTreeItem(): Promise<TreeItem> {
64-
// Start with any untracked files, since they won't be included in the next call
65-
let files = (this.status.files === undefined) ? 0 : this.status.files.filter(s => s.status === '?').length;
64+
let files = (this.status.files !== undefined && this.includeWorkingTree) ? this.status.files.length : 0;
6665

67-
const stats = await this.git.getChangedFilesCount(this.status.repoPath, this.git.config.insiders ? this.status.upstream : this.range);
68-
if (stats !== undefined) {
69-
files += stats.files;
66+
if (this.status.upstream !== undefined) {
67+
const stats = await this.git.getChangedFilesCount(this.status.repoPath, `${this.status.upstream}...`);
68+
if (stats !== undefined) {
69+
files += stats.files;
70+
}
7071
}
7172

7273
const label = `${files} file${files > 1 ? 's' : ''} changed`; // ${this.status.upstream === undefined ? '' : ` (ahead of ${this.status.upstream})`}`;
@@ -79,4 +80,9 @@ export class StatusFilesNode extends ExplorerNode {
7980

8081
return item;
8182
}
83+
84+
private get includeWorkingTree(): boolean {
85+
return this.git.config.gitExplorer.includeWorkingTree;
86+
}
87+
8288
}

src/views/statusNode.ts

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
1+
import { commands, Disposable, ExtensionContext, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
22
import { ExplorerNode, ResourceType } from './explorerNode';
3-
import { GitService, GitUri } from '../gitService';
3+
import { GitService, GitStatus, GitUri } from '../gitService';
44
import { StatusFilesNode } from './statusFilesNode';
55
import { StatusUpstreamNode } from './statusUpstreamNode';
66

7+
let _eventDisposable: Disposable | undefined;
8+
79
export class StatusNode extends ExplorerNode {
810

911
readonly resourceType: ResourceType = 'gitlens:status';
@@ -30,8 +32,8 @@ export class StatusNode extends ExplorerNode {
3032
children.push(new StatusUpstreamNode(status, 'ahead', this.context, this.git));
3133
}
3234

33-
if (status.state.ahead || (status.files.length !== 0 && this.git.config.insiders)) {
34-
const range = status.state.ahead
35+
if (status.state.ahead || (status.files.length !== 0 && this.includeWorkingTree)) {
36+
const range = status.upstream
3537
? `${status.upstream}..${status.branch}`
3638
: undefined;
3739
children.push(new StatusFilesNode(status, range, this.context, this.git));
@@ -40,12 +42,26 @@ export class StatusNode extends ExplorerNode {
4042
return children;
4143
}
4244

43-
async getTreeItem(): Promise<TreeItem> {
45+
private _status: GitStatus | undefined;
46+
47+
async getTreeItem(): Promise < TreeItem > {
4448
const status = await this.git.getStatusForRepo(this.uri.repoPath!);
4549
if (status === undefined) return new TreeItem('No repo status');
4650

51+
if (_eventDisposable !== undefined) {
52+
_eventDisposable.dispose();
53+
_eventDisposable = undefined;
54+
}
55+
56+
if (this.includeWorkingTree) {
57+
this._status = status;
58+
59+
_eventDisposable = this.git.onDidChangeFileSystem(this.onFileSystemChanged, this);
60+
this.git.startWatchingFileSystem();
61+
}
62+
4763
let hasChildren = false;
48-
const hasWorkingChanges = status.files.length !== 0 && this.git.config.insiders;
64+
const hasWorkingChanges = status.files.length !== 0 && this.includeWorkingTree;
4965
let label = '';
5066
let iconSuffix = '';
5167
if (status.upstream) {
@@ -68,7 +84,7 @@ export class StatusNode extends ExplorerNode {
6884
}
6985
}
7086
else {
71-
label = `${status.branch} ${hasWorkingChanges ? 'has uncommitted changes' : 'is clean'}`;
87+
label = `${status.branch} ${hasWorkingChanges ? 'has uncommitted changes' : this.includeWorkingTree ? 'has no changes' : 'has nothing to commit'}`;
7288
}
7389

7490
const item = new TreeItem(label, (hasChildren || hasWorkingChanges) ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.None);
@@ -81,4 +97,21 @@ export class StatusNode extends ExplorerNode {
8197

8298
return item;
8399
}
100+
101+
private get includeWorkingTree(): boolean {
102+
return this.git.config.gitExplorer.includeWorkingTree;
103+
}
104+
105+
private async onFileSystemChanged(uri: Uri) {
106+
const status = await this.git.getStatusForRepo(this.uri.repoPath!);
107+
108+
// If we haven't changed from having some working changes to none or vice versa then just refresh the node
109+
// This is because of https://github.com/Microsoft/vscode/issues/34789
110+
if (this._status !== undefined && status !== undefined &&
111+
((this._status.files.length === status.files.length) || (this._status.files.length > 0 && status.files.length > 0))) {
112+
commands.executeCommand('gitlens.gitExplorer.refreshNode', this);
113+
}
114+
115+
commands.executeCommand('gitlens.gitExplorer.refresh');
116+
}
84117
}

0 commit comments

Comments
 (0)