Browse Source

Refactors core git service into a provider model

This is to ultimately will support virtual repositories (without git)
main
Eric Amodio 3 years ago
parent
commit
b062e960b6
89 changed files with 3117 additions and 1324 deletions
  1. +1
    -1
      src/commands/closeUnchangedFiles.ts
  2. +1
    -1
      src/commands/git/branch.ts
  3. +1
    -1
      src/commands/git/cherry-pick.ts
  4. +6
    -7
      src/commands/git/coauthors.ts
  5. +1
    -1
      src/commands/git/fetch.ts
  6. +1
    -1
      src/commands/git/log.ts
  7. +1
    -1
      src/commands/git/merge.ts
  8. +1
    -1
      src/commands/git/pull.ts
  9. +1
    -1
      src/commands/git/push.ts
  10. +1
    -1
      src/commands/git/rebase.ts
  11. +1
    -1
      src/commands/git/reset.ts
  12. +1
    -1
      src/commands/git/revert.ts
  13. +1
    -1
      src/commands/git/search.ts
  14. +1
    -1
      src/commands/git/show.ts
  15. +1
    -1
      src/commands/git/stash.ts
  16. +1
    -1
      src/commands/git/status.ts
  17. +1
    -1
      src/commands/git/switch.ts
  18. +1
    -1
      src/commands/git/tag.ts
  19. +1
    -1
      src/commands/gitCommands.actions.ts
  20. +1
    -2
      src/commands/quickCommand.steps.ts
  21. +2
    -2
      src/commands/remoteProviders.ts
  22. +3
    -3
      src/commands/repositories.ts
  23. +1
    -1
      src/commands/showQuickBranchHistory.ts
  24. +7
    -0
      src/constants.ts
  25. +22
    -4
      src/container.ts
  26. +3
    -35
      src/extension.ts
  27. +17
    -0
      src/git/errors.ts
  28. +17
    -7
      src/git/git.ts
  29. +486
    -0
      src/git/gitProvider.ts
  30. +1878
    -0
      src/git/gitProviderService.ts
  31. +38
    -36
      src/git/models/repository.ts
  32. +195
    -684
      src/git/providers/localGitProvider.ts
  33. +18
    -3
      src/git/remotes/provider.ts
  34. +1
    -1
      src/quickpicks/repositoryPicker.ts
  35. +4
    -16
      src/system/function.ts
  36. +8
    -1
      src/system/iterable.ts
  37. +11
    -0
      src/system/path.ts
  38. +2
    -2
      src/system/promise.ts
  39. +1
    -1
      src/terminal/linkProvider.ts
  40. +61
    -25
      src/trackers/documentTracker.ts
  41. +2
    -2
      src/trackers/lineTracker.ts
  42. +32
    -59
      src/trackers/trackedDocument.ts
  43. +6
    -32
      src/views/branchesView.ts
  44. +6
    -32
      src/views/commitsView.ts
  45. +8
    -34
      src/views/contributorsView.ts
  46. +13
    -14
      src/views/nodes/branchNode.ts
  47. +2
    -3
      src/views/nodes/branchTrackingStatusFilesNode.ts
  48. +4
    -5
      src/views/nodes/branchTrackingStatusNode.ts
  49. +3
    -4
      src/views/nodes/commitFileNode.ts
  50. +8
    -9
      src/views/nodes/commitNode.ts
  51. +11
    -12
      src/views/nodes/compareBranchNode.ts
  52. +2
    -3
      src/views/nodes/comparePickerNode.ts
  53. +14
    -15
      src/views/nodes/compareResultsNode.ts
  54. +4
    -5
      src/views/nodes/contributorNode.ts
  55. +4
    -5
      src/views/nodes/contributorsNode.ts
  56. +7
    -8
      src/views/nodes/fileHistoryNode.ts
  57. +6
    -7
      src/views/nodes/fileHistoryTrackerNode.ts
  58. +14
    -15
      src/views/nodes/fileRevisionAsCommitNode.ts
  59. +8
    -9
      src/views/nodes/lineHistoryNode.ts
  60. +8
    -9
      src/views/nodes/lineHistoryTrackerNode.ts
  61. +3
    -4
      src/views/nodes/mergeConflictCurrentChangesNode.ts
  62. +3
    -4
      src/views/nodes/mergeConflictIncomingChangesNode.ts
  63. +2
    -3
      src/views/nodes/rebaseStatusNode.ts
  64. +3
    -4
      src/views/nodes/reflogNode.ts
  65. +1
    -2
      src/views/nodes/reflogRecordNode.ts
  66. +2
    -3
      src/views/nodes/remoteNode.ts
  67. +6
    -6
      src/views/nodes/repositoriesNode.ts
  68. +5
    -6
      src/views/nodes/repositoryNode.ts
  69. +2
    -3
      src/views/nodes/resultsCommitsNode.ts
  70. +2
    -3
      src/views/nodes/resultsFileNode.ts
  71. +4
    -5
      src/views/nodes/resultsFilesNode.ts
  72. +6
    -7
      src/views/nodes/searchResultsNode.ts
  73. +3
    -4
      src/views/nodes/stashNode.ts
  74. +2
    -3
      src/views/nodes/stashesNode.ts
  75. +2
    -3
      src/views/nodes/statusFileNode.ts
  76. +5
    -6
      src/views/nodes/statusFilesNode.ts
  77. +3
    -4
      src/views/nodes/tagNode.ts
  78. +43
    -3
      src/views/nodes/viewNode.ts
  79. +6
    -32
      src/views/remotesView.ts
  80. +2
    -9
      src/views/repositoriesView.ts
  81. +1
    -1
      src/views/searchAndCompareView.ts
  82. +13
    -32
      src/views/stashesView.ts
  83. +6
    -32
      src/views/tagsView.ts
  84. +3
    -3
      src/views/viewBase.ts
  85. +2
    -3
      src/views/viewCommands.ts
  86. +19
    -4
      src/vsls/guest.ts
  87. +2
    -1
      src/vsls/host.ts
  88. +1
    -1
      src/vsls/vsls.ts
  89. +3
    -3
      webpack.config.js

+ 1
- 1
src/commands/closeUnchangedFiles.ts View File

