OverviewFilesCommitsIssuesDocsReleasesPeers

pgh / lib / commands / _git-helpers.js

64 lines · javascript ref:
/**
 * Shared helpers for git-passthrough commands (push, pull, commit, etc.).
 */

import { spawn } from '#node:child_process'
import { execFileSync } from '../_compat-exec.js'

const PEAR_SCHEMES = ['pear://', 'punch://', 'git+pear://']

/**
 * List remotes with pear-style URLs. Returns [{ name, url }, …].
 */
export function listPearRemotes () {
  let out
  try {
    out = execFileSync('git', ['config', '--get-regexp', 'remote\\..*\\.url'], {
      encoding: 'utf8',
      stdio: ['ignore', 'pipe', 'pipe']
    })
  } catch (err) {
    // exit 1 = no matching keys (no remotes configured) — not an error
    if (err.status === 1) return []
    throw new Error('not in a git repo (or git config failed): ' + err.message)
  }
  const remotes = []
  for (const line of out.split('\n')) {
    const m = line.match(/^remote\.([^.]+)\.url\s+(.+)$/)
    if (!m) continue
    const [, name, url] = m
    const isPear = PEAR_SCHEMES.some(s => url.startsWith(s)) || name === 'pear'
    if (isPear) remotes.push({ name, url })
  }
  return remotes
}

/**
 * Pick a pear remote from the current repo. Throws if none found.
 * Prefers a remote literally named `pear`, else returns the first.
 */
export function detectPearRemote () {
  const pears = listPearRemotes()
  if (pears.length === 0) {
    throw new Error('no pear remote found in this repo. Add one with `git remote add pear <pear-url-or-punch-url>`')
  }
  const named = pears.find(r => r.name === 'pear')
  return (named || pears[0]).name
}

/**
 * Run `git <args...>` with stdio inherited so the user sees progress
 * and can answer prompts. Resolves on exit 0; rejects otherwise.
 * Uses array form (no shell interpretation) — safe with arbitrary args.
 */
export function runGit (gitArgs) {
  return new Promise((resolve, reject) => {
    const proc = spawn('git', gitArgs, { stdio: 'inherit' })
    proc.on('error', reject)
    proc.on('exit', (code) => {
      if (code === 0) resolve()
      else reject(Object.assign(new Error('git ' + gitArgs[0] + ' exit ' + code), { exitCode: code }))
    })
  })
}