mirror of
https://github.com/ckaczor/vscode-gitlens.git
synced 2026-01-23 01:35:37 -05:00
Adds gitlens.gitExplorer.includeWorkingTree setting
Adds auto-update for working trree Fixes issues with working tree status
This commit is contained in:
@@ -319,6 +319,7 @@ export interface IConfig {
|
||||
gitExplorer: {
|
||||
enabled: boolean;
|
||||
view: GitExplorerView;
|
||||
includeWorkingTree: boolean;
|
||||
showTrackingBranch: boolean;
|
||||
commitFormat: string;
|
||||
commitFileFormat: string;
|
||||
|
||||
@@ -340,6 +340,14 @@ export class Git {
|
||||
return gitCommand({ cwd: repoPath }, ...params, ...search);
|
||||
}
|
||||
|
||||
static log_shortstat(repoPath: string, sha?: string) {
|
||||
const params = [`log`, `--shortstat`, `--oneline`];
|
||||
if (sha) {
|
||||
params.push(sha);
|
||||
}
|
||||
return gitCommand({ cwd: repoPath }, ...params);
|
||||
}
|
||||
|
||||
static async ls_files(repoPath: string, fileName: string): Promise<string> {
|
||||
try {
|
||||
return await gitCommand({ cwd: repoPath, overrideErrorHandling: true }, 'ls-files', fileName);
|
||||
|
||||
@@ -86,6 +86,11 @@ export class GitService extends Disposable {
|
||||
return this._onDidChangeGitCache.event;
|
||||
}
|
||||
|
||||
private _onDidChangeFileSystem = new EventEmitter<Uri>();
|
||||
get onDidChangeFileSystem(): Event<Uri> {
|
||||
return this._onDidChangeFileSystem.event;
|
||||
}
|
||||
|
||||
private _onDidChangeRepo = new EventEmitter<RepoChangedReasons[]>();
|
||||
get onDidChangeRepo(): Event<RepoChangedReasons[]> {
|
||||
return this._onDidChangeRepo.event;
|
||||
@@ -121,14 +126,16 @@ export class GitService extends Disposable {
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.stopWatchingFileSystem();
|
||||
|
||||
this._repoWatcher && this._repoWatcher.dispose();
|
||||
this._repoWatcher = undefined;
|
||||
|
||||
this._disposable && this._disposable.dispose();
|
||||
|
||||
this._cacheDisposable && this._cacheDisposable.dispose();
|
||||
this._cacheDisposable = undefined;
|
||||
|
||||
this._repoWatcher && this._repoWatcher.dispose();
|
||||
this._repoWatcher = undefined;
|
||||
|
||||
this._gitCache.clear();
|
||||
this._remotesCache.clear();
|
||||
this._uriCache.clear();
|
||||
@@ -602,7 +609,8 @@ export class GitService extends Disposable {
|
||||
}
|
||||
|
||||
async getChangedFilesCount(repoPath: string, sha?: string): Promise<GitDiffShortStat | undefined> {
|
||||
return GitDiffParser.parseShortStat(await Git.diff_shortstat(repoPath, sha));
|
||||
const data = await Git.diff_shortstat(repoPath, sha);
|
||||
return GitDiffParser.parseShortStat(data);
|
||||
}
|
||||
|
||||
async getConfig(key: string, repoPath?: string): Promise<string> {
|
||||
@@ -1034,6 +1042,33 @@ export class GitService extends Disposable {
|
||||
return Git.difftool_dirDiff(repoPath, sha1, sha2);
|
||||
}
|
||||
|
||||
private _fsWatcherDisposable: Disposable | undefined;
|
||||
|
||||
startWatchingFileSystem() {
|
||||
if (this._fsWatcherDisposable !== undefined) return;
|
||||
|
||||
const debouncedFn = Functions.debounce((uri: Uri) => this._onDidChangeFileSystem.fire(uri), 2500);
|
||||
const fn = (uri: Uri) => {
|
||||
// Ignore .git changes
|
||||
if (/\.git/.test(uri.fsPath)) return;
|
||||
|
||||
debouncedFn(uri);
|
||||
};
|
||||
|
||||
const watcher = workspace.createFileSystemWatcher(`**`);
|
||||
this._fsWatcherDisposable = Disposable.from(
|
||||
watcher,
|
||||
watcher.onDidChange(fn),
|
||||
watcher.onDidCreate(fn),
|
||||
watcher.onDidDelete(fn)
|
||||
);
|
||||
}
|
||||
|
||||
stopWatchingFileSystem() {
|
||||
this._fsWatcherDisposable && this._fsWatcherDisposable.dispose();
|
||||
this._fsWatcherDisposable = undefined;
|
||||
}
|
||||
|
||||
stashApply(repoPath: string, stashName: string, deleteAfter: boolean = false) {
|
||||
Logger.log(`stashApply('${repoPath}', ${stashName}, ${deleteAfter})`);
|
||||
|
||||
|
||||
@@ -81,14 +81,14 @@ export class GitExplorer implements TreeDataProvider<ExplorerNode> {
|
||||
}
|
||||
|
||||
private getRootNode(editor?: TextEditor): ExplorerNode | undefined {
|
||||
const uri = new GitUri(Uri.file(this.git.repoPath), { repoPath: this.git.repoPath, fileName: this.git.repoPath });
|
||||
|
||||
switch (this._view) {
|
||||
case GitExplorerView.History: return this.getHistoryNode(editor || window.activeTextEditor);
|
||||
case GitExplorerView.Repository: return new RepositoryNode(uri, this.context, this.git);
|
||||
}
|
||||
case GitExplorerView.History:
|
||||
return this.getHistoryNode(editor || window.activeTextEditor);
|
||||
|
||||
return undefined;
|
||||
default:
|
||||
const uri = new GitUri(Uri.file(this.git.repoPath), { repoPath: this.git.repoPath, fileName: this.git.repoPath });
|
||||
return new RepositoryNode(uri, this.context, this.git);
|
||||
}
|
||||
}
|
||||
|
||||
private getHistoryNode(editor: TextEditor | undefined): ExplorerNode | undefined {
|
||||
@@ -114,11 +114,7 @@ export class GitExplorer implements TreeDataProvider<ExplorerNode> {
|
||||
private onConfigurationChanged() {
|
||||
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
|
||||
|
||||
let changed = false;
|
||||
if (!Objects.areEquivalent(cfg.gitExplorer, this._config && this._config.gitExplorer) ||
|
||||
!Objects.areEquivalent(cfg.insiders, this._config && this._config.insiders)) {
|
||||
changed = true;
|
||||
}
|
||||
const changed = !Objects.areEquivalent(cfg.gitExplorer, this._config && this._config.gitExplorer);
|
||||
|
||||
this._config = cfg;
|
||||
|
||||
@@ -165,6 +161,10 @@ export class GitExplorer implements TreeDataProvider<ExplorerNode> {
|
||||
|
||||
this._view = view;
|
||||
setCommandContext(CommandContext.GitExplorerView, this._view);
|
||||
|
||||
if (view !== GitExplorerView.Repository) {
|
||||
this.git.stopWatchingFileSystem();
|
||||
}
|
||||
}
|
||||
|
||||
switchTo(view: GitExplorerView) {
|
||||
|
||||
@@ -38,7 +38,7 @@ export class StatusFilesNode extends ExplorerNode {
|
||||
statuses = [];
|
||||
}
|
||||
|
||||
if (this.status.files.length !== 0 && this.git.config.insiders) {
|
||||
if (this.status.files.length !== 0 && this.includeWorkingTree) {
|
||||
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;
|
||||
}));
|
||||
@@ -61,12 +61,13 @@ export class StatusFilesNode extends ExplorerNode {
|
||||
}
|
||||
|
||||
async getTreeItem(): Promise<TreeItem> {
|
||||
// Start with any untracked files, since they won't be included in the next call
|
||||
let files = (this.status.files === undefined) ? 0 : this.status.files.filter(s => s.status === '?').length;
|
||||
let files = (this.status.files !== undefined && this.includeWorkingTree) ? this.status.files.length : 0;
|
||||
|
||||
const stats = await this.git.getChangedFilesCount(this.status.repoPath, this.git.config.insiders ? this.status.upstream : this.range);
|
||||
if (stats !== undefined) {
|
||||
files += stats.files;
|
||||
if (this.status.upstream !== undefined) {
|
||||
const stats = await this.git.getChangedFilesCount(this.status.repoPath, `${this.status.upstream}...`);
|
||||
if (stats !== undefined) {
|
||||
files += stats.files;
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private get includeWorkingTree(): boolean {
|
||||
return this.git.config.gitExplorer.includeWorkingTree;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { commands, Disposable, ExtensionContext, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
|
||||
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
import { GitService, GitStatus, GitUri } from '../gitService';
|
||||
import { StatusFilesNode } from './statusFilesNode';
|
||||
import { StatusUpstreamNode } from './statusUpstreamNode';
|
||||
|
||||
let _eventDisposable: Disposable | undefined;
|
||||
|
||||
export class StatusNode extends ExplorerNode {
|
||||
|
||||
readonly resourceType: ResourceType = 'gitlens:status';
|
||||
@@ -30,8 +32,8 @@ export class StatusNode extends ExplorerNode {
|
||||
children.push(new StatusUpstreamNode(status, 'ahead', this.context, this.git));
|
||||
}
|
||||
|
||||
if (status.state.ahead || (status.files.length !== 0 && this.git.config.insiders)) {
|
||||
const range = status.state.ahead
|
||||
if (status.state.ahead || (status.files.length !== 0 && this.includeWorkingTree)) {
|
||||
const range = status.upstream
|
||||
? `${status.upstream}..${status.branch}`
|
||||
: undefined;
|
||||
children.push(new StatusFilesNode(status, range, this.context, this.git));
|
||||
@@ -40,12 +42,26 @@ export class StatusNode extends ExplorerNode {
|
||||
return children;
|
||||
}
|
||||
|
||||
async getTreeItem(): Promise<TreeItem> {
|
||||
private _status: GitStatus | undefined;
|
||||
|
||||
async getTreeItem(): Promise < TreeItem > {
|
||||
const status = await this.git.getStatusForRepo(this.uri.repoPath!);
|
||||
if (status === undefined) return new TreeItem('No repo status');
|
||||
|
||||
if (_eventDisposable !== undefined) {
|
||||
_eventDisposable.dispose();
|
||||
_eventDisposable = undefined;
|
||||
}
|
||||
|
||||
if (this.includeWorkingTree) {
|
||||
this._status = status;
|
||||
|
||||
_eventDisposable = this.git.onDidChangeFileSystem(this.onFileSystemChanged, this);
|
||||
this.git.startWatchingFileSystem();
|
||||
}
|
||||
|
||||
let hasChildren = false;
|
||||
const hasWorkingChanges = status.files.length !== 0 && this.git.config.insiders;
|
||||
const hasWorkingChanges = status.files.length !== 0 && this.includeWorkingTree;
|
||||
let label = '';
|
||||
let iconSuffix = '';
|
||||
if (status.upstream) {
|
||||
@@ -68,7 +84,7 @@ export class StatusNode extends ExplorerNode {
|
||||
}
|
||||
}
|
||||
else {
|
||||
label = `${status.branch} ${hasWorkingChanges ? 'has uncommitted changes' : 'is clean'}`;
|
||||
label = `${status.branch} ${hasWorkingChanges ? 'has uncommitted changes' : this.includeWorkingTree ? 'has no changes' : 'has nothing to commit'}`;
|
||||
}
|
||||
|
||||
const item = new TreeItem(label, (hasChildren || hasWorkingChanges) ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.None);
|
||||
@@ -81,4 +97,21 @@ export class StatusNode extends ExplorerNode {
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private get includeWorkingTree(): boolean {
|
||||
return this.git.config.gitExplorer.includeWorkingTree;
|
||||
}
|
||||
|
||||
private async onFileSystemChanged(uri: Uri) {
|
||||
const status = await this.git.getStatusForRepo(this.uri.repoPath!);
|
||||
|
||||
// If we haven't changed from having some working changes to none or vice versa then just refresh the node
|
||||
// This is because of https://github.com/Microsoft/vscode/issues/34789
|
||||
if (this._status !== undefined && status !== undefined &&
|
||||
((this._status.files.length === status.files.length) || (this._status.files.length > 0 && status.files.length > 0))) {
|
||||
commands.executeCommand('gitlens.gitExplorer.refreshNode', this);
|
||||
}
|
||||
|
||||
commands.executeCommand('gitlens.gitExplorer.refresh');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user