diff --git a/extensions/git/src/historyProvider.ts b/extensions/git/src/historyProvider.ts index e652ca6dd20..896c46851c4 100644 --- a/extensions/git/src/historyProvider.ts +++ b/extensions/git/src/historyProvider.ts @@ -45,6 +45,29 @@ function toSourceControlHistoryItemRef(ref: Ref): SourceControlHistoryItemRef { } } +function compareSourceControlHistoryItemRef(ref1: SourceControlHistoryItemRef, ref2: SourceControlHistoryItemRef): number { + const getOrder = (ref: SourceControlHistoryItemRef): number => { + if (ref.id.startsWith('refs/heads/')) { + return 1; + } else if (ref.id.startsWith('refs/remotes/')) { + return 2; + } else if (ref.id.startsWith('refs/tags/')) { + return 3; + } + + return 99; + }; + + const ref1Order = getOrder(ref1); + const ref2Order = getOrder(ref2); + + if (ref1Order !== ref2Order) { + return ref1Order - ref2Order; + } + + return ref1.name.localeCompare(ref2.name); +} + export class GitHistoryProvider implements SourceControlHistoryProvider, FileDecorationProvider, IDisposable { private readonly _onDidChangeDecorations = new EventEmitter(); readonly onDidChangeFileDecorations: Event = this._onDidChangeDecorations.event; @@ -334,14 +357,6 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec icon: new ThemeIcon('target') }); break; - case ref.startsWith('tag: refs/tags/'): - references.push({ - id: ref.substring('tag: '.length), - name: ref.substring('tag: refs/tags/'.length), - revision: commit.hash, - icon: new ThemeIcon('tag') - }); - break; case ref.startsWith('refs/heads/'): references.push({ id: ref, @@ -358,10 +373,18 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec icon: new ThemeIcon('cloud') }); break; + case ref.startsWith('tag: refs/tags/'): + references.push({ + id: ref.substring('tag: '.length), + name: ref.substring('tag: refs/tags/'.length), + revision: commit.hash, + icon: new ThemeIcon('tag') + }); + break; } } - return references; + return references.sort(compareSourceControlHistoryItemRef); } private async resolveHEADMergeBase(): Promise { diff --git a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts index 4a7459dadf7..4de2e1841eb 100644 --- a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts @@ -63,6 +63,7 @@ import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hover import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; +import { groupBy as groupBy2 } from '../../../../base/common/collections.js'; const PICK_REPOSITORY_ACTION_ID = 'workbench.scm.action.graph.pickRepository'; const PICK_HISTORY_ITEM_REFS_ACTION_ID = 'workbench.scm.action.graph.pickHistoryItemRefs'; @@ -364,37 +365,57 @@ class HistoryItemRenderer implements ITreeRenderer { const labelConfig = this._badgesConfig.read(reader); - - templateData.labelContainer.textContent = ''; const firstColoredRef = historyItem.references?.find(ref => ref.color); - for (const ref of historyItem.references ?? []) { - if (!ref.color && labelConfig === 'filter') { + templateData.labelContainer.textContent = ''; + + // Group history item references by color + const historyItemRefsByColor = groupBy2( + (historyItem.references ?? []), + ref => ref.color ? ref.color : ''); + + for (const [key, historyItemRefs] of Object.entries(historyItemRefsByColor)) { + // Skip badges with no color + if (key === '' && labelConfig !== 'all') { continue; } - if (ref.icon && ThemeIcon.isThemeIcon(ref.icon)) { - const elements = h('div.label', { - style: { - color: ref.color ? asCssVariable(historyItemHoverLabelForeground) : asCssVariable(foreground), - backgroundColor: ref.color ? asCssVariable(ref.color) : asCssVariable(historyItemHoverDefaultLabelBackground) - } - }, [ - h('div.icon@icon'), - h('div.description@description') - ]); + // Group history item references by icon + const historyItemRefByIconId = groupBy2(historyItemRefs, ref => ThemeIcon.isThemeIcon(ref.icon) ? ref.icon.id : ''); + for (const [key, historyItemRefs] of Object.entries(historyItemRefByIconId)) { + if (key === '' || historyItemRefs.length === 0) { + continue; + } - elements.icon.classList.add(...ThemeIcon.asClassNameArray(ref.icon)); - - elements.description.textContent = ref.name; - elements.description.style.display = ref === firstColoredRef ? '' : 'none'; - - append(templateData.labelContainer, elements.root); + this._renderBadge(historyItemRefs[0], historyItemRefs[0] === firstColoredRef, templateData); } } })); } + private _renderBadge(historyItemRef: ISCMHistoryItemRef, showDescription: boolean, templateData: HistoryItemTemplate): void { + if (!ThemeIcon.isThemeIcon(historyItemRef.icon)) { + return; + } + + const elements = h('div.label', { + style: { + color: historyItemRef.color ? asCssVariable(historyItemHoverLabelForeground) : asCssVariable(foreground), + backgroundColor: historyItemRef.color ? asCssVariable(historyItemRef.color) : asCssVariable(historyItemHoverDefaultLabelBackground) + } + }, [ + h('div.icon@icon'), + h('div.description@description') + ]); + + elements.icon.classList.add(...ThemeIcon.asClassNameArray(historyItemRef.icon)); + + elements.description.textContent = historyItemRef.name; + elements.description.style.display = showDescription ? '' : 'none'; + + append(templateData.labelContainer, elements.root); + } + private _getHoverActions(historyItem: ISCMHistoryItem) { return [ { diff --git a/src/vs/workbench/contrib/scm/browser/util.ts b/src/vs/workbench/contrib/scm/browser/util.ts index 36a03fef9d1..61a063c1931 100644 --- a/src/vs/workbench/contrib/scm/browser/util.ts +++ b/src/vs/workbench/contrib/scm/browser/util.ts @@ -162,7 +162,7 @@ export function compareHistoryItemRefs( currentHistoryItemRemoteRef?: ISCMHistoryItemRef, currentHistoryItemBaseRef?: ISCMHistoryItemRef ): number { - const getHistoryItemRefPriority = (ref: ISCMHistoryItemRef) => { + const getHistoryItemRefOrder = (ref: ISCMHistoryItemRef) => { if (ref.id === currentHistoryItemRef?.id) { return 1; } else if (ref.id === currentHistoryItemRemoteRef?.id) { @@ -176,13 +176,9 @@ export function compareHistoryItemRefs( return 99; }; - // Assign priority (current > remote > base > color > name) - const ref1Priority = getHistoryItemRefPriority(ref1); - const ref2Priority = getHistoryItemRefPriority(ref2); + // Assign order (current > remote > base > color) + const ref1Order = getHistoryItemRefOrder(ref1); + const ref2Order = getHistoryItemRefOrder(ref2); - if (ref1Priority !== ref2Priority) { - return ref1Priority - ref2Priority; - } else { - return ref1.name.localeCompare(ref2.name); - } + return ref1Order - ref2Order; }