'use strict'

const fs = require('fs')
const path = require('path')

function packedRefsParse(gitDir, ref) {
  const packedRefsPath = path.join(gitDir, 'packed-refs')

  try {
    // eslint-disable-next-line no-sync
    fs.statSync(packedRefsPath)
  } catch (err) {
    // fail quietly if packed-refs not found
    return null
  }

  // eslint-disable-next-line no-sync
  const packedRefsContents = fs.readFileSync(packedRefsPath)

  // 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]
}

/**
 * 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.
 *
 * @param {string} gitDir The path to the Git repository's .git directory
 * @param {string} ref    A qualified git ref such as 'HEAD' or 'refs/heads/master'
 */
function revParse(gitDir, ref) {
  const refPath = path.join(gitDir, ref)

  try {
    // eslint-disable-next-line no-sync
    fs.statSync(refPath)
  } catch (err) {
    const packedRefMatch = packedRefsParse(gitDir, ref)
    if (packedRefMatch) {
      return packedRefMatch
    }

    throw new Error(
      `Could not de-reference HEAD to SHA, ref does not exist on disk: ${refPath}`
    )
  }
  // eslint-disable-next-line no-sync
  const refContents = fs.readFileSync(refPath)
  const refRe = /^([a-f0-9]{40})|(?:ref: (refs\/.*))$/m
  const refMatch = refRe.exec(refContents)

  if (!refMatch) {
    throw new Error(
      `Could not de-reference HEAD to SHA, invalid ref in ${refPath}: ${refContents}`
    )
  }

  return refMatch[1] || revParse(gitDir, refMatch[2])
}

function getSHA() {
  // CircleCI does some funny stuff where HEAD points to an packed ref, but
  // luckily it gives us the SHA we want in the environment.
  const circleSHA = process.env.CIRCLE_SHA1
  if (circleSHA) {
    return circleSHA
  }

  return revParse(path.resolve(__dirname, '../.git'), 'HEAD')
}

module.exports = {
  getSHA,
}

'use strict'

const fs = require('fs')
const path = require('path')

function packedRefsParse(gitDir, ref) {
  const packedRefsPath = path.join(gitDir, 'packed-refs')

  try {
    // by performing a `stat` before reading the file contents,
    // we can confirm the file exists on disk or try something else

    // eslint-disable-next-line no-sync
    fs.statSync(packedRefsPath)
  } catch (err) {
    // fail quietly if packed-refs not found
    return null
  }

  // eslint-disable-next-line no-sync
  const packedRefsContents = fs.readFileSync(packedRefsPath)

  // 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]
}

/**
 * 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.
 *
 * @param {string} gitDir The path to the Git repository's .git directory
 * @param {string} ref    A qualified git ref such as 'HEAD' or 'refs/heads/master'
 */
function revParse(gitDir, ref) {
  const refPath = path.join(gitDir, ref)

  try {
    // by performing a `stat` before reading the file contents,
    // we can confirm the file exists on disk or try something else

    // eslint-disable-next-line no-sync
    fs.statSync(refPath)
  } catch (err) {
    const packedRefMatch = packedRefsParse(gitDir, ref)
    if (packedRefMatch) {
      return packedRefMatch
    }

    throw new Error(
      `Could not de-reference HEAD to SHA, ref does not exist on disk: ${refPath}`
    )
  }
  // eslint-disable-next-line no-sync
  const refContents = fs.readFileSync(refPath)
  const refRe = /^([a-f0-9]{40})|(?:ref: (refs\/.*))$/m
  const refMatch = refRe.exec(refContents)

  if (!refMatch) {
    throw new Error(
      `Could not de-reference HEAD to SHA, invalid ref in ${refPath}: ${refContents}`
    )
  }

  return refMatch[1] || revParse(gitDir, refMatch[2])
}

function getSHA() {
  // CircleCI does some funny stuff where HEAD points to an packed ref, but
  // luckily it gives us the SHA we want in the environment.
  const circleSHA = process.env.CIRCLE_SHA1
  if (circleSHA) {
    return circleSHA
  }

  return revParse(path.resolve(__dirname, '../.git'), 'HEAD')
}

module.exports = {
  getSHA,
}

add note about why we stat