diff --git a/CHANGELOG.md b/CHANGELOG.md index e19e444..5352fb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ## [Unreleased] +### Changes + +- Improves the performance of the _Stashes_ and _Contributors_ views + ### Fixed - Fixes [#1294](https://github.com/eamodio/vscode-gitlens/issues/1294) - Error when open commits list diff --git a/src/git/gitService.ts b/src/git/gitService.ts index d071b14..e81511e 100644 --- a/src/git/gitService.ts +++ b/src/git/gitService.ts @@ -129,7 +129,9 @@ export class GitService implements Disposable { private _repositoriesLoadingPromise: Promise | undefined; private readonly _branchesCache = new Map(); + private readonly _contributorsCache = new Map(); private readonly _remotesWithApiProviderCache = new Map | null>(); + private readonly _stashesCache = new Map(); private readonly _tagsCache = new Map(); private readonly _trackedCache = new Map>(); private readonly _userMapCache = new Map(); @@ -157,7 +159,9 @@ export class GitService implements Disposable { dispose() { this._repositoryTree.forEach(r => r.dispose()); this._branchesCache.clear(); + this._contributorsCache.clear(); this._remotesWithApiProviderCache.clear(); + this._stashesCache.clear(); this._tagsCache.clear(); this._trackedCache.clear(); this._userMapCache.clear(); @@ -186,12 +190,20 @@ export class GitService implements Disposable { } private onAnyRepositoryChanged(repo: Repository, e: RepositoryChangeEvent) { - if (e.changed(RepositoryChange.Stash, true)) return; + if (e.changed(RepositoryChange.Stash, true)) { + this._stashesCache.delete(repo.path); + + return; + } this._branchesCache.delete(repo.path); + this._contributorsCache.delete(repo.path); if (e.changed(RepositoryChange.Remotes)) { this._remotesWithApiProviderCache.clear(); } + if (e.changed(RepositoryChange.Stash)) { + this._stashesCache.delete(repo.path); + } this._tagsCache.delete(repo.path); this._trackedCache.clear(); @@ -1393,31 +1405,43 @@ export class GitService implements Disposable { async getContributors(repoPath: string): Promise { if (repoPath == null) return []; - try { - const data = await Git.shortlog(repoPath); - const shortlog = GitShortLogParser.parse(data, repoPath); - if (shortlog == null) return []; - - // Mark the current user - const currentUser = await Container.git.getCurrentUser(repoPath); - if (currentUser != null) { - const index = shortlog.contributors.findIndex( - c => currentUser.email === c.email && currentUser.name === c.name, - ); - if (index !== -1) { - const c = shortlog.contributors[index]; - shortlog.contributors.splice( - index, - 1, - new GitContributor(c.repoPath, c.name, c.email, c.count, true), - ); + let contributors = this.useCaching ? this._contributorsCache.get(repoPath) : undefined; + if (contributors == null) { + try { + const data = await Git.shortlog(repoPath); + const shortlog = GitShortLogParser.parse(data, repoPath); + if (shortlog != null) { + // Mark the current user + const currentUser = await Container.git.getCurrentUser(repoPath); + if (currentUser != null) { + const index = shortlog.contributors.findIndex( + c => currentUser.email === c.email && currentUser.name === c.name, + ); + if (index !== -1) { + const c = shortlog.contributors[index]; + shortlog.contributors.splice( + index, + 1, + new GitContributor(c.repoPath, c.name, c.email, c.count, true), + ); + } + } + + contributors = shortlog.contributors; + } else { + contributors = []; } - } - return shortlog.contributors; - } catch (ex) { - return []; + const repo = await this.getRepository(repoPath); + if (repo?.supportsChangeEvents) { + this._contributorsCache.set(repoPath, contributors); + } + } catch (ex) { + return []; + } } + + return contributors; } @log() @@ -3236,11 +3260,20 @@ export class GitService implements Disposable { async getStash(repoPath: string | undefined): Promise { if (repoPath == null) return undefined; - const data = await Git.stash__list(repoPath, { - similarityThreshold: Container.config.advanced.similarityThreshold, - }); - const stash = GitStashParser.parse(data, repoPath); - return stash; + let stash = this.useCaching ? this._stashesCache.get(repoPath) : undefined; + if (stash === undefined) { + const data = await Git.stash__list(repoPath, { + similarityThreshold: Container.config.advanced.similarityThreshold, + }); + stash = GitStashParser.parse(data, repoPath); + + const repo = await this.getRepository(repoPath); + if (repo?.supportsChangeEvents) { + this._stashesCache.set(repoPath, stash ?? null); + } + } + + return stash ?? undefined; } @log() diff --git a/src/views/branchesView.ts b/src/views/branchesView.ts index 024b37f..a6b5798 100644 --- a/src/views/branchesView.ts +++ b/src/views/branchesView.ts @@ -85,6 +85,13 @@ export class BranchesViewNode extends ViewNode { const [child] = this.children; const branches = await child.repo.getBranches({ filter: b => !b.remote }); + if (branches.length === 0) { + this.view.message = 'No branches could be found.'; + + return []; + } + + this.view.message = undefined; this.view.title = `Branches (${branches.length})`; return child.getChildren(); diff --git a/src/views/contributorsView.ts b/src/views/contributorsView.ts index af214a3..03e4828 100644 --- a/src/views/contributorsView.ts +++ b/src/views/contributorsView.ts @@ -65,6 +65,13 @@ export class ContributorsViewNode extends ViewNode { const [child] = this.children; const contributors = await child.repo.getContributors(); + if (contributors.length === 0) { + this.view.message = 'No contributors could be found.'; + + return []; + } + + this.view.message = undefined; this.view.title = `Contributors (${contributors.length})`; return child.getChildren(); diff --git a/src/views/nodes/remotesNode.ts b/src/views/nodes/remotesNode.ts index 5219a20..5b0c9d3 100644 --- a/src/views/nodes/remotesNode.ts +++ b/src/views/nodes/remotesNode.ts @@ -33,7 +33,7 @@ export class RemotesNode extends ViewNode { async getChildren(): Promise { if (this._children == null) { const remotes = await this.repo.getRemotes({ sort: true }); - if (remotes == null || remotes.length === 0) { + if (remotes.length === 0) { return [new MessageNode(this.view, this, 'No remotes could be found')]; } diff --git a/src/views/nodes/stashesNode.ts b/src/views/nodes/stashesNode.ts index 9729051..897eeab 100644 --- a/src/views/nodes/stashesNode.ts +++ b/src/views/nodes/stashesNode.ts @@ -30,7 +30,7 @@ export class StashesNode extends ViewNode { async getChildren(): Promise { if (this._children == null) { const stash = await this.repo.getStash(); - if (stash === undefined) return [new MessageNode(this.view, this, 'No stashes could be found.')]; + if (stash == null) return [new MessageNode(this.view, this, 'No stashes could be found.')]; this._children = [...Iterables.map(stash.commits.values(), c => new StashNode(this.view, this, c))]; } diff --git a/src/views/remotesView.ts b/src/views/remotesView.ts index 450c29f..4cd678c 100644 --- a/src/views/remotesView.ts +++ b/src/views/remotesView.ts @@ -81,6 +81,13 @@ export class RemotesViewNode extends ViewNode { const [child] = this.children; const remotes = await child.repo.getRemotes(); + if (remotes.length === 0) { + this.view.message = 'No remotes could be found.'; + + return []; + } + + this.view.message = undefined; this.view.title = `Remotes (${remotes.length})`; return child.getChildren(); diff --git a/src/views/stashesView.ts b/src/views/stashesView.ts index b6aa43e..aec3323 100644 --- a/src/views/stashesView.ts +++ b/src/views/stashesView.ts @@ -63,6 +63,13 @@ export class StashesViewNode extends ViewNode { const [child] = this.children; const stash = await child.repo.getStash(); + if (stash == null) { + this.view.message = 'No stashes could be found.'; + + return []; + } + + this.view.message = undefined; this.view.title = `Stashes (${stash?.commits.size ?? 0})`; return child.getChildren(); diff --git a/src/views/tagsView.ts b/src/views/tagsView.ts index 6bf153d..ad9ce73 100644 --- a/src/views/tagsView.ts +++ b/src/views/tagsView.ts @@ -64,6 +64,13 @@ export class TagsViewNode extends ViewNode { const [child] = this.children; const tags = await child.repo.getTags(); + if (tags.length === 0) { + this.view.message = 'No tags could be found.'; + + return []; + } + + this.view.message = undefined; this.view.title = `Tags (${tags.length})`; return child.getChildren();