@ -130,7 +130,7 @@ export class CloseUnchangedFilesCommand extends Command {
private waitForEditorChange(timeout: number = 500): Promise<TextEditor | undefined> {
return new Promise<TextEditor | undefined>(resolve => {
let timer: NodeJS.Timer | undefined;
let timer: any | undefined;
this._onEditorChangedFn = (editor: TextEditor | undefined) => {
if (timer != null) {

+ 1
- 1
src/commands/git/branch.ts View File

@ -148,7 +148,7 @@ export class BranchGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
showTags: false,
title: this.title,
};

+ 1
- 1
src/commands/git/cherry-pick.ts View File

@ -80,7 +80,7 @@ export class CherryPickGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
cache: new Map<string, Promise<GitLog | undefined>>(),
destination: undefined!,
selectedBranchOrTag: undefined,

+ 6
- 7
src/commands/git/coauthors.ts View File

@ -2,7 +2,6 @@
import { commands } from 'vscode';
import { Container } from '../../container';
import { GitContributor, Repository } from '../../git/git';
import { GitService } from '../../git/providers/localGitProvider';
import { Strings } from '../../system';
import {
PartialStepState,
@ -60,7 +59,7 @@ export class CoAuthorsGitCommand extends QuickCommand {
}
async execute(state: CoAuthorStepState) {
const repo = await GitService.getOrOpenBuiltInGitRepository(state.repo.path);
const repo = await Container.instance.git.getOrOpenScmRepository(state.repo.path);
if (repo == null) return;
let message = repo.inputBox.value;
@ -93,23 +92,23 @@ export class CoAuthorsGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
activeRepo: undefined,
title: this.title,
};
const gitApi = await GitService.getBuiltInGitApi();
if (gitApi != null) {
const scmRepositories = await Container.instance.git.getOpenScmRepositories();
if (scmRepositories.length) {
// Filter out any repo's that are not known to the built-in git
context.repos = context.repos.filter(repo =>
gitApi.repositories.find(r => Strings.normalizePath(r.rootUri.fsPath) === repo.path),
scmRepositories.find(r => Strings.normalizePath(r.rootUri.fsPath) === repo.path),
);
// Ensure that the active repo is known to the built-in git
context.activeRepo = await Container.instance.git.getActiveRepository();
if (
context.activeRepo != null &&
!gitApi.repositories.some(r => r.rootUri.fsPath === context.activeRepo!.path)
!scmRepositories.some(r => r.rootUri.fsPath === context.activeRepo!.path)
) {
context.activeRepo = undefined;
}

+ 1
- 1
src/commands/git/fetch.ts View File

@ -67,7 +67,7 @@ export class FetchGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
title: this.title,
};

+ 1
- 1
src/commands/git/log.ts View File

@ -76,7 +76,7 @@ export class LogGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
cache: new Map<string, Promise<GitLog | undefined>>(),
selectedBranchOrTag: undefined,
title: this.title,

+ 1
- 1
src/commands/git/merge.ts View File

@ -77,7 +77,7 @@ export class MergeGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
cache: new Map<string, Promise<GitLog | undefined>>(),
destination: undefined!,
pickCommit: false,

+ 1
- 1
src/commands/git/pull.ts View File

@ -73,7 +73,7 @@ export class PullGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
title: this.title,
};

+ 1
- 1
src/commands/git/push.ts View File

@ -79,7 +79,7 @@ export class PushGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
title: this.title,
};

+ 1
- 1
src/commands/git/rebase.ts View File

@ -98,7 +98,7 @@ export class RebaseGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
cache: new Map<string, Promise<GitLog | undefined>>(),
destination: undefined!,
pickCommit: false,

+ 1
- 1
src/commands/git/reset.ts View File

@ -71,7 +71,7 @@ export class ResetGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
cache: new Map<string, Promise<GitLog | undefined>>(),
destination: undefined!,
title: this.title,

+ 1
- 1
src/commands/git/revert.ts View File

@ -73,7 +73,7 @@ export class RevertGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
cache: new Map<string, Promise<GitLog | undefined>>(),
destination: undefined!,
title: this.title,

+ 1
- 1
src/commands/git/search.ts View File

@ -91,7 +91,7 @@ export class SearchGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
commit: undefined,
resultsKey: undefined,
resultsPromise: undefined,

+ 1
- 1
src/commands/git/show.ts View File

@ -76,7 +76,7 @@ export class ShowGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
title: this.title,
};

+ 1
- 1
src/commands/git/stash.ts View File

@ -144,7 +144,7 @@ export class StashGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
title: this.title,
};

+ 1
- 1
src/commands/git/status.ts View File

@ -55,7 +55,7 @@ export class StatusGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
status: undefined!,
title: this.title,
};

+ 1
- 1
src/commands/git/switch.ts View File

@ -88,7 +88,7 @@ export class SwitchGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
showTags: false,
title: this.title,
};

+ 1
- 1
src/commands/git/tag.ts View File

@ -127,7 +127,7 @@ export class TagGitCommand extends QuickCommand {
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: [...(await Container.instance.git.getOrderedRepositories())],
repos: Container.instance.git.openRepositories,
showTags: false,
title: this.title,
};

+ 1
- 1
src/commands/gitCommands.actions.ts View File

@ -728,7 +728,7 @@ export namespace GitActions {
export namespace Remote {
export async function add(repo?: string | Repository) {
if (repo == null) {
repo = Container.instance.git.getHighlanderRepoPath();
repo = Container.instance.git.highlanderRepoPath;
if (repo == null) {
const pick = await RepositoryPicker.show(undefined, 'Choose a repository to add a remote to');

+ 1
- 2
src/commands/quickCommand.steps.ts View File

@ -25,7 +25,6 @@ import {
SearchPattern,
TagSortOptions,
} from '../git/git';
import { GitService } from '../git/gitService';
import { GitUri } from '../git/gitUri';
import {
BranchQuickPickItem,
@ -1075,7 +1074,7 @@ export async function* pickContributorsStep<
context: Context,
placeholder: string = 'Choose contributors',
): AsyncStepResultGenerator<GitContributor[]> {
const message = (await GitService.getOrOpenBuiltInGitRepository(state.repo.path))?.inputBox.value;
const message = (await Container.instance.git.getOrOpenScmRepository(state.repo.path))?.inputBox.value;
const step = QuickCommand.createPickStep<ContributorQuickPickItem>({
title: appendReposToTitle(context.title, state, context),

+ 2
- 2
src/commands/remoteProviders.ts View File

@ -47,7 +47,7 @@ export class ConnectRemoteProviderCommand extends Command {
if (args?.repoPath == null) {
const repos = new Map<Repository, GitRemote<RichRemoteProvider>>();
for (const repo of await Container.instance.git.getOrderedRepositories()) {
for (const repo of Container.instance.git.openRepositories) {
const remote = await repo.getRichRemote();
if (remote?.provider != null && !(await remote.provider.isConnected())) {
repos.set(repo, remote);
@ -135,7 +135,7 @@ export class DisconnectRemoteProviderCommand extends Command {
if (args?.repoPath == null) {
const repos = new Map<Repository, GitRemote<RichRemoteProvider>>();
for (const repo of await Container.instance.git.getOrderedRepositories()) {
for (const repo of Container.instance.git.openRepositories) {
const remote = await repo.getRichRemote(true);
if (remote != null) {
repos.set(repo, remote);

+ 3
- 3
src/commands/repositories.ts View File

@ -12,7 +12,7 @@ export class FetchRepositoriesCommand extends Command {
async execute() {
return executeGitCommand({
command: 'fetch',
state: { repos: await Container.instance.git.getOrderedRepositories() },
state: { repos: Container.instance.git.openRepositories },
});
}
}
@ -26,7 +26,7 @@ export class PullRepositoriesCommand extends Command {
async execute() {
return executeGitCommand({
command: 'pull',
state: { repos: await Container.instance.git.getOrderedRepositories() },
state: { repos: Container.instance.git.openRepositories },
});
}
}
@ -40,7 +40,7 @@ export class PushRepositoriesCommand extends Command {
async execute() {
return executeGitCommand({
command: 'push',
state: { repos: await Container.instance.git.getOrderedRepositories() },
state: { repos: Container.instance.git.openRepositories },
});
}
}

+ 1
- 1
src/commands/showQuickBranchHistory.ts View File

@ -32,7 +32,7 @@ export class ShowQuickBranchHistoryCommand extends ActiveEditorCachedCommand {
const gitUri = uri != null ? await GitUri.fromUri(uri) : undefined;
const repoPath = args?.repoPath ?? gitUri?.repoPath ?? Container.instance.git.getHighlanderRepoPath();
const repoPath = args?.repoPath ?? gitUri?.repoPath ?? Container.instance.git.highlanderRepoPath;
let ref: GitReference | 'HEAD' | undefined;
if (repoPath != null) {
if (args?.branch != null) {

+ 7
- 0
src/constants.ts View File

@ -95,6 +95,7 @@ export enum DocumentSchemes {
Output = 'output',
PRs = 'pr',
Vsls = 'vsls',
VirtualFS = 'vscode-vfs',
}
export function getEditorIfActive(document: TextDocument): TextEditor | undefined {
@ -107,6 +108,12 @@ export function isActiveDocument(document: TextDocument): boolean {
return editor != null && editor.document === document;
}
export function isVisibleDocument(document: TextDocument): boolean {
if (window.visibleTextEditors.length === 0) return false;
return window.visibleTextEditors.some(e => e.document === document);
}
export function isTextEditor(editor: TextEditor): boolean {
const scheme = editor.document.uri.scheme;
return scheme !== DocumentSchemes.Output && scheme !== DocumentSchemes.DebugConsole;

+ 22
- 4
src/container.ts View File

@ -1,5 +1,14 @@
'use strict';
import { commands, ConfigurationChangeEvent, ConfigurationScope, Event, EventEmitter, ExtensionContext } from 'vscode';
import {
commands,
ConfigurationChangeEvent,
ConfigurationScope,
env,
Event,
EventEmitter,
ExtensionContext,
UIKind,
} from 'vscode';
import { Autolinks } from './annotations/autolinks';
import { FileAnnotationController } from './annotations/fileAnnotationController';
import { LineAnnotationController } from './annotations/lineAnnotationController';
@ -15,7 +24,9 @@ import {
FileAnnotationType,
} from './configuration';
import { GitFileSystemProvider } from './git/fsProvider';
import { GitService } from './git/providers/localGitProvider';
import { GitProviderId } from './git/gitProvider';
import { GitProviderService } from './git/gitProviderService';
import { LocalGitProvider } from './git/providers/localGitProvider';
import { LineHoverController } from './hovers/lineHoverController';
import { Keyboard } from './keyboard';
import { Logger } from './logger';
@ -84,7 +95,7 @@ export class Container {
this._config = this.applyMode(config);
context.subscriptions.push(configuration.onWillChange(this.onConfigurationChanging, this));
context.subscriptions.push((this._git = new GitService(this)));
context.subscriptions.push((this._git = new GitProviderService(this)));
context.subscriptions.push(new GitFileSystemProvider(this));
context.subscriptions.push((this._actionRunners = new ActionRunners(this)));
@ -136,9 +147,16 @@ export class Container {
if (this._ready) throw new Error('Container is already ready');
this._ready = true;
this.registerGitProviders();
this._onReady.fire();
}
private registerGitProviders() {
if (env.uiKind !== UIKind.Web) {
this._context.subscriptions.push(this._git.register(GitProviderId.Git, new LocalGitProvider(this)));
}
}
private onConfigurationChanging(e: ConfigurationWillChangeEvent) {
this._config = undefined;
@ -235,7 +253,7 @@ export class Container {
return this._fileHistoryView;
}
private _git: GitService;
private _git: GitProviderService;
get git() {
return this._git;
}

+ 3
- 35
src/extension.ts View File

@ -7,17 +7,15 @@ import { CreatePullRequestOnRemoteCommandArgs } from './commands/createPullReque
import { configuration, Configuration, TraceLevel } from './configuration';
import { ContextKeys, GlobalState, GlyphChars, setContext, SyncedState } from './constants';
import { Container } from './container';
import { Git, GitBranch, GitCommit } from './git/git';
import { GitBranch, GitCommit } from './git/git';
import { GitUri } from './git/gitUri';
import { InvalidGitConfigError, UnableToFindGitError } from './git/locator';
import { GitService } from './git/providers/localGitProvider';
import { Logger } from './logger';
import { Messages } from './messages';
import { registerPartnerActionRunners } from './partners';
import { Strings, Versions } from './system';
import { ViewNode } from './views/nodes';
export async function activate(context: ExtensionContext): Promise<GitLensApi | undefined> {
export function activate(context: ExtensionContext): Promise<GitLensApi | undefined> | undefined {
const start = process.hrtime();
if (context.extension.id === 'eamodio.gitlens-insiders') {
@ -111,28 +109,6 @@ export async function activate(context: ExtensionContext): Promise
const cfg = configuration.get();
// await migrateSettings(context, previousVersion);
try {
// Try to use the same git as the built-in vscode git extension
const gitApi = await GitService.getBuiltInGitApi();
await Git.setOrFindGitPath(gitApi?.git.path ?? configuration.getAny<string | string[]>('git.path'));
} catch (ex) {
Logger.error(ex, `GitLens (v${gitlensVersion}) activate`);
void setEnabled(false);
if (ex instanceof InvalidGitConfigError) {
void Messages.showGitInvalidConfigErrorMessage();
} else if (ex instanceof UnableToFindGitError) {
void Messages.showGitMissingErrorMessage();
} else {
const msg: string = ex?.message ?? '';
if (msg) {
void window.showErrorMessage(`Unable to initialize Git; ${msg}`);
}
}
return undefined;
}
const container = Container.create(context, cfg);
// Signal that the container is now ready
container.ready();
@ -141,7 +117,6 @@ export async function activate(context: ExtensionContext): Promise
registerBuiltInActionRunners(container);
registerPartnerActionRunners(context);
notifyOnUnsupportedGitVersion(Git.getGitVersion());
void showWelcomeOrWhatsNew(container, gitlensVersion, previousVersion);
void context.globalState.update(GlobalState.Version, gitlensVersion);
@ -168,7 +143,7 @@ export async function activate(context: ExtensionContext): Promise
);
const api = new Api(container);
return api;
return Promise.resolve(api);
}
export function deactivate() {
@ -196,13 +171,6 @@ function setKeysForSync(context: ExtensionContext, ...keys: (SyncedState | strin
return context.globalState?.setKeysForSync([...keys, SyncedState.Version, SyncedState.WelcomeViewVisible]);
}
function notifyOnUnsupportedGitVersion(version: string) {
if (GitService.compareGitVersion('2.7.2') !== -1) return;
// If git is less than v2.7.2
void Messages.showGitVersionUnsupportedErrorMessage(version, '2.7.2');
}
function registerBuiltInActionRunners(container: Container): void {
container.context.subscriptions.push(
container.actionRunners.registerBuiltIn<CreatePullRequestActionContext>('createPullRequest', {

+ 17
- 0
src/git/errors.ts View File

@ -0,0 +1,17 @@
import { Uri } from 'vscode';
import { GitProviderId, GitProviderService } from './gitProviderService';
export class ProviderNotFoundError extends Error {
readonly id: GitProviderId;
constructor(id: GitProviderId);
constructor(uri: Uri);
constructor(idOrUri: GitProviderId | Uri);
constructor(idOrUri: GitProviderId | Uri) {
const id = typeof idOrUri === 'string' ? idOrUri : GitProviderService.getProviderId(idOrUri);
super(`No provider registered with ${id}`);
this.id = id;
Error.captureStackTrace?.(this, ProviderNotFoundError);
}
}

+ 17
- 7
src/git/git.ts View File

@ -6,7 +6,8 @@ import { Uri, window, workspace } from 'vscode';
import { GlyphChars } from '../constants';
import { Container } from '../container';
import { Logger } from '../logger';
import { Paths, Strings } from '../system';
import { Messages } from '../messages';
import { Paths, Strings, Versions } from '../system';
import { findGitPath, GitLocation } from './locator';
import { GitRevision } from './models/models';
import { GitBranchParser, GitLogParser, GitReflogParser, GitStashParser, GitTagParser } from './parsers/parsers';
@ -129,7 +130,7 @@ export async function git(options: GitCommandOptio
args.splice(0, 0, '-c', 'core.longpaths=true');
}
promise = run<TOut>(gitInfo.path, args, encoding ?? 'utf8', runOpts);
promise = run<TOut>(Git.getGitPath(), args, encoding ?? 'utf8', runOpts);
pendingCommands.set(command, promise);
} else {
@ -206,19 +207,23 @@ function defaultExceptionHandler(ex: Error, cwd: string | undefined, start?: [nu
throw ex;
}
let gitInfo: GitLocation;
export namespace Git {
let gitInfo: GitLocation | undefined;
export function getEncoding(encoding: string | undefined) {
return encoding !== undefined && iconv.encodingExists(encoding) ? encoding : 'utf8';
}
export function getGitPath(): string {
return gitInfo.path;
return gitInfo?.path ?? '';
}
export function getGitVersion(): string {
return gitInfo.version;
return gitInfo?.version ?? '';
}
export function hasGitPath(): boolean {
return Boolean(gitInfo?.path);
}
export async function setOrFindGitPath(gitPath?: string | string[]): Promise<void> {
@ -231,10 +236,15 @@ export namespace Git {
GlyphChars.Dot
} ${Strings.getDurationMilliseconds(start)} ms`,
);
// Warn if git is less than v2.7.2
if (Versions.compare(Versions.fromString(gitInfo.version), Versions.fromString('2.7.2')) === -1) {
void Messages.showGitVersionUnsupportedErrorMessage(gitInfo.version, '2.7.2');
}
}
export function validateVersion(major: number, minor: number): boolean {
const [gitMajor, gitMinor] = gitInfo.version.split('.');
const [gitMajor, gitMinor] = getGitVersion().split('.');
return parseInt(gitMajor, 10) >= major && parseInt(gitMinor, 10) >= minor;
}

+ 486
- 0
src/git/gitProvider.ts View File

@ -0,0 +1,486 @@
import { Disposable, Event, Range, TextEditor, Uri, WorkspaceFolder } from 'vscode';
import { Commit, InputBox } from '../@types/vscode.git';
import {
BranchSortOptions,
GitBlame,
GitBlameLine,
GitBlameLines,
GitBranch,
GitBranchReference,
GitContributor,
GitDiff,
GitDiffFilter,
GitDiffHunkLine,
GitDiffShortStat,
GitFile,
GitLog,
GitLogCommit,
GitMergeStatus,
GitRebaseStatus,
GitReflog,
GitRemote,
GitStash,
GitStatus,
GitStatusFile,
GitTag,
GitTree,
GitUser,
RemoteProvider,
Repository,
RepositoryChangeEvent,
RichRemoteProvider,
TagSortOptions,
} from './git';
import { GitUri } from './gitUri';
import { RemoteProviders } from './remotes/factory';
import { SearchPattern } from './search';
export enum GitProviderId {
Git = 'git',
GitHub = 'github',
}
export interface GitProviderDescriptor {
readonly id: GitProviderId;
readonly name: string;
}
export interface RepositoryInitWatcher extends Disposable {
readonly onDidCreate: Event<Uri>;
}
export interface ScmRepository {
readonly rootUri: Uri;
readonly inputBox: InputBox;
getCommit(ref: string): Promise<Commit>;
push(remoteName?: string, branchName?: string, setUpstream?: boolean): Promise<void>;
}
export interface GitProvider {
get onDidChangeRepository(): Event<RepositoryChangeEvent>;
readonly descriptor: GitProviderDescriptor;
// get readonly(): boolean;
// get useCaching(): boolean;
discoverRepositories(uri: Uri): Promise<Repository[]>;
createRepository(
folder: WorkspaceFolder,
path: string,
root: boolean,
suspended?: boolean,
closed?: boolean,
): Repository;
createRepositoryInitWatcher?(): RepositoryInitWatcher;
getOpenScmRepositories(): Promise<ScmRepository[]>;
getOrOpenScmRepository(repoPath: string): Promise<ScmRepository | undefined>;
addRemote(repoPath: string, name: string, url: string): Promise<void>;
pruneRemote(repoPath: string, remoteName: string): Promise<void>;
applyChangesToWorkingFile(uri: GitUri, ref1?: string, ref2?: string): Promise<void>;
branchContainsCommit(repoPath: string, name: string, ref: string): Promise<boolean>;
checkout(
repoPath: string,
ref: string,
options?: { createBranch?: string | undefined } | { fileName?: string | undefined },
): Promise<void>;
resetCaches(
...cache: ('branches' | 'contributors' | 'providers' | 'remotes' | 'stashes' | 'status' | 'tags')[]
): void;
excludeIgnoredUris(repoPath: string, uris: Uri[]): Promise<Uri[]>;
fetch(
repoPath: string,
options?: {
all?: boolean | undefined;
branch?: GitBranchReference | undefined;
prune?: boolean | undefined;
pull?: boolean | undefined;
remote?: string | undefined;
},
): Promise<void>;
// getActiveRepository(editor?: TextEditor): Promise<Repository | undefined>;
// getActiveRepoPath(editor?: TextEditor): Promise<string | undefined>;
// getHighlanderRepoPath(): string | undefined;
getBlameForFile(uri: GitUri): Promise<GitBlame | undefined>;
getBlameForFileContents(uri: GitUri, contents: string): Promise<GitBlame | undefined>;
getBlameForLine(
uri: GitUri,
editorLine: number,
options?: { skipCache?: boolean | undefined },
): Promise<GitBlameLine | undefined>;
getBlameForLineContents(
uri: GitUri,
editorLine: number,
contents: string,
options?: { skipCache?: boolean | undefined },
): Promise<GitBlameLine | undefined>;
getBlameForRange(uri: GitUri, range: Range): Promise<GitBlameLines | undefined>;
getBlameForRangeContents(uri: GitUri, range: Range, contents: string): Promise<GitBlameLines | undefined>;
getBlameForRangeSync(blame: GitBlame, uri: GitUri, range: Range): GitBlameLines | undefined;
getBranch(repoPath: string): Promise<GitBranch | undefined>;
// getBranchAheadRange(branch: GitBranch): Promise<string | undefined>;
getBranches(
repoPath: string,
options?: { filter?: ((b: GitBranch) => boolean) | undefined; sort?: boolean | BranchSortOptions | undefined },
): Promise<GitBranch[]>;
// getBranchesAndOrTags(
// repoPath: string | undefined,
// options?: {
// filter?:
// | { branches?: ((b: GitBranch) => boolean) | undefined; tags?: ((t: GitTag) => boolean) | undefined }
// | undefined;
// include?: 'branches' | 'tags' | 'all' | undefined;
// sort?:
// | boolean
// | { branches?: BranchSortOptions | undefined; tags?: TagSortOptions | undefined }
// | undefined;
// },
// ): Promise<any>;
// getBranchesAndTagsTipsFn(
// repoPath: string | undefined,
// currentName?: string,
// ): Promise<
// (sha: string, options?: { compact?: boolean | undefined; icons?: boolean | undefined }) => string | undefined
// >;
getChangedFilesCount(repoPath: string, ref?: string): Promise<GitDiffShortStat | undefined>;
getCommit(repoPath: string, ref: string): Promise<GitLogCommit | undefined>;
getCommitBranches(
repoPath: string,
ref: string,
options?: { mode?: 'contains' | 'pointsAt' | undefined; remotes?: boolean | undefined },
): Promise<string[]>;
getAheadBehindCommitCount(repoPath: string, refs: string[]): Promise<{ ahead: number; behind: number } | undefined>;
getCommitCount(repoPath: string, ref: string): Promise<number | undefined>;
getCommitForFile(
repoPath: string,
fileName: string,
options?: {
ref?: string | undefined;
firstIfNotFound?: boolean | undefined;
range?: Range | undefined;
reverse?: boolean | undefined;
},
): Promise<GitLogCommit | undefined>;
getOldestUnpushedRefForFile(repoPath: string, fileName: string): Promise<string | undefined>;
getConfig(key: string, repoPath?: string): Promise<string | undefined>;
getContributors(
repoPath: string,
options?: { all?: boolean | undefined; ref?: string | undefined; stats?: boolean | undefined },
): Promise<GitContributor[]>;
getCurrentUser(repoPath: string): Promise<GitUser | undefined>;
getDefaultBranchName(repoPath: string | undefined, remote?: string): Promise<string | undefined>;
getDiffForFile(
uri: GitUri,
ref1: string | undefined,
ref2?: string,
originalFileName?: string,
): Promise<GitDiff | undefined>;
getDiffForFileContents(
uri: GitUri,
ref: string,
contents: string,
originalFileName?: string,
): Promise<GitDiff | undefined>;
getDiffForLine(
uri: GitUri,
editorLine: number,
ref1: string | undefined,
ref2?: string,
originalFileName?: string,
): Promise<GitDiffHunkLine | undefined>;
getDiffStatus(
repoPath: string,
ref1?: string,
ref2?: string,
options?: { filters?: GitDiffFilter[] | undefined; similarityThreshold?: number | undefined },
): Promise<GitFile[] | undefined>;
getFileStatusForCommit(repoPath: string, fileName: string, ref: string): Promise<GitFile | undefined>;
getLog(
repoPath: string,
options?: {
all?: boolean | undefined;
authors?: string[] | undefined;
limit?: number | undefined;
merges?: boolean | undefined;
ordering?: string | null | undefined;
ref?: string | undefined;
reverse?: boolean | undefined;
since?: string | undefined;
},
): Promise<GitLog | undefined>;
getLogRefsOnly(
repoPath: string,
options?: {
authors?: string[] | undefined;
limit?: number | undefined;
merges?: boolean | undefined;
ordering?: string | null | undefined;
ref?: string | undefined;
reverse?: boolean | undefined;
since?: string | undefined;
},
): Promise<Set<string> | undefined>;
getLogForSearch(
repoPath: string,
search: SearchPattern,
options?: { limit?: number | undefined; ordering?: string | null | undefined; skip?: number | undefined },
): Promise<GitLog | undefined>;
getLogForFile(
repoPath: string,
fileName: string,
options?: {
all?: boolean | undefined;
limit?: number | undefined;
ordering?: string | null | undefined;
range?: Range | undefined;
ref?: string | undefined;
renames?: boolean | undefined;
reverse?: boolean | undefined;
since?: string | undefined;
skip?: number | undefined;
},
): Promise<GitLog | undefined>;
getMergeBase(
repoPath: string,
ref1: string,
ref2: string,
options?: { forkPoint?: boolean | undefined },
): Promise<string | undefined>;
getMergeStatus(repoPath: string): Promise<GitMergeStatus | undefined>;
getRebaseStatus(repoPath: string): Promise<GitRebaseStatus | undefined>;
getNextDiffUris(
repoPath: string,
uri: Uri,
ref: string | undefined,
skip?: number,
): Promise<{ current: GitUri; next: GitUri | undefined; deleted?: boolean | undefined } | undefined>;
getNextUri(repoPath: string, uri: Uri, ref?: string, skip?: number): Promise<GitUri | undefined>;
getPreviousDiffUris(
repoPath: string,
uri: Uri,
ref: string | undefined,
skip?: number,
firstParent?: boolean,
): Promise<{ current: GitUri; previous: GitUri | undefined } | undefined>;
getPreviousLineDiffUris(
repoPath: string,
uri: Uri,
editorLine: number,
ref: string | undefined,
skip?: number,
): Promise<{ current: GitUri; previous: GitUri | undefined; line: number } | undefined>;
getPreviousUri(
repoPath: string,
uri: Uri,
ref?: string,
skip?: number,
editorLine?: number,
firstParent?: boolean,
): Promise<GitUri | undefined>;
// getPullRequestForBranch(
// branch: string,
// remote: GitRemote<RemoteProvider | RichRemoteProvider | undefined>,
// options?: {
// avatarSize?: number | undefined;
// include?: PullRequestState[] | undefined;
// limit?: number | undefined;
// timeout?: number | undefined;
// },
// ): Promise<PullRequest | undefined>;
// getPullRequestForBranch(
// branch: string,
// provider: RichRemoteProvider,
// options?: {
// avatarSize?: number | undefined;
// include?: PullRequestState[] | undefined;
// limit?: number | undefined;
// timeout?: number | undefined;
// },
// ): Promise<PullRequest | undefined>;
// getPullRequestForBranch(
// branch: string,
// remoteOrProvider: RichRemoteProvider | GitRemote<RemoteProvider | RichRemoteProvider | undefined>,
// options?: {
// avatarSize?: number | undefined;
// include?: PullRequestState[] | undefined;
// limit?: number | undefined;
// timeout?: number | undefined;
// },
// ): Promise<PullRequest | undefined>;
// getPullRequestForCommit(
// ref: string,
// remote: GitRemote<RemoteProvider | RichRemoteProvider | undefined>,
// options?: { timeout?: number | undefined },
// ): Promise<PullRequest | undefined>;
// getPullRequestForCommit(
// ref: string,
// provider: RichRemoteProvider,
// options?: { timeout?: number | undefined },
// ): Promise<PullRequest | undefined>;
// getPullRequestForCommit(
// ref: string,
// remoteOrProvider: RichRemoteProvider | GitRemote<RemoteProvider | RichRemoteProvider | undefined>,
// { timeout }?: { timeout?: number | undefined },
// ): Promise<PullRequest | undefined>;
getIncomingActivity(
repoPath: string,
options?: {
all?: boolean | undefined;
branch?: string | undefined;
limit?: number | undefined;
ordering?: string | null | undefined;
skip?: number | undefined;
},
): Promise<GitReflog | undefined>;
getRichRemoteProvider(
repoPath: string | undefined,
options?: { includeDisconnected?: boolean | undefined },
): Promise<GitRemote<RichRemoteProvider> | undefined>;
getRichRemoteProvider(
remotes: GitRemote<RemoteProvider | RichRemoteProvider | undefined>[],
options?: { includeDisconnected?: boolean | undefined },
): Promise<GitRemote<RichRemoteProvider> | undefined>;
getRichRemoteProvider(
remotesOrRepoPath: string | GitRemote<RemoteProvider | RichRemoteProvider | undefined>[] | undefined,
options?: { includeDisconnected?: boolean | undefined },
): Promise<GitRemote<RichRemoteProvider> | undefined>;
getRemotes(
repoPath: string | undefined,
options?: { sort?: boolean | undefined },
): Promise<GitRemote<RemoteProvider>[]>;
getRemotesCore(
repoPath: string | undefined,
providers?: RemoteProviders,
options?: { sort?: boolean | undefined },
): Promise<GitRemote<RemoteProvider | RichRemoteProvider | undefined>[]>;
// getRepoPath(filePath: string, options?: { ref?: string | undefined }): Promise<string | undefined>;
// getRepoPath(uri: Uri | undefined, options?: { ref?: string | undefined }): Promise<string | undefined>;
// getRepoPath(
// filePathOrUri: string | Uri | undefined,
// options?: { ref?: string | undefined },
// ): Promise<string | undefined>;
getRepoPath(filePath: string, isDirectory: boolean): Promise<string | undefined>;
// getRepoPathOrActive(uri: Uri | undefined, editor: TextEditor | undefined): Promise<string | undefined>;
// getRepositories(predicate?: (repo: Repository) => boolean): Promise<Iterable<Repository>>;
// getOrderedRepositories(): Promise<Repository[]>;
// getRepository(
// repoPath: string,
// options?: { ref?: string | undefined; skipCacheUpdate?: boolean | undefined },
// ): Promise<Repository | undefined>;
// getRepository(
// uri: Uri,
// options?: { ref?: string | undefined; skipCacheUpdate?: boolean | undefined },
// ): Promise<Repository | undefined>;
// getRepository(
// repoPathOrUri: string | Uri,
// options?: { ref?: string | undefined; skipCacheUpdate?: boolean | undefined },
// ): Promise<Repository | undefined>;
// getRepository(
// repoPathOrUri: string | Uri,
// options?: { ref?: string | undefined; skipCacheUpdate?: boolean | undefined },
// ): Promise<Repository | undefined>;
// getLocalInfoFromRemoteUri(
// uri: Uri,
// options?: { validate?: boolean | undefined },
// ): Promise<{ uri: Uri; startLine?: number | undefined; endLine?: number | undefined } | undefined>;
// getRepositoryCount(): Promise<number>;
getStash(repoPath: string | undefined): Promise<GitStash | undefined>;
getStatusForFile(repoPath: string, fileName: string): Promise<GitStatusFile | undefined>;
getStatusForFiles(repoPath: string, pathOrGlob: string): Promise<GitStatusFile[] | undefined>;
getStatusForRepo(repoPath: string | undefined): Promise<GitStatus | undefined>;
getTags(
repoPath: string | undefined,
options?: { filter?: ((t: GitTag) => boolean) | undefined; sort?: boolean | TagSortOptions | undefined },
): Promise<GitTag[]>;
getTreeFileForRevision(repoPath: string, fileName: string, ref: string): Promise<GitTree | undefined>;
getTreeForRevision(repoPath: string, ref: string): Promise<GitTree[]>;
getVersionedFileBuffer(repoPath: string, fileName: string, ref: string): Promise<Buffer | undefined>;
getVersionedUri(repoPath: string | undefined, fileName: string, ref: string | undefined): Promise<Uri | undefined>;
getWorkingUri(repoPath: string, uri: Uri): Promise<Uri | undefined>;
// hasBranchesAndOrTags(
// repoPath: string | undefined,
// options?: {
// filter?:
// | { branches?: ((b: GitBranch) => boolean) | undefined; tags?: ((t: GitTag) => boolean) | undefined }
// | undefined;
// },
// ): Promise<boolean>;
hasRemotes(repoPath: string | undefined): Promise<boolean>;
hasTrackingBranch(repoPath: string | undefined): Promise<boolean>;
isActiveRepoPath(repoPath: string | undefined, editor?: TextEditor): Promise<boolean>;
isTrackable(scheme: string): boolean;
isTrackable(uri: Uri): boolean;
isTrackable(schemeOruri: string | Uri): boolean;
isTracked(
fileName: string,
repoPath?: string,
options?: { ref?: string | undefined; skipCacheUpdate?: boolean | undefined },
): Promise<boolean>;
isTracked(uri: GitUri): Promise<boolean>;
isTracked(
fileNameOrUri: string | GitUri,
repoPath?: string,
options?: { ref?: string | undefined; skipCacheUpdate?: boolean | undefined },
): Promise<boolean>;
getDiffTool(repoPath?: string): Promise<string | undefined>;
openDiffTool(
repoPath: string,
uri: Uri,
options?: {
ref1?: string | undefined;
ref2?: string | undefined;
staged?: boolean | undefined;
tool?: string | undefined;
},
): Promise<void>;
openDirectoryCompare(repoPath: string, ref1: string, ref2?: string, tool?: string): Promise<void>;
resolveReference(
repoPath: string,
ref: string,
fileName?: string,
options?: { timeout?: number | undefined },
): Promise<string>;
resolveReference(
repoPath: string,
ref: string,
uri?: Uri,
options?: { timeout?: number | undefined },
): Promise<string>;
resolveReference(
repoPath: string,
ref: string,
fileNameOrUri?: string | Uri,
options?: { timeout?: number | undefined },
): Promise<string>;
validateBranchOrTagName(repoPath: string, ref: string): Promise<boolean>;
validateReference(repoPath: string, ref: string): Promise<boolean>;
stageFile(repoPath: string, fileName: string): Promise<void>;
stageFile(repoPath: string, uri: Uri): Promise<void>;
stageFile(repoPath: string, fileNameOrUri: string | Uri): Promise<void>;
stageDirectory(repoPath: string, directory: string): Promise<void>;
stageDirectory(repoPath: string, uri: Uri): Promise<void>;
stageDirectory(repoPath: string, directoryOrUri: string | Uri): Promise<void>;
unStageFile(repoPath: string, fileName: string): Promise<void>;
unStageFile(repoPath: string, uri: Uri): Promise<void>;
unStageFile(repoPath: string, fileNameOrUri: string | Uri): Promise<void>;
unStageDirectory(repoPath: string, directory: string): Promise<void>;
unStageDirectory(repoPath: string, uri: Uri): Promise<void>;
unStageDirectory(repoPath: string, directoryOrUri: string | Uri): Promise<void>;
stashApply(repoPath: string, stashName: string, options?: { deleteAfter?: boolean | undefined }): Promise<void>;
stashDelete(repoPath: string, stashName: string, ref?: string): Promise<void>;
stashSave(
repoPath: string,
message?: string,
uris?: Uri[],
options?: { includeUntracked?: boolean | undefined; keepIndex?: boolean | undefined },
): Promise<void>;
}

+ 1878
- 0
src/git/gitProviderService.ts
File diff suppressed because it is too large
View File


+ 38
- 36
src/git/models/repository.ts View File

@ -32,8 +32,8 @@ import {
GitTag,
SearchPattern,
} from '../git';
import { GitProviderDescriptor } from '../gitProvider';
import { GitUri } from '../gitUri';
import { GitService } from '../providers/localGitProvider';
import { RemoteProviderFactory, RemoteProviders, RichRemoteProvider } from '../remotes/factory';
import {
BranchSortOptions,
@ -211,10 +211,12 @@ export class Repository implements Disposable {
private _suspended: boolean;
constructor(
private readonly container: Container,
private readonly onDidRepositoryChange: (repo: Repository, e: RepositoryChangeEvent) => void,
public readonly provider: GitProviderDescriptor,
public readonly folder: WorkspaceFolder,
public readonly path: string,
public readonly root: boolean,
private readonly onAnyRepositoryChanged: (repo: Repository, e: RepositoryChangeEvent) => void,
suspended: boolean,
closed: boolean = false,
) {
@ -354,7 +356,7 @@ export class Repository implements Disposable {
}
if (uri.path.endsWith('.git/HEAD') || uri.path.endsWith('.git/ORIG_HEAD')) {
this.resetCaches('branch');
this.resetCaches('branches');
this.fireChange(RepositoryChange.Heads);
return;
@ -394,7 +396,7 @@ export class Repository implements Disposable {
if (match != null) {
switch (match[1]) {
case 'heads':
this.resetCaches('branch');
this.resetCaches('branches');
this.fireChange(RepositoryChange.Heads);
return;
@ -528,7 +530,7 @@ export class Repository implements Disposable {
options: { all?: boolean; branch?: GitBranchReference; prune?: boolean; pull?: boolean; remote?: string } = {},
) {
try {
void (await Container.instance.git.fetch(this.path, options));
void (await this.container.git.fetch(this.path, options));
this.fireChange(RepositoryChange.Unknown);
} catch (ex) {
@ -544,7 +546,7 @@ export class Repository implements Disposable {
}
if (this._branch == null || !this.supportsChangeEvents) {
this._branch = Container.instance.git.getBranch(this.path);
this._branch = this.container.git.getBranch(this.path);
}
return this._branch;
}
@ -555,7 +557,7 @@ export class Repository implements Disposable {
sort?: boolean | BranchSortOptions;
} = {},
): Promise<GitBranch[]> {
return Container.instance.git.getBranches(this.path, options);
return this.container.git.getBranches(this.path, options);
}
getBranchesAndOrTags(
@ -565,19 +567,19 @@ export class Repository implements Disposable {
sort?: boolean | { branches?: BranchSortOptions; tags?: TagSortOptions };
} = {},
) {
return Container.instance.git.getBranchesAndOrTags(this.path, options);
return this.container.git.getBranchesAndOrTags(this.path, options);
}
getChangedFilesCount(sha?: string): Promise<GitDiffShortStat | undefined> {
return Container.instance.git.getChangedFilesCount(this.path, sha);
return this.container.git.getChangedFilesCount(this.path, sha);
}
getCommit(ref: string): Promise<GitLogCommit | undefined> {
return Container.instance.git.getCommit(this.path, ref);
return this.container.git.getCommit(this.path, ref);
}
getContributors(options?: { all?: boolean; ref?: string; stats?: boolean }): Promise<GitContributor[]> {
return Container.instance.git.getContributors(this.path, options);
return this.container.git.getContributors(this.path, options);
}
private _lastFetched: number | undefined;
@ -585,7 +587,7 @@ export class Repository implements Disposable {
async getLastFetched(): Promise<number> {
if (this._lastFetched == null) {
const hasRemotes = await this.hasRemotes();
if (!hasRemotes || Container.instance.vsls.isMaybeGuest) return 0;
if (!hasRemotes || this.container.vsls.isMaybeGuest) return 0;
}
try {
@ -602,11 +604,11 @@ export class Repository implements Disposable {
}
getMergeStatus(): Promise<GitMergeStatus | undefined> {
return Container.instance.git.getMergeStatus(this.path);
return this.container.git.getMergeStatus(this.path);
}
getRebaseStatus(): Promise<GitRebaseStatus | undefined> {
return Container.instance.git.getRebaseStatus(this.path);
return this.container.git.getRebaseStatus(this.path);
}
async getRemote(remote: string): Promise<GitRemote | undefined> {
@ -621,7 +623,7 @@ export class Repository implements Disposable {
}
// Since we are caching the results, always sort
this._remotes = Container.instance.git.getRemotesCore(this.path, this._providers, { sort: true });
this._remotes = this.container.git.getRemotesCore(this.path, this._providers, { sort: true });
void this.subscribeToRemotes(this._remotes);
}
@ -629,7 +631,7 @@ export class Repository implements Disposable {
}
async getRichRemote(connectedOnly: boolean = false): Promise<GitRemote<RichRemoteProvider> | undefined> {
return Container.instance.git.getRichRemoteProvider(await this.getRemotes(), {
return this.container.git.getRichRemoteProvider(await this.getRemotes(), {
includeDisconnected: !connectedOnly,
});
}
@ -648,15 +650,15 @@ export class Repository implements Disposable {
}
getStash(): Promise<GitStash | undefined> {
return Container.instance.git.getStash(this.path);
return this.container.git.getStash(this.path);
}
getStatus(): Promise<GitStatus | undefined> {
return Container.instance.git.getStatusForRepo(this.path);
return this.container.git.getStatusForRepo(this.path);
}
getTags(options?: { filter?: (t: GitTag) => boolean; sort?: boolean | TagSortOptions }): Promise<GitTag[]> {
return Container.instance.git.getTags(this.path, options);
return this.container.git.getTags(this.path, options);
}
async hasRemotes(): Promise<boolean> {
@ -704,7 +706,7 @@ export class Repository implements Disposable {
this.path,
));
} else if (configuration.getAny<boolean>(BuiltInGitConfiguration.FetchOnPull, Uri.file(this.path))) {
void (await Container.instance.git.fetch(this.path));
void (await this.container.git.fetch(this.path));
}
this.fireChange(RepositoryChange.Unknown);
@ -741,7 +743,7 @@ export class Repository implements Disposable {
}
private async showCreatePullRequestPrompt(remoteName: string, branch: GitBranchReference) {
if (!Container.instance.actionRunners.count('createPullRequest')) return;
if (!this.container.actionRunners.count('createPullRequest')) return;
if (!(await Messages.showCreatePullRequestPrompt(branch.name))) return;
const remote = await this.getRemote(remoteName);
@ -782,7 +784,7 @@ export class Repository implements Disposable {
) {
try {
if (GitReference.isBranch(options.reference)) {
const repo = await GitService.getOrOpenBuiltInGitRepository(this.path);
const repo = await this.container.git.getOrOpenScmRepository(this.path);
if (repo == null) return;
if (options.publish != null) {
@ -803,7 +805,7 @@ export class Repository implements Disposable {
}
}
} else if (options.reference != null) {
const repo = await GitService.getOrOpenBuiltInGitRepository(this.path);
const repo = await this.container.git.getOrOpenScmRepository(this.path);
if (repo == null) return;
const branch = await this.getBranch();
@ -839,8 +841,8 @@ export class Repository implements Disposable {
this.runTerminalCommand('reset', ...args);
}
resetCaches(...cache: ('branch' | 'remotes')[]) {
if (cache.length === 0 || cache.includes('branch')) {
resetCaches(...cache: ('branches' | 'remotes')[]) {
if (cache.length === 0 || cache.includes('branches')) {
this._branch = undefined;
}
@ -877,11 +879,11 @@ export class Repository implements Disposable {
search: SearchPattern,
options: { limit?: number; skip?: number } = {},
): Promise<GitLog | undefined> {
return Container.instance.git.getLogForSearch(this.path, search, options);
return this.container.git.getLogForSearch(this.path, search, options);
}
get starred() {
const starred = Container.instance.context.workspaceState.get<Starred>(WorkspaceState.StarredRepositories);
const starred = this.container.context.workspaceState.get<Starred>(WorkspaceState.StarredRepositories);
return starred != null && starred[this.id] === true;
}
@ -892,7 +894,7 @@ export class Repository implements Disposable {
@gate(() => '')
@log()
async stashApply(stashName: string, options: { deleteAfter?: boolean } = {}) {
void (await Container.instance.git.stashApply(this.path, stashName, options));
void (await this.container.git.stashApply(this.path, stashName, options));
this.fireChange(RepositoryChange.Stash);
}
@ -900,7 +902,7 @@ export class Repository implements Disposable {
@gate(() => '')
@log()
async stashDelete(stashName: string, ref?: string) {
void (await Container.instance.git.stashDelete(this.path, stashName, ref));
void (await this.container.git.stashDelete(this.path, stashName, ref));
this.fireChange(RepositoryChange.Stash);
}
@ -908,7 +910,7 @@ export class Repository implements Disposable {
@gate(() => '')
@log()
async stashSave(message?: string, uris?: Uri[], options: { includeUntracked?: boolean; keepIndex?: boolean } = {}) {
void (await Container.instance.git.stashSave(this.path, message, uris, options));
void (await this.container.git.stashSave(this.path, message, uris, options));
this.fireChange(RepositoryChange.Stash);
}
@ -931,7 +933,7 @@ export class Repository implements Disposable {
private async switchCore(ref: string, options: { createBranch?: string } = {}) {
try {
void (await Container.instance.git.checkout(this.path, ref, options));
void (await this.container.git.checkout(this.path, ref, options));
this.fireChange(RepositoryChange.Unknown);
} catch (ex) {
@ -960,7 +962,7 @@ export class Repository implements Disposable {
}
private async updateStarredCore(key: WorkspaceState, id: string, star: boolean) {
let starred = Container.instance.context.workspaceState.get<Starred>(key);
let starred = this.container.context.workspaceState.get<Starred>(key);
if (starred === undefined) {
starred = Object.create(null) as Starred;
}
@ -971,7 +973,7 @@ export class Repository implements Disposable {
const { [id]: _, ...rest } = starred;
starred = rest;
}
await Container.instance.context.workspaceState.update(key, starred);
await this.container.context.workspaceState.update(key, starred);
this.fireChange(RepositoryChange.Starred);
}
@ -1035,7 +1037,7 @@ export class Repository implements Disposable {
this._pendingRepoChange = this._pendingRepoChange?.with(changes) ?? new RepositoryChangeEvent(this, changes);
this.onAnyRepositoryChanged(this, new RepositoryChangeEvent(this, changes));
this.onDidRepositoryChange(this, new RepositoryChangeEvent(this, changes));
if (this._suspended) {
Logger.debug(cc, `queueing suspended ${this._pendingRepoChange.toString(true)}`);
@ -1087,7 +1089,7 @@ export class Repository implements Disposable {
this._pendingFileSystemChange = undefined;
const uris = await Container.instance.git.excludeIgnoredUris(this.path, e.uris);
const uris = await this.container.git.excludeIgnoredUris(this.path, e.uris);
if (uris.length === 0) return;
if (uris.length !== e.uris.length) {
@ -1109,7 +1111,7 @@ export class Repository implements Disposable {
}
private async tryWatchingForChangesViaBuiltInApi() {
const repo = await GitService.getOrOpenBuiltInGitRepository(this.path);
const repo = await this.container.git.getOrOpenScmRepository(this.path);
if (repo != null) {
const internalRepo = (repo as any)._repository;
if (internalRepo != null && 'onDidChangeRepository' in internalRepo) {

+ 195
- 684
src/git/providers/localGitProvider.ts
File diff suppressed because it is too large
View File


+ 18
- 3
src/git/remotes/provider.ts View File

@ -238,7 +238,20 @@ export abstract class RemoteProvider implements RemoteProviderReference {
}
}
const _connectedCache = new Set<string>();
const _onDidChangeAuthentication = new EventEmitter<{ reason: 'connected' | 'disconnected'; key: string }>();
function fireAuthenticationChanged(key: string, reason: 'connected' | 'disconnected') {
// Only fire events if the key is being connected for the first time (we could probably do the same for disconnected, but better safe on those imo)
if (_connectedCache.has(key)) {
if (reason === 'connected') return;
_connectedCache.delete(key);
} else if (reason === 'connected') {
_connectedCache.add(key);
}
_onDidChangeAuthentication.fire({ key: key, reason: reason });
}
export class Authentication {
static get onDidChange(): Event<{ reason: 'connected' | 'disconnected'; key: string }> {
@ -350,7 +363,7 @@ export abstract class RichRemoteProvider extends RemoteProvider {
this._onDidChange.fire();
if (!silent) {
_onDidChangeAuthentication.fire({ reason: 'disconnected', key: this.key });
fireAuthenticationChanged(this.key, 'disconnected');
}
}
}
@ -603,8 +616,10 @@ export abstract class RichRemoteProvider extends RemoteProvider {
if (session != null) {
await Container.instance.context.workspaceState.update(this.connectedKey, true);
this._onDidChange.fire();
_onDidChangeAuthentication.fire({ reason: 'connected', key: this.key });
queueMicrotask(() => {
this._onDidChange.fire();
fireAuthenticationChanged(this.key, 'connected');
});
}
return session ?? undefined;

+ 1
- 1
src/quickpicks/repositoryPicker.ts View File

@ -12,7 +12,7 @@ export namespace RepositoryPicker {
repositories?: Repository[],
): Promise<RepositoryQuickPickItem | undefined> {
const items: RepositoryQuickPickItem[] = await Promise.all([
...Iterables.map(repositories ?? (await Container.instance.git.getOrderedRepositories()), r =>
...Iterables.map(repositories ?? Container.instance.git.openRepositories, r =>
RepositoryQuickPickItem.create(r, undefined, { branch: true, status: true }),
),
]);

+ 4
- 16
src/system/function.ts View File

@ -152,7 +152,7 @@ export function propOf>(o: T, key: K) {
}
export function interval(fn: (...args: any[]) => void, ms: number): Disposable {
let timer: NodeJS.Timer | undefined;
let timer: any | undefined;
const disposable = {
dispose: () => {
if (timer !== undefined) {
@ -161,15 +161,15 @@ export function interval(fn: (...args: any[]) => void, ms: number): Disposable {
}
},
};
timer = global.setInterval(fn, ms);
timer = globalThis.setInterval(fn, ms);
return disposable;
}
export function progress<T>(promise: Promise<T>, intervalMs: number, onProgress: () => boolean): Promise<T> {
return new Promise((resolve, reject) => {
let timer: NodeJS.Timer | undefined;
timer = global.setInterval(() => {
let timer: any | undefined;
timer = globalThis.setInterval(() => {
if (onProgress()) {
if (timer !== undefined) {
clearInterval(timer);
@ -202,15 +202,3 @@ export function progress(promise: Promise, intervalMs: number, onProgress:
export async function wait(ms: number) {
await new Promise(resolve => setTimeout(resolve, ms));
}
export async function waitUntil(fn: (...args: any[]) => boolean, timeout: number): Promise<boolean> {
const max = Math.round(timeout / 100);
let counter = 0;
while (true) {
if (fn()) return true;
if (counter > max) return false;
await wait(100);
counter++;
}
}

+ 8
- 1
src/system/iterable.ts View File

@ -65,7 +65,14 @@ export function every(source: Iterable | IterableIterator, predicate: (
export function filter<T>(source: Iterable<T | undefined | null> | IterableIterator<T | undefined | null>): Iterable<T>;
export function filter<T>(source: Iterable<T> | IterableIterator<T>, predicate: (item: T) => boolean): Iterable<T>;
export function* filter<T>(source: Iterable<T> | IterableIterator<T>, predicate?: (item: T) => boolean): Iterable<T> {
export function filter<T, U extends T>(
source: Iterable<T> | IterableIterator<T>,
predicate: (item: T) => item is U,
): Iterable<U>;
export function* filter<T, U extends T = T>(
source: Iterable<T> | IterableIterator<T>,
predicate?: ((item: T) => item is U) | ((item: T) => boolean),
): Iterable<T | U> {
if (predicate === undefined) {
for (const item of source) {
if (item != null) yield item;

+ 11
- 0
src/system/path.ts View File

@ -1,6 +1,7 @@
'use strict';
import * as paths from 'path';
import { Uri } from 'vscode';
import { Strings } from '../system';
import { normalizePath } from './string';
const slash = '/';
@ -36,10 +37,20 @@ export function isDescendent(path: string, basePath: string): boolean;
export function isDescendent(uriOrPath: Uri | string, baseUriOrPath: Uri | string): boolean;
export function isDescendent(uriOrPath: Uri | string, baseUriOrPath: Uri | string): boolean {
if (typeof baseUriOrPath === 'string') {
baseUriOrPath = Strings.normalizePath(baseUriOrPath);
if (!baseUriOrPath.startsWith('/')) {
baseUriOrPath = `/${baseUriOrPath}`;
}
}
if (typeof uriOrPath === 'string') {
uriOrPath = Strings.normalizePath(uriOrPath);
if (!uriOrPath.startsWith('/')) {
uriOrPath = `/${uriOrPath}`;
}
}
if (typeof baseUriOrPath === 'string') {
return (
baseUriOrPath.length === 1 ||
(typeof uriOrPath === 'string' ? uriOrPath : uriOrPath.path).startsWith(

+ 2
- 2
src/system/promise.ts View File

@ -26,9 +26,9 @@ export function cancellable(
return new Promise((resolve, reject) => {
let fulfilled = false;
let timer: NodeJS.Timer | undefined;
let timer: any | undefined;
if (typeof timeoutOrToken === 'number') {
timer = global.setTimeout(() => {
timer = globalThis.setTimeout(() => {
if (typeof options.onDidCancel === 'function') {
options.onDidCancel(resolve, reject);
} else {

+ 1
- 1
src/terminal/linkProvider.ts View File

@ -37,7 +37,7 @@ export class GitTerminalLinkProvider implements Disposable, TerminalLinkProvider
async provideTerminalLinks(context: TerminalLinkContext): Promise<GitTerminalLink[]> {
if (context.line.trim().length === 0) return [];
const repoPath = this.container.git.getHighlanderRepoPath();
const repoPath = this.container.git.highlanderRepoPath;
if (repoPath == null) return [];
const links: GitTerminalLink[] = [];

+ 61
- 25
src/trackers/documentTracker.ts View File

@ -19,8 +19,10 @@ import {
import { configuration } from '../configuration';
import { ContextKeys, DocumentSchemes, isActiveDocument, isTextEditor, setContext } from '../constants';
import { Container } from '../container';
import { RepositoryChange, RepositoryChangeComparisonMode, RepositoryChangeEvent } from '../git/git';
import { RepositoriesChangedEvent } from '../git/gitProviderService';
import { GitUri } from '../git/gitUri';
import { Functions } from '../system';
import { Functions, Iterables } from '../system';
import { DocumentBlameStateChangeEvent, TrackedDocument } from './trackedDocument';
export * from './trackedDocument';
@ -63,7 +65,7 @@ export class DocumentTracker implements Disposable {
return this._onDidTriggerDirtyIdle.event;
}
private _dirtyIdleTriggerDelay!: number;
private _dirtyIdleTriggerDelay: number;
private readonly _disposable: Disposable;
private readonly _documentMap = new Map<TextDocument | string, Promise<TrackedDocument<T>>>();
@ -76,7 +78,11 @@ export class DocumentTracker implements Disposable {
workspace.onDidChangeTextDocument(Functions.debounce(this.onTextDocumentChanged, 50), this),
workspace.onDidCloseTextDocument(this.onTextDocumentClosed, this),
workspace.onDidSaveTextDocument(this.onTextDocumentSaved, this),
this.container.git.onDidChangeRepositories(Functions.debounce(this.onRepositoriesChanged, 250), this),
this.container.git.onDidChangeRepository(Functions.debounce(this.onRepositoryChanged, 250), this),
);
this._dirtyIdleTriggerDelay = configuration.get('advanced.blame.delayAfterEdit');
}
dispose() {
@ -86,29 +92,12 @@ export class DocumentTracker implements Disposable {
}
private onReady(): void {
void this.onConfigurationChanged();
void this.onActiveTextEditorChanged(window.activeTextEditor);
this.onConfigurationChanged();
this.onActiveTextEditorChanged(window.activeTextEditor);
}
private async onConfigurationChanged(e?: ConfigurationChangeEvent) {
// Only rest the cached state if we aren't initializing
if (
e != null &&
(configuration.changed(e, 'blame.ignoreWhitespace') || configuration.changed(e, 'advanced.caching.enabled'))
) {
for (const d of this._documentMap.values()) {
(await d).reset('config');
}
}
if (configuration.changed(e, 'advanced.blame.delayAfterEdit')) {
this._dirtyIdleTriggerDelay = configuration.get('advanced.blame.delayAfterEdit');
this._dirtyIdleTriggeredDebounced = undefined;
}
}
private _timer: NodeJS.Timer | undefined;
private async onActiveTextEditorChanged(editor: TextEditor | undefined) {
private _timer: any | undefined;
private onActiveTextEditorChanged(editor: TextEditor | undefined) {
if (editor != null && !isTextEditor(editor)) return;
if (this._timer != null) {
@ -128,7 +117,10 @@ export class DocumentTracker implements Disposable {
const doc = this._documentMap.get(editor.document);
if (doc != null) {
void (await doc).activate();
void doc.then(
d => d.activate(),
() => {},
);
return;
}
@ -137,6 +129,41 @@ export class DocumentTracker implements Disposable {
void this.addCore(editor.document);
}
private onConfigurationChanged(e?: ConfigurationChangeEvent) {
// Only rest the cached state if we aren't initializing
if (
e != null &&
(configuration.changed(e, 'blame.ignoreWhitespace') || configuration.changed(e, 'advanced.caching.enabled'))
) {
this.reset('config');
}
if (configuration.changed(e, 'advanced.blame.delayAfterEdit')) {
this._dirtyIdleTriggerDelay = configuration.get('advanced.blame.delayAfterEdit');
this._dirtyIdleTriggeredDebounced = undefined;
}
}
private onRepositoriesChanged(_e: RepositoriesChangedEvent) {
this.reset('repository');
}
private onRepositoryChanged(e: RepositoryChangeEvent) {
if (
e.changed(
RepositoryChange.Index,
RepositoryChange.Heads,
RepositoryChange.Status,
RepositoryChange.Unknown,
RepositoryChangeComparisonMode.Any,
)
) {
void Promise.allSettled(
Iterables.map(this._documentMap.values(), async d => (await d).reset('repository')),
);
}
}
private async onTextDocumentChanged(e: TextDocumentChangeEvent) {
const { scheme } = e.document.uri;
if (scheme !== DocumentSchemes.File && scheme !== DocumentSchemes.Git && scheme !== DocumentSchemes.Vsls) {
@ -321,7 +348,7 @@ export class DocumentTracker implements Disposable {
| undefined;
private fireDocumentDirtyStateChanged(e: DocumentDirtyStateChangeEvent<T>) {
if (e.dirty) {
setImmediate(() => {
queueMicrotask(() => {
this._dirtyStateChangedDebounced?.cancel();
if (window.activeTextEditor !== e.editor) return;
@ -358,6 +385,15 @@ export class DocumentTracker implements Disposable {
this._dirtyStateChangedDebounced(e);
}
private reset(reason: 'config' | 'dispose' | 'document' | 'repository') {
void Promise.allSettled(
Iterables.map(
Iterables.filter(this._documentMap, ([key]) => typeof key === 'string'),
async ([, d]) => (await d).reset(reason),
),
);
}
}
class EmptyTextDocument implements TextDocument {

+ 2
- 2
src/trackers/lineTracker.ts View File

@ -135,7 +135,7 @@ export class LineTracker implements Disposable {
this.onStart?.() ?? { dispose: () => {} },
);
setImmediate(() => this.onActiveTextEditorChanged(window.activeTextEditor));
queueMicrotask(() => this.onActiveTextEditorChanged(window.activeTextEditor));
}
return disposable;
@ -202,7 +202,7 @@ export class LineTracker implements Disposable {
private onLinesChanged(e: LinesChangeEvent) {
if (e.selections === undefined) {
setImmediate(() => {
queueMicrotask(() => {
if (window.activeTextEditor !== e.editor) return;
if (this._linesChangedDebounced !== undefined) {

+ 32
- 59
src/trackers/trackedDocument.ts View File

@ -2,13 +2,7 @@
import { Disposable, Event, EventEmitter, TextDocument, TextEditor } from 'vscode';
import { ContextKeys, getEditorIfActive, isActiveDocument, setContext } from '../constants';
import { Container } from '../container';
import {
GitRevision,
Repository,
RepositoryChange,
RepositoryChangeComparisonMode,
RepositoryChangeEvent,
} from '../git/git';
import { GitRevision } from '../git/git';
import { GitUri } from '../git/gitUri';
import { Logger } from '../logger';
@ -40,7 +34,6 @@ export class TrackedDocument implements Disposable {
private _disposable: Disposable | undefined;
private _disposed: boolean = false;
private _repo: Repository | undefined;
private _uri!: GitUri;
private constructor(
@ -58,41 +51,15 @@ export class TrackedDocument implements Disposable {
}
private initializing = true;
private async initialize(): Promise<Repository | undefined> {
this._uri = await GitUri.fromUri(this._document.uri);
if (this._disposed) return undefined;
private async initialize(): Promise<void> {
const uri = this._document.uri;
const repo = await this.container.git.getRepository(this._uri);
this._repo = repo;
if (this._disposed) return undefined;
if (repo != null) {
this._disposable = repo.onDidChange(this.onRepositoryChanged, this);
this._uri = await GitUri.fromUri(uri);
if (!this._disposed) {
await this.update();
}
await this.update();
this.initializing = false;
return repo;
}
private onRepositoryChanged(e: RepositoryChangeEvent) {
if (
!e.changed(
RepositoryChange.Index,
RepositoryChange.Heads,
RepositoryChange.Status,
RepositoryChange.Unknown,
RepositoryChangeComparisonMode.Any,
)
) {
return;
}
// Reset any cached state
this.reset('repository');
void this.update();
}
private _forceDirtyStateChangeOnNextDocumentChange: boolean = false;
@ -106,9 +73,7 @@ export class TrackedDocument implements Disposable {
}
get isBlameable() {
if (this._blameFailed) return false;
return this._isTracked;
return this._blameFailed ? false : this._isTracked;
}
private _isDirtyIdle: boolean = false;
@ -136,7 +101,10 @@ export class TrackedDocument implements Disposable {
return this._uri;
}
activate() {
async activate(): Promise<void> {
if (this._requiresUpdate) {
await this.update();
}
void setContext(ContextKeys.ActiveFileStatus, this.getStatus());
}
@ -145,18 +113,23 @@ export class TrackedDocument implements Disposable {
}
reset(reason: 'config' | 'dispose' | 'document' | 'repository') {
this._requiresUpdate = true;
this._blameFailed = false;
this._isDirtyIdle = false;
if (this.state == null) return;
if (this.state != null) {
// // Don't remove broken blame on change (since otherwise we'll have to run the broken blame again)
// if (!this.state.hasErrors) {
// // Don't remove broken blame on change (since otherwise we'll have to run the broken blame again)
// if (!this.state.hasErrors) {
this.state = undefined;
Logger.log(`Reset state for '${this.key}', reason=${reason}`);
this.state = undefined;
Logger.log(`Reset state for '${this.key}', reason=${reason}`);
// }
}
// }
if (reason === 'repository' && isActiveDocument(this._document)) {
void this.update();
}
}
private _blameFailed: boolean = false;
@ -178,7 +151,10 @@ export class TrackedDocument implements Disposable {
this._forceDirtyStateChangeOnNextDocumentChange = true;
}
private _requiresUpdate: boolean = true;
async update({ forceBlameChange }: { forceBlameChange?: boolean } = {}) {
this._requiresUpdate = false;
if (this._disposed || this._uri == null) {
this._hasRemotes = false;
this._isTracked = false;
@ -188,20 +164,17 @@ export class TrackedDocument implements Disposable {
this._isDirtyIdle = false;
// Caches these before the awaits
const active = getEditorIfActive(this._document);
const wasBlameable = forceBlameChange ? undefined : this.isBlameable;
this._isTracked = await this.container.git.isTracked(this._uri);
let repo = undefined;
if (this._isTracked) {
repo = this._repo;
}
if (repo != null) {
this._hasRemotes = await repo.hasRemotes();
} else {
const repo = await this.container.git.getRepository(this._uri);
if (repo == null) {
this._isTracked = false;
this._hasRemotes = false;
} else {
this._isTracked = true;
this._hasRemotes = await repo.hasRemotes();
}
if (active != null) {

+ 6
- 32
src/views/branchesView.ts View File

@ -28,14 +28,14 @@ import {
RepositoryChangeEvent,
} from '../git/git';
import { GitUri } from '../git/gitUri';
import { debug, gate, Strings } from '../system';
import { gate, Strings } from '../system';
import {
BranchesNode,
BranchNode,
BranchOrTagFolderNode,
RepositoriesSubscribeableNode,
RepositoryFolderNode,
RepositoryNode,
unknownGitUri,
ViewNode,
} from './nodes';
import { ViewBase } from './viewBase';
@ -63,17 +63,10 @@ export class BranchesRepositoryNode extends RepositoryFolderNode
}
}
export class BranchesViewNode extends ViewNode<BranchesView> {
protected override splatted = true;
private children: BranchesRepositoryNode[] | undefined;
constructor(view: BranchesView) {
super(unknownGitUri, view);
}
export class BranchesViewNode extends RepositoriesSubscribeableNode<BranchesView, BranchesRepositoryNode> {
async getChildren(): Promise<ViewNode[]> {
if (this.children == null) {
const repositories = await Container.instance.git.getOrderedRepositories();
const repositories = this.view.container.git.openRepositories;
if (repositories.length === 0) {
this.view.message = 'No branches could be found.';
@ -118,25 +111,6 @@ export class BranchesViewNode extends ViewNode {
const item = new TreeItem('Branches', TreeItemCollapsibleState.Expanded);
return item;
}
override async getSplattedChild() {
if (this.children == null) {
await this.getChildren();
}
return this.children?.length === 1 ? this.children[0] : undefined;
}
@gate()
@debug()
override refresh(reset: boolean = false) {
if (reset && this.children != null) {
for (const child of this.children) {
child.dispose();
}
this.children = undefined;
}
}
}
export class BranchesView extends ViewBase<BranchesViewNode, BranchesViewConfig> {
@ -161,8 +135,8 @@ export class BranchesView extends ViewBase
),
commands.registerCommand(
this.getQualifiedCommand('refresh'),
async () => {
await this.container.git.resetCaches('branches');
() => {
this.container.git.resetCaches('branches');
return this.refresh(true);
},
this,

+ 6
- 32
src/views/commitsView.ts View File

@ -26,9 +26,9 @@ import { debug, Functions, gate, Strings } from '../system';
import {
BranchNode,
BranchTrackingStatusNode,
RepositoriesSubscribeableNode,
RepositoryFolderNode,
RepositoryNode,
unknownGitUri,
ViewNode,
} from './nodes';
import { ViewBase } from './viewBase';
@ -47,7 +47,7 @@ export class CommitsRepositoryNode extends RepositoryFolderNode
let authors;
if (this.view.state.myCommitsOnly) {
const user = await Container.instance.git.getCurrentUser(this.repo.path);
const user = await this.view.container.git.getCurrentUser(this.repo.path);
if (user != null) {
authors = [`^${user.name} <${user.email}>$`];
}
@ -118,17 +118,10 @@ export class CommitsRepositoryNode extends RepositoryFolderNode
}
}
export class CommitsViewNode extends ViewNode<CommitsView> {
protected override splatted = true;
private children: CommitsRepositoryNode[] | undefined;
constructor(view: CommitsView) {
super(unknownGitUri, view);
}
export class CommitsViewNode extends RepositoriesSubscribeableNode<CommitsView, CommitsRepositoryNode> {
async getChildren(): Promise<ViewNode[]> {
if (this.children == null) {
const repositories = await Container.instance.git.getOrderedRepositories();
const repositories = this.view.container.git.openRepositories;
if (repositories.length === 0) {
this.view.message = 'No commits could be found.';
@ -177,25 +170,6 @@ export class CommitsViewNode extends ViewNode {
const item = new TreeItem('Commits', TreeItemCollapsibleState.Expanded);
return item;
}
override async getSplattedChild() {
if (this.children == null) {
await this.getChildren();
}
return this.children?.length === 1 ? this.children[0] : undefined;
}
@gate()
@debug()
override refresh(reset: boolean = false) {
if (reset && this.children != null) {
for (const child of this.children) {
child.dispose();
}
this.children = undefined;
}
}
}
interface CommitsViewState {
@ -229,8 +203,8 @@ export class CommitsView extends ViewBase {
),
commands.registerCommand(
this.getQualifiedCommand('refresh'),
async () => {
await this.container.git.resetCaches('branches', 'status', 'tags');
() => {
this.container.git.resetCaches('branches', 'status', 'tags');
return this.refresh(true);
},
this,

+ 8
- 34
src/views/contributorsView.ts View File

@ -6,8 +6,8 @@ import { GlyphChars } from '../constants';
import { Container } from '../container';
import { RepositoryChange, RepositoryChangeComparisonMode, RepositoryChangeEvent } from '../git/git';
import { GitUri } from '../git/gitUri';
import { debug, gate, Strings } from '../system';
import { ContributorsNode, RepositoryFolderNode, unknownGitUri, ViewNode } from './nodes';
import { debug, Strings } from '../system';
import { ContributorsNode, RepositoriesSubscribeableNode, RepositoryFolderNode, ViewNode } from './nodes';
import { ViewBase } from './viewBase';
export class ContributorsRepositoryNode extends RepositoryFolderNode<ContributorsView, ContributorsNode> {
@ -38,17 +38,10 @@ export class ContributorsRepositoryNode extends RepositoryFolderNode
}
}
export class ContributorsViewNode extends ViewNode<ContributorsView> {
protected override splatted = true;
private children: ContributorsRepositoryNode[] | undefined;
constructor(view: ContributorsView) {
super(unknownGitUri, view);
}
export class ContributorsViewNode extends RepositoriesSubscribeableNode<ContributorsView, ContributorsRepositoryNode> {
async getChildren(): Promise<ViewNode[]> {
if (this.children == null) {
const repositories = await Container.instance.git.getOrderedRepositories();
const repositories = this.view.container.git.openRepositories;
if (repositories.length === 0) {
this.view.message = 'No contributors could be found.';
@ -72,13 +65,13 @@ export class ContributorsViewNode extends ViewNode {
const children = await child.getChildren();
// const all = Container.instance.config.views.contributors.showAllBranches;
// const all = this.view.container.config.views.contributors.showAllBranches;
// let ref: string | undefined;
// // If we aren't getting all branches, get the upstream of the current branch if there is one
// if (!all) {
// try {
// const branch = await Container.instance.git.getBranch(this.uri.repoPath);
// const branch = await this.view.container.git.getBranch(this.uri.repoPath);
// if (branch?.upstream?.name != null && !branch.upstream.missing) {
// ref = '@{u}';
// }
@ -108,25 +101,6 @@ export class ContributorsViewNode extends ViewNode {
const item = new TreeItem('Contributors', TreeItemCollapsibleState.Expanded);
return item;
}
override async getSplattedChild() {
if (this.children == null) {
await this.getChildren();
}
return this.children?.length === 1 ? this.children[0] : undefined;
}
@gate()
@debug()
override refresh(reset: boolean = false) {
if (reset && this.children != null) {
for (const child of this.children) {
child.dispose();
}
this.children = undefined;
}
}
}
export class ContributorsView extends ViewBase<ContributorsViewNode, ContributorsViewConfig> {
@ -151,8 +125,8 @@ export class ContributorsView extends ViewBase
),
commands.registerCommand(
this.getQualifiedCommand('refresh'),
async () => {
await this.container.git.resetCaches('contributors');
() => {
this.container.git.resetCaches('contributors');
return this.refresh(true);
},
this,

+ 13
- 14
src/views/nodes/branchNode.ts View File

@ -2,7 +2,6 @@
import { MarkdownString, ThemeColor, ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri, window } from 'vscode';
import { ViewBranchesLayout, ViewShowBranchComparison } from '../../configuration';
import { Colors, GlyphChars } from '../../constants';
import { Container } from '../../container';
import {
BranchDateFormatting,
GitBranch,
@ -131,18 +130,18 @@ export class BranchNode
if (this._children == null) {
const children = [];
const range = await Container.instance.git.getBranchAheadRange(this.branch);
const range = await this.view.container.git.getBranchAheadRange(this.branch);
const [log, getBranchAndTagTips, status, mergeStatus, rebaseStatus, pr, unpublishedCommits] =
await Promise.all([
this.getLog(),
Container.instance.git.getBranchesAndTagsTipsFn(this.uri.repoPath, this.branch.name),
this.view.container.git.getBranchesAndTagsTipsFn(this.uri.repoPath, this.branch.name),
this.options.showStatus && this.branch.current
? Container.instance.git.getStatusForRepo(this.uri.repoPath)
? this.view.container.git.getStatusForRepo(this.uri.repoPath)
: undefined,
this.options.showStatus && this.branch.current
? Container.instance.git.getMergeStatus(this.uri.repoPath!)
? this.view.container.git.getMergeStatus(this.uri.repoPath!)
: undefined,
this.options.showStatus ? Container.instance.git.getRebaseStatus(this.uri.repoPath!) : undefined,
this.options.showStatus ? this.view.container.git.getRebaseStatus(this.uri.repoPath!) : undefined,
this.view.config.pullRequests.enabled &&
this.view.config.pullRequests.showForBranches &&
(this.branch.upstream != null || this.branch.remote)
@ -151,7 +150,7 @@ export class BranchNode
)
: undefined,
range && !this.branch.remote
? Container.instance.git.getLogRefsOnly(this.uri.repoPath!, {
? this.view.container.git.getLogRefsOnly(this.uri.repoPath!, {
limit: 0,
ref: range,
})
@ -183,7 +182,7 @@ export class BranchNode
this,
this.branch,
mergeStatus,
status ?? (await Container.instance.git.getStatusForRepo(this.uri.repoPath)),
status ?? (await this.view.container.git.getStatusForRepo(this.uri.repoPath)),
this.root,
),
);
@ -198,7 +197,7 @@ export class BranchNode
this,
this.branch,
rebaseStatus,
status ?? (await Container.instance.git.getStatusForRepo(this.uri.repoPath)),
status ?? (await this.view.container.git.getStatusForRepo(this.uri.repoPath)),
this.root,
),
);
@ -260,7 +259,7 @@ export class BranchNode
if (log.hasMore) {
children.push(
new LoadMoreNode(this.view, this, children[children.length - 1], undefined, () =>
Container.instance.git.getCommitCount(this.branch.repoPath, this.branch.name),
this.view.container.git.getCommitCount(this.branch.repoPath, this.branch.name),
),
);
}
@ -370,7 +369,7 @@ export class BranchNode
}
} else {
const providers = GitRemote.getHighlanderProviders(
await Container.instance.git.getRemotes(this.branch.repoPath),
await this.view.container.git.getRemotes(this.branch.repoPath),
);
const providerName = providers?.length ? providers[0].name : undefined;
@ -406,8 +405,8 @@ export class BranchNode
item.iconPath = this.options.showAsCommits
? new ThemeIcon('git-commit', color)
: {
dark: Container.instance.context.asAbsolutePath(`images/dark/icon-branch${iconSuffix}.svg`),
light: Container.instance.context.asAbsolutePath(`images/light/icon-branch${iconSuffix}.svg`),
dark: this.view.container.context.asAbsolutePath(`images/dark/icon-branch${iconSuffix}.svg`),
light: this.view.container.context.asAbsolutePath(`images/light/icon-branch${iconSuffix}.svg`),
};
item.tooltip = tooltip;
item.resourceUri = Uri.parse(
@ -453,7 +452,7 @@ export class BranchNode
limit = Math.min(this.branch.state.ahead + 1, limit * 2);
}
this._log = await Container.instance.git.getLog(this.uri.repoPath!, {
this._log = await this.view.container.git.getLog(this.uri.repoPath!, {
limit: limit,
ref: this.ref.ref,
authors: this.options?.authors,

+ 2
- 3
src/views/nodes/branchTrackingStatusFilesNode.ts View File

@ -2,7 +2,6 @@
import * as paths from 'path';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ViewFilesLayout } from '../../configuration';
import { Container } from '../../container';
import { GitBranch, GitFileWithCommit, GitRevision } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { Arrays, Iterables, Strings } from '../../system';
@ -45,7 +44,7 @@ export class BranchTrackingStatusFilesNode extends ViewNode {
}
async getChildren(): Promise<ViewNode[]> {
const log = await Container.instance.git.getLog(this.repoPath, {
const log = await this.view.container.git.getLog(this.repoPath, {
limit: 0,
ref: GitRevision.createRange(
this.status.upstream,
@ -103,7 +102,7 @@ export class BranchTrackingStatusFilesNode extends ViewNode {
}
async getTreeItem(): Promise<TreeItem> {
const stats = await Container.instance.git.getChangedFilesCount(
const stats = await this.view.container.git.getChangedFilesCount(
this.repoPath,
`${this.status.upstream}${this.direction === 'behind' ? '..' : '...'}`,
);

+ 4
- 5
src/views/nodes/branchTrackingStatusNode.ts View File

@ -1,7 +1,6 @@
'use strict';
import { MarkdownString, ThemeColor, ThemeIcon, TreeItem, TreeItemCollapsibleState, window } from 'vscode';
import { Colors } from '../../constants';
import { Container } from '../../container';
import { GitBranch, GitLog, GitRemote, GitRevision, GitTrackingState } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { Dates, debug, gate, Iterables, Strings } from '../../system';
@ -79,7 +78,7 @@ export class BranchTrackingStatusNode extends ViewNode impleme
commits = [...log.commits.values()];
const commit = commits[commits.length - 1];
if (commit.previousSha == null) {
const previousLog = await Container.instance.git.getLog(this.uri.repoPath!, {
const previousLog = await this.view.container.git.getLog(this.uri.repoPath!, {
limit: 2,
ref: commit.sha,
});
@ -151,7 +150,7 @@ export class BranchTrackingStatusNode extends ViewNode impleme
let lastFetched = 0;
if (this.upstreamType !== 'none') {
const repo = await Container.instance.git.getRepository(this.repoPath);
const repo = await this.view.container.git.getRepository(this.repoPath);
lastFetched = (await repo?.getLastFetched()) ?? 0;
}
@ -228,7 +227,7 @@ export class BranchTrackingStatusNode extends ViewNode impleme
break;
}
case 'none': {
const remotes = await Container.instance.git.getRemotes(this.branch.repoPath);
const remotes = await this.view.container.git.getRemotes(this.branch.repoPath);
const providers = GitRemote.getHighlanderProviders(remotes);
const providerName = providers?.length ? providers[0].name : undefined;
@ -284,7 +283,7 @@ export class BranchTrackingStatusNode extends ViewNode impleme
? GitRevision.createRange(this.status.upstream, this.status.ref)
: GitRevision.createRange(this.status.ref, this.status.upstream);
this._log = await Container.instance.git.getLog(this.uri.repoPath!, {
this._log = await this.view.container.git.getLog(this.uri.repoPath!, {
limit: this.limit ?? this.view.config.defaultItemLimit,
ref: range,
});

+ 3
- 4
src/views/nodes/commitFileNode.ts View File

@ -2,7 +2,6 @@
import * as paths from 'path';
import { Command, Selection, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
import { Commands, DiffWithPreviousCommandArgs } from '../../commands';
import { Container } from '../../container';
import { GitBranch, GitFile, GitLogCommit, GitRevisionReference, StatusFileFormatter } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { FileHistoryView } from '../fileHistoryView';
@ -49,7 +48,7 @@ export class CommitFileNode
// Try to get the commit directly from the multi-file commit
const commit = this.commit.toFileCommit(this.file);
if (commit == null) {
const log = await Container.instance.git.getLogForFile(this.repoPath, this.file.fileName, {
const log = await this.view.container.git.getLogForFile(this.repoPath, this.file.fileName, {
limit: 2,
ref: this.commit.sha,
});
@ -69,8 +68,8 @@ export class CommitFileNode
const icon = GitFile.getStatusIcon(this.file.status);
item.iconPath = {
dark: Container.instance.context.asAbsolutePath(paths.join('images', 'dark', icon)),
light: Container.instance.context.asAbsolutePath(paths.join('images', 'light', icon)),
dark: this.view.container.context.asAbsolutePath(paths.join('images', 'dark', icon)),
light: this.view.container.context.asAbsolutePath(paths.join('images', 'light', icon)),
};
item.command = this.getCommand();

+ 8
- 9
src/views/nodes/commitNode.ts View File

@ -4,7 +4,6 @@ import { Command, MarkdownString, ThemeColor, ThemeIcon, TreeItem, TreeItemColla
import { Commands, DiffWithPreviousCommandArgs } from '../../commands';
import { ViewFilesLayout } from '../../configuration';
import { Colors, GlyphChars } from '../../constants';
import { Container } from '../../container';
import { CommitFormatter, GitBranch, GitLogCommit, GitRevisionReference } from '../../git/git';
import { Arrays, Strings } from '../../system';
import { FileHistoryView } from '../fileHistoryView';
@ -83,7 +82,7 @@ export class CommitNode extends ViewRefNode
async getTreeItem(): Promise<TreeItem> {
const label = CommitFormatter.fromTemplate(this.view.config.formats.commits.label, this.commit, {
dateFormat: Container.instance.config.defaultDateFormat,
dateFormat: this.view.container.config.defaultDateFormat,
getBranchAndTagTips: (sha: string) => this.getBranchAndTagTips?.(sha, { compact: true }),
messageTruncateAtNewLine: true,
});
@ -98,14 +97,14 @@ export class CommitNode extends ViewRefNode
}${this.unpublished ? '+unpublished' : ''}`;
item.description = CommitFormatter.fromTemplate(this.view.config.formats.commits.description, this.commit, {
dateFormat: Container.instance.config.defaultDateFormat,
dateFormat: this.view.container.config.defaultDateFormat,
getBranchAndTagTips: (sha: string) => this.getBranchAndTagTips?.(sha, { compact: true }),
messageTruncateAtNewLine: true,
});
item.iconPath = this.unpublished
? new ThemeIcon('arrow-up', new ThemeColor(Colors.UnpublishedCommitIconColor))
: this.view.config.avatars
? await this.commit.getAvatarUri({ defaultStyle: Container.instance.config.defaultGravatarsStyle })
? await this.commit.getAvatarUri({ defaultStyle: this.view.container.config.defaultGravatarsStyle })
: new ThemeIcon('git-commit');
// item.tooltip = this.tooltip;
@ -137,16 +136,16 @@ export class CommitNode extends ViewRefNode
}
private async getTooltip() {
const remotes = await Container.instance.git.getRemotes(this.commit.repoPath);
const remote = await Container.instance.git.getRichRemoteProvider(remotes);
const remotes = await this.view.container.git.getRemotes(this.commit.repoPath);
const remote = await this.view.container.git.getRichRemoteProvider(remotes);
let autolinkedIssuesOrPullRequests;
let pr;
if (remote?.provider != null) {
[autolinkedIssuesOrPullRequests, pr] = await Promise.all([
Container.instance.autolinks.getIssueOrPullRequestLinks(this.commit.message, remote),
Container.instance.git.getPullRequestForCommit(this.commit.ref, remote.provider),
this.view.container.autolinks.getIssueOrPullRequestLinks(this.commit.message, remote),
this.view.container.git.getPullRequestForCommit(this.commit.ref, remote.provider),
]);
}
@ -155,7 +154,7 @@ export class CommitNode extends ViewRefNode
this.commit,
{
autolinkedIssuesOrPullRequests: autolinkedIssuesOrPullRequests,
dateFormat: Container.instance.config.defaultDateFormat,
dateFormat: this.view.container.config.defaultDateFormat,
getBranchAndTagTips: this.getBranchAndTagTips,
markdown: true,
messageAutolinks: true,

+ 11
- 12
src/views/nodes/compareBranchNode.ts View File

@ -2,7 +2,6 @@
import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ViewShowBranchComparison } from '../../configuration';
import { BranchComparison, BranchComparisons, GlyphChars, WorkspaceState } from '../../constants';
import { Container } from '../../container';
import { GitBranch, GitRevision } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { CommandQuickPickItem, ReferencePicker } from '../../quickpicks';
@ -67,13 +66,13 @@ export class CompareBranchNode extends ViewNode
const ahead = this.ahead;
const behind = this.behind;
const aheadBehindCounts = await Container.instance.git.getAheadBehindCommitCount(this.branch.repoPath, [
const aheadBehindCounts = await this.view.container.git.getAheadBehindCommitCount(this.branch.repoPath, [
GitRevision.createRange(behind.ref1, behind.ref2, '...'),
]);
const mergeBase =
(await Container.instance.git.getMergeBase(this.repoPath, behind.ref1, behind.ref2, {
(await this.view.container.git.getMergeBase(this.repoPath, behind.ref1, behind.ref2, {
forkPoint: true,
})) ?? (await Container.instance.git.getMergeBase(this.repoPath, behind.ref1, behind.ref2));
})) ?? (await this.view.container.git.getMergeBase(this.repoPath, behind.ref1, behind.ref2));
this._children = [
new ResultsCommitsNode(
@ -248,13 +247,13 @@ export class CompareBranchNode extends ViewNode
}
private async getAheadFilesQuery(): Promise<FilesQueryResults> {
let files = await Container.instance.git.getDiffStatus(
let files = await this.view.container.git.getDiffStatus(
this.repoPath,
GitRevision.createRange(this._compareWith?.ref || 'HEAD', this.branch.ref || 'HEAD', '...'),
);
if (this.compareWithWorkingTree) {
const workingFiles = await Container.instance.git.getDiffStatus(this.repoPath, 'HEAD');
const workingFiles = await this.view.container.git.getDiffStatus(this.repoPath, 'HEAD');
if (workingFiles != null) {
if (files != null) {
for (const wf of workingFiles) {
@ -278,7 +277,7 @@ export class CompareBranchNode extends ViewNode
}
private async getBehindFilesQuery(): Promise<FilesQueryResults> {
const files = await Container.instance.git.getDiffStatus(
const files = await this.view.container.git.getDiffStatus(
this.uri.repoPath!,
GitRevision.createRange(this.branch.ref, this._compareWith?.ref || 'HEAD', '...'),
);
@ -292,7 +291,7 @@ export class CompareBranchNode extends ViewNode
private getCommitsQuery(range: string): (limit: number | undefined) => Promise<CommitsQueryResults> {
const repoPath = this.uri.repoPath!;
return async (limit: number | undefined) => {
const log = await Container.instance.git.getLog(repoPath, {
const log = await this.view.container.git.getLog(repoPath, {
limit: limit,
ref: range,
});
@ -322,7 +321,7 @@ export class CompareBranchNode extends ViewNode
comparison = `${this._compareWith!.ref}..${this.branch.ref}`;
}
const files = await Container.instance.git.getDiffStatus(this.uri.repoPath!, comparison);
const files = await this.view.container.git.getDiffStatus(this.uri.repoPath!, comparison);
return {
label: `${Strings.pluralize('file', files?.length ?? 0, { zero: 'No' })} changed`,
@ -331,7 +330,7 @@ export class CompareBranchNode extends ViewNode
}
private loadCompareWith() {
const comparisons = Container.instance.context.workspaceState.get<BranchComparisons>(
const comparisons = this.view.container.context.workspaceState.get<BranchComparisons>(
WorkspaceState.BranchComparisons,
);
@ -351,7 +350,7 @@ export class CompareBranchNode extends ViewNode
private async updateCompareWith(compareWith: BranchComparison | undefined) {
this._compareWith = compareWith;
let comparisons = Container.instance.context.workspaceState.get<BranchComparisons>(
let comparisons = this.view.container.context.workspaceState.get<BranchComparisons>(
WorkspaceState.BranchComparisons,
);
if (comparisons == null) {
@ -366,6 +365,6 @@ export class CompareBranchNode extends ViewNode
const { [id]: _, ...rest } = comparisons;
comparisons = rest;
}
await Container.instance.context.workspaceState.update(WorkspaceState.BranchComparisons, comparisons);
await this.view.container.context.workspaceState.update(WorkspaceState.BranchComparisons, comparisons);
}
}

+ 2
- 3
src/views/nodes/comparePickerNode.ts View File

@ -1,7 +1,6 @@
'use strict';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { GlyphChars, NamedRef } from '../../constants';
import { Container } from '../../container';
import { SearchAndCompareView, SearchAndCompareViewNode } from '../searchAndCompareView';
import { ContextValues, unknownGitUri, ViewNode } from './viewNode';
@ -33,8 +32,8 @@ export class ComparePickerNode extends ViewNode {
let description;
if (repoPath !== undefined) {
if ((await Container.instance.git.getRepositoryCount()) > 1) {
const repo = await Container.instance.git.getRepository(repoPath);
if (this.view.container.git.repositoryCount > 1) {
const repo = await this.view.container.git.getRepository(repoPath);
description = repo?.formattedName ?? repoPath;
}
}

+ 14
- 15
src/views/nodes/compareResultsNode.ts View File

@ -1,7 +1,6 @@
'use strict';
import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { NamedRef } from '../../constants';
import { Container } from '../../container';
import { GitRevision } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { debug, gate, log, Strings } from '../../system';
@ -74,13 +73,13 @@ export class CompareResultsNode extends ViewNode {
const ahead = this.ahead;
const behind = this.behind;
const aheadBehindCounts = await Container.instance.git.getAheadBehindCommitCount(this.repoPath, [
const aheadBehindCounts = await this.view.container.git.getAheadBehindCommitCount(this.repoPath, [
GitRevision.createRange(behind.ref1 || 'HEAD', behind.ref2, '...'),
]);
const mergeBase =
(await Container.instance.git.getMergeBase(this.repoPath, behind.ref1, behind.ref2, {
(await this.view.container.git.getMergeBase(this.repoPath, behind.ref1, behind.ref2, {
forkPoint: true,
})) ?? (await Container.instance.git.getMergeBase(this.repoPath, behind.ref1, behind.ref2));
})) ?? (await this.view.container.git.getMergeBase(this.repoPath, behind.ref1, behind.ref2));
this._children = [
new ResultsCommitsNode(
@ -144,8 +143,8 @@ export class CompareResultsNode extends ViewNode {
async getTreeItem(): Promise<TreeItem> {
let description;
if ((await Container.instance.git.getRepositoryCount()) > 1) {
const repo = await Container.instance.git.getRepository(this.uri.repoPath!);
if (this.view.container.git.repositoryCount > 1) {
const repo = await this.view.container.git.getRepository(this.uri.repoPath!);
description = repo?.formattedName ?? this.uri.repoPath;
}
@ -181,7 +180,7 @@ export class CompareResultsNode extends ViewNode {
this._pinned = Date.now();
await this.updatePinned();
setImmediate(() => this.view.reveal(this, { focus: true, select: true }));
queueMicrotask(() => this.view.reveal(this, { focus: true, select: true }));
}
@gate()
@ -209,7 +208,7 @@ export class CompareResultsNode extends ViewNode {
this._children = undefined;
this.view.triggerNodeChange(this.parent);
setImmediate(() => this.view.reveal(this, { expand: true, focus: true, select: true }));
queueMicrotask(() => this.view.reveal(this, { expand: true, focus: true, select: true }));
}
@log()
@ -219,7 +218,7 @@ export class CompareResultsNode extends ViewNode {
this._pinned = 0;
await this.view.updatePinned(this.getPinnableId());
setImmediate(() => this.view.reveal(this, { focus: true, select: true }));
queueMicrotask(() => this.view.reveal(this, { focus: true, select: true }));
}
private getPinnableId() {
@ -227,13 +226,13 @@ export class CompareResultsNode extends ViewNode {
}
private async getAheadFilesQuery(): Promise<FilesQueryResults> {
let files = await Container.instance.git.getDiffStatus(
let files = await this.view.container.git.getDiffStatus(
this.repoPath,
GitRevision.createRange(this._compareWith?.ref || 'HEAD', this._ref.ref || 'HEAD', '...'),
);
if (this._ref.ref === '') {
const workingFiles = await Container.instance.git.getDiffStatus(this.repoPath, 'HEAD');
const workingFiles = await this.view.container.git.getDiffStatus(this.repoPath, 'HEAD');
if (workingFiles != null) {
if (files != null) {
for (const wf of workingFiles) {
@ -257,13 +256,13 @@ export class CompareResultsNode extends ViewNode {
}
private async getBehindFilesQuery(): Promise<FilesQueryResults> {
let files = await Container.instance.git.getDiffStatus(
let files = await this.view.container.git.getDiffStatus(
this.repoPath,
GitRevision.createRange(this._ref.ref || 'HEAD', this._compareWith.ref || 'HEAD', '...'),
);
if (this._compareWith.ref === '') {
const workingFiles = await Container.instance.git.getDiffStatus(this.repoPath, 'HEAD');
const workingFiles = await this.view.container.git.getDiffStatus(this.repoPath, 'HEAD');
if (workingFiles != null) {
if (files != null) {
for (const wf of workingFiles) {
@ -289,7 +288,7 @@ export class CompareResultsNode extends ViewNode {
private getCommitsQuery(range: string): (limit: number | undefined) => Promise<CommitsQueryResults> {
const repoPath = this.repoPath;
return async (limit: number | undefined) => {
const log = await Container.instance.git.getLog(repoPath, {
const log = await this.view.container.git.getLog(repoPath, {
limit: limit,
ref: range,
});
@ -319,7 +318,7 @@ export class CompareResultsNode extends ViewNode {
comparison = `${this._compareWith.ref}..${this._ref.ref}`;
}
const files = await Container.instance.git.getDiffStatus(this.uri.repoPath!, comparison);
const files = await this.view.container.git.getDiffStatus(this.uri.repoPath!, comparison);
return {
label: `${Strings.pluralize('file', files?.length ?? 0, { zero: 'No' })} changed`,

+ 4
- 5
src/views/nodes/contributorNode.ts View File

@ -2,7 +2,6 @@
import { MarkdownString, TreeItem, TreeItemCollapsibleState, window } from 'vscode';
import { getPresenceDataUri } from '../../avatars';
import { GlyphChars } from '../../constants';
import { Container } from '../../container';
import { GitContributor, GitLog } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { debug, gate, Iterables, Strings } from '../../system';
@ -47,7 +46,7 @@ export class ContributorNode extends ViewNode
const log = await this.getLog();
if (log == null) return [new MessageNode(this.view, this, 'No commits could be found.')];
const getBranchAndTagTips = await Container.instance.git.getBranchesAndTagsTipsFn(this.uri.repoPath);
const getBranchAndTagTips = await this.view.container.git.getBranchesAndTagsTipsFn(this.uri.repoPath);
const children = [
...insertDateMarkers(
Iterables.map(
@ -84,9 +83,9 @@ export class ContributorNode extends ViewNode
let avatarUri;
let avatarMarkdown;
if (this.view.config.avatars) {
const size = Container.instance.config.hovers.avatarSize;
const size = this.view.container.config.hovers.avatarSize;
avatarUri = await this.contributor.getAvatarUri({
defaultStyle: Container.instance.config.defaultGravatarsStyle,
defaultStyle: this.view.container.config.defaultGravatarsStyle,
size: size,
});
@ -151,7 +150,7 @@ export class ContributorNode extends ViewNode
private _log: GitLog | undefined;
private async getLog() {
if (this._log == null) {
this._log = await Container.instance.git.getLog(this.uri.repoPath!, {
this._log = await this.view.container.git.getLog(this.uri.repoPath!, {
all: this._options?.all,
ref: this._options?.ref,
limit: this.limit ?? this.view.config.defaultItemLimit,

+ 4
- 5
src/views/nodes/contributorsNode.ts View File

@ -1,6 +1,5 @@
'use strict';
import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Container } from '../../container';
import { GitContributor, Repository } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { debug, gate, timeout } from '../../system';
@ -36,20 +35,20 @@ export class ContributorsNode extends ViewNode
async getChildren(): Promise<ViewNode[]> {
if (this._children == null) {
const all = Container.instance.config.views.contributors.showAllBranches;
const all = this.view.container.config.views.contributors.showAllBranches;
let ref: string | undefined;
// If we aren't getting all branches, get the upstream of the current branch if there is one
if (!all) {
try {
const branch = await Container.instance.git.getBranch(this.uri.repoPath);
const branch = await this.view.container.git.getBranch(this.uri.repoPath);
if (branch?.upstream?.name != null && !branch.upstream.missing) {
ref = '@{u}';
}
} catch {}
}
const stats = Container.instance.config.views.contributors.showStatistics;
const stats = this.view.container.config.views.contributors.showStatistics;
const contributors = await this.repo.getContributors({ all: all, ref: ref, stats: stats });
if (contributors.length === 0) return [new MessageNode(this.view, this, 'No contributors could be found.')];
@ -98,6 +97,6 @@ export class ContributorsNode extends ViewNode
const email = contributors.find(c => c.current)?.email;
if (email == null) return undefined;
return Container.instance.vsls.getContactsPresence([email]);
return this.view.container.vsls.getContactsPresence([email]);
}
}

+ 7
- 8
src/views/nodes/fileHistoryNode.ts View File

@ -2,7 +2,6 @@
import * as paths from 'path';
import { Disposable, TreeItem, TreeItemCollapsibleState, window } from 'vscode';
import { configuration } from '../../configuration';
import { Container } from '../../container';
import {
GitBranch,
GitLog,
@ -58,18 +57,18 @@ export class FileHistoryNode extends SubscribeableViewNode impl
const children: ViewNode[] = [];
const range = this.branch != null ? await Container.instance.git.getBranchAheadRange(this.branch) : undefined;
const range = this.branch != null ? await this.view.container.git.getBranchAheadRange(this.branch) : undefined;
const [log, fileStatuses, currentUser, getBranchAndTagTips, unpublishedCommits] = await Promise.all([
this.getLog(),
this.uri.sha == null
? Container.instance.git.getStatusForFiles(this.uri.repoPath!, this.getPathOrGlob())
? this.view.container.git.getStatusForFiles(this.uri.repoPath!, this.getPathOrGlob())
: undefined,
this.uri.sha == null ? Container.instance.git.getCurrentUser(this.uri.repoPath!) : undefined,
this.uri.sha == null ? this.view.container.git.getCurrentUser(this.uri.repoPath!) : undefined,
this.branch != null
? Container.instance.git.getBranchesAndTagsTipsFn(this.uri.repoPath, this.branch.name)
? this.view.container.git.getBranchesAndTagsTipsFn(this.uri.repoPath, this.branch.name)
: undefined,
range
? Container.instance.git.getLogRefsOnly(this.uri.repoPath!, {
? this.view.container.git.getLogRefsOnly(this.uri.repoPath!, {
limit: 0,
ref: range,
})
@ -169,7 +168,7 @@ export class FileHistoryNode extends SubscribeableViewNode impl
@debug()
protected async subscribe() {
const repo = await Container.instance.git.getRepository(this.uri);
const repo = await this.view.container.git.getRepository(this.uri);
if (repo == null) return undefined;
const subscription = Disposable.from(
@ -233,7 +232,7 @@ export class FileHistoryNode extends SubscribeableViewNode impl
private _log: GitLog | undefined;
private async getLog() {
if (this._log == null) {
this._log = await Container.instance.git.getLogForFile(this.uri.repoPath, this.getPathOrGlob(), {
this._log = await this.view.container.git.getLogForFile(this.uri.repoPath, this.getPathOrGlob(), {
limit: this.limit ?? this.view.config.pageItemLimit,
ref: this.uri.sha,
});

+ 6
- 7
src/views/nodes/fileHistoryTrackerNode.ts View File

@ -2,7 +2,6 @@
import { Disposable, FileType, TextEditor, TreeItem, TreeItemCollapsibleState, window, workspace } from 'vscode';
import { UriComparer } from '../../comparers';
import { ContextKeys, setContext } from '../../constants';
import { Container } from '../../container';
import { GitReference, GitRevision } from '../../git/git';
import { GitCommitish, GitUri } from '../../git/gitUri';
import { Logger } from '../../logger';
@ -65,9 +64,9 @@ export class FileHistoryTrackerNode extends SubscribeableViewNode
let branch;
if (!commitish.sha || commitish.sha === 'HEAD') {
branch = await Container.instance.git.getBranch(this.uri.repoPath);
branch = await this.view.container.git.getBranch(this.uri.repoPath);
} else if (!GitRevision.isSha(commitish.sha)) {
[branch] = await Container.instance.git.getBranches(this.uri.repoPath, {
[branch] = await this.view.container.git.getBranches(this.uri.repoPath, {
filter: b => b.name === commitish.sha,
});
}
@ -111,7 +110,7 @@ export class FileHistoryTrackerNode extends SubscribeableViewNode
if (pick == null) return;
if (GitReference.isBranch(pick)) {
const branch = await Container.instance.git.getBranch(this.uri.repoPath);
const branch = await this.view.container.git.getBranch(this.uri.repoPath);
this._base = branch?.name === pick.name ? undefined : pick.ref;
} else {
this._base = pick.ref;
@ -137,10 +136,10 @@ export class FileHistoryTrackerNode extends SubscribeableViewNode
}
const editor = window.activeTextEditor;
if (editor == null || !Container.instance.git.isTrackable(editor.document.uri)) {
if (editor == null || !this.view.container.git.isTrackable(editor.document.uri)) {
if (
!this.hasUri ||
(Container.instance.git.isTrackable(this.uri) &&
(this.view.container.git.isTrackable(this.uri) &&
window.visibleTextEditors.some(e => e.document?.uri.path === this.uri.path))
) {
return true;
@ -167,7 +166,7 @@ export class FileHistoryTrackerNode extends SubscribeableViewNode
let uri;
if (gitUri.sha != null) {
// If we have a sha, normalize the history to the working file (so we get a full history all the time)
const workingUri = await Container.instance.git.getWorkingUri(gitUri.repoPath!, gitUri);
const workingUri = await this.view.container.git.getWorkingUri(gitUri.repoPath!, gitUri);
if (workingUri != null) {
uri = workingUri;
}

+ 14
- 15
src/views/nodes/fileRevisionAsCommitNode.ts View File

@ -12,7 +12,6 @@ import {
} from 'vscode';
import { Commands, DiffWithPreviousCommandArgs } from '../../commands';
import { Colors, GlyphChars } from '../../constants';
import { Container } from '../../container';
import {
CommitFormatter,
GitBranch,
@ -71,8 +70,8 @@ export class FileRevisionAsCommitNode extends ViewRefFileNode
if (!this.commit.hasConflicts) return [];
const [mergeStatus, rebaseStatus] = await Promise.all([
Container.instance.git.getMergeStatus(this.commit.repoPath),
Container.instance.git.getRebaseStatus(this.commit.repoPath),
this.view.container.git.getMergeStatus(this.commit.repoPath),
this.view.container.git.getRebaseStatus(this.commit.repoPath),
]);
if (mergeStatus == null && rebaseStatus == null) return [];
@ -87,7 +86,7 @@ export class FileRevisionAsCommitNode extends ViewRefFileNode
// Try to get the commit directly from the multi-file commit
const commit = this.commit.toFileCommit(this.file);
if (commit == null) {
const log = await Container.instance.git.getLogForFile(this.repoPath, this.file.fileName, {
const log = await this.view.container.git.getLogForFile(this.repoPath, this.file.fileName, {
limit: 2,
ref: this.commit.sha,
});
@ -101,7 +100,7 @@ export class FileRevisionAsCommitNode extends ViewRefFileNode
const item = new TreeItem(
CommitFormatter.fromTemplate(this.view.config.formats.commits.label, this.commit, {
dateFormat: Container.instance.config.defaultDateFormat,
dateFormat: this.view.container.config.defaultDateFormat,
getBranchAndTagTips: (sha: string) => this._options.getBranchAndTagTips?.(sha, { compact: true }),
messageTruncateAtNewLine: true,
}),
@ -111,7 +110,7 @@ export class FileRevisionAsCommitNode extends ViewRefFileNode
item.contextValue = this.contextValue;
item.description = CommitFormatter.fromTemplate(this.view.config.formats.commits.description, this.commit, {
dateFormat: Container.instance.config.defaultDateFormat,
dateFormat: this.view.container.config.defaultDateFormat,
getBranchAndTagTips: (sha: string) => this._options.getBranchAndTagTips?.(sha, { compact: true }),
messageTruncateAtNewLine: true,
});
@ -121,14 +120,14 @@ export class FileRevisionAsCommitNode extends ViewRefFileNode
if (!this.commit.isUncommitted && this.view.config.avatars) {
item.iconPath = this._options.unpublished
? new ThemeIcon('arrow-up', new ThemeColor(Colors.UnpublishedCommitIconColor))
: await this.commit.getAvatarUri({ defaultStyle: Container.instance.config.defaultGravatarsStyle });
: await this.commit.getAvatarUri({ defaultStyle: this.view.container.config.defaultGravatarsStyle });
}
if (item.iconPath == null) {
const icon = GitFile.getStatusIcon(this.file.status);
item.iconPath = {
dark: Container.instance.context.asAbsolutePath(paths.join('images', 'dark', icon)),
light: Container.instance.context.asAbsolutePath(paths.join('images', 'light', icon)),
dark: this.view.container.context.asAbsolutePath(paths.join('images', 'dark', icon)),
light: this.view.container.context.asAbsolutePath(paths.join('images', 'light', icon)),
};
}
@ -210,21 +209,21 @@ export class FileRevisionAsCommitNode extends ViewRefFileNode
async getConflictBaseUri(): Promise<Uri | undefined> {
if (!this.commit.hasConflicts) return undefined;
const mergeBase = await Container.instance.git.getMergeBase(this.repoPath, 'MERGE_HEAD', 'HEAD');
const mergeBase = await this.view.container.git.getMergeBase(this.repoPath, 'MERGE_HEAD', 'HEAD');
return GitUri.fromFile(this.file, this.repoPath, mergeBase ?? 'HEAD');
}
private async getTooltip() {
const remotes = await Container.instance.git.getRemotes(this.commit.repoPath);
const remote = await Container.instance.git.getRichRemoteProvider(remotes);
const remotes = await this.view.container.git.getRemotes(this.commit.repoPath);
const remote = await this.view.container.git.getRichRemoteProvider(remotes);
let autolinkedIssuesOrPullRequests;
let pr;
if (remote?.provider != null) {
[autolinkedIssuesOrPullRequests, pr] = await Promise.all([
Container.instance.autolinks.getIssueOrPullRequestLinks(this.commit.message, remote),
Container.instance.git.getPullRequestForCommit(this.commit.ref, remote.provider),
this.view.container.autolinks.getIssueOrPullRequestLinks(this.commit.message, remote),
this.view.container.git.getPullRequestForCommit(this.commit.ref, remote.provider),
]);
}
@ -234,7 +233,7 @@ export class FileRevisionAsCommitNode extends ViewRefFileNode
this.commit,
{
autolinkedIssuesOrPullRequests: autolinkedIssuesOrPullRequests,
dateFormat: Container.instance.config.defaultDateFormat,
dateFormat: this.view.container.config.defaultDateFormat,
getBranchAndTagTips: this._options.getBranchAndTagTips,
markdown: true,
messageAutolinks: true,

+ 8
- 9
src/views/nodes/lineHistoryNode.ts View File

@ -1,6 +1,5 @@
'use strict';
import { Disposable, Selection, TreeItem, TreeItemCollapsibleState, window } from 'vscode';
import { Container } from '../../container';
import {
GitBranch,
GitCommitType,
@ -67,19 +66,19 @@ export class LineHistoryNode
let selection = this.selection;
const range = this.branch != null ? await Container.instance.git.getBranchAheadRange(this.branch) : undefined;
const range = this.branch != null ? await this.view.container.git.getBranchAheadRange(this.branch) : undefined;
const [log, blame, getBranchAndTagTips, unpublishedCommits] = await Promise.all([
this.getLog(selection),
this.uri.sha == null
? this.editorContents
? await Container.instance.git.getBlameForRangeContents(this.uri, selection, this.editorContents)
: await Container.instance.git.getBlameForRange(this.uri, selection)
? await this.view.container.git.getBlameForRangeContents(this.uri, selection, this.editorContents)
: await this.view.container.git.getBlameForRange(this.uri, selection)
: undefined,
this.branch != null
? Container.instance.git.getBranchesAndTagsTipsFn(this.uri.repoPath, this.branch.name)
? this.view.container.git.getBranchesAndTagsTipsFn(this.uri.repoPath, this.branch.name)
: undefined,
range
? Container.instance.git.getLogRefsOnly(this.uri.repoPath!, {
? this.view.container.git.getLogRefsOnly(this.uri.repoPath!, {
limit: 0,
ref: range,
})
@ -104,7 +103,7 @@ export class LineHistoryNode
selection.active.character,
);
const status = await Container.instance.git.getStatusForFile(this.uri.repoPath!, this.uri.fsPath);
const status = await this.view.container.git.getStatusForFile(this.uri.repoPath!, this.uri.fsPath);
const file: GitFile = {
conflictStatus: status?.conflictStatus,
@ -263,7 +262,7 @@ export class LineHistoryNode
@debug()
protected async subscribe() {
const repo = await Container.instance.git.getRepository(this.uri);
const repo = await this.view.container.git.getRepository(this.uri);
if (repo == null) return undefined;
const subscription = Disposable.from(
@ -318,7 +317,7 @@ export class LineHistoryNode
private _log: GitLog | undefined;
private async getLog(selection?: Selection) {
if (this._log == null) {
this._log = await Container.instance.git.getLogForFile(this.uri.repoPath, this.uri.fsPath, {
this._log = await this.view.container.git.getLogForFile(this.uri.repoPath, this.uri.fsPath, {
all: false,
limit: this.limit ?? this.view.config.pageItemLimit,
range: selection ?? this.selection,

+ 8
- 9
src/views/nodes/lineHistoryTrackerNode.ts View File

@ -2,7 +2,6 @@
import { Selection, TreeItem, TreeItemCollapsibleState, window } from 'vscode';
import { UriComparer } from '../../comparers';
import { ContextKeys, setContext } from '../../constants';
import { Container } from '../../container';
import { GitReference, GitRevision } from '../../git/git';
import { GitCommitish, GitUri } from '../../git/gitUri';
import { Logger } from '../../logger';
@ -75,9 +74,9 @@ export class LineHistoryTrackerNode extends SubscribeableViewNode
let branch;
if (!commitish.sha || commitish.sha === 'HEAD') {
branch = await Container.instance.git.getBranch(this.uri.repoPath);
branch = await this.view.container.git.getBranch(this.uri.repoPath);
} else if (!GitRevision.isSha(commitish.sha)) {
[branch] = await Container.instance.git.getBranches(this.uri.repoPath, {
[branch] = await this.view.container.git.getBranches(this.uri.repoPath, {
filter: b => b.name === commitish.sha,
});
}
@ -123,7 +122,7 @@ export class LineHistoryTrackerNode extends SubscribeableViewNode
if (pick == null) return;
if (GitReference.isBranch(pick)) {
const branch = await Container.instance.git.getBranch(this.uri.repoPath);
const branch = await this.view.container.git.getBranch(this.uri.repoPath);
this._base = branch?.name === pick.name ? undefined : pick.ref;
} else {
this._base = pick.ref;
@ -151,10 +150,10 @@ export class LineHistoryTrackerNode extends SubscribeableViewNode
}
const editor = window.activeTextEditor;
if (editor == null || !Container.instance.git.isTrackable(editor.document.uri)) {
if (editor == null || !this.view.container.git.isTrackable(editor.document.uri)) {
if (
!this.hasUri ||
(Container.instance.git.isTrackable(this.uri) &&
(this.view.container.git.isTrackable(this.uri) &&
window.visibleTextEditors.some(e => e.document?.uri.path === this.uri.path))
) {
return true;
@ -211,13 +210,13 @@ export class LineHistoryTrackerNode extends SubscribeableViewNode
@debug()
protected subscribe() {
if (Container.instance.lineTracker.isSubscribed(this)) return undefined;
if (this.view.container.lineTracker.isSubscribed(this)) return undefined;
const onActiveLinesChanged = Functions.debounce(this.onActiveLinesChanged.bind(this), 250);
return Container.instance.lineTracker.start(
return this.view.container.lineTracker.start(
this,
Container.instance.lineTracker.onDidChangeActiveLines((e: LinesChangeEvent) => {
this.view.container.lineTracker.onDidChangeActiveLines((e: LinesChangeEvent) => {
if (e.pending) return;
onActiveLinesChanged(e);

+ 3
- 4
src/views/nodes/mergeConflictCurrentChangesNode.ts View File

@ -2,7 +2,6 @@
import { Command, MarkdownString, ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Commands, DiffWithCommandArgs } from '../../commands';
import { BuiltInCommands, GlyphChars } from '../../constants';
import { Container } from '../../container';
import { CommitFormatter, GitFile, GitMergeStatus, GitRebaseStatus, GitReference } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { FileHistoryView } from '../fileHistoryView';
@ -25,7 +24,7 @@ export class MergeConflictCurrentChangesNode extends ViewNode
}
async getTreeItem(): Promise<TreeItem> {
const commit = await Container.instance.git.getCommit(this.status.repoPath, 'HEAD');
const commit = await this.view.container.git.getCommit(this.status.repoPath, 'HEAD');
const item = new TreeItem('Current changes', TreeItemCollapsibleState.None);
item.contextValue = ContextValues.MergeConflictCurrentChanges;
@ -33,7 +32,7 @@ export class MergeConflictCurrentChangesNode extends ViewNode
commit != null ? ` (${GitReference.toString(commit, { expand: false, icon: false })})` : ' (HEAD)'
}`;
item.iconPath = this.view.config.avatars
? (await commit?.getAvatarUri({ defaultStyle: Container.instance.config.defaultGravatarsStyle })) ??
? (await commit?.getAvatarUri({ defaultStyle: this.view.container.config.defaultGravatarsStyle })) ??
new ThemeIcon('diff')
: new ThemeIcon('diff');
@ -47,7 +46,7 @@ export class MergeConflictCurrentChangesNode extends ViewNode
commit,
{
avatarSize: 16,
dateFormat: Container.instance.config.defaultDateFormat,
dateFormat: this.view.container.config.defaultDateFormat,
markdown: true,
// messageAutolinks: true,
messageIndent: 4,

+ 3
- 4
src/views/nodes/mergeConflictIncomingChangesNode.ts View File

@ -2,7 +2,6 @@
import { Command, MarkdownString, ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Commands, DiffWithCommandArgs } from '../../commands';
import { BuiltInCommands, GlyphChars } from '../../constants';
import { Container } from '../../container';
import { CommitFormatter, GitFile, GitMergeStatus, GitRebaseStatus, GitReference } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { FileHistoryView } from '../fileHistoryView';
@ -25,7 +24,7 @@ export class MergeConflictIncomingChangesNode extends ViewNode
}
async getTreeItem(): Promise<TreeItem> {
const commit = await Container.instance.git.getCommit(
const commit = await this.view.container.git.getCommit(
this.status.repoPath,
this.status.type === 'rebase' ? this.status.steps.current.commit.ref : this.status.HEAD.ref,
);
@ -38,7 +37,7 @@ export class MergeConflictIncomingChangesNode extends ViewNode
: ` (${GitReference.toString(this.status.HEAD, { expand: false, icon: false })})`
}`;
item.iconPath = this.view.config.avatars
? (await commit?.getAvatarUri({ defaultStyle: Container.instance.config.defaultGravatarsStyle })) ??
? (await commit?.getAvatarUri({ defaultStyle: this.view.container.config.defaultGravatarsStyle })) ??
new ThemeIcon('diff')
: new ThemeIcon('diff');
@ -52,7 +51,7 @@ export class MergeConflictIncomingChangesNode extends ViewNode
commit,
{
avatarSize: 16,
dateFormat: Container.instance.config.defaultDateFormat,
dateFormat: this.view.container.config.defaultDateFormat,
markdown: true,
// messageAutolinks: true,
messageIndent: 4,

+ 2
- 3
src/views/nodes/rebaseStatusNode.ts View File

@ -13,7 +13,6 @@ import {
import { Commands, DiffWithPreviousCommandArgs } from '../../commands';
import { ViewFilesLayout } from '../../configuration';
import { BuiltInCommands, GlyphChars } from '../../constants';
import { Container } from '../../container';
import {
CommitFormatter,
GitBranch,
@ -78,7 +77,7 @@ export class RebaseStatusNode extends ViewNode {
);
}
const commit = await Container.instance.git.getCommit(
const commit = await this.view.container.git.getCommit(
this.rebaseStatus.repoPath,
this.rebaseStatus.steps.current.commit.ref,
);
@ -168,7 +167,7 @@ export class RebaseCommitNode extends ViewRefNode
})}\${\n\n${GlyphChars.Dash.repeat(2)}\nfootnotes}`,
this.commit,
{
dateFormat: Container.instance.config.defaultDateFormat,
dateFormat: this.view.container.config.defaultDateFormat,
messageIndent: 4,
},
);

+ 3
- 4
src/views/nodes/reflogNode.ts View File

@ -1,6 +1,5 @@
'use strict';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Container } from '../../container';
import { GitReflog, Repository } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { debug, gate } from '../../system';
@ -52,8 +51,8 @@ export class ReflogNode extends ViewNode implements PageableVi
item.contextValue = ContextValues.Reflog;
item.description = 'experimental';
item.iconPath = {
dark: Container.instance.context.asAbsolutePath('images/dark/icon-activity.svg'),
light: Container.instance.context.asAbsolutePath('images/light/icon-activity.svg'),
dark: this.view.container.context.asAbsolutePath('images/dark/icon-activity.svg'),
light: this.view.container.context.asAbsolutePath('images/light/icon-activity.svg'),
};
return item;
@ -71,7 +70,7 @@ export class ReflogNode extends ViewNode implements PageableVi
private _reflog: GitReflog | undefined;
private async getReflog() {
if (this._reflog === undefined) {
this._reflog = await Container.instance.git.getIncomingActivity(this.repo.path, {
this._reflog = await this.view.container.git.getIncomingActivity(this.repo.path, {
all: true,
limit: this.limit ?? this.view.config.defaultItemLimit,
});

+ 1
- 2
src/views/nodes/reflogRecordNode.ts View File

@ -1,7 +1,6 @@
'use strict';
import { TreeItem, TreeItemCollapsibleState, window } from 'vscode';
import { GlyphChars } from '../../constants';
import { Container } from '../../container';
import { GitLog, GitReflogRecord } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { debug, gate, Iterables } from '../../system';
@ -90,7 +89,7 @@ export class ReflogRecordNode extends ViewNode implements Page
private async getLog() {
if (this._log === undefined) {
const range = `${this.record.previousSha}..${this.record.sha}`;
this._log = await Container.instance.git.getLog(this.uri.repoPath!, {
this._log = await this.view.container.git.getLog(this.uri.repoPath!, {
limit: this.limit ?? this.view.config.defaultItemLimit,
ref: range,
});

+ 2
- 3
src/views/nodes/remoteNode.ts View File

@ -2,7 +2,6 @@
import { ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
import { ViewBranchesLayout } from '../../configuration';
import { GlyphChars } from '../../constants';
import { Container } from '../../container';
import { GitRemote, GitRemoteType, Repository } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { Arrays, log } from '../../system';
@ -117,8 +116,8 @@ export class RemoteNode extends ViewNode {
provider.icon === 'remote'
? new ThemeIcon('cloud')
: {
dark: Container.instance.context.asAbsolutePath(`images/dark/icon-${provider.icon}.svg`),
light: Container.instance.context.asAbsolutePath(`images/light/icon-${provider.icon}.svg`),
dark: this.view.container.context.asAbsolutePath(`images/dark/icon-${provider.icon}.svg`),
light: this.view.container.context.asAbsolutePath(`images/light/icon-${provider.icon}.svg`),
};
if (provider.hasApi()) {

+ 6
- 6
src/views/nodes/repositoriesNode.ts View File

@ -1,6 +1,6 @@
'use strict';
import { Disposable, TextEditor, TreeItem, TreeItemCollapsibleState, window } from 'vscode';
import { Container } from '../../container';
import { RepositoriesChangedEvent } from '../../git/gitProviderService';
import { GitUri } from '../../git/gitUri';
import { Logger } from '../../logger';
import { debug, Functions, gate } from '../../system';
@ -34,9 +34,9 @@ export class RepositoriesNode extends SubscribeableViewNode {
this._children = undefined;
}
async getChildren(): Promise<ViewNode[]> {
getChildren(): ViewNode[] {
if (this._children === undefined) {
const repositories = await Container.instance.git.getOrderedRepositories();
const repositories = this.view.container.git.openRepositories;
if (repositories.length === 0) return [new MessageNode(this.view, this, 'No repositories could be found.')];
this._children = repositories.map(r => new RepositoryNode(GitUri.fromRepoPath(r.path), this.view, this, r));
@ -65,7 +65,7 @@ export class RepositoriesNode extends SubscribeableViewNode {
return;
}
const repositories = await Container.instance.git.getOrderedRepositories();
const repositories = this.view.container.git.openRepositories;
if (repositories.length === 0 && (this._children === undefined || this._children.length === 0)) return;
if (repositories.length === 0) {
@ -98,7 +98,7 @@ export class RepositoriesNode extends SubscribeableViewNode {
@debug()
protected subscribe() {
const subscriptions = [Container.instance.git.onDidChangeRepositories(this.onRepositoriesChanged, this)];
const subscriptions = [this.view.container.git.onDidChangeRepositories(this.onRepositoriesChanged, this)];
if (this.view.config.autoReveal) {
subscriptions.push(
@ -141,7 +141,7 @@ export class RepositoriesNode extends SubscribeableViewNode {
}
@debug()
private onRepositoriesChanged() {
private onRepositoriesChanged(_e: RepositoriesChangedEvent) {
void this.triggerChange();
}
}

+ 5
- 6
src/views/nodes/repositoryNode.ts View File

@ -1,7 +1,6 @@
'use strict';
import { Disposable, MarkdownString, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { GlyphChars } from '../../constants';
import { Container } from '../../container';
import {
GitBranch,
GitRemote,
@ -87,8 +86,8 @@ export class RepositoryNode extends SubscribeableViewNode {
}
const [mergeStatus, rebaseStatus] = await Promise.all([
Container.instance.git.getMergeStatus(status.repoPath),
Container.instance.git.getRebaseStatus(status.repoPath),
this.view.container.git.getMergeStatus(status.repoPath),
this.view.container.git.getRebaseStatus(status.repoPath),
]);
if (mergeStatus != null) {
@ -212,7 +211,7 @@ export class RepositoryNode extends SubscribeableViewNode {
let providerName;
if (status.upstream != null) {
const providers = GitRemote.getHighlanderProviders(
await Container.instance.git.getRemotes(status.repoPath),
await this.view.container.git.getRemotes(status.repoPath),
);
providerName = providers?.length ? providers[0].name : undefined;
} else {
@ -267,8 +266,8 @@ export class RepositoryNode extends SubscribeableViewNode {
: ''
}`;
item.iconPath = {
dark: Container.instance.context.asAbsolutePath(`images/dark/icon-repo${iconSuffix}.svg`),
light: Container.instance.context.asAbsolutePath(`images/light/icon-repo${iconSuffix}.svg`),
dark: this.view.container.context.asAbsolutePath(`images/dark/icon-repo${iconSuffix}.svg`),
light: this.view.container.context.asAbsolutePath(`images/light/icon-repo${iconSuffix}.svg`),
};
const markdown = new MarkdownString(tooltip, true);

+ 2
- 3
src/views/nodes/resultsCommitsNode.ts View File

@ -1,6 +1,5 @@
'use strict';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Container } from '../../container';
import { GitLog } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { debug, gate, Iterables, Promises } from '../../system';
@ -69,7 +68,7 @@ export class ResultsCommitsNode
const { log } = await this.getCommitsQueryResults();
if (log == null) return [];
const getBranchAndTagTips = await Container.instance.git.getBranchesAndTagsTipsFn(this.uri.repoPath);
const getBranchAndTagTips = await this.view.container.git.getBranchesAndTagsTipsFn(this.uri.repoPath);
const children = [];
const { files } = this._results;
@ -161,7 +160,7 @@ export class ResultsCommitsNode
private async getCommitsQueryResults() {
if (this._commitsQueryResults == null) {
this._commitsQueryResults = this._results.query(
this.limit ?? Container.instance.config.advanced.maxSearchItems,
this.limit ?? this.view.container.config.advanced.maxSearchItems,
);
const results = await this._commitsQueryResults;
this._hasMore = results.hasMore;

+ 2
- 3
src/views/nodes/resultsFileNode.ts View File

@ -2,7 +2,6 @@
import * as paths from 'path';
import { Command, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Commands, DiffWithCommandArgs } from '../../commands';
import { Container } from '../../container';
import { GitFile, GitReference, GitRevisionReference, StatusFileFormatter } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { View } from '../viewBase';
@ -49,8 +48,8 @@ export class ResultsFileNode extends ViewRefFileNode implements FileNode {
const statusIcon = GitFile.getStatusIcon(this.file.status);
item.iconPath = {
dark: Container.instance.context.asAbsolutePath(paths.join('images', 'dark', statusIcon)),
light: Container.instance.context.asAbsolutePath(paths.join('images', 'light', statusIcon)),
dark: this.view.container.context.asAbsolutePath(paths.join('images', 'dark', statusIcon)),
light: this.view.container.context.asAbsolutePath(paths.join('images', 'light', statusIcon)),
};
item.command = this.getCommand();

+ 4
- 5
src/views/nodes/resultsFilesNode.ts View File

@ -2,7 +2,6 @@
import * as paths from 'path';
import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ViewFilesLayout } from '../../configuration';
import { Container } from '../../container';
import { GitFile } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { Arrays, debug, gate, Iterables, Promises, Strings } from '../../system';
@ -120,7 +119,7 @@ export class ResultsFilesNode extends ViewNode {
: TreeItemCollapsibleState.Collapsed;
} catch (ex) {
if (ex instanceof Promises.CancellationError) {
ex.promise.then(() => setTimeout(() => this.triggerChange(false), 0));
ex.promise.then(() => queueMicrotask(() => this.triggerChange(false)));
}
label = 'files changed';
@ -184,18 +183,18 @@ export class ResultsFilesNode extends ViewNode {
const ref = this.filter === 'left' ? this.ref2 : this.ref1;
const mergeBase = await Container.instance.git.getMergeBase(
const mergeBase = await this.view.container.git.getMergeBase(
this.repoPath,
this.ref1 || 'HEAD',
this.ref2 || 'HEAD',
);
if (mergeBase != null) {
const files = await Container.instance.git.getDiffStatus(this.uri.repoPath!, `${mergeBase}..${ref}`);
const files = await this.view.container.git.getDiffStatus(this.uri.repoPath!, `${mergeBase}..${ref}`);
if (files != null) {
filterTo = new Set<string>(files.map(f => f.fileName));
}
} else {
const commit = await Container.instance.git.getCommit(this.uri.repoPath!, ref || 'HEAD');
const commit = await this.view.container.git.getCommit(this.uri.repoPath!, ref || 'HEAD');
if (commit?.files != null) {
filterTo = new Set<string>(commit.files.map(f => f.fileName));
}

+ 6
- 7
src/views/nodes/searchResultsNode.ts View File

@ -1,7 +1,6 @@
'use strict';
import { ThemeIcon, TreeItem } from 'vscode';
import { executeGitCommand } from '../../commands';
import { Container } from '../../container';
import { GitLog, SearchPattern } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { debug, gate, log, Strings } from '../../system';
@ -132,8 +131,8 @@ export class SearchResultsNode extends ViewNode implements
const item = await this.ensureResults().getTreeItem();
item.id = this.id;
item.contextValue = `${ContextValues.SearchResults}${this._pinned ? '+pinned' : ''}`;
if ((await Container.instance.git.getRepositoryCount()) > 1) {
const repo = await Container.instance.git.getRepository(this.repoPath);
if (this.view.container.git.repositoryCount > 1) {
const repo = await this.view.container.git.getRepository(this.repoPath);
item.description = repo?.formattedName ?? this.repoPath;
}
if (this._pinned) {
@ -194,7 +193,7 @@ export class SearchResultsNode extends ViewNode implements
}
void this.triggerChange(false);
setImmediate(() => this.view.reveal(this, { expand: true, focus: true, select: true }));
queueMicrotask(() => this.view.reveal(this, { expand: true, focus: true, select: true }));
}
@gate()
@ -210,7 +209,7 @@ export class SearchResultsNode extends ViewNode implements
this._pinned = Date.now();
await this.updatePinned();
setImmediate(() => this.view.reveal(this, { focus: true, select: true }));
queueMicrotask(() => this.view.reveal(this, { focus: true, select: true }));
}
@log()
@ -220,7 +219,7 @@ export class SearchResultsNode extends ViewNode implements
this._pinned = 0;
await this.view.updatePinned(this.getPinnableId());
setImmediate(() => this.view.reveal(this, { focus: true, select: true }));
queueMicrotask(() => this.view.reveal(this, { focus: true, select: true }));
}
private getPinnableId() {
@ -264,7 +263,7 @@ export class SearchResultsNode extends ViewNode implements
let useCacheOnce = true;
return async (limit: number | undefined) => {
log = await (log ?? Container.instance.git.getLogForSearch(this.repoPath, this.search));
log = await (log ?? this.view.container.git.getLogForSearch(this.repoPath, this.search));
if (!useCacheOnce && log != null && log.query != null) {
log = await log.query(limit);

+ 3
- 4
src/views/nodes/stashNode.ts View File

@ -2,7 +2,6 @@
import * as paths from 'path';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ViewFilesLayout } from '../../config';
import { Container } from '../../container';
import { CommitFormatter, GitStashCommit, GitStashReference } from '../../git/git';
import { Arrays, Strings } from '../../system';
import { ContextValues, FileNode, FolderNode, RepositoryNode, StashFileNode, ViewNode, ViewRefNode } from '../nodes';
@ -61,18 +60,18 @@ export class StashNode extends ViewRefNode
const item = new TreeItem(
CommitFormatter.fromTemplate(this.view.config.formats.stashes.label, this.commit, {
messageTruncateAtNewLine: true,
dateFormat: Container.instance.config.defaultDateFormat,
dateFormat: this.view.container.config.defaultDateFormat,
}),
TreeItemCollapsibleState.Collapsed,
);
item.id = this.id;
item.description = CommitFormatter.fromTemplate(this.view.config.formats.stashes.description, this.commit, {
messageTruncateAtNewLine: true,
dateFormat: Container.instance.config.defaultDateFormat,
dateFormat: this.view.container.config.defaultDateFormat,
});
item.contextValue = ContextValues.Stash;
item.tooltip = CommitFormatter.fromTemplate(`\${ago} (\${date})\n\n\${message}`, this.commit, {
dateFormat: Container.instance.config.defaultDateFormat,
dateFormat: this.view.container.config.defaultDateFormat,
// messageAutolinks: true,
});

+ 2
- 3
src/views/nodes/stashesNode.ts View File

@ -1,6 +1,5 @@
'use strict';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Container } from '../../container';
import { Repository } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { debug, gate, Iterables } from '../../system';
@ -44,8 +43,8 @@ export class StashesNode extends ViewNode {
item.contextValue = ContextValues.Stashes;
item.iconPath = {
dark: Container.instance.context.asAbsolutePath('images/dark/icon-stash.svg'),
light: Container.instance.context.asAbsolutePath('images/light/icon-stash.svg'),
dark: this.view.container.context.asAbsolutePath('images/dark/icon-stash.svg'),
light: this.view.container.context.asAbsolutePath('images/light/icon-stash.svg'),
};
return item;

+ 2
- 3
src/views/nodes/statusFileNode.ts View File

@ -2,7 +2,6 @@
import * as paths from 'path';
import { Command, ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Commands, DiffWithCommandArgs, DiffWithPreviousCommandArgs } from '../../commands';
import { Container } from '../../container';
import { GitFile, GitLogCommit, StatusFileFormatter } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { Strings } from '../../system';
@ -111,8 +110,8 @@ export class StatusFileNode extends ViewNode implements FileNo
const icon = GitFile.getStatusIcon(this.file.status);
item.iconPath = {
dark: Container.instance.context.asAbsolutePath(paths.join('images', 'dark', icon)),
light: Container.instance.context.asAbsolutePath(paths.join('images', 'light', icon)),
dark: this.view.container.context.asAbsolutePath(paths.join('images', 'dark', icon)),
light: this.view.container.context.asAbsolutePath(paths.join('images', 'light', icon)),
};
}

+ 5
- 6
src/views/nodes/statusFilesNode.ts View File

@ -2,7 +2,6 @@
import * as paths from 'path';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ViewFilesLayout } from '../../configuration';
import { Container } from '../../container';
import {
GitCommitType,
GitFileWithCommit,
@ -57,7 +56,7 @@ export class StatusFilesNode extends ViewNode {
let log: GitLog | undefined;
if (this.range != null) {
log = await Container.instance.git.getLog(repoPath, { limit: 0, ref: this.range });
log = await this.view.container.git.getLog(repoPath, { limit: 0, ref: this.range });
if (log != null) {
files = [
...Iterables.flatMap(log.commits.values(), c =>
@ -135,7 +134,7 @@ export class StatusFilesNode extends ViewNode {
if (this.range != null) {
if (this.status.upstream != null && this.status.state.ahead > 0) {
if (files > 0) {
const aheadFiles = await Container.instance.git.getDiffStatus(
const aheadFiles = await this.view.container.git.getDiffStatus(
this.repoPath,
`${this.status.upstream}...`,
);
@ -152,7 +151,7 @@ export class StatusFilesNode extends ViewNode {
files = uniques.size;
}
} else {
const stats = await Container.instance.git.getChangedFilesCount(
const stats = await this.view.container.git.getChangedFilesCount(
this.repoPath,
`${this.status.upstream}...`,
);
@ -170,8 +169,8 @@ export class StatusFilesNode extends ViewNode {
item.id = this.id;
item.contextValue = ContextValues.StatusFiles;
item.iconPath = {
dark: Container.instance.context.asAbsolutePath('images/dark/icon-diff.svg'),
light: Container.instance.context.asAbsolutePath('images/light/icon-diff.svg'),
dark: this.view.container.context.asAbsolutePath('images/dark/icon-diff.svg'),
light: this.view.container.context.asAbsolutePath('images/light/icon-diff.svg'),
};
return item;

+ 3
- 4
src/views/nodes/tagNode.ts View File

@ -2,7 +2,6 @@
import { TreeItem, TreeItemCollapsibleState, window } from 'vscode';
import { ViewBranchesLayout } from '../../configuration';
import { GlyphChars } from '../../constants';
import { Container } from '../../container';
import { emojify } from '../../emojis';
import { GitLog, GitRevision, GitTag, GitTagReference, TagDateFormatting } from '../../git/git';
import { GitUri } from '../../git/gitUri';
@ -45,7 +44,7 @@ export class TagNode extends ViewRefNode
const log = await this.getLog();
if (log == null) return [new MessageNode(this.view, this, 'No commits could be found.')];
const getBranchAndTagTips = await Container.instance.git.getBranchesAndTagsTipsFn(
const getBranchAndTagTips = await this.view.container.git.getBranchesAndTagsTipsFn(
this.uri.repoPath,
this.tag.name,
);
@ -62,7 +61,7 @@ export class TagNode extends ViewRefNode
if (log.hasMore) {
children.push(
new LoadMoreNode(this.view, this, children[children.length - 1], undefined, () =>
Container.instance.git.getCommitCount(this.tag.repoPath, this.tag.name),
this.view.container.git.getCommitCount(this.tag.repoPath, this.tag.name),
),
);
}
@ -98,7 +97,7 @@ export class TagNode extends ViewRefNode
private _log: GitLog | undefined;
private async getLog() {
if (this._log == null) {
this._log = await Container.instance.git.getLog(this.uri.repoPath!, {
this._log = await this.view.container.git.getLog(this.uri.repoPath!, {
limit: this.limit ?? this.view.config.defaultItemLimit,
ref: this.tag.name,
});

+ 43
- 3
src/views/nodes/viewNode.ts View File

@ -9,7 +9,6 @@ import {
TreeViewVisibilityChangeEvent,
} from 'vscode';
import { GlyphChars } from '../../constants';
import { Container } from '../../container';
import {
GitFile,
GitReference,
@ -20,6 +19,7 @@ import {
RepositoryChangeComparisonMode,
RepositoryChangeEvent,
} from '../../git/git';
import { RepositoriesChangedEvent } from '../../git/gitProviderService';
import { GitUri } from '../../git/gitUri';
import { Logger } from '../../logger';
import { debug, Functions, gate, log, logName, Strings } from '../../system';
@ -357,7 +357,7 @@ export abstract class RepositoryFolderNode<
let expand = this.repo.starred;
const [active, branch] = await Promise.all([
expand ? undefined : Container.instance.git.isActiveRepoPath(this.uri.repoPath),
expand ? undefined : this.view.container.git.isActiveRepoPath(this.uri.repoPath),
this.repo.getBranch(),
]);
@ -395,7 +395,7 @@ export abstract class RepositoryFolderNode<
let providerName;
if (branch.upstream != null) {
const providers = GitRemote.getHighlanderProviders(
await Container.instance.git.getRemotes(branch.repoPath),
await this.view.container.git.getRemotes(branch.repoPath),
);
providerName = providers?.length ? providers[0].name : undefined;
} else {
@ -518,6 +518,46 @@ export abstract class RepositoryFolderNode<
}
}
export abstract class RepositoriesSubscribeableNode<
TView extends View = View,
TChild extends ViewNode & Disposable = ViewNode & Disposable,
> extends SubscribeableViewNode<TView> {
protected override splatted = true;
protected children: TChild[] | undefined;
constructor(view: TView) {
super(unknownGitUri, view);
}
override async getSplattedChild() {
if (this.children == null) {
await this.getChildren();
}
return this.children?.length === 1 ? this.children[0] : undefined;
}
@gate()
@debug()
override refresh(reset: boolean = false) {
if (reset && this.children != null) {
for (const child of this.children) {
child.dispose();
}
this.children = undefined;
}
}
@debug()
protected subscribe(): Disposable | Promise<Disposable> {
return this.view.container.git.onDidChangeRepositories(this.onRepositoriesChanged, this);
}
private onRepositoriesChanged(_e: RepositoriesChangedEvent) {
void this.triggerChange(true);
}
}
interface AutoRefreshableView {
autoRefresh: boolean;
onDidChangeAutoRefresh: Event<void>;

+ 6
- 32
src/views/remotesView.ts View File

@ -24,15 +24,15 @@ import {
RepositoryChangeEvent,
} from '../git/git';
import { GitUri } from '../git/gitUri';
import { debug, gate, Strings } from '../system';
import { gate, Strings } from '../system';
import {
BranchNode,
BranchOrTagFolderNode,
RemoteNode,
RemotesNode,
RepositoriesSubscribeableNode,
RepositoryFolderNode,
RepositoryNode,
unknownGitUri,
ViewNode,
} from './nodes';
import { ViewBase } from './viewBase';
@ -57,17 +57,10 @@ export class RemotesRepositoryNode extends RepositoryFolderNode
}
}
export class RemotesViewNode extends ViewNode<RemotesView> {
protected override splatted = true;
private children: RemotesRepositoryNode[] | undefined;
constructor(view: RemotesView) {
super(unknownGitUri, view);
}
export class RemotesViewNode extends RepositoriesSubscribeableNode<RemotesView, RemotesRepositoryNode> {
async getChildren(): Promise<ViewNode[]> {
if (this.children == null) {
const repositories = await Container.instance.git.getOrderedRepositories();
const repositories = this.view.container.git.openRepositories;
if (repositories.length === 0) {
this.view.message = 'No remotes could be found.';
@ -112,25 +105,6 @@ export class RemotesViewNode extends ViewNode {
const item = new TreeItem('Remotes', TreeItemCollapsibleState.Expanded);
return item;
}
override async getSplattedChild() {
if (this.children == null) {
await this.getChildren();
}
return this.children?.length === 1 ? this.children[0] : undefined;
}
@gate()
@debug()
override refresh(reset: boolean = false) {
if (reset && this.children != null) {
for (const child of this.children) {
child.dispose();
}
this.children = undefined;
}
}
}
export class RemotesView extends ViewBase<RemotesViewNode, RemotesViewConfig> {
@ -155,8 +129,8 @@ export class RemotesView extends ViewBase {
),
commands.registerCommand(
this.getQualifiedCommand('refresh'),
async () => {
await this.container.git.resetCaches('branches', 'remotes');
() => {
this.container.git.resetCaches('branches', 'remotes');
return this.refresh(true);
},
this,

+ 2
- 9
src/views/repositoriesView.ts View File

@ -73,15 +73,8 @@ export class RepositoriesView extends ViewBase
),
commands.registerCommand(
this.getQualifiedCommand('refresh'),
async () => {
await this.container.git.resetCaches(
'branches',
'contributors',
'remotes',
'stashes',
'status',
'tags',
);
() => {
this.container.git.resetCaches('branches', 'contributors', 'remotes', 'stashes', 'status', 'tags');
return this.refresh(true);
},
this,

+ 1
- 1
src/views/searchAndCompareView.ts View File

@ -514,7 +514,7 @@ export class SearchAndCompareView extends ViewBase
const root = this.ensureRoot();
root.addOrReplace(results, !this.keepResults);
setImmediate(() => this.reveal(results, options));
queueMicrotask(() => this.reveal(results, options));
}
private setFilesLayout(layout: ViewFilesLayout) {

+ 13
- 32
src/views/stashesView.ts View File

@ -20,8 +20,15 @@ import {
RepositoryChangeEvent,
} from '../git/git';
import { GitUri } from '../git/gitUri';
import { debug, gate, Strings } from '../system';
import { RepositoryFolderNode, RepositoryNode, StashesNode, StashNode, unknownGitUri, ViewNode } from './nodes';
import { gate, Strings } from '../system';
import {
RepositoriesSubscribeableNode,
RepositoryFolderNode,
RepositoryNode,
StashesNode,
StashNode,
ViewNode,
} from './nodes';
import { ViewBase } from './viewBase';
export class StashesRepositoryNode extends RepositoryFolderNode<StashesView, StashesNode> {
@ -38,17 +45,10 @@ export class StashesRepositoryNode extends RepositoryFolderNode
}
}
export class StashesViewNode extends ViewNode<StashesView> {
protected override splatted = true;
private children: StashesRepositoryNode[] | undefined;
constructor(view: StashesView) {
super(unknownGitUri, view);
}
export class StashesViewNode extends RepositoriesSubscribeableNode<StashesView, StashesRepositoryNode> {
async getChildren(): Promise<ViewNode[]> {
if (this.children == null) {
const repositories = await Container.instance.git.getOrderedRepositories();
const repositories = this.view.container.git.openRepositories;
if (repositories.length === 0) {
this.view.message = 'No stashes could be found.';
@ -93,25 +93,6 @@ export class StashesViewNode extends ViewNode {
const item = new TreeItem('Stashes', TreeItemCollapsibleState.Expanded);
return item;
}
override async getSplattedChild() {
if (this.children == null) {
await this.getChildren();
}
return this.children?.length === 1 ? this.children[0] : undefined;
}
@gate()
@debug()
override refresh(reset: boolean = false) {
if (reset && this.children != null) {
for (const child of this.children) {
child.dispose();
}
this.children = undefined;
}
}
}
export class StashesView extends ViewBase<StashesViewNode, StashesViewConfig> {
@ -136,8 +117,8 @@ export class StashesView extends ViewBase {
),
commands.registerCommand(
this.getQualifiedCommand('refresh'),
async () => {
await this.container.git.resetCaches('stashes');
() => {
this.container.git.resetCaches('stashes');
return this.refresh(true);
},
this,

+ 6
- 32
src/views/tagsView.ts View File

@ -20,13 +20,13 @@ import {
RepositoryChangeEvent,
} from '../git/git';
import { GitUri } from '../git/gitUri';
import { debug, gate, Strings } from '../system';
import { gate, Strings } from '../system';
import {
BranchOrTagFolderNode,
RepositoriesSubscribeableNode,
RepositoryFolderNode,
RepositoryNode,
TagsNode,
unknownGitUri,
ViewNode,
} from './nodes';
import { ViewBase } from './viewBase';
@ -45,17 +45,10 @@ export class TagsRepositoryNode extends RepositoryFolderNode
}
}
export class TagsViewNode extends ViewNode<TagsView> {
protected override splatted = true;
private children: TagsRepositoryNode[] | undefined;
constructor(view: TagsView) {
super(unknownGitUri, view);
}
export class TagsViewNode extends RepositoriesSubscribeableNode<TagsView, TagsRepositoryNode> {
async getChildren(): Promise<ViewNode[]> {
if (this.children == null) {
const repositories = await Container.instance.git.getOrderedRepositories();
const repositories = this.view.container.git.openRepositories;
if (repositories.length === 0) {
this.view.message = 'No tags could be found.';
@ -100,25 +93,6 @@ export class TagsViewNode extends ViewNode {
const item = new TreeItem('Tags', TreeItemCollapsibleState.Expanded);
return item;
}
override async getSplattedChild() {
if (this.children == null) {
await this.getChildren();
}
return this.children?.length === 1 ? this.children[0] : undefined;
}
@gate()
@debug()
override refresh(reset: boolean = false) {
if (reset && this.children != null) {
for (const child of this.children) {
child.dispose();
}
this.children = undefined;
}
}
}
export class TagsView extends ViewBase<TagsViewNode, TagsViewConfig> {
@ -143,8 +117,8 @@ export class TagsView extends ViewBase {
),
commands.registerCommand(
this.getQualifiedCommand('refresh'),
async () => {
await this.container.git.resetCaches('tags');
() => {
this.container.git.resetCaches('tags');
return this.refresh(true);
},
this,

+ 3
- 3
src/views/viewBase.ts View File

@ -114,7 +114,7 @@ export abstract class ViewBase<
private readonly _lastKnownLimits = new Map<string, number | undefined>();
constructor(public readonly id: string, public readonly name: string, protected readonly container: Container) {
constructor(public readonly id: string, public readonly name: string, public readonly container: Container) {
this.disposables.push(container.onReady(this.onReady, this));
if (Logger.isDebugging || this.container.config.debug) {
@ -174,7 +174,7 @@ export abstract class ViewBase<
private onReady() {
this.initialize({ showCollapseAll: this.showCollapseAll });
setImmediate(() => this.onConfigurationChanged());
queueMicrotask(() => this.onConfigurationChanged());
}
protected get showCollapseAll(): boolean {
@ -374,7 +374,7 @@ export abstract class ViewBase<
// If we have no root (e.g. never been initialized) force it so the tree will load properly
await this.show({ preserveFocus: true });
// Since we have to show the view, let the callstack unwind before we try to find the node
return new Promise<ViewNode | undefined>(resolve => setTimeout(() => resolve(find.call(this)), 0));
return new Promise<ViewNode | undefined>(resolve => queueMicrotask(() => resolve(find.call(this))));
}
private async findNodeCoreBFS(

+ 2
- 3
src/views/viewCommands.ts View File

@ -17,7 +17,6 @@ import { BuiltInCommands, BuiltInGitCommands, ContextKeys, setContext } from '..
import { Container } from '../container';
import { GitReference, GitRevision } from '../git/git';
import { GitUri } from '../git/gitUri';
import { GitService } from '../git/providers/localGitProvider';
import { debug } from '../system';
import { runGitCommandInTerminal } from '../terminal';
import {
@ -657,7 +656,7 @@ export class ViewCommands {
@debug()
private switch(node?: ViewRefNode | BranchesNode) {
if (node == null) {
return GitActions.switchTo(this.container.git.getHighlanderRepoPath());
return GitActions.switchTo(this.container.git.highlanderRepoPath);
}
if (!(node instanceof ViewRefNode) && !(node instanceof BranchesNode)) return Promise.resolve();
@ -672,7 +671,7 @@ export class ViewCommands {
private async undoCommit(node: CommitNode | FileRevisionAsCommitNode) {
if (!(node instanceof CommitNode) && !(node instanceof FileRevisionAsCommitNode)) return;
const repo = await GitService.getOrOpenBuiltInGitRepository(node.repoPath);
const repo = await Container.instance.git.getOrOpenScmRepository(node.repoPath);
const commit = await repo?.getCommit('HEAD');
if (commit?.hash !== node.ref.ref) {

+ 19
- 4
src/vsls/guest.ts View File

@ -1,6 +1,7 @@
'use strict';
import { CancellationToken, Disposable, window, WorkspaceFolder } from 'vscode';
import type { LiveShare, SharedServiceProxy } from '../@types/vsls';
import { Container } from '../container';
import { setEnabled } from '../extension';
import { GitCommandOptions, Repository, RepositoryChangeEvent } from '../git/git';
import { Logger } from '../logger';
@ -10,7 +11,7 @@ import { GitCommandRequestType, RepositoriesInFolderRequestType, RepositoryProxy
export class VslsGuestService implements Disposable {
@log()
static async connect(api: LiveShare) {
static async connect(api: LiveShare, container: Container) {
const cc = Logger.getCorrelationContext();
try {
@ -19,14 +20,18 @@ export class VslsGuestService implements Disposable {
throw new Error('Failed to connect to host service');
}
return new VslsGuestService(api, service);
return new VslsGuestService(api, service, container);
} catch (ex) {
Logger.error(ex, cc);
return undefined;
}
}
constructor(private readonly _api: LiveShare, private readonly _service: SharedServiceProxy) {
constructor(
private readonly _api: LiveShare,
private readonly _service: SharedServiceProxy,
private readonly container: Container,
) {
_service.onDidChangeIsServiceAvailable(this.onAvailabilityChanged.bind(this));
this.onAvailabilityChanged(_service.isServiceAvailable);
}
@ -70,7 +75,17 @@ export class VslsGuestService implements Disposable {
return response.repositories.map(
(r: RepositoryProxy) =>
new Repository(folder, r.path, r.root, onAnyRepositoryChanged, !window.state.focused, r.closed),
new Repository(
this.container,
onAnyRepositoryChanged,
// TODO@eamodio add live share provider
undefined!,
folder,
r.path,
r.root,
!window.state.focused,
r.closed,
),
);
}

+ 2
- 1
src/vsls/host.ts View File

@ -205,6 +205,7 @@ export class VslsHostService implements Disposable {
return { data: data.toString('binary'), isBuffer: true };
}
// eslint-disable-next-line @typescript-eslint/require-await
@log()
private async onRepositoriesInFolderRequest(
request: RepositoriesInFolderRequest,
@ -214,7 +215,7 @@ export class VslsHostService implements Disposable {
const normalized = Strings.normalizePath(uri.fsPath, { stripTrailingSlash: true }).toLowerCase();
const repos = [
...Iterables.filterMap(await this.container.git.getRepositories(), r => {
...Iterables.filterMap(this.container.git.repositories, r => {
if (!r.normalizedPath.startsWith(normalized)) return undefined;
const vslsUri = this.convertLocalUriToShared(r.folder.uri);

+ 1
- 1
src/vsls/vsls.ts View File

@ -209,7 +209,7 @@ export class VslsController implements Disposable {
case 2 /*Role.Guest*/:
this.setReadonly(true);
void setContext(ContextKeys.Vsls, 'guest');
this._guest = await VslsGuestService.connect(api);
this._guest = await VslsGuestService.connect(api, this.container);
break;
default:

+ 3
- 3
webpack.config.js View File

@ -127,13 +127,13 @@ function getExtensionConfig(target, mode, env) {
treeShaking: true,
// Keep the class names otherwise @log won't provide a useful name
keepNames: true,
target: 'es2019',
target: 'es2020',
})
: new TerserPlugin({
extractComments: false,
parallel: true,
terserOptions: {
ecma: 2019,
ecma: 2020,
// Keep the class names otherwise @log won't provide a useful name
keep_classnames: true,
module: true,
@ -284,7 +284,7 @@ function getWebviewsConfig(mode, env) {
eslint: {
enabled: true,
files: path.join(basePath, '**', '*.ts'),
options: { cache: true },
// options: { cache: true },
},
formatter: 'basic',
typescript: {

Loading…
Cancel
Save