@claudexor/workspace 4.8.2 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/dist/anchor-store.d.ts +20 -9
  2. package/dist/anchor-store.d.ts.map +1 -1
  3. package/dist/anchor-store.js +89 -14
  4. package/dist/anchor-store.js.map +1 -1
  5. package/dist/capture-failure.d.ts.map +1 -1
  6. package/dist/capture-failure.js +7 -2
  7. package/dist/capture-failure.js.map +1 -1
  8. package/dist/capture-inventory.d.ts +10 -0
  9. package/dist/capture-inventory.d.ts.map +1 -0
  10. package/dist/capture-inventory.js +63 -0
  11. package/dist/capture-inventory.js.map +1 -0
  12. package/dist/capture-objects.d.ts +32 -0
  13. package/dist/capture-objects.d.ts.map +1 -0
  14. package/dist/capture-objects.js +129 -0
  15. package/dist/capture-objects.js.map +1 -0
  16. package/dist/cleanup-fault.d.ts +18 -0
  17. package/dist/cleanup-fault.d.ts.map +1 -0
  18. package/dist/cleanup-fault.js +45 -0
  19. package/dist/cleanup-fault.js.map +1 -0
  20. package/dist/continuation-evidence.d.ts +61 -15
  21. package/dist/continuation-evidence.d.ts.map +1 -1
  22. package/dist/continuation-evidence.js +151 -36
  23. package/dist/continuation-evidence.js.map +1 -1
  24. package/dist/continuation-facts.d.ts +93 -0
  25. package/dist/continuation-facts.d.ts.map +1 -0
  26. package/dist/continuation-facts.js +162 -0
  27. package/dist/continuation-facts.js.map +1 -0
  28. package/dist/directory-workspace.d.ts.map +1 -1
  29. package/dist/directory-workspace.js +1 -3
  30. package/dist/directory-workspace.js.map +1 -1
  31. package/dist/files-io.d.ts.map +1 -1
  32. package/dist/files-io.js +1 -7
  33. package/dist/files-io.js.map +1 -1
  34. package/dist/fs-presence.d.ts +12 -0
  35. package/dist/fs-presence.d.ts.map +1 -0
  36. package/dist/fs-presence.js +35 -0
  37. package/dist/fs-presence.js.map +1 -0
  38. package/dist/git-nested-repositories.d.ts.map +1 -1
  39. package/dist/git-nested-repositories.js +11 -2
  40. package/dist/git-nested-repositories.js.map +1 -1
  41. package/dist/git-observed.d.ts +75 -0
  42. package/dist/git-observed.d.ts.map +1 -0
  43. package/dist/git-observed.js +172 -0
  44. package/dist/git-observed.js.map +1 -0
  45. package/dist/git-result.d.ts +36 -0
  46. package/dist/git-result.d.ts.map +1 -0
  47. package/dist/git-result.js +96 -0
  48. package/dist/git-result.js.map +1 -0
  49. package/dist/git-run.d.ts +11 -0
  50. package/dist/git-run.d.ts.map +1 -1
  51. package/dist/git-run.js +24 -0
  52. package/dist/git-run.js.map +1 -1
  53. package/dist/git-snapshot.d.ts +40 -0
  54. package/dist/git-snapshot.d.ts.map +1 -0
  55. package/dist/git-snapshot.js +238 -0
  56. package/dist/git-snapshot.js.map +1 -0
  57. package/dist/git-transient-capture.d.ts +63 -16
  58. package/dist/git-transient-capture.d.ts.map +1 -1
  59. package/dist/git-transient-capture.js +170 -57
  60. package/dist/git-transient-capture.js.map +1 -1
  61. package/dist/git.d.ts +46 -21
  62. package/dist/git.d.ts.map +1 -1
  63. package/dist/git.js +164 -103
  64. package/dist/git.js.map +1 -1
  65. package/dist/index.d.ts +9 -1
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +9 -1
  68. package/dist/index.js.map +1 -1
  69. package/dist/lanes.d.ts +2 -1
  70. package/dist/lanes.d.ts.map +1 -1
  71. package/dist/lanes.js +7 -6
  72. package/dist/lanes.js.map +1 -1
  73. package/dist/manager.d.ts +21 -7
  74. package/dist/manager.d.ts.map +1 -1
  75. package/dist/manager.js +30 -17
  76. package/dist/manager.js.map +1 -1
  77. package/dist/object-compose.d.ts +58 -0
  78. package/dist/object-compose.d.ts.map +1 -0
  79. package/dist/object-compose.js +444 -0
  80. package/dist/object-compose.js.map +1 -0
  81. package/dist/object-materialize.d.ts +52 -0
  82. package/dist/object-materialize.d.ts.map +1 -0
  83. package/dist/object-materialize.js +467 -0
  84. package/dist/object-materialize.js.map +1 -0
  85. package/dist/object-occupied.d.ts +27 -0
  86. package/dist/object-occupied.d.ts.map +1 -0
  87. package/dist/object-occupied.js +131 -0
  88. package/dist/object-occupied.js.map +1 -0
  89. package/dist/object-revert.d.ts +45 -0
  90. package/dist/object-revert.d.ts.map +1 -0
  91. package/dist/object-revert.js +265 -0
  92. package/dist/object-revert.js.map +1 -0
  93. package/dist/object-saved-files.d.ts +50 -0
  94. package/dist/object-saved-files.d.ts.map +1 -0
  95. package/dist/object-saved-files.js +176 -0
  96. package/dist/object-saved-files.js.map +1 -0
  97. package/dist/object-sparse.d.ts +22 -0
  98. package/dist/object-sparse.d.ts.map +1 -0
  99. package/dist/object-sparse.js +72 -0
  100. package/dist/object-sparse.js.map +1 -0
  101. package/dist/object-store-leases.d.ts +41 -0
  102. package/dist/object-store-leases.d.ts.map +1 -0
  103. package/dist/object-store-leases.js +277 -0
  104. package/dist/object-store-leases.js.map +1 -0
  105. package/dist/object-store-roots.d.ts +25 -0
  106. package/dist/object-store-roots.d.ts.map +1 -0
  107. package/dist/object-store-roots.js +114 -0
  108. package/dist/object-store-roots.js.map +1 -0
  109. package/dist/object-store.d.ts +110 -0
  110. package/dist/object-store.d.ts.map +1 -0
  111. package/dist/object-store.js +404 -0
  112. package/dist/object-store.js.map +1 -0
  113. package/dist/object-transfer.d.ts +41 -0
  114. package/dist/object-transfer.d.ts.map +1 -0
  115. package/dist/object-transfer.js +267 -0
  116. package/dist/object-transfer.js.map +1 -0
  117. package/dist/plain-diff-capture.d.ts +1 -2
  118. package/dist/plain-diff-capture.d.ts.map +1 -1
  119. package/dist/plain-diff-capture.js +2 -7
  120. package/dist/plain-diff-capture.js.map +1 -1
  121. package/dist/plain-diff.d.ts +0 -4
  122. package/dist/plain-diff.d.ts.map +1 -1
  123. package/dist/plain-diff.js +2 -44
  124. package/dist/plain-diff.js.map +1 -1
  125. package/dist/raw-patch.d.ts.map +1 -1
  126. package/dist/raw-patch.js +5 -8
  127. package/dist/raw-patch.js.map +1 -1
  128. package/dist/run-expiry.d.ts +3 -0
  129. package/dist/run-expiry.d.ts.map +1 -0
  130. package/dist/run-expiry.js +15 -0
  131. package/dist/run-expiry.js.map +1 -0
  132. package/dist/thread-tree.d.ts +36 -2
  133. package/dist/thread-tree.d.ts.map +1 -1
  134. package/dist/thread-tree.js +87 -11
  135. package/dist/thread-tree.js.map +1 -1
  136. package/package.json +5 -5
