import assert from "node:assert/strict"; import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { execFileSync } from "node:child_process"; import { fileURLToPath } from "node:url"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); const driftPath = path.join(root, "tests/golden/corrective/rehydration/M12-manifest-sha-drift.json"); const outputArgumentIndex = process.argv.indexOf("--out"); const outputRoot = path.resolve(root, outputArgumentIndex >= 0 ? process.argv[outputArgumentIndex + 1] : "tests/golden/corrective/rehydration/M12-git-history-snapshot"); const filesRoot = path.join(outputRoot, "files"); const drift = JSON.parse(fs.readFileSync(driftPath, "utf8")); const sha256 = (bytes) => crypto.createHash("sha256").update(bytes).digest("hex"); const run = (args) => execFileSync("git", args, { cwd: root, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 }); const uniqueEntries = new Map(); for (const entry of drift.entries) { const key = `${entry.path}\0${entry.originalSha256}`; if (!uniqueEntries.has(key)) uniqueEntries.set(key, entry); } function historicalBlob(pathname, expectedSha) { let objectListing = ""; try { objectListing = run(["rev-list", "--objects", "--all", "--", pathname]); } catch { return null; } const candidates = [...new Set(objectListing.split("\n").map((line) => line.split(" ")[0]).filter((id) => /^[0-9a-f]{40}$/u.test(id)))]; for (const blob of candidates) { let type; try { type = run(["cat-file", "-t", blob]).trim(); } catch { continue; } if (type !== "blob") continue; const bytes = execFileSync("git", ["cat-file", "blob", blob], { cwd: root, maxBuffer: 64 * 1024 * 1024 }); if (sha256(bytes) !== expectedSha) continue; let commits = []; try { commits = run(["log", "--all", "--format=%H", "--", pathname]).trim().split("\n").filter(Boolean); } catch { commits = []; } const commit = commits.find((candidate) => { try { return run(["rev-parse", `${candidate}:${pathname}`]).trim() === blob; } catch { return false; } }) ?? null; return { blob, commit, bytes }; } return null; } const matched = []; const unresolved = []; const pathPairCounts = new Map(); for (const entry of uniqueEntries.values()) pathPairCounts.set(entry.path, (pathPairCounts.get(entry.path) ?? 0) + 1); for (const entry of uniqueEntries.values()) { const pathname = entry.path; const result = historicalBlob(pathname, entry.originalSha256); if (!result) { unresolved.push({ path: pathname, originalSha256: entry.originalSha256, reason: "exact SHA-256 content was not found in Git history" }); continue; } const suffix = (pathPairCounts.get(pathname) ?? 0) > 1 ? `.${entry.originalSha256.slice(0, 16)}` : ""; const snapshotPath = path.join("files", `${pathname}${suffix}`); const destination = path.join(outputRoot, snapshotPath); fs.mkdirSync(path.dirname(destination), { recursive: true }); fs.writeFileSync(destination, result.bytes); matched.push({ path: pathname, snapshotPath, sha256: entry.originalSha256, bytes: result.bytes.length, gitBlob: result.blob, gitCommit: result.commit, }); } const repository = run(["remote", "get-url", "origin"]).trim(); const head = run(["rev-parse", "HEAD"]).trim(); const manifest = { schemaVersion: 1, mode: "IMMUTABLE_REPOSITORY", authorized: true, source: { kind: "GIT_HISTORY", repository, head, selection: "exact original SHA-256 content match; current worktree is never read", }, coverage: { requestedPaths: new Set([...uniqueEntries.values()].map((entry) => entry.path)).size, requestedPairs: uniqueEntries.size, matchedPaths: matched.length, unmatchedPaths: unresolved.length, unresolved, }, files: matched.sort((a, b) => a.path.localeCompare(b.path)), }; fs.mkdirSync(outputRoot, { recursive: true }); fs.writeFileSync(path.join(outputRoot, "snapshot-manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`, "utf8"); process.stdout.write(`m12-git-snapshot-ok requested=${uniqueEntries.size} matched=${matched.length} unmatched=${unresolved.length} output=${path.relative(root, outputRoot).replaceAll(path.sep, "/")}\n`);