Git - only show "Incoming changes" node for upstream (#202836)

* Git - only show "Incoming changes" node for upstream

* 💄 Revert some of the changes now that base is limited to upstream

* Fixed a bug

* 💄 More clean-up
This commit is contained in:
Ladislau Szomoru 2024-01-19 12:56:34 +01:00 committed by GitHub
parent 600e284b84
commit 945bbd01aa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 59 additions and 65 deletions

View file

@ -12,14 +12,6 @@ import { Branch, RefType, UpstreamRef } from './api/git';
import { emojify, ensureEmojis } from './emoji';
import { Operation } from './operation';
function isBranchRefEqual(brach1: Branch | undefined, branch2: Branch | undefined): boolean {
return brach1?.name === branch2?.name && brach1?.commit === branch2?.commit;
}
function isUpstreamRefEqual(upstream1: UpstreamRef | undefined, upstream2: UpstreamRef | undefined): boolean {
return upstream1?.name === upstream2?.name && upstream1?.remote === upstream2?.remote && upstream1?.commit === upstream2?.commit;
}
export class GitHistoryProvider implements SourceControlHistoryProvider, FileDecorationProvider, IDisposable {
private readonly _onDidChangeCurrentHistoryItemGroup = new EventEmitter<void>();
@ -29,15 +21,10 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
readonly onDidChangeFileDecorations: Event<Uri[]> = this._onDidChangeDecorations.event;
private _HEAD: Branch | undefined;
private _HEADBase: UpstreamRef | undefined;
private _currentHistoryItemGroup: SourceControlHistoryItemGroup | undefined;
get currentHistoryItemGroup(): SourceControlHistoryItemGroup | undefined { return this._currentHistoryItemGroup; }
set currentHistoryItemGroup(value: SourceControlHistoryItemGroup | undefined) {
if (this._currentHistoryItemGroup === undefined && value === undefined) {
return;
}
this._currentHistoryItemGroup = value;
this._onDidChangeCurrentHistoryItemGroup.fire();
}
@ -54,31 +41,30 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
}
private async onDidRunGitStatus(): Promise<void> {
// 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) {
this._HEAD = this._HEADBase = undefined;
this.currentHistoryItemGroup = undefined;
return;
}
// Resolve HEAD base
const HEADBase = await this.resolveHEADBase(this.repository.HEAD);
// Check if HEAD or HEADBase has changed
if (isBranchRefEqual(this._HEAD, this.repository.HEAD) && isUpstreamRefEqual(this._HEADBase, HEADBase)) {
// Check if HEAD has changed
if (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) {
return;
}
this._HEAD = this.repository.HEAD;
this._HEADBase = HEADBase;
// Check if HEAD does not support incoming/outgoing (detached commit, tag)
if (!this._HEAD?.name || !this._HEAD?.commit || this._HEAD.type === RefType.Tag) {
this.currentHistoryItemGroup = undefined;
return;
}
this.currentHistoryItemGroup = {
id: `refs/heads/${this._HEAD.name ?? ''}`,
label: this._HEAD.name ?? '',
base: this._HEADBase ?
base: this._HEAD.upstream ?
{
id: `refs/remotes/${this._HEADBase.remote}/${this._HEADBase.name}`,
label: `${this._HEADBase.remote}/${this._HEADBase.name}`,
id: `refs/remotes/${this._HEAD.upstream.remote}/${this._HEAD.upstream.name}`,
label: `${this._HEAD.upstream.remote}/${this._HEAD.upstream.name}`,
} : undefined
};
}
@ -165,17 +151,26 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
return historyItemChanges;
}
async resolveHistoryItemGroupCommonAncestor(refId1: string, refId2: string): Promise<{ id: string; ahead: number; behind: number } | undefined> {
const ancestor = await this.repository.getMergeBase(refId1, refId2);
async resolveHistoryItemGroupCommonAncestor(historyItemId1: string, historyItemId2: string | undefined): Promise<{ id: string; ahead: number; behind: number } | undefined> {
if (!historyItemId2) {
const upstreamRef = await this.resolveHistoryItemGroupBase(historyItemId1);
if (!upstreamRef) {
return undefined;
}
historyItemId2 = `refs/remotes/${upstreamRef.remote}/${upstreamRef.name}`;
}
const ancestor = await this.repository.getMergeBase(historyItemId1, historyItemId2);
if (!ancestor) {
return undefined;
}
try {
const commitCount = await this.repository.getCommitCount(`${refId1}...${refId2}`);
const commitCount = await this.repository.getCommitCount(`${historyItemId1}...${historyItemId2}`);
return { id: ancestor, ahead: commitCount.ahead, behind: commitCount.behind };
} catch (err) {
this.logger.error(`Failed to get ahead/behind for '${refId1}...${refId2}': ${err.message}`);
this.logger.error(`Failed to get ahead/behind for '${historyItemId1}...${historyItemId2}': ${err.message}`);
}
return undefined;
@ -185,27 +180,22 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
return this.historyItemDecorations.get(uri.toString());
}
private async resolveHEADBase(HEAD: Branch): Promise<UpstreamRef | undefined> {
// Upstream
if (HEAD.upstream) {
return HEAD.upstream;
private async resolveHistoryItemGroupBase(historyItemId: string): Promise<UpstreamRef | undefined> {
try {
const remoteBranch = await this.repository.getBranchBase(historyItemId);
if (!remoteBranch?.remote || !remoteBranch?.name || !remoteBranch?.commit || remoteBranch?.type !== RefType.RemoteHead) {
return undefined;
}
return {
name: remoteBranch.name,
remote: remoteBranch.remote,
commit: remoteBranch.commit
};
}
catch (err) {
this.logger.error(`Failed to get branch base for '${historyItemId}': ${err.message}`);
}
// try {
// const remoteBranch = await this.repository.getBranchBase(HEAD.name ?? '');
// if (!remoteBranch?.remote || !remoteBranch?.name || !remoteBranch?.commit || remoteBranch?.type !== RefType.RemoteHead) {
// return undefined;
// }
// return {
// name: remoteBranch.name,
// remote: remoteBranch.remote,
// commit: remoteBranch.commit
// };
// }
// catch (err) {
// this.logger.error(`Failed to get branch base for '${HEAD.name}': ${err.message}`);
// }
return undefined;
}

View file

@ -140,7 +140,7 @@ class MainThreadSCMHistoryProvider implements ISCMHistoryProvider {
constructor(private readonly proxy: ExtHostSCMShape, private readonly handle: number) { }
async resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string): Promise<{ id: string; ahead: number; behind: number } | undefined> {
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

@ -2239,7 +2239,7 @@ export interface ExtHostSCMShape {
$provideHistoryItems(sourceControlHandle: number, historyItemGroupId: string, 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>;
$resolveHistoryItemGroupCommonAncestor(sourceControlHandle: number, historyItemGroupId1: string, historyItemGroupId2: string, token: CancellationToken): Promise<{ id: string; ahead: number; behind: number } | undefined>;
$resolveHistoryItemGroupCommonAncestor(sourceControlHandle: number, historyItemGroupId1: string, historyItemGroupId2: string | undefined, token: CancellationToken): Promise<{ id: string; ahead: number; behind: number } | undefined>;
}
export interface ExtHostQuickDiffShape {

View file

@ -960,7 +960,7 @@ export class ExtHostSCM implements ExtHostSCMShape {
return Promise.resolve(undefined);
}
async $resolveHistoryItemGroupCommonAncestor(sourceControlHandle: number, historyItemGroupId1: string, historyItemGroupId2: string, token: CancellationToken): Promise<{ id: string; ahead: number; behind: number } | 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

@ -3291,7 +3291,7 @@ class SCMTreeDataSource implements IAsyncDataSource<ISCMViewService, TreeElement
}
async getChildren(inputOrElement: ISCMViewService | TreeElement): Promise<Iterable<TreeElement>> {
const { alwaysShowRepositories, showActionButton, showIncomingChanges, showOutgoingChanges } = this.getConfiguration();
const { alwaysShowRepositories, showActionButton } = this.getConfiguration();
const repositoryCount = this.scmViewService.visibleRepositories.length;
if (isSCMViewService(inputOrElement) && (repositoryCount > 1 || alwaysShowRepositories)) {
@ -3331,10 +3331,13 @@ class SCMTreeDataSource implements IAsyncDataSource<ISCMViewService, TreeElement
let label = localize('syncSeparatorHeader', "Incoming/Outgoing");
let ariaLabel = localize('syncSeparatorHeaderAriaLabel', "Incoming and outgoing changes");
if (showIncomingChanges !== 'never' && showOutgoingChanges === 'never') {
const incomingHistoryItems = historyItemGroups.find(g => g.direction === 'incoming');
const outgoingHistoryItems = historyItemGroups.find(g => g.direction === 'outgoing');
if (incomingHistoryItems && !outgoingHistoryItems) {
label = localize('syncIncomingSeparatorHeader', "Incoming");
ariaLabel = localize('syncIncomingSeparatorHeaderAriaLabel', "Incoming changes");
} else if (showIncomingChanges === 'never' && showOutgoingChanges !== 'never') {
} else if (!incomingHistoryItems && outgoingHistoryItems) {
label = localize('syncOutgoingSeparatorHeader', "Outgoing");
ariaLabel = localize('syncOutgoingSeparatorHeaderAriaLabel', "Outgoing changes");
}
@ -3384,7 +3387,7 @@ class SCMTreeDataSource implements IAsyncDataSource<ISCMViewService, TreeElement
const historyProvider = scmProvider.historyProvider;
const currentHistoryItemGroup = historyProvider?.currentHistoryItemGroup;
if (!historyProvider || !currentHistoryItemGroup || !currentHistoryItemGroup.base || (showIncomingChanges === 'never' && showOutgoingChanges === 'never')) {
if (!historyProvider || !currentHistoryItemGroup || (showIncomingChanges === 'never' && showOutgoingChanges === 'never')) {
return [];
}
@ -3396,12 +3399,13 @@ 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.base?.id);
if (!ancestor) {
return [];
}
incomingHistoryItemGroup = {
// Only show "Incoming" node if there is a base branch
incomingHistoryItemGroup = currentHistoryItemGroup.base ? {
id: currentHistoryItemGroup.base.id,
label: currentHistoryItemGroup.base.label,
ariaLabel: localize('incomingChangesAriaLabel', "Incoming changes from {0}", currentHistoryItemGroup.base.label),
@ -3411,7 +3415,7 @@ class SCMTreeDataSource implements IAsyncDataSource<ISCMViewService, TreeElement
count: ancestor.behind,
repository: element,
type: 'historyItemGroup'
};
} : undefined;
outgoingHistoryItemGroup = {
id: currentHistoryItemGroup.id,

View file

@ -26,7 +26,7 @@ export interface ISCMHistoryProvider {
provideHistoryItems(historyItemGroupId: string, options: ISCMHistoryOptions): Promise<ISCMHistoryItem[] | undefined>;
provideHistoryItemSummary(historyItemId: string, historyItemParentId: string | undefined): Promise<ISCMHistoryItem | undefined>;
provideHistoryItemChanges(historyItemId: string, historyItemParentId: string | undefined): Promise<ISCMHistoryItemChange[] | undefined>;
resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string): Promise<{ id: string; ahead: number; behind: number } | undefined>;
resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string | undefined): Promise<{ id: string; ahead: number; behind: number } | undefined>;
}
export interface ISCMHistoryProviderCacheEntry {

View file

@ -28,7 +28,7 @@ declare module 'vscode' {
provideHistoryItemSummary?(historyItemId: string, historyItemParentId: string | undefined, token: CancellationToken): ProviderResult<SourceControlHistoryItem>;
provideHistoryItemChanges(historyItemId: string, historyItemParentId: string | undefined, token: CancellationToken): ProviderResult<SourceControlHistoryItemChange[]>;
resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string, token: CancellationToken): ProviderResult<{ id: string; ahead: number; behind: number }>;
resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string | undefined, token: CancellationToken): ProviderResult<{ id: string; ahead: number; behind: number }>;
}
export interface SourceControlHistoryOptions {