|
|
@ -51,7 +51,7 @@ import type { RepositoryChangeEvent, RepositoryFileSystemChangeEvent } from '../ |
|
|
|
import { Repository, RepositoryChange, RepositoryChangeComparisonMode } from '../../../git/models/repository'; |
|
|
|
import type { GitSearch } from '../../../git/search'; |
|
|
|
import { getSearchQueryComparisonKey } from '../../../git/search'; |
|
|
|
import type { StoredGraphExcludedRef, StoredGraphFilters, StoredGraphIncludeOnlyRef } from '../../../storage'; |
|
|
|
import type { StoredGraphFilters, StoredGraphIncludeOnlyRef } from '../../../storage'; |
|
|
|
import { executeActionCommand, executeCommand, executeCoreGitCommand, registerCommand } from '../../../system/command'; |
|
|
|
import { gate } from '../../../system/decorators/gate'; |
|
|
|
import { debug } from '../../../system/decorators/log'; |
|
|
@ -91,7 +91,6 @@ import type { |
|
|
|
GraphExcludeTypes, |
|
|
|
GraphHostingServiceType, |
|
|
|
GraphIncludeOnlyRef, |
|
|
|
GraphIncludeOnlyRefs, |
|
|
|
GraphMissingRefsMetadataType, |
|
|
|
GraphPullRequestMetadata, |
|
|
|
GraphRefMetadata, |
|
|
@ -612,7 +611,7 @@ export class GraphWebview extends WebviewBase { |
|
|
|
} |
|
|
|
|
|
|
|
private onRefsVisibilityChanged(e: UpdateRefsVisibilityParams) { |
|
|
|
this.updateExcludedRefs(e.refs, e.visible); |
|
|
|
this.updateExcludedRefs(this._graph, e.refs, e.visible); |
|
|
|
} |
|
|
|
|
|
|
|
private onDoubleClickRef(e: DoubleClickedRefParams) { |
|
|
@ -1286,88 +1285,56 @@ export class GraphWebview extends WebviewBase { |
|
|
|
private getExcludedTypes(graph: GitGraph | undefined): GraphExcludeTypes | undefined { |
|
|
|
if (graph == null) return undefined; |
|
|
|
|
|
|
|
return this.getRepoFilters(graph)?.excludeTypes; |
|
|
|
return this.getFiltersByRepo(graph)?.excludeTypes; |
|
|
|
} |
|
|
|
|
|
|
|
private getExcludedRefs(graph: GitGraph | undefined): Record<string, GraphExcludedRef> | undefined { |
|
|
|
// TODO: Migrate from graph:hiddenRefs to graph:filters[repoPath].excludeRefs
|
|
|
|
return this.filterExcludedRefs(this.container.storage.getWorkspace('graph:hiddenRefs'), graph); |
|
|
|
} |
|
|
|
|
|
|
|
private getIncludeOnlyRefs(graph: GitGraph | undefined): Record<string, GraphIncludeOnlyRef> | undefined { |
|
|
|
if (graph == null) return undefined; |
|
|
|
|
|
|
|
const storedFilters = this.getRepoFilters(graph); |
|
|
|
const storedIncludeOnlyRefs = storedFilters?.includeOnlyRefs; |
|
|
|
if (storedIncludeOnlyRefs == null || Object.keys(storedIncludeOnlyRefs).length === 0) return undefined; |
|
|
|
let filtersByRepo: Record<string, StoredGraphFilters> | undefined; |
|
|
|
|
|
|
|
const includeRemotes = !(storedFilters?.excludeTypes?.remotes ?? false); |
|
|
|
const storedHiddenRefs = this.container.storage.getWorkspace('graph:hiddenRefs'); |
|
|
|
if (storedHiddenRefs != null && Object.keys(storedHiddenRefs).length !== 0) { |
|
|
|
// Migrate hidden refs to exclude refs
|
|
|
|
filtersByRepo = this.container.storage.getWorkspace('graph:filtersByRepo') ?? {}; |
|
|
|
|
|
|
|
const includeOnlyRefs: Record<string, StoredGraphIncludeOnlyRef> = {}; |
|
|
|
for (const id in storedHiddenRefs) { |
|
|
|
const repoPath = getRepoPathFromBranchOrTagId(id); |
|
|
|
|
|
|
|
for (const [key, value] of Object.entries(storedIncludeOnlyRefs)) { |
|
|
|
let branch; |
|
|
|
if (value.id === 'HEAD') { |
|
|
|
branch = find(graph.branches.values(), b => b.current); |
|
|
|
if (branch == null) continue; |
|
|
|
|
|
|
|
includeOnlyRefs[key] = { ...value, id: branch.id, name: branch.name }; |
|
|
|
} else { |
|
|
|
includeOnlyRefs[key] = value; |
|
|
|
filtersByRepo[repoPath] = updateRecordValue( |
|
|
|
filtersByRepo[repoPath]?.excludeRefs, |
|
|
|
'excludeRefs', |
|
|
|
storedHiddenRefs[id], |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
// Add the upstream branches for any local branches if there are any and we aren't excluding them
|
|
|
|
if (includeRemotes && value.type === 'head') { |
|
|
|
branch = branch ?? graph.branches.get(value.name); |
|
|
|
if (branch?.upstream != null && !branch.upstream.missing) { |
|
|
|
const id = getBranchId(graph.repoPath, true, branch.upstream.name); |
|
|
|
includeOnlyRefs[id] = { |
|
|
|
id: id, |
|
|
|
type: 'remote', |
|
|
|
name: getBranchNameWithoutRemote(branch.upstream.name), |
|
|
|
owner: getRemoteNameFromBranchName(branch.upstream.name), |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
void this.container.storage.storeWorkspace('graph:filtersByRepo', filtersByRepo); |
|
|
|
void this.container.storage.deleteWorkspace('graph:hiddenRefs'); |
|
|
|
} |
|
|
|
|
|
|
|
return includeOnlyRefs; |
|
|
|
} |
|
|
|
|
|
|
|
private getRepoFilters(graph: GitGraph | undefined): StoredGraphFilters | undefined { |
|
|
|
if (graph == null) return undefined; |
|
|
|
|
|
|
|
const filters = this.container.storage.getWorkspace('graph:filters'); |
|
|
|
return filters?.[graph.repoPath]; |
|
|
|
} |
|
|
|
|
|
|
|
private filterExcludedRefs( |
|
|
|
excludeRefs: Record<string, StoredGraphExcludedRef> | undefined, |
|
|
|
graph: GitGraph | undefined, |
|
|
|
): GraphExcludeRefs | undefined { |
|
|
|
if (excludeRefs == null || graph == null) return undefined; |
|
|
|
const storedExcludeRefs = (filtersByRepo?.[graph.repoPath] ?? this.getFiltersByRepo(graph))?.excludeRefs; |
|
|
|
if (storedExcludeRefs == null || Object.keys(storedExcludeRefs).length === 0) return undefined; |
|
|
|
|
|
|
|
const useAvatars = configuration.get('graph.avatars', undefined, true); |
|
|
|
|
|
|
|
const filteredRefs: GraphExcludeRefs = {}; |
|
|
|
|
|
|
|
for (const id in excludeRefs) { |
|
|
|
if (getRepoPathFromBranchOrTagId(id) === graph.repoPath) { |
|
|
|
const ref: GraphExcludedRef = { ...excludeRefs[id] }; |
|
|
|
if (ref.type === 'remote' && ref.owner) { |
|
|
|
const remote = graph.remotes.get(ref.owner); |
|
|
|
if (remote != null) { |
|
|
|
ref.avatarUrl = ( |
|
|
|
(useAvatars ? remote.provider?.avatarUri : undefined) ?? |
|
|
|
getRemoteIconUri(this.container, remote, this._panel!.webview.asWebviewUri.bind(this)) |
|
|
|
)?.toString(true); |
|
|
|
} |
|
|
|
const excludeRefs: GraphExcludeRefs = {}; |
|
|
|
|
|
|
|
for (const id in storedExcludeRefs) { |
|
|
|
const ref: GraphExcludedRef = { ...storedExcludeRefs[id] }; |
|
|
|
if (ref.type === 'remote' && ref.owner) { |
|
|
|
const remote = graph.remotes.get(ref.owner); |
|
|
|
if (remote != null) { |
|
|
|
ref.avatarUrl = ( |
|
|
|
(useAvatars ? remote.provider?.avatarUri : undefined) ?? |
|
|
|
getRemoteIconUri(this.container, remote, this._panel!.webview.asWebviewUri.bind(this)) |
|
|
|
)?.toString(true); |
|
|
|
} |
|
|
|
filteredRefs[id] = ref; |
|
|
|
} |
|
|
|
|
|
|
|
excludeRefs[id] = ref; |
|
|
|
} |
|
|
|
|
|
|
|
return filteredRefs; |
|
|
|
// For v13, we return directly the hidden refs without validating them
|
|
|
|
|
|
|
|
// This validation has too much performance impact. So we decided to comment those lines
|
|
|
|
// for v13 and have it as tech debt to solve after we launch.
|
|
|
@ -1379,57 +1346,73 @@ export class GraphWebview extends WebviewBase { |
|
|
|
|
|
|
|
// const [hiddenBranches, hiddenTags] = await Promise.all([
|
|
|
|
// this.repository.getBranches({
|
|
|
|
// filter: b => !b.current && hiddenRefs[b.id] != undefined,
|
|
|
|
// filter: b => !b.current && excludeRefs[b.id] != undefined,
|
|
|
|
// }),
|
|
|
|
// this.repository.getTags({
|
|
|
|
// filter: t => hiddenRefs[t.id] != undefined,
|
|
|
|
// filter: t => excludeRefs[t.id] != undefined,
|
|
|
|
// }),
|
|
|
|
// ]);
|
|
|
|
|
|
|
|
// const filteredHiddenRefsById: GraphHiddenRefs = {};
|
|
|
|
|
|
|
|
// for (const hiddenBranch of hiddenBranches.values) {
|
|
|
|
// filteredHiddenRefsById[hiddenBranch.id] = hiddenRefs[hiddenBranch.id];
|
|
|
|
// filteredHiddenRefsById[hiddenBranch.id] = excludeRefs[hiddenBranch.id];
|
|
|
|
// }
|
|
|
|
|
|
|
|
// for (const hiddenTag of hiddenTags.values) {
|
|
|
|
// filteredHiddenRefsById[hiddenTag.id] = hiddenRefs[hiddenTag.id];
|
|
|
|
// filteredHiddenRefsById[hiddenTag.id] = excludeRefs[hiddenTag.id];
|
|
|
|
// }
|
|
|
|
|
|
|
|
// return filteredHiddenRefsById;
|
|
|
|
|
|
|
|
// For v13, we return directly the hidden refs without validating them
|
|
|
|
// return excludeRefs;
|
|
|
|
return excludeRefs; |
|
|
|
} |
|
|
|
|
|
|
|
// TODO: Use this in getIncludeOnlyRefs once we are actually updating the value in storage (UX is hooked up)
|
|
|
|
private filterIncludeOnlyRefs( |
|
|
|
includeOnlyRefs: Record<string, StoredGraphIncludeOnlyRef> | undefined, |
|
|
|
graph: GitGraph | undefined, |
|
|
|
): GraphIncludeOnlyRefs | undefined { |
|
|
|
if (includeOnlyRefs == null || graph == null) return undefined; |
|
|
|
private getIncludeOnlyRefs(graph: GitGraph | undefined): Record<string, GraphIncludeOnlyRef> | undefined { |
|
|
|
if (graph == null) return undefined; |
|
|
|
|
|
|
|
const useAvatars = configuration.get('graph.avatars', undefined, true); |
|
|
|
const storedFilters = this.getFiltersByRepo(graph); |
|
|
|
const storedIncludeOnlyRefs = storedFilters?.includeOnlyRefs; |
|
|
|
if (storedIncludeOnlyRefs == null || Object.keys(storedIncludeOnlyRefs).length === 0) return undefined; |
|
|
|
|
|
|
|
const filteredRefs: GraphIncludeOnlyRefs = {}; |
|
|
|
|
|
|
|
for (const id in includeOnlyRefs) { |
|
|
|
if (getRepoPathFromBranchOrTagId(id) === graph.repoPath) { |
|
|
|
const ref: GraphIncludeOnlyRef = { ...includeOnlyRefs[id] }; |
|
|
|
if (ref.type === 'remote' && ref.owner) { |
|
|
|
const remote = graph.remotes.get(ref.owner); |
|
|
|
if (remote != null) { |
|
|
|
ref.avatarUrl = ( |
|
|
|
(useAvatars ? remote.provider?.avatarUri : undefined) ?? |
|
|
|
getRemoteIconUri(this.container, remote, this._panel!.webview.asWebviewUri.bind(this)) |
|
|
|
)?.toString(true); |
|
|
|
} |
|
|
|
const includeRemotes = !(storedFilters?.excludeTypes?.remotes ?? false); |
|
|
|
|
|
|
|
const includeOnlyRefs: Record<string, StoredGraphIncludeOnlyRef> = {}; |
|
|
|
|
|
|
|
for (const [key, value] of Object.entries(storedIncludeOnlyRefs)) { |
|
|
|
let branch; |
|
|
|
if (value.id === 'HEAD') { |
|
|
|
branch = find(graph.branches.values(), b => b.current); |
|
|
|
if (branch == null) continue; |
|
|
|
|
|
|
|
includeOnlyRefs[key] = { ...value, id: branch.id, name: branch.name }; |
|
|
|
} else { |
|
|
|
includeOnlyRefs[key] = value; |
|
|
|
} |
|
|
|
|
|
|
|
// Add the upstream branches for any local branches if there are any and we aren't excluding them
|
|
|
|
if (includeRemotes && value.type === 'head') { |
|
|
|
branch = branch ?? graph.branches.get(value.name); |
|
|
|
if (branch?.upstream != null && !branch.upstream.missing) { |
|
|
|
const id = getBranchId(graph.repoPath, true, branch.upstream.name); |
|
|
|
includeOnlyRefs[id] = { |
|
|
|
id: id, |
|
|
|
type: 'remote', |
|
|
|
name: getBranchNameWithoutRemote(branch.upstream.name), |
|
|
|
owner: getRemoteNameFromBranchName(branch.upstream.name), |
|
|
|
}; |
|
|
|
} |
|
|
|
filteredRefs[id] = ref; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return filteredRefs; |
|
|
|
return includeOnlyRefs; |
|
|
|
} |
|
|
|
|
|
|
|
private getFiltersByRepo(graph: GitGraph | undefined): StoredGraphFilters | undefined { |
|
|
|
if (graph == null) return undefined; |
|
|
|
|
|
|
|
const filters = this.container.storage.getWorkspace('graph:filtersByRepo'); |
|
|
|
return filters?.[graph.repoPath]; |
|
|
|
} |
|
|
|
|
|
|
|
private getColumnSettings(columns: Record<GraphColumnName, GraphColumnConfig> | undefined): GraphColumnsSettings { |
|
|
@ -1613,72 +1596,64 @@ export class GraphWebview extends WebviewBase { |
|
|
|
void this.notifyDidChangeColumns(); |
|
|
|
} |
|
|
|
|
|
|
|
private updateExcludedRefs(refs: GraphExcludedRef[], visible: boolean) { |
|
|
|
let storedExcludedRefs = this.container.storage.getWorkspace('graph:hiddenRefs'); |
|
|
|
private updateExcludedRefs(graph: GitGraph | undefined, refs: GraphExcludedRef[], visible: boolean) { |
|
|
|
if (refs == null || refs.length === 0) return; |
|
|
|
|
|
|
|
let storedExcludeRefs: StoredGraphFilters['excludeRefs'] = this.getFiltersByRepo(graph)?.excludeRefs ?? {}; |
|
|
|
for (const ref of refs) { |
|
|
|
storedExcludedRefs = updateRecordValue( |
|
|
|
storedExcludedRefs, |
|
|
|
storedExcludeRefs = updateRecordValue( |
|
|
|
storedExcludeRefs, |
|
|
|
ref.id, |
|
|
|
visible ? undefined : { id: ref.id, type: ref.type, name: ref.name, owner: ref.owner }, |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
void this.container.storage.storeWorkspace('graph:hiddenRefs', storedExcludedRefs); |
|
|
|
void this.updateFiltersByRepo(graph, { excludeRefs: storedExcludeRefs }); |
|
|
|
void this.notifyDidChangeRefsVisibility(); |
|
|
|
} |
|
|
|
|
|
|
|
private updateRepoFilters(graph: GitGraph | undefined, filters: StoredGraphFilters) { |
|
|
|
private updateFiltersByRepo(graph: GitGraph | undefined, updates: Partial<StoredGraphFilters>) { |
|
|
|
if (graph == null) throw new Error('Cannot save repository filters since Graph is undefined'); |
|
|
|
|
|
|
|
const filtersByRepo = this.container.storage.getWorkspace('graph:filters'); |
|
|
|
const filtersByRepo = this.container.storage.getWorkspace('graph:filtersByRepo'); |
|
|
|
return this.container.storage.storeWorkspace( |
|
|
|
'graph:filters', |
|
|
|
updateRecordValue(filtersByRepo, graph.repoPath, filters), |
|
|
|
'graph:filtersByRepo', |
|
|
|
updateRecordValue(filtersByRepo, graph.repoPath, { ...filtersByRepo?.[graph.repoPath], ...updates }), |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
private updateIncludeOnlyRefs(graph: GitGraph | undefined, refs: GraphIncludeOnlyRef[] | undefined) { |
|
|
|
let storedFilters = this.getRepoFilters(graph); |
|
|
|
let storedIncludeOnlyRefs: StoredGraphFilters['includeOnlyRefs']; |
|
|
|
|
|
|
|
if (refs == null || refs.length === 0) { |
|
|
|
if (storedFilters?.includeOnlyRefs == null) return; |
|
|
|
if (this.getFiltersByRepo(graph)?.includeOnlyRefs == null) return; |
|
|
|
|
|
|
|
storedFilters.includeOnlyRefs = undefined; |
|
|
|
storedIncludeOnlyRefs = undefined; |
|
|
|
} else { |
|
|
|
if (storedFilters == null) { |
|
|
|
storedFilters = {}; |
|
|
|
} |
|
|
|
|
|
|
|
let storedIncludeOnlyRefs = storedFilters.includeOnlyRefs ?? {}; |
|
|
|
storedIncludeOnlyRefs = {}; |
|
|
|
for (const ref of refs) { |
|
|
|
storedIncludeOnlyRefs = updateRecordValue(storedIncludeOnlyRefs, ref.id, { |
|
|
|
storedIncludeOnlyRefs[ref.id] = { |
|
|
|
id: ref.id, |
|
|
|
type: ref.type, |
|
|
|
name: ref.name, |
|
|
|
owner: ref.owner, |
|
|
|
}); |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
storedFilters.includeOnlyRefs = storedIncludeOnlyRefs; |
|
|
|
} |
|
|
|
|
|
|
|
void this.updateRepoFilters(graph, storedFilters); |
|
|
|
void this.updateFiltersByRepo(graph, { includeOnlyRefs: storedIncludeOnlyRefs }); |
|
|
|
void this.notifyDidChangeRefsVisibility(); |
|
|
|
} |
|
|
|
|
|
|
|
private updateExcludedType(graph: GitGraph | undefined, { key, value }: UpdateExcludeTypeParams) { |
|
|
|
let storedFilters = this.getRepoFilters(graph); |
|
|
|
|
|
|
|
const storedExcludedTypes = storedFilters?.excludeTypes; |
|
|
|
if ((storedExcludedTypes == null || Object.keys(storedExcludedTypes).length === 0) && value === false) { |
|
|
|
let excludeTypes = this.getFiltersByRepo(graph)?.excludeTypes; |
|
|
|
if ((excludeTypes == null || Object.keys(excludeTypes).length === 0) && value === false) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (storedFilters == null) { |
|
|
|
storedFilters = {}; |
|
|
|
} |
|
|
|
storedFilters.excludeTypes = updateRecordValue(storedExcludedTypes, key, value); |
|
|
|
excludeTypes = updateRecordValue(excludeTypes, key, value); |
|
|
|
|
|
|
|
void this.updateRepoFilters(graph, storedFilters); |
|
|
|
void this.updateFiltersByRepo(graph, { excludeTypes: excludeTypes }); |
|
|
|
void this.notifyDidChangeRefsVisibility(); |
|
|
|
} |
|
|
|
|
|
|
@ -1976,6 +1951,7 @@ export class GraphWebview extends WebviewBase { |
|
|
|
|
|
|
|
if (refs != null) { |
|
|
|
this.updateExcludedRefs( |
|
|
|
this._graph, |
|
|
|
refs.map(r => { |
|
|
|
const remoteBranch = r.refType === 'branch' && r.remote; |
|
|
|
return { |
|
|
|