/**
* 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 }))
})
})
}