Git - detect when HEAD is a tag (#170271)

* HEAD set when checking out a tag

* Remove more calls

* Remove tag references when in detached mode
This commit is contained in:
Ladislau Szomoru 2023-01-03 15:07:46 +01:00 committed by GitHub
parent 2c7fc7c978
commit 1d18dfc209
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 28 additions and 33 deletions

View file

@ -36,7 +36,8 @@ class CheckoutItem implements QuickPickItem {
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
const pullBeforeCheckout = config.get<boolean>('pullBeforeCheckout', false) === true;
await this.repository.checkout(this.ref.name, { ...opts, pullBeforeCheckout });
const treeish = opts?.detached ? this.ref.commit ?? this.ref.name : this.ref.name;
await this.repository.checkout(treeish, { ...opts, pullBeforeCheckout });
}
}
@ -69,7 +70,7 @@ class CheckoutRemoteHeadItem extends CheckoutItem {
}
if (opts?.detached) {
await this.repository.checkout(this.ref.name, opts);
await this.repository.checkout(this.ref.commit ?? this.ref.name, opts);
return;
}
@ -249,7 +250,7 @@ async function categorizeResourceByResolution(resources: Resource[]): Promise<{
return { merge, resolved, unresolved, deletionConflicts };
}
function createCheckoutItems(repository: Repository): CheckoutItem[] {
function createCheckoutItems(repository: Repository, detached = false): CheckoutItem[] {
const config = workspace.getConfiguration('git');
const checkoutTypeConfig = config.get<string | string[]>('checkoutType');
let checkoutTypes: string[];
@ -262,6 +263,11 @@ function createCheckoutItems(repository: Repository): CheckoutItem[] {
checkoutTypes = checkoutTypeConfig;
}
if (detached) {
// Remove tags when in detached mode
checkoutTypes = checkoutTypes.filter(t => t !== 'tags');
}
const processors = checkoutTypes.map(type => getCheckoutProcessor(repository, type))
.filter(p => !!p) as CheckoutProcessor[];
@ -2019,12 +2025,12 @@ export class CommandCenter {
picks.push(createBranch, createBranchFrom, checkoutDetached);
}
picks.push(...createCheckoutItems(repository));
picks.push(...createCheckoutItems(repository, opts?.detached));
const quickpick = window.createQuickPick();
quickpick.items = picks;
quickpick.placeholder = opts?.detached
? l10n.t('Select a branch or tag to checkout in detached mode')
? l10n.t('Select a branch to checkout in detached mode')
: l10n.t('Select a branch or tag to checkout');
quickpick.show();

View file

@ -2077,17 +2077,22 @@ export class Repository {
}
}
async getHEADBranch(): Promise<Branch | undefined> {
async getHEADRef(): Promise<Branch | undefined> {
let HEAD: Branch | undefined;
try {
HEAD = await this.getHEAD();
if (HEAD.name) {
try {
HEAD = await this.getBranch(HEAD.name);
} catch (err) {
// noop
// Branch
HEAD = await this.getBranch(HEAD.name);
} else if (HEAD.commit) {
// Tag || Commit
const tags = await this.getRefs({ pattern: 'refs/tags/*' });
const tag = tags.find(tag => tag.commit === HEAD!.commit);
if (tag) {
HEAD = { ...HEAD, name: tag.name, type: RefType.Tag };
}
}
} catch (err) {

View file

@ -8,7 +8,7 @@ import * as path from 'path';
import * as picomatch from 'picomatch';
import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands, Tab, TabInputTextDiff, TabInputNotebookDiff, RelativePattern, CancellationTokenSource, LogOutputChannel, LogLevel, CancellationError, l10n } from 'vscode';
import TelemetryReporter from '@vscode/extension-telemetry';
import { Branch, Change, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, CommitOptions, BranchQuery, FetchOptions } from './api/git';
import { Branch, Change, ForcePushMode, GitErrorCodes, LogOptions, Ref, Remote, Status, CommitOptions, BranchQuery, FetchOptions } from './api/git';
import { AutoFetcher } from './autofetch';
import { debounce, memoize, throttle } from './decorators';
import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions, PullOptions } from './git';
@ -667,13 +667,6 @@ export class Repository implements Disposable {
return HEAD.name;
}
const tag = this.refs.filter(iref => iref.type === RefType.Tag && iref.commit === HEAD.commit)[0];
const tagName = tag && tag.name;
if (tagName) {
return tagName;
}
return (HEAD.commit || '').substr(0, 8);
}
@ -1410,7 +1403,7 @@ export class Repository implements Disposable {
}
async checkout(treeish: string, opts?: { detached?: boolean; pullBeforeCheckout?: boolean }): Promise<void> {
const refLabel = this.checkoutRefLabel(treeish, opts?.detached);
const refLabel = opts?.detached ? treeish.substring(0, 8) : treeish;
await this.run(Operation.Checkout(refLabel),
async () => {
@ -1428,7 +1421,7 @@ export class Repository implements Disposable {
}
async checkoutTracking(treeish: string, opts: { detached?: boolean } = {}): Promise<void> {
const refLabel = this.checkoutRefLabel(treeish, opts?.detached);
const refLabel = opts.detached ? treeish.substring(0, 8) : treeish;
await this.run(Operation.CheckoutTracking(refLabel), () => this.repository.checkout(treeish, [], { ...opts, track: true }));
}
@ -1971,7 +1964,7 @@ export class Repository implements Disposable {
const [HEAD, remotes, submodules, rebaseCommit, mergeInProgress, commitTemplate] =
await Promise.all([
this.repository.getHEADBranch(),
this.repository.getHEADRef(),
this.repository.getRemotes(),
this.repository.getSubmodules(),
this.getRebaseCommit(),
@ -2284,9 +2277,7 @@ export class Repository implements Disposable {
return '';
}
const tag = this.refs.filter(iref => iref.type === RefType.Tag && iref.commit === HEAD.commit)[0];
const tagName = tag && tag.name;
const head = HEAD.name || tagName || (HEAD.commit || '').substr(0, 8);
const head = HEAD.name || (HEAD.commit || '').substr(0, 8);
return head
+ (this.workingTreeGroup.resourceStates.length + this.untrackedGroup.resourceStates.length > 0 ? '*' : '')
@ -2395,13 +2386,6 @@ export class Repository implements Disposable {
return true;
}
private checkoutRefLabel(treeish: string, detached?: boolean): string {
if (!detached) { return treeish; }
const ref = this.refs.filter(r => r.name === treeish);
return ref[0]?.commit?.substring(0, 8) ?? treeish;
}
public isBranchProtected(name = this.HEAD?.name ?? ''): boolean {
return this.isBranchProtectedMatcher ? this.isBranchProtectedMatcher(name) : false;
}

View file

@ -69,12 +69,12 @@ class CheckoutStatusBar {
}
// Branch
if (this.repository.HEAD?.name) {
if (this.repository.HEAD.type !== RefType.Tag) {
return this.repository.isBranchProtected() ? '$(lock)' : '$(git-branch)';
}
// Tag
if (this.repository.HEAD?.commit && this.repository.refs.filter(iref => iref.type === RefType.Tag && iref.commit === this.repository.HEAD!.commit).length) {
if (this.repository.HEAD.type === RefType.Tag) {
return '$(tag)';
}