@@ -0,0 +1,467 @@
1
+ import { copyFileSync, existsSync, lstatSync, mkdtempSync, readlinkSync, renameSync, rmSync, } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { classifyGitFailure, diskProgress, gitObserved, GitOperationError, proofStallMs, } from "./git-observed.js";
5
+ import { withDisposableScratch } from "./cleanup-fault.js";
6
+ import { lstatPresent } from "./fs-presence.js";
7
+ import { SCRATCH_INDEX_CONFIG, STAT_TRUST_OFF } from "./git-run.js";
8
+ import { markedEntries } from "./git-snapshot.js";
9
+ import { parseRawRecords } from "./object-compose.js";
10
+ import { aliasJudge, byFold, foldsOnto, occupiedPaths } from "./object-occupied.js";
11
+ import { sparseCheckoutRefusal } from "./object-sparse.js";
12
+ import { assertSpaceFor } from "./object-transfer.js";
13
+ const GIT_FAULT_REFUSAL = {
14
+ capture_space_insufficient: "space_insufficient",
15
+ capture_git_stalled: "git_stalled",
16
+ capture_git_signal: "git_signal",
17
+ };
18
+ /** The typed refusal of a Git child's failure on an object path. */
19
+ export function gitFaultRefusalCode(code) {
20
+ return GIT_FAULT_REFUSAL[code] ?? "git_failed";
21
+ }
22
+ /** A Git child's typed failure, or a filesystem fault as one (its scratch: a full disk). */
23
+ function gitOrFilesystemFault(error) {
24
+ if (error instanceof GitOperationError)
25
+ return error;
26
+ const errno = error?.code;
27
+ if (typeof errno !== "string")
28
+ return null;
29
+ return new GitOperationError(errno === "ENOSPC" || errno === "EDQUOT" ? "capture_space_insufficient" : "capture_failed", error.message);
30
+ }
31
+ export { proofStallMs } from "./git-observed.js";
32
+ /** How `read-tree -m -u` refuses before its first write (its checks run first;
33
+ * object-path Git speaks the C locale). It only names the refusal: whether the
34
+ * checkout wrote anything is measured (`wroteAny`), since a path name in a
35
+ * partial checkout's message can read like a refusal. */
36
+ const REFUSED_BEFORE_WRITE = /not uptodate|would be overwritten|would be removed|would lose untracked|Cannot merge|index\.lock|failed to unpack tree|Refusing to remove/i;
37
+ /** What lstat sees at `path` (a checkout replaces a file it writes, so its
38
+ * identity changes; nothing is re-hashed through a filter). */
39
+ function stamp(worktree, path) {
40
+ try {
41
+ const stat = lstatSync(join(worktree, path), { bigint: true, throwIfNoEntry: false });
42
+ return stat
43
+ ? `${stat.mode}:${stat.size}:${stat.ino}:${stat.mtimeNs}:${stat.ctimeNs}`
44
+ : "absent";
45
+ }
46
+ catch (error) {
47
+ return `error:${error.code ?? "unknown"}`;
48
+ }
49
+ }
50
+ /** What lstat sees at `paths` in bytes (what a refresh reads at most). */
51
+ function diskBytes(worktree, paths) {
52
+ let bytes = 0;
53
+ for (const path of paths) {
54
+ try {
55
+ bytes += lstatSync(join(worktree, path), { throwIfNoEntry: false })?.size ?? 0;
56
+ }
57
+ catch {
58
+ // Unreadable here: Git will say so when it gets there.
59
+ }
60
+ }
61
+ return bytes;
62
+ }
63
+ /**
64
+ * Refuse a checkout that cannot fit, before its first write: it would stop partway,
65
+ * and its take-back needs space too. A replaced file makes room just before its
66
+ * new content is written, so each path needs only what it grows by; a filter can
67
+ * change sizes, which the margin of `assertSpaceFor` covers. Returns the new bytes.
68
+ */
69
+ async function assertWriteFits(worktree, records, git) {
70
+ const written = records.filter((record) => /^(?:100|120)/.test(record.newMode));
71
+ if (written.length === 0)
72
+ return 0;
73
+ const listed = await git(["cat-file", "--batch-check=%(objectname) %(objectsize)"], "cat-file --batch-check (checkout)", { input: `${[...new Set(written.map((record) => record.newOid))].join("\n")}\n` });
74
+ const sizes = new Map(listed.stdout
75
+ .split("\n")
76
+ .map((line) => line.split(" "))
77
+ .filter(([, size]) => size && /^\d+$/.test(size))
78
+ .map(([oid, size]) => [oid, Number(size)]));
79
+ let total = 0;
80
+ let growth = 0;
81
+ for (const record of written) {
82
+ const size = sizes.get(record.newOid) ?? 0;
83
+ total += size;
84
+ growth += Math.max(0, size - diskBytes(worktree, [record.src]));
85
+ }
86
+ assertSpaceFor(worktree, growth, "the checkout");
87
+ return total;
88
+ }
89
+ function refusal(code, message, paths = []) {
90
+ const sorted = [...new Set(paths)].sort();
91
+ return {
92
+ code,
93
+ message: message.slice(0, 4_000),
94
+ paths: sorted.slice(0, 200),
95
+ pathCount: sorted.length,
96
+ };
97
+ }
98
+ /** The refusal of a write over what Git does not track, naming it. */
99
+ function occupiedRefusal(worktree, records) {
100
+ const occupied = occupiedPaths(worktree, records);
101
+ return occupied.length === 0
102
+ ? null
103
+ : refusal("delivery_path_occupied", `a path this operation writes holds what Git does not track (an ignored file, for one), and writing would destroy it; nothing was written: ${occupied.slice(0, 20).join(", ")}`, occupied);
104
+ }
105
+ /** The same refusals for a dry run: what a checkout from `from` to `to` would answer
106
+ * here before writing (a path holding what Git does not track, or one outside the
107
+ * sparse-checkout patterns). */
108
+ export async function occupiedByWrite(worktree, from, to, env = {}) {
109
+ const listed = await gitObserved(worktree, ["diff-tree", "-r", "--raw", "-z", "--no-renames", "--no-abbrev", from, to], { env, label: "diff-tree --raw (dry run)" });
110
+ const records = parseRawRecords(listed.stdout);
111
+ return (occupiedRefusal(worktree, records) ??
112
+ (await sparseCheckoutRefusal(worktree, records.map((record) => record.src), "delivery", env)));
113
+ }
114
+ /**
115
+ * Write `to` over a worktree that holds `from` (rev 11 C): a two-tree
116
+ * `read-tree -m -u` that refuses "not uptodate" when a path it must change no
117
+ * longer holds `from` (a live preimage recheck) and streams big blobs to disk.
118
+ * It runs with the sparse-checkout patterns off: with them on, Git reapplies them
119
+ * to every entry and deletes a clean file outside them that holds no skip-worktree
120
+ * mark (a conflict resolved there), a path this write never addressed. Off, only
121
+ * the paths `from` and `to` differ in move; every other entry keeps its file, and its
122
+ * mark unless Git itself clears it (Git clears the skip-worktree mark of a present
123
+ * file when it reads a sparse checkout's index). What Git does not track is refused
124
+ * first: a file at a path `to` adds (ignored, so outside `from`) would be
125
+ * overwritten, and no revert could bring it back. So is an addressed path outside
126
+ * the patterns: a snapshot's `add -A` skips it, so neither a take-back nor a revert
127
+ * could tell what it holds. A write that cannot fit refuses too, with nothing
128
+ * written. Afterwards every written path is proven by content, the way `git status`
129
+ * compares it (Git's clean conversion through an index), not by a stat refresh.
130
+ * Its temporary index's scratch goes last, never replacing the outcome.
131
+ */
132
+ export function materializeTree(input) {
133
+ return withDisposableScratch((held) => writeTree(input, held));
134
+ }
135
+ async function writeTree(input, held) {
136
+ const env = { GIT_OPTIONAL_LOCKS: "0" };
137
+ // A checkout writes files without stdout: disk activity is its progress.
138
+ const progress = diskProgress(input.worktree);
139
+ // The real index keeps the user's form, but no stat shortcut decides for it either.
140
+ const git = (args, label, extra = {}) => gitObserved(input.worktree, [...(held.dir ? SCRATCH_INDEX_CONFIG : STAT_TRUST_OFF), ...args], {
141
+ env,
142
+ label,
143
+ progress,
144
+ ...extra,
145
+ });
146
+ let records;
147
+ let paths;
148
+ let writtenBytes;
149
+ const before = new Map();
150
+ try {
151
+ if (input.index === "temporary")
152
+ held.dir = mkdtempSync(join(tmpdir(), "claudexor-materialize-"));
153
+ records = parseRawRecords((await git(["diff-tree", "-r", "--raw", "-z", "--no-renames", "--no-abbrev", input.from, input.to], "diff-tree --raw")).stdout);
154
+ if (records.length === 0)
155
+ return { ok: true, changed: 0, treeMutated: false };
156
+ paths = records.map((record) => record.src);
157
+ const blocked = occupiedRefusal(input.worktree, records) ??
158
+ (await sparseCheckoutRefusal(input.worktree, paths, "delivery", env));
159
+ if (blocked)
160
+ return { ok: false, treeMutated: false, refusal: blocked };
161
+ writtenBytes = await assertWriteFits(input.worktree, records, git);
162
+ if (held.dir)
163
+ await seedScratchIndex(input, held.dir, records, env, git);
164
+ // The real index keeps the user's assume-unchanged marks, so its refresh
165
+ // compares an entry whose stat moved by content, mark or not (the checkout
166
+ // stat-checks a marked entry it replaces): an unchanged file is not "not uptodate".
167
+ // Git drops the mark of an entry whose file changed, a local edit the user keeps
168
+ // out of commits: those marks are put back. A marked name stdin cannot carry
169
+ // back keeps the plain refresh, where a stale mark refuses before writing.
170
+ const marks = held.dir ? null : await markedEntries(input.worktree, env);
171
+ const refresh = marks && marks.unnamed === 0 ? "--really-refresh" : "--refresh";
172
+ await git(["update-index", "-q", refresh], `update-index ${refresh}`, {
173
+ allowFailure: true,
174
+ stallMs: proofStallMs(diskBytes(input.worktree, paths)),
175
+ });
176
+ if (refresh === "--really-refresh" && marks && marks.assumed.length > 0)
177
+ await git(["update-index", "--assume-unchanged", "-z", "--stdin"], "update-index --assume-unchanged", { input: marks.assumed.map((path) => `${path}\0`).join("") });
178
+ for (const path of paths)
179
+ before.set(path, stamp(input.worktree, path));
180
+ }
181
+ catch (error) {
182
+ const fault = gitOrFilesystemFault(error);
183
+ if (!fault)
184
+ throw error;
185
+ // Nothing has been written yet: the fault is the whole answer.
186
+ return {
187
+ ok: false,
188
+ treeMutated: false,
189
+ refusal: refusal(gitFaultRefusalCode(fault.code), fault.message),
190
+ };
191
+ }
192
+ let moved;
193
+ try {
194
+ moved = await git(["read-tree", "--no-sparse-checkout", "-m", "-u", input.from, input.to], "read-tree -m -u", { allowFailure: true, stallMs: proofStallMs(writtenBytes) });
195
+ }
196
+ catch (error) {
197
+ if (!(error instanceof GitOperationError))
198
+ throw error;
199
+ // A stalled or killed checkout: part of `to` may have reached the target.
200
+ return {
201
+ ok: false,
202
+ treeMutated: true,
203
+ refusal: refusal(gitFaultRefusalCode(error.code), error.message, paths),
204
+ };
205
+ }
206
+ if (moved.code !== 0) {
207
+ const stderr = moved.stderr.trim();
208
+ const wrote = await wroteAny(input, before, env);
209
+ if (!wrote && REFUSED_BEFORE_WRITE.test(stderr))
210
+ return {
211
+ ok: false,
212
+ treeMutated: false,
213
+ refusal: refusal("delivery_target_changed", `the target changed under paths this operation writes: ${stderr}`, paths),
214
+ };
215
+ return {
216
+ ok: false,
217
+ treeMutated: wrote,
218
+ refusal: refusal(gitFaultRefusalCode(classifyGitFailure(stderr)), `${wrote ? "the checkout stopped partway" : "the checkout failed before writing"}: ${stderr}`, paths),
219
+ };
220
+ }
221
+ let mismatched;
222
+ try {
223
+ mismatched = await contentMismatches(input.worktree, records, env);
224
+ }
225
+ catch (error) {
226
+ const fault = gitOrFilesystemFault(error);
227
+ if (!fault)
228
+ throw error;
229
+ return {
230
+ ok: false,
231
+ treeMutated: true,
232
+ unproven: paths,
233
+ refusal: refusal(gitFaultRefusalCode(fault.code), `the written paths could not be proven: ${fault.message}`, paths),
234
+ };
235
+ }
236
+ if (mismatched.length > 0) {
237
+ return {
238
+ ok: false,
239
+ treeMutated: true,
240
+ unproven: mismatched,
241
+ refusal: refusal("delivery_content_mismatch", "written paths do not hold the delivered content after checkout conversion", mismatched),
242
+ };
243
+ }
244
+ return { ok: true, changed: records.length, treeMutated: true };
245
+ }
246
+ /**
247
+ * Point `GIT_INDEX_FILE` at a scratch index holding `from` that keeps the stat
248
+ * cache of the user's index, so the refresh before the checkout costs what
249
+ * `git status` costs instead of re-hashing every tracked file. The user's
250
+ * index path is resolved first (`--git-path index` honours `GIT_INDEX_FILE`);
251
+ * only that file is read, never its lock, and it is copied whole under a
252
+ * private name before use. A user's index that cannot seed (none yet,
253
+ * unmerged, unreadable) leaves a plain read of `from`. The paths this checkout
254
+ * replaces or removes are then re-added without stat data, so the refresh
255
+ * compares exactly those by content through the target's own conversion, as
256
+ * an index without any stat cache did: a path that does not hold `from` stays
257
+ * "not uptodate" and the checkout refuses before writing. A re-added entry carries
258
+ * none of the user's assume-unchanged or skip-worktree marks, and the scratch
259
+ * trusts no stat shortcut (`SCRATCH_INDEX_CONFIG`): with `core.ignoreStat` every
260
+ * re-added entry would be assume-valid, its content never compared.
261
+ */
262
+ async function seedScratchIndex(input, scratch, records, env, git) {
263
+ const located = await git(["rev-parse", "--path-format=absolute", "--git-path", "index"], "rev-parse index", { allowFailure: true });
264
+ const userIndex = located.code === 0 ? located.stdout.trim() : "";
265
+ const scratchIndex = join(scratch, "index");
266
+ env["GIT_INDEX_FILE"] = scratchIndex;
267
+ let seeded = false;
268
+ if (userIndex && existsSync(userIndex)) {
269
+ try {
270
+ copyFileSync(userIndex, `${scratchIndex}.copy`);
271
+ renameSync(`${scratchIndex}.copy`, scratchIndex);
272
+ seeded =
273
+ (await git(["read-tree", "-m", input.from], "read-tree -m (seed)", { allowFailure: true }))
274
+ .code === 0;
275
+ }
276
+ catch {
277
+ seeded = false;
278
+ }
279
+ }
280
+ if (!seeded) {
281
+ rmSync(scratchIndex, { force: true });
282
+ await git(["read-tree", input.from], "read-tree (seed)");
283
+ }
284
+ const replaced = records.filter((record) => record.oldMode !== "000000");
285
+ if (replaced.length > 0)
286
+ await git(["update-index", "-z", "--index-info"], "update-index --index-info", {
287
+ input: replaced
288
+ .map((record) => `${record.oldMode} ${record.oldOid}\t${record.src}\0`)
289
+ .join(""),
290
+ });
291
+ }
292
+ /**
293
+ * After a failed checkout: did it write anything? Every path it would write
294
+ * must be as lstat saw it just before (type, mode, size, inode, times), and a
295
+ * real index must still be `from`. Nothing is re-hashed: a filter that does not
296
+ * round-trip would make an untouched file look written. A measurement that
297
+ * cannot run counts as written.
298
+ */
299
+ async function wroteAny(input, before, env) {
300
+ try {
301
+ for (const [path, was] of before)
302
+ if (stamp(input.worktree, path) !== was)
303
+ return true;
304
+ if (input.index === "temporary")
305
+ return false;
306
+ const index = await gitObserved(input.worktree, ["write-tree"], { env, label: "write-tree" });
307
+ return index.stdout.trim() !== input.from;
308
+ }
309
+ catch {
310
+ return true;
311
+ }
312
+ }
313
+ /**
314
+ * Written paths whose content differs from their entries, compared as `git status`
315
+ * compares them: with the blob in the index, so a committed CRLF blob keeps its CR
316
+ * (`hash-object` loads no index and would call it changed). A private copy of the
317
+ * index the checkout just moved keeps every other path's stat cache; the written
318
+ * paths are re-added without stat data or the user's marks, so the refresh reads
319
+ * exactly those, whatever `core.ignoreStat` or a filesystem monitor would answer
320
+ * (`SCRATCH_INDEX_CONFIG`). A filter that does not round-trip still reads as different.
321
+ */
322
+ async function indexProofMismatches(worktree, records, env, bytes) {
323
+ if (records.length === 0)
324
+ return [];
325
+ const located = await gitObserved(worktree, ["rev-parse", "--path-format=absolute", "--git-path", "index"], { env, label: "rev-parse index (proof)", allowFailure: true });
326
+ const moved = located.code === 0 ? located.stdout.trim() : "";
327
+ const scratch = mkdtempSync(join(tmpdir(), "claudexor-proof-"));
328
+ const proofEnv = { ...env, GIT_INDEX_FILE: join(scratch, "index") };
329
+ const git = (args, label, extra = {}) => gitObserved(worktree, [...SCRATCH_INDEX_CONFIG, ...args], {
330
+ env: proofEnv,
331
+ label,
332
+ ...extra,
333
+ });
334
+ try {
335
+ if (moved && existsSync(moved))
336
+ copyFileSync(moved, proofEnv.GIT_INDEX_FILE);
337
+ await git(["update-index", "--add", "-z", "--index-info"], "update-index --index-info (proof)", {
338
+ input: records
339
+ .map((record) => `${record.newMode} ${record.newOid}\t${record.src}\0`)
340
+ .join(""),
341
+ });
342
+ await git(["update-index", "-q", "--refresh"], "update-index --refresh (proof)", {
343
+ stallMs: proofStallMs(bytes),
344
+ });
345
+ const listed = await git(["diff-files", "--name-only", "-z"], "diff-files (proof)");
346
+ const different = new Set(listed.stdout.split("\0").filter(Boolean));
347
+ return records.filter((record) => different.has(record.src)).map((record) => record.src);
348
+ }
349
+ finally {
350
+ rmSync(scratch, { recursive: true, force: true });
351
+ }
352
+ }
353
+ /**
354
+ * One `--stdin-paths` line naming `path` exactly. Git reads a line that starts
355
+ * with `"` as a C-quoted name and strips a trailing CR, so such names (and any
356
+ * with control characters) are quoted the way Git's unquote_c_style reads them;
357
+ * every other name, backslashes included, is taken verbatim.
358
+ */
359
+ function stdinPathLine(path) {
360
+ if (!/^"|[\x00-\x1f\x7f]/.test(path))
361
+ return path;
362
+ const escapes = {
363
+ '"': '\\"',
364
+ "\\": "\\\\",
365
+ "\n": "\\n",
366
+ "\r": "\\r",
367
+ "\t": "\\t",
368
+ };
369
+ let quoted = '"';
370
+ for (const char of path) {
371
+ if (escapes[char])
372
+ quoted += escapes[char];
373
+ else if (/[\x00-\x1f\x7f]/.test(char))
374
+ quoted += `\\${char.charCodeAt(0).toString(8).padStart(3, "0")}`;
375
+ else
376
+ quoted += char;
377
+ }
378
+ return `${quoted}"`;
379
+ }
380
+ /** Paths whose worktree content or mode differs from the tree just written; a path
381
+ * that cannot be looked at throws, and its caller refuses the write as unproven. */
382
+ async function contentMismatches(worktree, records, env) {
383
+ const mismatched = [];
384
+ const hashed = [];
385
+ let hashedBytes = 0;
386
+ // Symlinks checked out as plain files holding their target (core.symlinks=false).
387
+ const linkFiles = [];
388
+ // On a filesystem that folds case, a removed path can read as the file written
389
+ // under its other spelling: one entry, not a survivor (a hard link is no alias).
390
+ const removedThere = [];
391
+ const boolConfig = async (key) => (await gitObserved(worktree, ["config", "--bool", key], {
392
+ env,
393
+ label: `config ${key}`,
394
+ allowFailure: true,
395
+ })).stdout.trim() !== "false";
396
+ const fileMode = await boolConfig("core.fileMode");
397
+ const symlinks = await boolConfig("core.symlinks");
398
+ for (const record of records) {
399
+ const path = join(worktree, record.src);
400
+ // Only ENOENT and ENOTDIR say a path is gone: any other error (an I/O error, a
401
+ // parent that can no longer be searched) leaves the write unproven, never a
402
+ // removal proven by not looking.
403
+ const stat = lstatPresent(path);
404
+ if (record.status === "D") {
405
+ if (stat && !stat.isDirectory())
406
+ removedThere.push(record.src);
407
+ continue;
408
+ }
409
+ if (record.newMode === "160000")
410
+ continue; // a gitlink is another repository's commit
411
+ if (!stat) {
412
+ mismatched.push(record.src);
413
+ continue;
414
+ }
415
+ if (record.newMode === "120000") {
416
+ if (!symlinks) {
417
+ // Git writes the link target's exact bytes, unconverted, as a plain file.
418
+ if (stat.isFile())
419
+ linkFiles.push(record);
420
+ else
421
+ mismatched.push(record.src);
422
+ continue;
423
+ }
424
+ const blob = await gitObserved(worktree, ["cat-file", "blob", record.newOid], {
425
+ env,
426
+ label: "cat-file blob",
427
+ });
428
+ if (!stat.isSymbolicLink() || readlinkSync(path) !== blob.stdout)
429
+ mismatched.push(record.src);
430
+ continue;
431
+ }
432
+ if (!stat.isFile()) {
433
+ mismatched.push(record.src);
434
+ continue;
435
+ }
436
+ if (fileMode && ((stat.mode & 0o100) !== 0) !== (record.newMode === "100755")) {
437
+ mismatched.push(record.src);
438
+ continue;
439
+ }
440
+ hashed.push(record);
441
+ hashedBytes += stat.size;
442
+ }
443
+ if (removedThere.length > 0) {
444
+ const judge = aliasJudge(worktree);
445
+ const written = byFold(records.filter((record) => record.status !== "D").map((r) => r.src));
446
+ for (const path of removedThere)
447
+ if (!foldsOnto(judge, written, path))
448
+ mismatched.push(path);
449
+ }
450
+ mismatched.push(...(await indexProofMismatches(worktree, hashed, env, hashedBytes)));
451
+ // Link files hold their target's exact bytes, unconverted, one line each on stdin.
452
+ for (let index = 0; index < linkFiles.length; index += 500) {
453
+ const chunk = linkFiles.slice(index, index + 500);
454
+ const out = await gitObserved(worktree, ["hash-object", "--no-filters", "--stdin-paths"], {
455
+ env,
456
+ label: "hash-object --stdin-paths",
457
+ input: `${chunk.map((record) => stdinPathLine(record.src)).join("\n")}\n`,
458
+ });
459
+ const oids = out.stdout.trim().split("\n");
460
+ chunk.forEach((record, at) => {
461
+ if (oids[at] !== record.newOid)
462
+ mismatched.push(record.src);
463
+ });
464
+ }
465
+ return mismatched;
466
+ }
467
+ //# sourceMappingURL=object-materialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-materialize.js","sourceRoot":"","sources":["../src/object-materialize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { RawRecord } from "./object-compose.js";
2
+ /**
3
+ * Whether two tree paths name one directory entry through the filesystem's folding
4
+ * of their spelling: they differ only by case, lstat finds one inode for both, and
5
+ * they are not both spelled exactly as entries on disk. A hard link is an entry of
6
+ * its own, spelled exactly, so two of its names never count as one. Directory
7
+ * listings are read once, and only for such a prospective alias.
8
+ */
9
+ export declare function aliasJudge(worktree: string): (path: string, other: string) => boolean;
10
+ /** Paths keyed by their folded spelling. */
11
+ export declare function byFold(paths: Iterable<string>): Map<string, string[]>;
12
+ /** Whether `path` is one of `paths` under another spelling (see {@link aliasJudge}). */
13
+ export declare function foldsOnto(judge: (path: string, other: string) => boolean, folded: ReadonlyMap<string, readonly string[]>, path: string): boolean;
14
+ /**
15
+ * What a checkout from one tree to another would destroy without Git knowing:
16
+ * `read-tree -m -u` writes a path the first tree does not name over whatever is
17
+ * there, and removes a file or symlink standing where it needs a directory. A
18
+ * snapshot leaves ignored files out of that tree, so such a file is the user's and
19
+ * no revert could bring it back. For each added path, one lstat of it and of each
20
+ * parent it does not share with another; a directory holding what stays is only
21
+ * walked when a file is about to take its name. A directory the path merely enters
22
+ * loses nothing, and neither do the entries the same checkout removes. Only ENOENT
23
+ * and ENOTDIR are absence: a path that cannot be looked at is thrown, so the write
24
+ * and its dry run both refuse instead of reading it as free.
25
+ */
26
+ export declare function occupiedPaths(worktree: string, records: readonly RawRecord[]): string[];
27
+ //# sourceMappingURL=object-occupied.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-occupied.d.ts","sourceRoot":"","sources":["../src/object-occupied.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAgBrD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAuBrF;AAED,4CAA4C;AAC5C,wBAAgB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAIrE;AAED,wFAAwF;AACxF,wBAAgB,SAAS,CACvB,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,EAC/C,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,EAC9C,IAAI,EAAE,MAAM,GACX,OAAO,CAET;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,EAAE,CAsCvF"}
@@ -0,0 +1,131 @@
1
+ import { readdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { lstatPresent } from "./fs-presence.js";
4
+ const ABSENT_MODE = "000000";
5
+ /** For the alias judge only: a path it cannot look at is no alias (the exemption is denied). */
6
+ const lstatOrNull = (path) => {
7
+ try {
8
+ return lstatPresent(path);
9
+ }
10
+ catch {
11
+ return undefined;
12
+ }
13
+ };
14
+ /** A name as a volume that folds case reads it (normalization included). */
15
+ const fold = (path) => path.normalize("NFC").toLowerCase();
16
+ /**
17
+ * Whether two tree paths name one directory entry through the filesystem's folding
18
+ * of their spelling: they differ only by case, lstat finds one inode for both, and
19
+ * they are not both spelled exactly as entries on disk. A hard link is an entry of
20
+ * its own, spelled exactly, so two of its names never count as one. Directory
21
+ * listings are read once, and only for such a prospective alias.
22
+ */
23
+ export function aliasJudge(worktree) {
24
+ const listings = new Map();
25
+ const listing = (dir) => {
26
+ if (!listings.has(dir)) {
27
+ try {
28
+ listings.set(dir, new Set(readdirSync(join(worktree, dir))));
29
+ }
30
+ catch {
31
+ listings.set(dir, null);
32
+ }
33
+ }
34
+ return listings.get(dir);
35
+ };
36
+ const spelledExactly = (path) => {
37
+ const parts = path.split("/");
38
+ return parts.every((part, at) => listing(parts.slice(0, at).join("/"))?.has(part) === true);
39
+ };
40
+ return (path, other) => {
41
+ if (path === other || fold(path) !== fold(other))
42
+ return false;
43
+ const one = lstatOrNull(join(worktree, path));
44
+ const two = lstatOrNull(join(worktree, other));
45
+ if (!one || !two || one.dev !== two.dev || one.ino !== two.ino)
46
+ return false;
47
+ return !(spelledExactly(path) && spelledExactly(other));
48
+ };
49
+ }
50
+ /** Paths keyed by their folded spelling. */
51
+ export function byFold(paths) {
52
+ const map = new Map();
53
+ for (const path of paths)
54
+ map.set(fold(path), [...(map.get(fold(path)) ?? []), path]);
55
+ return map;
56
+ }
57
+ /** Whether `path` is one of `paths` under another spelling (see {@link aliasJudge}). */
58
+ export function foldsOnto(judge, folded, path) {
59
+ return (folded.get(fold(path)) ?? []).some((other) => judge(path, other));
60
+ }
61
+ /** What a directory holds that is not on its way out, as tree paths (empty ones hold nothing). */
62
+ function survivors(worktree, dir, removed) {
63
+ const found = [];
64
+ for (const entry of readdirSync(join(worktree, dir), { withFileTypes: true })) {
65
+ const path = `${dir}/${entry.name}`;
66
+ if (entry.isDirectory())
67
+ found.push(...survivors(worktree, path, removed));
68
+ else if (!removed.has(path))
69
+ found.push(path);
70
+ }
71
+ return found;
72
+ }
73
+ /**
74
+ * What a checkout from one tree to another would destroy without Git knowing:
75
+ * `read-tree -m -u` writes a path the first tree does not name over whatever is
76
+ * there, and removes a file or symlink standing where it needs a directory. A
77
+ * snapshot leaves ignored files out of that tree, so such a file is the user's and
78
+ * no revert could bring it back. For each added path, one lstat of it and of each
79
+ * parent it does not share with another; a directory holding what stays is only
80
+ * walked when a file is about to take its name. A directory the path merely enters
81
+ * loses nothing, and neither do the entries the same checkout removes. Only ENOENT
82
+ * and ENOTDIR are absence: a path that cannot be looked at is thrown, so the write
83
+ * and its dry run both refuse instead of reading it as free.
84
+ */
85
+ export function occupiedPaths(worktree, records) {
86
+ const removed = new Set(records.filter((record) => record.newMode === ABSENT_MODE).map((record) => record.src));
87
+ // Parents already looked at: a real directory is entered; anything else stops the walk.
88
+ const parents = new Map();
89
+ const occupied = new Set();
90
+ /** On a filesystem that folds case, a path about to go can answer for a new spelling. */
91
+ const judge = aliasJudge(worktree);
92
+ const leaving = byFold(removed);
93
+ const isLeaving = (path) => foldsOnto(judge, leaving, path);
94
+ const enterable = (parent) => {
95
+ const known = parents.get(parent);
96
+ if (known)
97
+ return known === "enter";
98
+ const cut = parent.lastIndexOf("/");
99
+ const reachable = cut <= 0 || enterable(parent.slice(0, cut));
100
+ let answer = "stop";
101
+ if (reachable) {
102
+ const stat = lstatPresent(join(worktree, parent));
103
+ if (stat?.isDirectory())
104
+ answer = "enter";
105
+ else if (stat && !removed.has(parent) && !isLeaving(parent))
106
+ occupied.add(parent);
107
+ }
108
+ parents.set(parent, answer);
109
+ return answer === "enter";
110
+ };
111
+ for (const record of records) {
112
+ if (record.oldMode !== ABSENT_MODE)
113
+ continue;
114
+ const cut = record.src.lastIndexOf("/");
115
+ if (cut > 0 && !enterable(record.src.slice(0, cut)))
116
+ continue;
117
+ const stat = lstatPresent(join(worktree, record.src));
118
+ if (!stat)
119
+ continue;
120
+ if (stat.isDirectory()) {
121
+ // A gitlink's own directory stays; a file taking its name would remove it.
122
+ if (record.newMode !== "160000")
123
+ for (const path of survivors(worktree, record.src, removed))
124
+ occupied.add(path);
125
+ }
126
+ else if (!isLeaving(record.src))
127
+ occupied.add(record.src);
128
+ }
129
+ return [...occupied];
130
+ }
131
+ //# sourceMappingURL=object-occupied.js.map