diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 46d58ce6d90cd..eaf4dc7416314 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -18,6 +18,7 @@ import { detectEncoding } from './encoding'; import { Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, BranchQuery } from './api/git'; import * as byline from 'byline'; import { StringDecoder } from 'string_decoder'; +import { OutputChannelLogger } from './log'; import TelemetryReporter from '@vscode/extension-telemetry'; // https://github.com/microsoft/vscode/issues/65693 @@ -400,8 +401,8 @@ export class Git { return Versions.compare(Versions.fromString(this.version), Versions.fromString(version)); } - open(repository: string, dotGit: { path: string; commonPath?: string }): Repository { - return new Repository(this, repository, dotGit); + open(repository: string, dotGit: { path: string; commonPath?: string }, outputChannelLogger: OutputChannelLogger): Repository { + return new Repository(this, repository, dotGit, outputChannelLogger); } async init(repository: string): Promise { @@ -913,7 +914,8 @@ export class Repository { constructor( private _git: Git, private repositoryRoot: string, - readonly dotGit: { path: string; commonPath?: string } + readonly dotGit: { path: string; commonPath?: string }, + private outputChannelLogger: OutputChannelLogger ) { } get git(): Git { @@ -1996,6 +1998,16 @@ export class Repository { async getHEAD(): Promise { try { + // Attempt to parse the HEAD file + const result = await this.getHEADFS(); + return result; + } + catch (err) { + this.outputChannelLogger.logWarning(err.message); + } + + try { + // Fallback to using git to determine HEAD const result = await this.exec(['symbolic-ref', '--short', 'HEAD']); if (!result.stdout) { @@ -2003,15 +2015,35 @@ export class Repository { } return { name: result.stdout.trim(), commit: undefined, type: RefType.Head }; - } catch (err) { - const result = await this.exec(['rev-parse', 'HEAD']); + } + catch (err) { } - if (!result.stdout) { - throw new Error('Error parsing HEAD'); - } + // Detached HEAD + const result = await this.exec(['rev-parse', 'HEAD']); - return { name: undefined, commit: result.stdout.trim(), type: RefType.Head }; + if (!result.stdout) { + throw new Error('Error parsing HEAD'); } + + return { name: undefined, commit: result.stdout.trim(), type: RefType.Head }; + } + + async getHEADFS(): Promise { + const raw = await fs.readFile(path.join(this.dotGit.commonPath ?? this.dotGit.path, 'HEAD'), 'utf8'); + + // Branch + const branchMatch = raw.match(/^ref: refs\/heads\/(?.*)$/m); + if (branchMatch?.groups?.name) { + return { name: branchMatch.groups.name, commit: undefined, type: RefType.Head }; + } + + // Detached + const commitMatch = raw.match(/^(?[0-9a-f]{40})$/m); + if (commitMatch?.groups?.commit) { + return { name: undefined, commit: commitMatch.groups.commit, type: RefType.Head }; + } + + throw new Error(`Unable to parse HEAD file. HEAD file contents: ${raw}.`); } async findTrackingBranches(upstreamBranch: string): Promise { diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 1914f1fb13402..d25289c5f3f12 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -387,7 +387,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPostCommitCommand } const dotGit = await this.git.getRepositoryDotGit(repositoryRoot); - const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this, this, this.globalState, this.outputChannelLogger, this.telemetryReporter); + const repository = new Repository(this.git.open(repositoryRoot, dotGit, this.outputChannelLogger), this, this, this, this.globalState, this.outputChannelLogger, this.telemetryReporter); this.open(repository); repository.status(); // do not await this, we want SCM to know about the repo asap