SCM - more work related to history (#216641)

* Rename base to remote

* introduce base

* Remove resolveHistoryItemGroupBase

* 💄

* More 💄

* Remove file that is out of scope
This commit is contained in:
Ladislau Szomoru 2024-06-19 22:05:10 +02:00 committed by GitHub
parent 5c1b006c49
commit 856fba0b67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 58 additions and 60 deletions

View file

@ -1967,23 +1967,23 @@
{
"command": "git.pushRef",
"group": "navigation",
"when": "scmProvider == git && scmHistoryItemGroupHasUpstream"
"when": "scmProvider == git && scmHistoryItemGroupHasRemote"
},
{
"command": "git.publish",
"group": "navigation",
"when": "scmProvider == git && !scmHistoryItemGroupHasUpstream"
"when": "scmProvider == git && !scmHistoryItemGroupHasRemote"
}
],
"scm/outgoingChanges/context": [
{
"command": "git.pushRef",
"when": "scmProvider == git && scmHistoryItemGroupHasUpstream",
"when": "scmProvider == git && scmHistoryItemGroupHasRemote",
"group": "1_modification@1"
},
{
"command": "git.publish",
"when": "scmProvider == git && !scmHistoryItemGroupHasUpstream",
"when": "scmProvider == git && !scmHistoryItemGroupHasRemote",
"group": "1_modification@1"
}
],

View file

