github-desktop/app/git-info.ts

89 lines
2.7 KiB
TypeScript
Raw Normal View History

2018-01-17 14:06:32 +00:00
import * as Fs from 'fs'
import * as Path from 'path'
2017-11-20 01:27:05 +00:00
2018-01-09 18:17:44 +00:00
/**
* Attempt to find a ref in the .git/packed-refs file, which is often
* created by Git as part of cleaning up loose refs in the repository.
*
* Will return null if the packed-refs file is missing.
* Will throw an error if the entry is not found in the packed-refs file
*
2018-01-17 14:06:32 +00:00
* @param gitDir The path to the Git repository's .git directory
* @param ref A qualified git ref such as 'refs/heads/main'
2018-01-09 18:17:44 +00:00
*/
2018-01-17 14:06:32 +00:00
function readPackedRefsFile(gitDir: string, ref: string) {
const packedRefsPath = Path.join(gitDir, 'packed-refs')
try {
// eslint-disable-next-line no-sync
2018-01-17 14:06:32 +00:00
Fs.statSync(packedRefsPath)
} catch (err) {
2018-01-09 13:20:40 +00:00
// fail quietly if packed-refs not found
return null
}
// eslint-disable-next-line no-sync
2018-01-17 14:06:32 +00:00
const packedRefsContents = Fs.readFileSync(packedRefsPath, 'utf8')
// we need to build up the regex on the fly using the ref
const refRe = new RegExp('([a-f0-9]{40}) ' + ref)
const packedRefMatch = refRe.exec(packedRefsContents)
if (!packedRefMatch) {
throw new Error(`Could not find ref entry in .git/packed-refs file: ${ref}`)
}
return packedRefMatch[1]
}
2017-11-20 01:27:05 +00:00
/**
* Attempt to dereference the given ref without requiring a Git environment
* to be present. Note that this method will not be able to dereference packed
* refs but should suffice for simple refs like 'HEAD'.
*
* Will throw an error for unborn HEAD.
*
2018-01-17 14:06:32 +00:00
* @param gitDir The path to the Git repository's .git directory
* @param ref A qualified git ref such as 'HEAD' or 'refs/heads/main'
2018-01-17 14:06:32 +00:00
* @returns The ref SHA
2017-11-20 01:27:05 +00:00
*/
2018-01-17 14:06:32 +00:00
function revParse(gitDir: string, ref: string): string {
const refPath = Path.join(gitDir, ref)
try {
// eslint-disable-next-line no-sync
2018-01-17 14:06:32 +00:00
Fs.statSync(refPath)
} catch (err) {
2018-01-09 18:17:44 +00:00
const packedRefMatch = readPackedRefsFile(gitDir, ref)
if (packedRefMatch) {
return packedRefMatch
}
throw new Error(
`Could not de-reference HEAD to SHA, ref does not exist on disk: ${refPath}`
)
}
2017-11-20 01:27:05 +00:00
// eslint-disable-next-line no-sync
2018-01-17 14:06:32 +00:00
const refContents = Fs.readFileSync(refPath, 'utf8')
2017-11-20 01:27:05 +00:00
const refRe = /^([a-f0-9]{40})|(?:ref: (refs\/.*))$/m
const refMatch = refRe.exec(refContents)
if (!refMatch) {
throw new Error(
2017-12-12 05:21:47 +00:00
`Could not de-reference HEAD to SHA, invalid ref in ${refPath}: ${refContents}`
2017-11-20 01:27:05 +00:00
)
}
return refMatch[1] || revParse(gitDir, refMatch[2])
}
2018-01-17 14:06:32 +00:00
export function getSHA() {
2017-11-20 01:27:05 +00:00
// CircleCI does some funny stuff where HEAD points to an packed ref, but
// luckily it gives us the SHA we want in the environment.
2018-04-20 17:10:46 +00:00
const circleSHA = process.env.CIRCLE_SHA1
if (circleSHA != null) {
2017-11-20 01:27:05 +00:00
return circleSHA
}
2018-01-17 14:06:32 +00:00
return revParse(Path.resolve(__dirname, '../.git'), 'HEAD')
2017-11-20 01:27:05 +00:00
}