import { mkdtempSync, rmSync } from "node:fs"; import os from "node:os"; import path from "node:path"; import { $ as _$ } from "bun"; import { MAX_BRANCH_CACHE, MAX_REF_LIST, MAX_TAG_CACHE, paths, REF_CACHE_TTL_MS, } from "../constants.ts"; export const gitEnv = { ...process.env, LC_ALL: "C", LANG: "C", GIT_CONFIG_GLOBAL: "/dev/null", GIT_CONFIG_SYSTEM: "/dev/null", GIT_CONFIG_COUNT: "0", GIT_ASKPASS: "echo", GIT_TERMINAL_PROMPT: "0", }; const $ = _$.env(gitEnv); // Per-repo mutex: prevents concurrent git write operations on the same repo // (e.g. two patches being merged simultaneously, which would corrupt the index). const repoWriteLocks = new Map>(); // Short-lived caches for ref lists — these change only on push/branch ops. const branchCache = new Map(); const tagCache = new Map(); export function invalidateRefCache(name: string): void { branchCache.delete(name); tagCache.delete(name); } async function withRepoLock(name: string, fn: () => Promise): Promise { const prev = repoWriteLocks.get(name) ?? Promise.resolve(); let unlock!: () => void; repoWriteLocks.set( name, prev.then( () => new Promise((res) => { unlock = res; }), ), ); await prev; try { return await fn(); } finally { unlock(); } } export function repoPath(name: string): string { return path.join(paths.REPOS_DIR, `${name}.git`); } // Log an unexpected git failure. Many git calls legitimately fail for benign // reasons (a ref that doesn't exist yet, an empty repo), so callers still // swallow the error and return an empty result — but we surface it here so a // corrupted repo, permission problem, or missing binary isn't completely // invisible. function logGitError(op: string, name: string, err: unknown): void { console.error(`[git] ${op} failed for ${name}:`, err); } // Create a private, uniquely-named temp directory (mode 0700, created // atomically by the OS) and remove it afterward. Replaces predictable // /tmp/hf-*-