Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
339 changes: 174 additions & 165 deletions package.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,9 @@ export interface GraphConfig {
readonly showGhostRefsOnRowHover: boolean;
readonly showRemoteNames: boolean;
readonly showUpstreamStatus: boolean;
readonly sidebar: {
readonly enabled: boolean;
};
readonly statusBar: {
readonly enabled: boolean;
};
Expand Down
22 changes: 17 additions & 5 deletions src/env/node/git/localGitProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ import type { GitTreeEntry } from '../../../git/models/tree';
import type { GitUser } from '../../../git/models/user';
import { isUserMatch } from '../../../git/models/user';
import type { GitWorktree } from '../../../git/models/worktree';
import { getWorktreeId, getWorktreesByBranch } from '../../../git/models/worktree';
import { getWorktreeId, groupWorktreesByBranch } from '../../../git/models/worktree';
import { parseGitBlame } from '../../../git/parsers/blameParser';
import { parseGitBranches } from '../../../git/parsers/branchParser';
import {
Expand Down Expand Up @@ -2319,21 +2319,25 @@ export class LocalGitProvider implements GitProvider, Disposable {
const refParser = getRefParser();
const statsParser = getGraphStatsParser();

const [refResult, stashResult, branchesResult, remotesResult, currentUserResult, worktreesByBranchResult] =
const [refResult, stashResult, branchesResult, remotesResult, currentUserResult, worktreesResult] =
await Promise.allSettled([
this.git.log(repoPath, undefined, ...refParser.arguments, '-n1', options?.ref ?? 'HEAD'),
this.getStash(repoPath),
this.getBranches(repoPath),
this.getRemotes(repoPath),
this.getCurrentUser(repoPath),
getWorktreesByBranch(this.container.git.getRepository(repoPath), { includeMainWorktree: true }),
this.container.git
.getWorktrees(repoPath)
.then(w => [w, groupWorktreesByBranch(w, { includeMainWorktree: true })]) satisfies Promise<
[GitWorktree[], Map<string, GitWorktree>]
>,
]);

const branches = getSettledValue(branchesResult)?.values;
const branchMap = branches != null ? new Map(branches.map(r => [r.name, r])) : new Map<string, GitBranch>();
const headBranch = branches?.find(b => b.current);
const headRefUpstreamName = headBranch?.upstream?.name;
const worktreesByBranch = getSettledValue(worktreesByBranchResult);
const [worktrees, worktreesByBranch] = getSettledValue(worktreesResult) ?? [[], new Map<string, GitWorktree>()];

let branchIdOfMainWorktree: string | undefined;
if (worktreesByBranch != null) {
Expand All @@ -2351,13 +2355,15 @@ export class LocalGitProvider implements GitProvider, Disposable {

const downstreamMap = new Map<string, string[]>();

let stashes: Map<string, GitStashCommit> | undefined;
let stdin: string | undefined;

// TODO@eamodio this is insanity -- there *HAS* to be a better way to get git log to return stashes
const stash = getSettledValue(stashResult);
if (stash?.commits.size) {
stashes = new Map(stash.commits);
stdin = join(
map(stash.commits.values(), c => c.sha.substring(0, 9)),
map(stashes.values(), c => c.sha.substring(0, 9)),
'\n',
);
}
Expand Down Expand Up @@ -2421,6 +2427,8 @@ export class LocalGitProvider implements GitProvider, Disposable {
branches: branchMap,
remotes: remoteMap,
downstreams: downstreamMap,
stashes: stashes,
worktrees: worktrees,
worktreesByBranch: worktreesByBranch,
rows: [],
};
Expand All @@ -2444,6 +2452,8 @@ export class LocalGitProvider implements GitProvider, Disposable {
branches: branchMap,
remotes: remoteMap,
downstreams: downstreamMap,
stashes: stashes,
worktrees: worktrees,
worktreesByBranch: worktreesByBranch,
rows: [],
};
Expand Down Expand Up @@ -2838,6 +2848,8 @@ export class LocalGitProvider implements GitProvider, Disposable {
branches: branchMap,
remotes: remoteMap,
downstreams: downstreamMap,
stashes: stashes,
worktrees: worktrees,
worktreesByBranch: worktreesByBranch,
rows: rows,
id: sha,
Expand Down
3 changes: 3 additions & 0 deletions src/git/models/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
import type { GkProviderId } from '../../gk/models/repositoryIdentities';
import type { Brand, Unbrand } from '../../system/brand';
import type { GitBranch } from './branch';
import type { GitStashCommit } from './commit';
import type { GitRemote } from './remote';
import type { GitWorktree } from './worktree';

Expand Down Expand Up @@ -50,6 +51,8 @@ export interface GitGraph {
readonly branches: Map<string, GitBranch>;
readonly remotes: Map<string, GitRemote>;
readonly downstreams: Map<string, string[]>;
readonly stashes: Map<string, GitStashCommit> | undefined;
readonly worktrees: GitWorktree[] | undefined;
readonly worktreesByBranch: Map<string, GitWorktree> | undefined;

/** The rows for the set of commits requested */
Expand Down
26 changes: 20 additions & 6 deletions src/git/models/worktree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,12 +345,10 @@ export async function getWorktreesByBranch(
if (repos == null) return worktreesByBranch;

async function addWorktrees(repo: Repository) {
const worktrees = await repo.getWorktrees();
for (const wt of worktrees) {
if (wt.branch == null || (!options?.includeMainWorktree && wt.main)) continue;

worktreesByBranch.set(wt.branch.id, wt);
}
groupWorktreesByBranch(await repo.getWorktrees(), {
includeMainWorktree: options?.includeMainWorktree,
worktreesByBranch: worktreesByBranch,
});
}

if (!Array.isArray(repos)) {
Expand All @@ -362,6 +360,22 @@ export async function getWorktreesByBranch(
return worktreesByBranch;
}

export function groupWorktreesByBranch(
worktrees: GitWorktree[],
options?: { includeMainWorktree?: boolean; worktreesByBranch?: Map<string, GitWorktree> },
) {
const worktreesByBranch = options?.worktreesByBranch ?? new Map<string, GitWorktree>();
if (worktrees == null) return worktreesByBranch;

for (const wt of worktrees) {
if (wt.branch == null || (!options?.includeMainWorktree && wt.main)) continue;

worktreesByBranch.set(wt.branch.id, wt);
}

return worktreesByBranch;
}

export function getOpenedWorktreesByBranch(
worktreesByBranch: Map<string, GitWorktree> | undefined,
): Set<string> | undefined {
Expand Down
24 changes: 20 additions & 4 deletions src/plus/integrations/providers/github/githubGitProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import { GitUri } from '../../../../git/gitUri';
import type { GitBlame, GitBlameAuthor, GitBlameLine, GitBlameLines } from '../../../../git/models/blame';
import type { BranchSortOptions } from '../../../../git/models/branch';
import { getBranchId, getBranchNameWithoutRemote, GitBranch, sortBranches } from '../../../../git/models/branch';
import type { GitCommitLine } from '../../../../git/models/commit';
import type { GitCommitLine, GitStashCommit } from '../../../../git/models/commit';
import { getChangedFilesCount, GitCommit, GitCommitIdentity } from '../../../../git/models/commit';
import { deletedOrMissing, uncommitted } from '../../../../git/models/constants';
import { GitContributor } from '../../../../git/models/contributor';
Expand Down Expand Up @@ -84,6 +84,7 @@ import { getTagId, GitTag, sortTags } from '../../../../git/models/tag';
import type { GitTreeEntry } from '../../../../git/models/tree';
import type { GitUser } from '../../../../git/models/user';
import { isUserMatch } from '../../../../git/models/user';
import type { GitWorktree } from '../../../../git/models/worktree';
import { getRemoteProviderMatcher, loadRemoteProviders } from '../../../../git/remotes/remoteProviders';
import type { GitSearch, GitSearchResultData, GitSearchResults } from '../../../../git/search';
import { getSearchQueryComparisonKey, parseSearchQuery } from '../../../../git/search';
Expand Down Expand Up @@ -1329,6 +1330,9 @@ export class GitHubGitProvider implements GitProvider, Disposable {
getSettledValue(currentUserResult),
avatars,
ids,
undefined,
undefined,
undefined,
{ ...options, useAvatars: useAvatars },
);
}
Expand All @@ -1346,6 +1350,9 @@ export class GitHubGitProvider implements GitProvider, Disposable {
currentUser: GitUser | undefined,
avatars: Map<string, string>,
ids: Set<string>,
stashes: Map<string, GitStashCommit> | undefined,
worktrees: GitWorktree[] | undefined,
worktreesByBranch: Map<string, GitWorktree> | undefined,
options?: {
branch?: string;
include?: { stats?: boolean };
Expand All @@ -1365,7 +1372,9 @@ export class GitHubGitProvider implements GitProvider, Disposable {
branches: branchMap,
remotes: remoteMap,
downstreams: downstreamMap,
worktreesByBranch: undefined,
stashes: stashes,
worktrees: worktrees,
worktreesByBranch: worktreesByBranch,
rows: [],
};
}
Expand All @@ -1380,7 +1389,9 @@ export class GitHubGitProvider implements GitProvider, Disposable {
branches: branchMap,
remotes: remoteMap,
downstreams: downstreamMap,
worktreesByBranch: undefined,
stashes: stashes,
worktrees: worktrees,
worktreesByBranch: worktreesByBranch,
rows: [],
};
}
Expand Down Expand Up @@ -1629,7 +1640,9 @@ export class GitHubGitProvider implements GitProvider, Disposable {
branches: branchMap,
remotes: remoteMap,
downstreams: downstreamMap,
worktreesByBranch: undefined,
stashes: stashes,
worktrees: worktrees,
worktreesByBranch: worktreesByBranch,
rows: rows,
id: options?.ref,

Expand All @@ -1653,6 +1666,9 @@ export class GitHubGitProvider implements GitProvider, Disposable {
currentUser,
avatars,
ids,
stashes,
worktrees,
worktreesByBranch,
options,
);
},
Expand Down
26 changes: 25 additions & 1 deletion src/plus/webviews/graph/graphWebview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ import { gate } from '../../../system/decorators/gate';
import { debug, log } from '../../../system/decorators/log';
import type { Deferrable } from '../../../system/function';
import { debounce, disposableInterval } from '../../../system/function';
import { find, last, map } from '../../../system/iterable';
import { count, find, last, map } from '../../../system/iterable';
import { updateRecordValue } from '../../../system/object';
import {
getSettledValue,
Expand All @@ -118,6 +118,7 @@ import type { ConnectionStateChangeEvent } from '../../integrations/integrationS
import type {
BranchState,
DidChangeRefsVisibilityParams,
DidGetCountParams,
DidGetRowHoverParams,
DidSearchParams,
DoubleClickedParams,
Expand Down Expand Up @@ -190,6 +191,7 @@ import {
DidSearchNotification,
DoubleClickedCommandType,
EnsureRowRequest,
GetCountsRequest,
GetMissingAvatarsCommand,
GetMissingRefsMetadataCommand,
GetMoreRowsCommand,
Expand Down Expand Up @@ -679,6 +681,9 @@ export class GraphWebviewProvider implements WebviewProvider<State, State, Graph
case EnsureRowRequest.is(e):
void this.onEnsureRowRequest(EnsureRowRequest, e);
break;
case GetCountsRequest.is(e):
void this.onGetCounts(GetCountsRequest, e);
break;
case GetMissingAvatarsCommand.is(e):
void this.onGetMissingAvatars(e.params);
break;
Expand Down Expand Up @@ -720,6 +725,24 @@ export class GraphWebviewProvider implements WebviewProvider<State, State, Graph
break;
}
}
private async onGetCounts<T extends typeof GetCountsRequest>(requestType: T, msg: IpcCallMessageType<T>) {
let counts: DidGetCountParams;
if (this._graph != null) {
const tags = await this.container.git.getTags(this._graph.repoPath);
counts = {
branches: count(this._graph.branches?.values(), b => !b.remote),
remotes: this._graph.remotes.size,
stashes: this._graph.stashes?.size,
// Subtract the default worktree
worktrees: this._graph.worktrees != null ? this._graph.worktrees.length - 1 : undefined,
tags: tags.values.length,
};
} else {
counts = undefined;
}

void this.host.respond(requestType, msg, counts);
}

updateGraphConfig(params: UpdateGraphConfigurationParams) {
const config = this.getComponentConfig();
Expand Down Expand Up @@ -2161,6 +2184,7 @@ export class GraphWebviewProvider implements WebviewProvider<State, State, Graph
scrollMarkerTypes: this.getScrollMarkerTypes(),
showGhostRefsOnRowHover: configuration.get('graph.showGhostRefsOnRowHover'),
showRemoteNamesOnRefs: configuration.get('graph.showRemoteNames'),
sidebar: configuration.get('graph.sidebar.enabled') ?? true,
};
return config;
}
Expand Down
25 changes: 19 additions & 6 deletions src/plus/webviews/graph/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ export interface GraphComponentConfig {
scrollRowPadding?: number;
showGhostRefsOnRowHover?: boolean;
showRemoteNamesOnRefs?: boolean;
sidebar: boolean;
}

export interface GraphColumnConfig {
Expand Down Expand Up @@ -222,12 +223,6 @@ export type UpdateStateCallback = (

export const ChooseRepositoryCommand = new IpcCommand(scope, 'chooseRepository');

export interface ChooseRefParams {
alt: boolean;
}
export type DidChooseRefParams = { name: string; sha: string } | undefined;
export const ChooseRefRequest = new IpcRequest<ChooseRefParams, DidChooseRefParams>(scope, 'chooseRef');

export type DoubleClickedParams =
| {
type: 'ref';
Expand Down Expand Up @@ -307,6 +302,12 @@ export const UpdateSelectionCommand = new IpcCommand<UpdateSelectionParams>(scop

// REQUESTS

export interface ChooseRefParams {
alt: boolean;
}
export type DidChooseRefParams = { name: string; sha: string } | undefined;
export const ChooseRefRequest = new IpcRequest<ChooseRefParams, DidChooseRefParams>(scope, 'chooseRef');

export interface EnsureRowParams {
id: string;
select?: boolean;
Expand All @@ -317,6 +318,18 @@ export interface DidEnsureRowParams {
}
export const EnsureRowRequest = new IpcRequest<EnsureRowParams, DidEnsureRowParams>(scope, 'rows/ensure');

export interface GetCountParams {}
export type DidGetCountParams =
| {
branches: number;
remotes: number;
stashes?: number;
tags: number;
worktrees?: number;
}
| undefined;
export const GetCountsRequest = new IpcRequest<GetCountParams, DidGetCountParams>(scope, 'counts');

export type GetRowHoverParams = {
type: GitGraphRowType;
id: string;
Expand Down
20 changes: 16 additions & 4 deletions src/system/logger.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { LogInstanceNameFn } from './decorators/log';
import type { LogLevel } from './logger.constants';
import type { LogScope } from './logger.scope';
import { padOrTruncateEnd } from './string';

const enum OrderedLevel {
Off = 0,
Expand Down Expand Up @@ -87,7 +88,7 @@ export const Logger = new (class Logger {
}

if (this.isDebugging) {
console.log(this.timestamp, `[${this.provider!.name}]`, message ?? '', ...params);
console.log(`[${padOrTruncateEnd(this.provider!.name, 13)}]`, this.timestamp, message ?? '', ...params);
}

if (this.output == null || this.level < OrderedLevel.Debug) return;
Expand Down Expand Up @@ -118,9 +119,20 @@ export const Logger = new (class Logger {

if (this.isDebugging) {
if (ex != null) {
console.error(this.timestamp, `[${this.provider!.name}]`, message ?? '', ...params, ex);
console.error(
`[${padOrTruncateEnd(this.provider!.name, 13)}]`,
this.timestamp,
message ?? '',
...params,
ex,
);
} else {
console.error(this.timestamp, `[${this.provider!.name}]`, message ?? '', ...params);
console.error(
`[${padOrTruncateEnd(this.provider!.name, 13)}]`,
this.timestamp,
message ?? '',
...params,
);
}
}

Expand Down Expand Up @@ -149,7 +161,7 @@ export const Logger = new (class Logger {
}

if (this.isDebugging) {
console.log(this.timestamp, `[${this.provider!.name}]`, message ?? '', ...params);
console.log(`[${padOrTruncateEnd(this.provider!.name, 13)}]`, this.timestamp, message ?? '', ...params);
}

if (this.output == null || this.level < OrderedLevel.Info) return;
Expand Down
Loading
Loading