Open History to see the last commit
1import { spawn } from "node:child_process";
2import { constants } from "node:fs";
3import { access, copyFile, mkdir, readFile, readdir, realpath, rm } from "node:fs/promises";
4import { availableParallelism } from "node:os";
5import { basename, dirname, resolve } from "node:path";
6import { setTimeout as sleep } from "node:timers/promises";
7
8// Git hooks export repository-specific variables. Do not pass those through to
9// commands operating on distilled or a different Alchemy worktree.
10const env = { ...process.env };
11for (const name of (await run("git", ["rev-parse", "--local-env-vars"], {})).stdout
12 .trim()
13 .split("\n")) {
14 delete env[name];
15}
16
17// Resolves with the exit status and output; rejects only when the command
18// cannot start.
19function run(command, args, { cwd, input, stderr = "pipe" } = {}) {
20 return new Promise((done, fail) => {
21 const child = spawn(command, args, { cwd, env, stdio: ["pipe", "pipe", stderr] });
22 const stdout = [];
23 const errors = [];
24 child.stdout.on("data", (chunk) => stdout.push(chunk));
25 child.stderr?.on("data", (chunk) => errors.push(chunk));
26 child.on("error", fail);
27 child.on("close", (status) =>
28 done({
29 status,
30 stdout: Buffer.concat(stdout).toString("utf8"),
31 stderr: Buffer.concat(errors).toString("utf8"),
32 }),
33 );
34 child.stdin.end(input);
35 });
36}
37
38async function git(args, cwd, input) {
39 const result = await run("git", args, { cwd, input, stderr: "inherit" });
40 if (result.status !== 0) throw new Error(`Command failed: git ${args.join(" ")}`);
41 return result.stdout.trim();
42}
43
44// Parallel checkouts (several `git worktree add` at once) run this hook
45// concurrently against the same shared config files. Retry briefly when another
46// hook holds a lock instead of failing the checkout.
47async function gitLocked(args, cwd) {
48 for (let attempt = 1; ; attempt++) {
49 const result = await run("git", args, { cwd });
50 if (result.status === 0) return result.stdout.trim();
51 const locked = /could not lock|Unable to create '.*\.lock'/.test(result.stderr);
52 if (!locked || attempt === 50) {
53 process.stderr.write(result.stderr);
54 throw new Error(`Command failed: git ${args.join(" ")}`);
55 }
56 await sleep(100);
57 }
58}
59
60// Skip the write, and its lock, when the value is already set.
61async function setConfig(file, key, value, cwd) {
62 if ((await tryGit(["config", "--file", file, "--get", key], cwd)) === value) return;
63 await gitLocked(["config", "--file", file, key, value], cwd);
64}
65
66async function tryGit(args, cwd) {
67 const result = await run("git", args, { cwd });
68 return result.status === 0 ? result.stdout.trim() : undefined;
69}
70
71const exists = (path) =>
72 access(path).then(
73 () => true,
74 () => false,
75 );
76
77export async function bootstrap(root, previousHead) {
78 const checkout = resolve(root, "submodules/distilled");
79 // Use the index, just like `git submodule update`, including a staged pin.
80 const [pin, commonDir, gitDir, worktrees] = await Promise.all([
81 git(["rev-parse", ":submodules/distilled"], root),
82 git(["rev-parse", "--path-format=absolute", "--git-common-dir"], root),
83 git(["rev-parse", "--path-format=absolute", "--git-dir"], root),
84 git(["worktree", "list", "--porcelain", "-z"], root),
85 ]);
86 const mainRoot = worktrees.split("\0")[0].slice("worktree ".length);
87 if (previousHead !== undefined) await isolateWorktreeConfig(mainRoot);
88
89 if (await exists(resolve(checkout, ".git"))) {
90 const toplevel = await git(["rev-parse", "--show-toplevel"], checkout);
91 if ((await realpath(toplevel)) !== (await realpath(checkout))) {
92 throw new Error(`Cannot bootstrap distilled: invalid checkout at ${checkout}`);
93 }
94 const current = await git(["rev-parse", "HEAD"], checkout);
95 if (current === pin) return;
96
97 const previousPin = previousHead
98 ? await tryGit(["rev-parse", `${previousHead}:submodules/distilled`], root)
99 : undefined;
100 // Only follow an Alchemy checkout when distilled is still at its old pin.
101 // Intentional distilled development never moves an existing HEAD.
102 if (
103 current !== previousPin ||
104 (await tryGit(["symbolic-ref", "--quiet", "HEAD"], checkout)) !== undefined ||
105 (await git(["status", "--porcelain"], checkout)) !== ""
106 ) {
107 console.warn(
108 `Preserving distilled at ${current}; Alchemy pins ${pin}. To sync explicitly, run: git -C "${root}" submodule update --init --checkout -- submodules/distilled`,
109 );
110 return;
111 }
112 await ensureCommit(checkout, pin);
113 await git(["checkout", "--detach", pin], checkout);
114 return;
115 }
116
117 if ((await exists(checkout)) && (await readdir(checkout)).length !== 0) {
118 throw new Error(`Cannot bootstrap distilled: ${checkout} is nonempty but has no .git entry`);
119 }
120
121 if (gitDir === commonDir) {
122 await git(["submodule", "update", "--init", "--checkout", "--", "submodules/distilled"], root);
123 if (previousHead !== undefined) await isolateWorktreeConfig(mainRoot);
124 return;
125 }
126
127 // Initialize the primary checkout at its own pin first. Every linked checkout
128 // then gets an independent detached HEAD backed by that same object database.
129 await bootstrap(mainRoot);
130 await isolateWorktreeConfig(mainRoot);
131 const mainCheckout = resolve(mainRoot, "submodules/distilled");
132 await ensureCommit(mainCheckout, pin);
133 // The destination was checked above: it is missing or empty. One --force
134 // replaces only its stale registration; Git still refuses locked worktrees
135 // (overriding a lock would require --force twice). Do not prune other entries.
136 await git(
137 ["worktree", "add", "--force", "--detach", "--no-checkout", checkout, pin],
138 mainCheckout,
139 );
140 await isolateWorktreeConfig(mainRoot);
141 await populate(mainCheckout, checkout, pin);
142 await gitLocked(["submodule", "init", "--", "submodules/distilled"], root);
143}
144
145// Errors that mean "this filesystem cannot clone files": fall back to writing them.
146const NO_CLONE = new Set(["ENOTSUP", "EOPNOTSUPP", "EXDEV", "EINVAL", "ENOSYS"]);
147
148/**
149 * Fill a `--no-checkout` distilled worktree with the files at `pin`.
150 *
151 * A distilled checkout is about 1.1 GB, and every Alchemy worktree gets one.
152 * On a copy-on-write filesystem (APFS on macOS, btrfs, XFS) a file can be
153 * cloned: the copy shares the original's blocks until one of them changes. So
154 * every file whose content at `pin` matches the primary checkout's clean copy
155 * is cloned from it, which costs a few megabytes in total; Git writes the rest.
156 * On other filesystems the first clone fails and Git writes every file, as a
157 * plain `git worktree add` would. Set ALCHEMY_DISTILLED_CLONE=0 to skip cloning.
158 */
159async function populate(mainCheckout, checkout, pin) {
160 if (env.ALCHEMY_DISTILLED_CLONE !== "0") await cloneUnchanged(mainCheckout, checkout, pin);
161 await git(["reset", "--quiet", "--mixed", pin], checkout);
162 // Hash the cloned files once so they count as clean, then let Git write
163 // every file that is still missing or different.
164 await tryGit(["update-index", "-q", "--refresh"], checkout);
165 const stale = await git(["diff", "--name-only", "-z"], checkout);
166 if (stale !== "") {
167 await git(
168 ["checkout", "--quiet", "--pathspec-from-file=-", "--pathspec-file-nul"],
169 checkout,
170 stale,
171 );
172 }
173}
174
175async function cloneUnchanged(mainCheckout, checkout, pin) {
176 const [status, staged, tree] = await Promise.all([
177 git(["status", "--porcelain=v1", "-z", "--untracked-files=no", "--no-renames"], mainCheckout),
178 git(["ls-files", "--stage", "-z"], mainCheckout),
179 git(["ls-tree", "-r", "-z", "--full-tree", pin], checkout),
180 ]);
181 // The primary checkout's index, minus files with uncommitted changes.
182 const dirty = new Set(
183 status
184 .split("\0")
185 .filter(Boolean)
186 .map((entry) => entry.slice(3)),
187 );
188 const indexed = new Map();
189 for (const entry of staged.split("\0")) {
190 const [meta, path] = entry.split("\t");
191 const [mode, oid, stage] = (meta ?? "").split(" ");
192 if (path && stage === "0" && mode !== "120000" && mode !== "160000" && !dirty.has(path)) {
193 indexed.set(path, oid);
194 }
195 }
196 const sources = new Map();
197 for (const entry of tree.split("\0")) {
198 const [meta, path] = entry.split("\t");
199 const oid = (meta ?? "").split(" ")[2];
200 if (!path || indexed.get(path) !== oid) continue;
201 const dir = dirname(resolve(checkout, path));
202 if (!sources.has(dir)) sources.set(dir, []);
203 sources.get(dir).push(resolve(mainCheckout, path));
204 }
205 await Promise.all([...sources.keys()].map((dir) => mkdir(dir, { recursive: true })));
206 if (process.platform === "darwin") await cloneWithCp(sources);
207 else await cloneWithNode(sources);
208}
209
210// Run `task` over `items`, at most `limit` at a time.
211async function eachLimit(items, limit, task) {
212 let next = 0;
213 const worker = async () => {
214 while (next < items.length) await task(items[next++]);
215 };
216 await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
217}
218
219// Node's copyFile never clones on macOS: COPYFILE_FICLONE_FORCE fails with
220// ENOSYS and COPYFILE_FICLONE silently writes a full copy. `cp -c` calls
221// clonefile(2). A distilled checkout spans over a thousand directories, so a
222// `cp -c` runs per directory batch, one per core at a time. A failed batch
223// leaves files missing, which populate() rewrites.
224async function cloneWithCp(sources) {
225 const batches = [];
226 for (const [dir, files] of sources) {
227 for (let i = 0; i < files.length; i += 500) batches.push([...files.slice(i, i + 500), dir]);
228 }
229 await eachLimit(batches, availableParallelism(), (batch) =>
230 run("cp", ["-c", ...batch], { stderr: "ignore" }),
231 );
232}
233
234async function cloneWithNode(sources) {
235 const copies = [];
236 for (const [dir, files] of sources) {
237 for (const source of files) copies.push([source, resolve(dir, basename(source))]);
238 }
239 let supported = true;
240 await eachLimit(copies, 64, async ([source, target]) => {
241 if (!supported) return;
242 try {
243 await copyFile(source, target, constants.COPYFILE_FICLONE_FORCE);
244 } catch (error) {
245 await rm(target, { force: true });
246 if (!NO_CLONE.has(error.code)) throw error;
247 supported = false;
248 }
249 });
250}
251
252async function isolateWorktreeConfig(mainRoot) {
253 const checkout = resolve(mainRoot, "submodules/distilled");
254 if (!(await exists(resolve(checkout, ".git")))) return;
255
256 // Resolve the gitfile without opening the repository: its core.worktree may
257 // already point at a removed Alchemy worktree, preventing normal Git commands.
258 const commonDir = resolve(
259 checkout,
260 await git(["rev-parse", "--resolve-git-dir", ".git"], checkout),
261 );
262 if (await exists(resolve(commonDir, "commondir"))) {
263 throw new Error("The primary distilled checkout must not be a linked worktree");
264 }
265 const configs = [[commonDir, checkout]];
266 const worktrees = resolve(commonDir, "worktrees");
267 if (await exists(worktrees)) {
268 for (const entry of await readdir(worktrees)) {
269 const admin = resolve(worktrees, entry);
270 const gitfile = (await readFile(resolve(admin, "gitdir"), "utf8")).trim();
271 configs.push([admin, dirname(resolve(admin, gitfile))]);
272 }
273 }
274 // Protect every registered worktree, including temporarily missing/locked
275 // ones. Submodule sync/update can write core.worktree to the shared config;
276 // explicit per-worktree overrides prevent that from redirecting any checkout.
277 await Promise.all(
278 configs.map(([admin, path]) =>
279 setConfig(resolve(admin, "config.worktree"), "core.worktree", path, mainRoot),
280 ),
281 );
282 const config = resolve(commonDir, "config");
283 await setConfig(config, "extensions.worktreeConfig", "true", mainRoot);
284 // New worktrees must not inherit a path before their override is installed.
285 if (
286 (await tryGit(["config", "--file", config, "--get", "core.worktree"], mainRoot)) !== undefined
287 ) {
288 await gitLocked(["config", "--file", config, "--unset-all", "core.worktree"], mainRoot);
289 }
290}
291
292async function ensureCommit(checkout, pin) {
293 if ((await tryGit(["cat-file", "-e", `${pin}^{commit}`], checkout)) === undefined) {
294 // Never replace an unavailable pin with a branch tip.
295 await git(["fetch", "origin", pin], checkout);
296 await git(["cat-file", "-e", `${pin}^{commit}`], checkout);
297 }
298}
299
300// Run only for post-checkout branch/worktree checkouts, not file checkouts.
301// Hook arguments are old HEAD, new HEAD, and the branch-checkout flag.
302if (import.meta.main && process.argv[4] === "1") {
303 await bootstrap(resolve(import.meta.dirname, ".."), process.argv[2]);
304}