/** pgh clone <repo> [dest] — clone over gip (local materialized repo) with
* a legacy-URL fallback.
*
* gip-overlay path: the daemon materializes each repo's bare repo locally —
* refs via the per-repo autobase, objects via the overlay bundle chain — so
* the bytes are already on this host. We clone from that local bare repo
* instead of fetching over git-upload-pack across the swarm. (pgh runs on the
* daemon's host.) This is the read path that retires hyper-sync's upload-pack
* for full peers; receive-pack (push) is unchanged, and lightweight cloners
* with no local daemon still use the legacy URL fallback below.
*/
import fs from '#node:fs'
import { execFileSync } from '../_compat-exec.js'
const ALLOWED_SCHEMES = ['punch://', 'pear://', 'git+pear://', 'https://', 'http://', 'ssh://', 'git://', 'git@']
export const usage = `pgh clone — clone a daemon-served repo over gip
pgh clone <repo> [dest]
Clones from the daemon's locally-materialized bare repo (refs via the
per-repo autobase, objects via the overlay) when available, falling back
to the legacy clone URL otherwise.`
export async function run (client, args, opts = {}) {
const exec = opts.exec || execFileSync
const exists = opts.exists || fs.existsSync
const repo = args[0]
if (!repo) throw new Error('usage: pgh clone <repo> [dest]')
const dest = args[1]
const got = await client.call('get-repo', { name: repo })
if (!got) throw new Error('repo not found: ' + repo)
const info = got.info || {}
// ── gip path: clone from the locally-materialized bare repo ──────
if (info.path && exists(info.path)) {
console.log('Cloning ' + repo + ' from local gip repo ' + info.path)
exec('git', ['clone', '--', info.path, ...(dest ? [dest] : [])], { stdio: 'inherit' })
return null
}
// ── fallback: legacy URL clone (hyper-sync upload-pack / HTTP) ───
const url = got.punchUrl || info.httpUrl || info.cloneUrl
if (!url) {
throw new Error('repo not materialized locally — run `pgh repo join <invitation>` first (no clone URL available)')
}
// Validate scheme — the URL came over RPC (trusted on UDS, NOT over swarm).
// execFileSync's array form blocks shell injection, but a URL like
// `--upload-pack=…` would be parsed by git as a flag. The leading `--`
// below separates flags from positionals; the scheme check is defence in depth.
if (!ALLOWED_SCHEMES.some(s => url.startsWith(s))) {
throw new Error('refusing to clone from URL with unrecognised scheme: ' + url)
}
console.log('Cloning ' + repo + ' from ' + url)
exec('git', ['clone', '--', url, ...(dest ? [dest] : [])], { stdio: 'inherit' })
return null
}