@ -220,16 +220,16 @@ class GitIncomingChangesFileDecorationProvider implements FileDecorationProvider
const historyProvider = this.repository.historyProvider;
const currentHistoryItemGroup = historyProvider.currentHistoryItemGroup;
if (!currentHistoryItemGroup?.base) {
if (!currentHistoryItemGroup?.remote) {
return [];
}
const ancestor = await historyProvider.resolveHistoryItemGroupCommonAncestor(currentHistoryItemGroup.id, currentHistoryItemGroup.base.id);
const ancestor = await historyProvider.resolveHistoryItemGroupCommonAncestor(currentHistoryItemGroup.id, currentHistoryItemGroup.remote.id);
if (!ancestor) {
return [];
}
const changes = await this.repository.diffBetween(ancestor.id, currentHistoryItemGroup.base.id);
const changes = await this.repository.diffBetween(ancestor.id, currentHistoryItemGroup.remote.id);
return changes;
} catch (err) {
return [];

View file

@ -1167,6 +1167,7 @@ export class Repository {
if (options?.refNames) {
args.push('--topo-order');
args.push('--decorate=full');
args.push(...options.refNames);
}

View file

@ -22,6 +22,8 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
readonly onDidChangeFileDecorations: Event<Uri[]> = this._onDidChangeDecorations.event;
private _HEAD: Branch | undefined;
private _HEADMergeBase: Branch | undefined;
private _currentHistoryItemGroup: SourceControlHistoryItemGroup | undefined;
get currentHistoryItemGroup(): SourceControlHistoryItemGroup | undefined { return this._currentHistoryItemGroup; }
set currentHistoryItemGroup(value: SourceControlHistoryItemGroup | undefined) {
@ -44,18 +46,25 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
this.logger.trace('GitHistoryProvider:onDidRunGitStatus - HEAD:', JSON.stringify(this._HEAD));
this.logger.trace('GitHistoryProvider:onDidRunGitStatus - repository.HEAD:', JSON.stringify(this.repository.HEAD));
// Get the merge base of the current history item group
const mergeBase = await this.resolveHEADMergeBase();
// Check if HEAD has changed
if (!force &&
this._HEAD?.name === this.repository.HEAD?.name &&
this._HEAD?.commit === this.repository.HEAD?.commit &&
this._HEAD?.upstream?.name === this.repository.HEAD?.upstream?.name &&
this._HEAD?.upstream?.remote === this.repository.HEAD?.upstream?.remote &&
this._HEAD?.upstream?.commit === this.repository.HEAD?.upstream?.commit) {
this._HEAD?.upstream?.commit === this.repository.HEAD?.upstream?.commit &&
this._HEADMergeBase?.name === mergeBase?.name &&
this._HEADMergeBase?.remote === mergeBase?.remote &&
this._HEADMergeBase?.commit === mergeBase?.commit) {
this.logger.trace('GitHistoryProvider:onDidRunGitStatus - HEAD has not changed');
return;
}
this._HEAD = this.repository.HEAD;
this._HEADMergeBase = mergeBase;
// Check if HEAD does not support incoming/outgoing (detached commit, tag)
if (!this.repository.HEAD?.name || !this.repository.HEAD?.commit || this.repository.HEAD.type === RefType.Tag) {
@ -68,11 +77,14 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
this.currentHistoryItemGroup = {
id: `refs/heads/${this.repository.HEAD.name ?? ''}`,
name: this.repository.HEAD.name ?? '',
base: this.repository.HEAD.upstream ?
{
id: `refs/remotes/${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`,
name: `${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`,
} : undefined
remote: this.repository.HEAD.upstream ? {
id: `refs/remotes/${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`,
name: `${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`,
} : undefined,
base: mergeBase ? {
id: `refs/remotes/${mergeBase.remote}/${mergeBase.name}`,
name: `${mergeBase.remote}/${mergeBase.name}`,
} : undefined
};
this.logger.trace(`GitHistoryProvider:onDidRunGitStatus - currentHistoryItemGroup (${force}): ${JSON.stringify(this.currentHistoryItemGroup)}`);
@ -201,23 +213,9 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
return historyItemChanges;
}
async resolveHistoryItemGroupBase(historyItemGroupId: string): Promise<SourceControlHistoryItemGroup | undefined> {
// Base (config -> reflog -> default)
const remoteBranch = await this.repository.getBranchBase(historyItemGroupId);
if (!remoteBranch?.remote || !remoteBranch?.name || !remoteBranch?.commit || remoteBranch?.type !== RefType.RemoteHead) {
this.logger.info(`GitHistoryProvider:resolveHistoryItemGroupBase - Failed to resolve history item group base for '${historyItemGroupId}'`);
return undefined;
}
return {
id: `refs/remotes/${remoteBranch.remote}/${remoteBranch.name}`,
name: `${remoteBranch.remote}/${remoteBranch.name}`,
};
}
async resolveHistoryItemGroupCommonAncestor(historyItemId1: string, historyItemId2: string | undefined): Promise<{ id: string; ahead: number; behind: number } | undefined> {
if (!historyItemId2) {
const upstreamRef = await this.resolveHistoryItemGroupUpstreamOrBase(historyItemId1);
const upstreamRef = await this.resolveHistoryItemGroupMergeBase(historyItemId1);
if (!upstreamRef) {
this.logger.info(`GitHistoryProvider:resolveHistoryItemGroupCommonAncestor - Failed to resolve history item group base for '${historyItemId1}'`);
return undefined;
@ -268,14 +266,14 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
const labels: SourceControlHistoryItemLabel[] = [];
for (const label of commit.refNames) {
if (label === 'origin/HEAD' || label === '') {
if (label === 'refs/remotes/origin/HEAD' || label === '') {
continue;
}
if (label.startsWith('HEAD -> ')) {
labels.push(
{
title: label.substring(8),
title: label.substring(19),
icon: new ThemeIcon('git-branch')
}
);
@ -283,19 +281,19 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
}
if (refNames.has(label)) {
if (label.startsWith('tag: ')) {
if (label.startsWith('refs/tags/')) {
labels.push({
title: label.substring(5),
title: label.substring(10),
icon: new ThemeIcon('tag')
});
} else if (label.startsWith('origin/')) {
} else if (label.startsWith('refs/remotes/')) {
labels.push({
title: label,
title: label.substring(13),
icon: new ThemeIcon('cloud')
});
} else {
labels.push({
title: label,
title: label.substring(11),
icon: new ThemeIcon('git-branch')
});
}
@ -305,7 +303,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
return labels;
}
private async resolveHistoryItemGroupUpstreamOrBase(historyItemId: string): Promise<UpstreamRef | undefined> {
private async resolveHistoryItemGroupMergeBase(historyItemId: string): Promise<UpstreamRef | undefined> {
try {
// Upstream
const branch = await this.repository.getBranch(historyItemId);
@ -333,6 +331,15 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
return undefined;
}
private async resolveHEADMergeBase(): Promise<Branch | undefined> {
if (this.repository.HEAD?.type !== RefType.Head || !this.repository.HEAD?.name) {
return undefined;
}
const mergeBase = await this.repository.getBranchBase(this.repository.HEAD.name);
return mergeBase;
}
dispose(): void {
dispose(this.disposables);
}

View file

@ -177,10 +177,6 @@ class MainThreadSCMHistoryProvider implements ISCMHistoryProvider {
constructor(private readonly proxy: ExtHostSCMShape, private readonly handle: number) { }
async resolveHistoryItemGroupBase(historyItemGroupId: string): Promise<ISCMHistoryItemGroup | undefined> {
return this.proxy.$resolveHistoryItemGroupBase(this.handle, historyItemGroupId, CancellationToken.None);
}
async resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string | undefined): Promise<{ id: string; ahead: number; behind: number } | undefined> {
return this.proxy.$resolveHistoryItemGroupCommonAncestor(this.handle, historyItemGroupId1, historyItemGroupId2, CancellationToken.None);
}

View file

@ -1515,7 +1515,8 @@ export type SCMRawResourceSplices = [
export interface SCMHistoryItemGroupDto {
readonly id: string;
readonly name: string;
readonly base?: Omit<SCMHistoryItemGroupDto, 'base'>;
readonly base?: Omit<Omit<SCMHistoryItemGroupDto, 'base'>, 'remote'>;
readonly remote?: Omit<Omit<SCMHistoryItemGroupDto, 'base'>, 'remote'>;
}
export interface SCMHistoryItemDto {
@ -2329,7 +2330,6 @@ export interface ExtHostSCMShape {
$provideHistoryItems2(sourceControlHandle: number, options: any, token: CancellationToken): Promise<SCMHistoryItemDto[] | undefined>;
$provideHistoryItemSummary(sourceControlHandle: number, historyItemId: string, historyItemParentId: string | undefined, token: CancellationToken): Promise<SCMHistoryItemDto | undefined>;
$provideHistoryItemChanges(sourceControlHandle: number, historyItemId: string, historyItemParentId: string | undefined, token: CancellationToken): Promise<SCMHistoryItemChangeDto[] | undefined>;
$resolveHistoryItemGroupBase(sourceControlHandle: number, historyItemGroupId: string, token: CancellationToken): Promise<SCMHistoryItemGroupDto | undefined>;
$resolveHistoryItemGroupCommonAncestor(sourceControlHandle: number, historyItemGroupId1: string, historyItemGroupId2: string | undefined, token: CancellationToken): Promise<{ id: string; ahead: number; behind: number } | undefined>;
}

View file

@ -11,7 +11,7 @@ import { debounce } from 'vs/base/common/decorators';
import { DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { asPromise } from 'vs/base/common/async';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, ICommandDto, MainThreadTelemetryShape, SCMGroupFeatures, SCMHistoryItemDto, SCMHistoryItemChangeDto, SCMHistoryItemGroupDto } from './extHost.protocol';
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, ICommandDto, MainThreadTelemetryShape, SCMGroupFeatures, SCMHistoryItemDto, SCMHistoryItemChangeDto } from './extHost.protocol';
import { sortedDiff, equals } from 'vs/base/common/arrays';
import { comparePaths } from 'vs/base/common/comparers';
import type * as vscode from 'vscode';
@ -970,11 +970,6 @@ export class ExtHostSCM implements ExtHostSCMShape {
return Promise.resolve(undefined);
}
async $resolveHistoryItemGroupBase(sourceControlHandle: number, historyItemGroupId: string, token: CancellationToken): Promise<SCMHistoryItemGroupDto | undefined> {
const historyProvider = this._sourceControls.get(sourceControlHandle)?.historyProvider;
return await historyProvider?.resolveHistoryItemGroupBase(historyItemGroupId, token) ?? undefined;
}
async $resolveHistoryItemGroupCommonAncestor(sourceControlHandle: number, historyItemGroupId1: string, historyItemGroupId2: string | undefined, token: CancellationToken): Promise<{ id: string; ahead: number; behind: number } | undefined> {
const historyProvider = this._sourceControls.get(sourceControlHandle)?.historyProvider;
return await historyProvider?.resolveHistoryItemGroupCommonAncestor(historyItemGroupId1, historyItemGroupId2, token) ?? undefined;

View file

@ -306,7 +306,7 @@ export class SCMHistoryProviderMenus implements ISCMHistoryProviderMenus, IDispo
private getOutgoingHistoryItemGroupMenu(menuId: MenuId, historyItemGroup: SCMHistoryItemGroupTreeElement): IMenu {
const contextKeyService = this.contextKeyService.createOverlay([
['scmHistoryItemGroupHasUpstream', !!historyItemGroup.repository.provider.historyProvider?.currentHistoryItemGroup?.base],
['scmHistoryItemGroupHasRemote', !!historyItemGroup.repository.provider.historyProvider?.currentHistoryItemGroup?.remote],
]);
return this.menuService.createMenu(menuId, contextKeyService);

View file

@ -3855,16 +3855,16 @@ class SCMTreeDataSource implements IAsyncDataSource<ISCMViewService, TreeElement
if (!incomingHistoryItemGroup && !outgoingHistoryItemGroup) {
// Common ancestor, ahead, behind
const ancestor = await historyProvider.resolveHistoryItemGroupCommonAncestor(currentHistoryItemGroup.id, currentHistoryItemGroup.base?.id);
const ancestor = await historyProvider.resolveHistoryItemGroupCommonAncestor(currentHistoryItemGroup.id, currentHistoryItemGroup.remote?.id);
if (!ancestor) {
return [];
}
// Only show "Incoming" node if there is a base branch
incomingHistoryItemGroup = currentHistoryItemGroup.base ? {
id: currentHistoryItemGroup.base.id,
label: currentHistoryItemGroup.base.name,
ariaLabel: localize('incomingChangesAriaLabel', "Incoming changes from {0}", currentHistoryItemGroup.base.name),
incomingHistoryItemGroup = currentHistoryItemGroup.remote ? {
id: currentHistoryItemGroup.remote.id,
label: currentHistoryItemGroup.remote.name,
ariaLabel: localize('incomingChangesAriaLabel', "Incoming changes from {0}", currentHistoryItemGroup.remote.name),
icon: Codicon.arrowCircleDown,
direction: 'incoming',
ancestor: ancestor.id,
@ -3973,11 +3973,10 @@ class SCMTreeDataSource implements IAsyncDataSource<ISCMViewService, TreeElement
const historyItemsMap = historyProviderCacheEntry.historyItems2;
if (!historyItemsElement) {
const historyItemGroupBase = await historyProvider.resolveHistoryItemGroupBase(currentHistoryItemGroup.id);
const historyItemGroupIds = [
currentHistoryItemGroup.id,
...currentHistoryItemGroup.remote ? [currentHistoryItemGroup.remote.id] : [],
...currentHistoryItemGroup.base ? [currentHistoryItemGroup.base.id] : [],
...historyItemGroupBase ? [historyItemGroupBase.id] : []
];
historyItemsElement = await historyProvider.provideHistoryItems2({ historyItemGroupIds }) ?? [];

View file

@ -29,7 +29,6 @@ export interface ISCMHistoryProvider {
provideHistoryItems2(options: ISCMHistoryOptions): Promise<ISCMHistoryItem[] | undefined>;
provideHistoryItemSummary(historyItemId: string, historyItemParentId: string | undefined): Promise<ISCMHistoryItem | undefined>;
provideHistoryItemChanges(historyItemId: string, historyItemParentId: string | undefined): Promise<ISCMHistoryItemChange[] | undefined>;
resolveHistoryItemGroupBase(historyItemGroupId: string): Promise<ISCMHistoryItemGroup | undefined>;
resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string | undefined): Promise<{ id: string; ahead: number; behind: number } | undefined>;
}
@ -50,7 +49,8 @@ export interface ISCMHistoryOptions {
export interface ISCMHistoryItemGroup {
readonly id: string;
readonly name: string;
readonly base?: Omit<ISCMHistoryItemGroup, 'base'>;
readonly base?: Omit<Omit<ISCMHistoryItemGroup, 'base'>, 'remote'>;
readonly remote?: Omit<Omit<ISCMHistoryItemGroup, 'base'>, 'remote'>;
}
export interface SCMHistoryItemGroupTreeElement {

View file

@ -29,7 +29,6 @@ declare module 'vscode' {
provideHistoryItemSummary?(historyItemId: string, historyItemParentId: string | undefined, token: CancellationToken): ProviderResult<SourceControlHistoryItem>;
provideHistoryItemChanges(historyItemId: string, historyItemParentId: string | undefined, token: CancellationToken): ProviderResult<SourceControlHistoryItemChange[]>;
resolveHistoryItemGroupBase(historyItemGroupId: string, token: CancellationToken): ProviderResult<SourceControlHistoryItemGroup>;
resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string | undefined, token: CancellationToken): ProviderResult<{ id: string; ahead: number; behind: number }>;
}
@ -42,7 +41,8 @@ declare module 'vscode' {
export interface SourceControlHistoryItemGroup {
readonly id: string;
readonly name: string;
readonly base?: Omit<SourceControlHistoryItemGroup, 'base'>;
readonly base?: Omit<Omit<SourceControlHistoryItemGroup, 'base'>, 'remote'>;
readonly remote?: Omit<Omit<SourceControlHistoryItemGroup, 'base'>, 'remote'>;
}
export interface SourceControlHistoryItemStatistics {