@ricsam/r5d-worker 0.0.136 → 0.0.137
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.
- package/dist/cjs/main.cjs +7 -1
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/workspace-git-sync.cjs +241 -96
- package/dist/cjs/workspace-hydration-ledger.cjs +28 -11
- package/dist/cjs/workspace-hydration-merge.cjs +17 -2
- package/dist/cjs/workspace-merge-projection.cjs +190 -29
- package/dist/mjs/main.mjs +7 -1
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/workspace-git-sync.mjs +243 -97
- package/dist/mjs/workspace-hydration-ledger.mjs +27 -11
- package/dist/mjs/workspace-hydration-merge.mjs +17 -2
- package/dist/mjs/workspace-merge-projection.mjs +190 -29
- package/dist/types/runtime/adapter.d.ts +1 -0
- package/dist/types/runtime/client.d.ts +15 -0
- package/dist/types/runtime/daemon.d.ts +8 -0
- package/dist/types/runtime/executor.d.ts +30 -0
- package/dist/types/runtime/index.d.ts +5 -0
- package/dist/types/runtime/protocol.d.ts +372 -0
- package/dist/types/runtime/pty.d.ts +29 -0
- package/dist/types/runtime/storage.d.ts +21 -0
- package/dist/types/runtime/test-harness.d.ts +53 -0
- package/dist/types/workspace-filesystem-job-types.d.ts +19 -5
- package/dist/types/workspace-git-sync.d.ts +25 -6
- package/dist/types/workspace-hydration-ledger.d.ts +51 -18
- package/dist/types/workspace-hydration-merge.d.ts +8 -0
- package/dist/types/workspace-merge-projection.d.ts +10 -1
- package/package.json +2 -2
|
@@ -18,34 +18,49 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var workspace_hydration_ledger_exports = {};
|
|
20
20
|
__export(workspace_hydration_ledger_exports, {
|
|
21
|
+
WorkspaceHydrationLedger: () => WorkspaceHydrationLedger,
|
|
21
22
|
WorkspaceHydrationPreBlobLedger: () => WorkspaceHydrationPreBlobLedger
|
|
22
23
|
});
|
|
23
24
|
module.exports = __toCommonJS(workspace_hydration_ledger_exports);
|
|
24
|
-
class
|
|
25
|
+
class WorkspaceHydrationLedger {
|
|
25
26
|
mounts = /* @__PURE__ */ new Map();
|
|
26
27
|
static key(mount) {
|
|
27
28
|
return `${mount.hydrationIncarnationKey}\0${mount.id}`;
|
|
28
29
|
}
|
|
29
|
-
/**
|
|
30
|
+
/** Entries by mount-relative path for the mount, or an empty map. */
|
|
31
|
+
entries(mount) {
|
|
32
|
+
return this.mounts.get(WorkspaceHydrationLedger.key(mount)) ?? /* @__PURE__ */ new Map();
|
|
33
|
+
}
|
|
34
|
+
/** Blob ids by mount-relative path for the mount (the §3.3 detector's input), or an empty map. */
|
|
30
35
|
preBlobs(mount) {
|
|
31
|
-
return this.
|
|
36
|
+
return new Map([...this.entries(mount)].map(([relativePath, entry]) => [relativePath, entry.preBlob]));
|
|
32
37
|
}
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Record what a merge hydration replaced; earlier entries for other paths
|
|
40
|
+
* are kept. A path that already carries a live entry keeps its `preBlob`
|
|
41
|
+
* while the new hydration also ran across a live process: the process's
|
|
42
|
+
* read predates both hydrations, so what it read is still the first blob.
|
|
43
|
+
*/
|
|
44
|
+
record(mount, entries, options = {}) {
|
|
45
|
+
const incoming = entries instanceof Map ? [...entries] : Object.entries(entries);
|
|
46
|
+
if (incoming.length === 0) return;
|
|
47
|
+
const replace = new Set(options.replacePreBlobFor ?? []);
|
|
48
|
+
const key = WorkspaceHydrationLedger.key(mount);
|
|
38
49
|
const current = this.mounts.get(key) ?? /* @__PURE__ */ new Map();
|
|
39
|
-
for (const [relativePath,
|
|
50
|
+
for (const [relativePath, entry] of incoming) {
|
|
51
|
+
const previous = current.get(relativePath);
|
|
52
|
+
const preBlob = previous && previous.processLiveAcrossHydration && entry.processLiveAcrossHydration && !replace.has(relativePath) ? previous.preBlob : entry.preBlob;
|
|
53
|
+
current.set(relativePath, { ...entry, preBlob });
|
|
54
|
+
}
|
|
40
55
|
this.mounts.set(key, current);
|
|
41
56
|
}
|
|
42
57
|
/** Forget the mount: its checkout was rewritten by something this ledger did not observe. */
|
|
43
58
|
clearMount(mount) {
|
|
44
|
-
this.mounts.delete(
|
|
59
|
+
this.mounts.delete(WorkspaceHydrationLedger.key(mount));
|
|
45
60
|
}
|
|
46
61
|
/** Forget paths whose checkout content moved on from the hydrated bytes: a later return to the old bytes is a deliberate edit. */
|
|
47
62
|
forget(mount, paths) {
|
|
48
|
-
const key =
|
|
63
|
+
const key = WorkspaceHydrationLedger.key(mount);
|
|
49
64
|
const current = this.mounts.get(key);
|
|
50
65
|
if (!current) return;
|
|
51
66
|
for (const relativePath of paths) current.delete(relativePath);
|
|
@@ -60,7 +75,9 @@ class WorkspaceHydrationPreBlobLedger {
|
|
|
60
75
|
return total;
|
|
61
76
|
}
|
|
62
77
|
}
|
|
78
|
+
const WorkspaceHydrationPreBlobLedger = WorkspaceHydrationLedger;
|
|
63
79
|
// Annotate the CommonJS export names for ESM import in node:
|
|
64
80
|
0 && (module.exports = {
|
|
81
|
+
WorkspaceHydrationLedger,
|
|
65
82
|
WorkspaceHydrationPreBlobLedger
|
|
66
83
|
});
|
|
@@ -120,6 +120,8 @@ function decideWorkspaceHydrationMerge(input) {
|
|
|
120
120
|
const overrides = /* @__PURE__ */ new Map();
|
|
121
121
|
const expectations = /* @__PURE__ */ new Map();
|
|
122
122
|
const preBlobs = /* @__PURE__ */ new Map();
|
|
123
|
+
const postBlobs = /* @__PURE__ */ new Map();
|
|
124
|
+
const blobsToStore = /* @__PURE__ */ new Map();
|
|
123
125
|
const mergedPaths = [];
|
|
124
126
|
const keptPaths = /* @__PURE__ */ new Set();
|
|
125
127
|
const desiredForIndex = new Map(desired);
|
|
@@ -172,13 +174,17 @@ function decideWorkspaceHydrationMerge(input) {
|
|
|
172
174
|
remove.add(relativePath);
|
|
173
175
|
expectations.set(relativePath, oursStat);
|
|
174
176
|
}
|
|
175
|
-
if (change.base && isBlobMode(change.base.mode))
|
|
177
|
+
if (change.base && isBlobMode(change.base.mode)) {
|
|
178
|
+
preBlobs.set(relativePath, change.base.objectId);
|
|
179
|
+
postBlobs.set(relativePath, null);
|
|
180
|
+
}
|
|
176
181
|
continue;
|
|
177
182
|
}
|
|
178
183
|
write.add(relativePath);
|
|
179
184
|
expectations.set(relativePath, oursStat ?? currentTargetStat(targetRoot, relativePath));
|
|
180
185
|
if (change.base && isBlobMode(change.base.mode) && change.base.objectId !== change.theirs.objectId) {
|
|
181
186
|
preBlobs.set(relativePath, change.base.objectId);
|
|
187
|
+
postBlobs.set(relativePath, isBlobMode(change.theirs.mode) ? change.theirs.objectId : null);
|
|
182
188
|
}
|
|
183
189
|
continue;
|
|
184
190
|
}
|
|
@@ -292,7 +298,14 @@ function decideWorkspaceHydrationMerge(input) {
|
|
|
292
298
|
overrides.set(relativePath, { content: merged.content, mode });
|
|
293
299
|
write.add(relativePath);
|
|
294
300
|
expectations.set(relativePath, candidate.oursStat);
|
|
295
|
-
if (!contentUnchanged)
|
|
301
|
+
if (!contentUnchanged) {
|
|
302
|
+
const preBlob = hashOf(oursBytes);
|
|
303
|
+
const postBlob = hashOf(merged.content);
|
|
304
|
+
preBlobs.set(relativePath, preBlob);
|
|
305
|
+
postBlobs.set(relativePath, postBlob);
|
|
306
|
+
blobsToStore.set(preBlob, oursBytes);
|
|
307
|
+
blobsToStore.set(postBlob, merged.content);
|
|
308
|
+
}
|
|
296
309
|
mergedPaths.push(relativePath);
|
|
297
310
|
}
|
|
298
311
|
}
|
|
@@ -366,6 +379,8 @@ function decideWorkspaceHydrationMerge(input) {
|
|
|
366
379
|
overrides,
|
|
367
380
|
expectations,
|
|
368
381
|
preBlobs,
|
|
382
|
+
postBlobs,
|
|
383
|
+
blobsToStore,
|
|
369
384
|
mergedPaths: mergedPaths.sort(),
|
|
370
385
|
keptPaths: [...keptPaths].sort()
|
|
371
386
|
};
|
|
@@ -251,9 +251,12 @@ function synthesizeMountTree(input) {
|
|
|
251
251
|
removeTemporaryIndex(indexPath);
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
|
-
function
|
|
255
|
-
|
|
256
|
-
|
|
254
|
+
function ledgerStatMatches(recorded, projected) {
|
|
255
|
+
return recorded !== null && projected?.kind === "file" && projected.ino === recorded.ino && projected.size === recorded.size && projected.mode === recorded.mode && projected.mtimeMs === recorded.mtimeMs && projected.ctimeMs === recorded.ctimeMs;
|
|
256
|
+
}
|
|
257
|
+
function classifyHydrationLedger(input) {
|
|
258
|
+
const entries = Object.entries(input.hydrationLedger ?? {});
|
|
259
|
+
if (entries.length === 0) return { stale: [], merge: [], cleared: [] };
|
|
257
260
|
const probed = gitText(input.workspacePath, ["cat-file", "--batch-check"], "resolve workspace projection basis subtree", {
|
|
258
261
|
stdin: Buffer.from(`${input.basisHead}:${input.workspaceRelativePath}
|
|
259
262
|
`)
|
|
@@ -274,21 +277,116 @@ function staleRewritePaths(input) {
|
|
|
274
277
|
for (let index = 0; index + 1 < records.length; index += 2) changed.add(records[index + 1].toString());
|
|
275
278
|
}
|
|
276
279
|
const stale = [];
|
|
280
|
+
const merge = [];
|
|
277
281
|
const cleared = [];
|
|
278
|
-
for (const [relativePath,
|
|
282
|
+
for (const [relativePath, entry] of entries) {
|
|
279
283
|
const synthesized = input.blobs.get(relativePath);
|
|
280
284
|
if (synthesized === void 0) {
|
|
285
|
+
if (entry.postBlob === null) continue;
|
|
286
|
+
cleared.push(relativePath);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (ledgerStatMatches(entry.stat, input.projectedFiles[relativePath]) || synthesized === entry.postBlob) continue;
|
|
290
|
+
if (!changed.has(relativePath)) {
|
|
281
291
|
cleared.push(relativePath);
|
|
282
292
|
continue;
|
|
283
293
|
}
|
|
284
|
-
if (
|
|
285
|
-
|
|
286
|
-
else cleared.push(relativePath);
|
|
294
|
+
if (entry.processLiveAcrossHydration) {
|
|
295
|
+
merge.push({ relativePath, preBlob: entry.preBlob, postBlob: entry.postBlob });
|
|
287
296
|
continue;
|
|
288
297
|
}
|
|
289
|
-
if (
|
|
298
|
+
if (synthesized === entry.preBlob) {
|
|
299
|
+
stale.push(relativePath);
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
cleared.push(relativePath);
|
|
303
|
+
}
|
|
304
|
+
if (merge.length > 0) {
|
|
305
|
+
const ids = [...new Set(merge.flatMap(({ preBlob, postBlob }) => postBlob ? [preBlob, postBlob] : [preBlob]))];
|
|
306
|
+
const checked = gitText(input.workspacePath, ["cat-file", "--batch-check"], "verify hydration ledger blobs", {
|
|
307
|
+
stdin: Buffer.from(`${ids.join("\n")}
|
|
308
|
+
`)
|
|
309
|
+
});
|
|
310
|
+
const missing = new Set(
|
|
311
|
+
checked.split("\n").filter((line) => / missing$/u.test(line)).map((line) => line.split(" ")[0])
|
|
312
|
+
);
|
|
313
|
+
if (missing.size > 0) {
|
|
314
|
+
for (let index = merge.length - 1; index >= 0; index -= 1) {
|
|
315
|
+
const { relativePath, preBlob, postBlob } = merge[index];
|
|
316
|
+
if (missing.has(preBlob) || postBlob !== null && missing.has(postBlob)) {
|
|
317
|
+
merge.splice(index, 1);
|
|
318
|
+
cleared.push(relativePath);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return { stale: stale.sort(), merge: merge.sort((left, right) => left.relativePath.localeCompare(right.relativePath)), cleared: cleared.sort() };
|
|
324
|
+
}
|
|
325
|
+
function hydrationLedgerMergeBase(input) {
|
|
326
|
+
if (input.merges.length === 0) return input.basisHead;
|
|
327
|
+
return commitWithLedgerPaths({
|
|
328
|
+
workspacePath: input.workspacePath,
|
|
329
|
+
workspaceRelativePath: input.workspaceRelativePath,
|
|
330
|
+
parent: input.basisHead,
|
|
331
|
+
paths: input.merges.map(({ relativePath, preBlob }) => ({ relativePath, blob: preBlob })),
|
|
332
|
+
message: JSON.stringify({
|
|
333
|
+
type: "workspace_hydration_ledger_basis",
|
|
334
|
+
mountId: input.mountId,
|
|
335
|
+
attemptId: input.attemptId,
|
|
336
|
+
paths: input.merges.map(({ relativePath }) => relativePath)
|
|
337
|
+
}),
|
|
338
|
+
action: "hydration ledger merge base"
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
function hydrationLedgerTheirs(input) {
|
|
342
|
+
const paths = input.merges.flatMap(({ relativePath, postBlob }) => postBlob ? [{ relativePath, blob: postBlob }] : []);
|
|
343
|
+
if (paths.length === 0) return input.currentHead;
|
|
344
|
+
return commitWithLedgerPaths({
|
|
345
|
+
workspacePath: input.workspacePath,
|
|
346
|
+
workspaceRelativePath: input.workspaceRelativePath,
|
|
347
|
+
parent: input.currentHead,
|
|
348
|
+
paths,
|
|
349
|
+
message: JSON.stringify({
|
|
350
|
+
type: "workspace_hydration_ledger_theirs",
|
|
351
|
+
mountId: input.mountId,
|
|
352
|
+
attemptId: input.attemptId,
|
|
353
|
+
paths: paths.map(({ relativePath }) => relativePath)
|
|
354
|
+
}),
|
|
355
|
+
action: "hydration ledger theirs"
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
function commitWithLedgerPaths(input) {
|
|
359
|
+
const indexPath = temporaryIndexPath(input.workspacePath);
|
|
360
|
+
const environment = temporaryIndexEnvironment(indexPath);
|
|
361
|
+
try {
|
|
362
|
+
git(input.workspacePath, ["read-tree", input.parent], `read workspace tree for the ${input.action}`, { environment });
|
|
363
|
+
const paths = input.paths.map(({ relativePath }) => `${input.workspaceRelativePath}/${relativePath}`);
|
|
364
|
+
const listed = git(
|
|
365
|
+
input.workspacePath,
|
|
366
|
+
["ls-files", "-z", "--stage", "--", ...paths.map((entry) => `:(literal)${entry}`)],
|
|
367
|
+
`inspect modes for the ${input.action}`,
|
|
368
|
+
{ environment }
|
|
369
|
+
);
|
|
370
|
+
const modes = /* @__PURE__ */ new Map();
|
|
371
|
+
for (const record of nulRecords(listed)) {
|
|
372
|
+
const match = /^([0-7]{6}) [0-9a-f]{40,64} [0-3]\t(.+)$/su.exec(record.toString());
|
|
373
|
+
if (match) modes.set(match[2], match[1]);
|
|
374
|
+
}
|
|
375
|
+
const records = input.paths.map(
|
|
376
|
+
({ relativePath, blob }, index) => indexInfoRecord(modes.get(paths[index]) ?? "100644", requireObjectId(blob, `hydration ledger blob for ${relativePath}`), paths[index])
|
|
377
|
+
);
|
|
378
|
+
git(input.workspacePath, ["update-index", "-z", "--index-info"], `rewrite paths for the ${input.action}`, {
|
|
379
|
+
environment,
|
|
380
|
+
stdin: Buffer.concat(records)
|
|
381
|
+
});
|
|
382
|
+
const tree = requireObjectId(gitText(input.workspacePath, ["write-tree"], `write the ${input.action}`, { environment }), `${input.action} tree`);
|
|
383
|
+
return requireObjectId(
|
|
384
|
+
gitText(input.workspacePath, ["commit-tree", tree, "-p", input.parent, "-m", input.message], `commit the ${input.action}`),
|
|
385
|
+
`${input.action} commit`
|
|
386
|
+
);
|
|
387
|
+
} finally {
|
|
388
|
+
removeTemporaryIndex(indexPath);
|
|
290
389
|
}
|
|
291
|
-
return { stale: stale.sort(), cleared: cleared.sort() };
|
|
292
390
|
}
|
|
293
391
|
function nulRecords(content) {
|
|
294
392
|
const records = [];
|
|
@@ -322,12 +420,14 @@ function graftMountTree(input) {
|
|
|
322
420
|
stdin: removedPaths
|
|
323
421
|
});
|
|
324
422
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
423
|
+
if (input.mountTree !== null) {
|
|
424
|
+
git(
|
|
425
|
+
input.workspacePath,
|
|
426
|
+
["read-tree", "-i", `--prefix=${input.workspaceRelativePath}/`, input.mountTree],
|
|
427
|
+
"graft workspace projection mount tree",
|
|
428
|
+
{ environment }
|
|
429
|
+
);
|
|
430
|
+
}
|
|
331
431
|
return requireObjectId(
|
|
332
432
|
gitText(input.workspacePath, ["write-tree"], "write grafted workspace projection tree", { environment }),
|
|
333
433
|
"Grafted workspace projection tree"
|
|
@@ -336,25 +436,41 @@ function graftMountTree(input) {
|
|
|
336
436
|
removeTemporaryIndex(indexPath);
|
|
337
437
|
}
|
|
338
438
|
}
|
|
439
|
+
function mountSubtreeObjectId(workspacePath, revision, workspaceRelativePath, label) {
|
|
440
|
+
const spec = `${revision}:${workspaceRelativePath}`;
|
|
441
|
+
if (gitResult(workspacePath, ["cat-file", "-e", spec]).exitCode !== 0) return null;
|
|
442
|
+
const type = gitText(workspacePath, ["cat-file", "-t", spec], `inspect ${label}`);
|
|
443
|
+
if (type !== "tree") throw new Error(`${label} is not a directory at ${workspaceRelativePath}`);
|
|
444
|
+
return requireObjectId(gitText(workspacePath, ["rev-parse", spec], `resolve ${label}`), label);
|
|
445
|
+
}
|
|
339
446
|
function synthesizeOursCommit(input) {
|
|
340
447
|
const synthesized = synthesizeMountTree({
|
|
341
448
|
workspacePath: input.workspacePath,
|
|
342
449
|
sourcePath: input.mount.sourcePath,
|
|
343
450
|
sourceMode: input.mount.sourceMode
|
|
344
451
|
});
|
|
345
|
-
const
|
|
452
|
+
const classified = classifyHydrationLedger({
|
|
346
453
|
workspacePath: input.workspacePath,
|
|
347
454
|
workspaceRelativePath: input.workspaceRelativePath,
|
|
348
455
|
basisHead: input.basisHead,
|
|
349
456
|
mountTree: synthesized.tree,
|
|
350
457
|
blobs: synthesized.blobs,
|
|
351
|
-
|
|
458
|
+
projectedFiles: synthesized.projectedFiles,
|
|
459
|
+
hydrationLedger: input.hydrationLedger
|
|
352
460
|
});
|
|
353
|
-
if (
|
|
354
|
-
const
|
|
461
|
+
if (classified.stale.length > 0) return { staleRewritePaths: classified.stale, staleRewriteCleared: classified.cleared };
|
|
462
|
+
const mergeBase = hydrationLedgerMergeBase({
|
|
355
463
|
workspacePath: input.workspacePath,
|
|
356
464
|
workspaceRelativePath: input.workspaceRelativePath,
|
|
357
465
|
basisHead: input.basisHead,
|
|
466
|
+
merges: classified.merge,
|
|
467
|
+
mountId: input.mount.id,
|
|
468
|
+
attemptId: input.attemptId
|
|
469
|
+
});
|
|
470
|
+
const rootTree = graftMountTree({
|
|
471
|
+
workspacePath: input.workspacePath,
|
|
472
|
+
workspaceRelativePath: input.workspaceRelativePath,
|
|
473
|
+
basisHead: mergeBase,
|
|
358
474
|
mountTree: synthesized.tree
|
|
359
475
|
});
|
|
360
476
|
const message = JSON.stringify({
|
|
@@ -365,12 +481,18 @@ function synthesizeOursCommit(input) {
|
|
|
365
481
|
const oursCommit = requireObjectId(
|
|
366
482
|
gitText(
|
|
367
483
|
input.workspacePath,
|
|
368
|
-
["commit-tree", rootTree, "-p",
|
|
484
|
+
["commit-tree", rootTree, "-p", mergeBase, "-m", message],
|
|
369
485
|
`commit synthesized workspace projection for mount ${input.mount.id}`
|
|
370
486
|
),
|
|
371
487
|
`Synthesized workspace projection commit for mount ${input.mount.id}`
|
|
372
488
|
);
|
|
373
|
-
return {
|
|
489
|
+
return {
|
|
490
|
+
oursCommit,
|
|
491
|
+
mergeBase,
|
|
492
|
+
projectedFiles: synthesized.projectedFiles,
|
|
493
|
+
staleRewriteCleared: classified.cleared,
|
|
494
|
+
ledgerMerges: classified.merge
|
|
495
|
+
};
|
|
374
496
|
}
|
|
375
497
|
function mergeWorkspaceProjectionMount(input) {
|
|
376
498
|
const support = workspaceMergeProjectionSupport();
|
|
@@ -385,31 +507,69 @@ function mergeWorkspaceProjectionMount(input) {
|
|
|
385
507
|
workspaceRelativePath,
|
|
386
508
|
basisHead,
|
|
387
509
|
attemptId: input.attemptId,
|
|
388
|
-
...input.
|
|
510
|
+
...input.hydrationLedger ? { hydrationLedger: input.hydrationLedger } : {}
|
|
389
511
|
});
|
|
390
512
|
if ("staleRewritePaths" in synthesized) {
|
|
391
513
|
return { kind: "stale_rewrite", paths: synthesized.staleRewritePaths, staleRewriteCleared: synthesized.staleRewriteCleared };
|
|
392
514
|
}
|
|
393
|
-
const { oursCommit, projectedFiles, staleRewriteCleared } = synthesized;
|
|
515
|
+
const { oursCommit, mergeBase, projectedFiles, staleRewriteCleared, ledgerMerges } = synthesized;
|
|
516
|
+
const ledgerMergedPaths = ledgerMerges.map(({ relativePath }) => relativePath);
|
|
517
|
+
const theirsHead = hydrationLedgerTheirs({
|
|
518
|
+
workspacePath,
|
|
519
|
+
workspaceRelativePath,
|
|
520
|
+
currentHead,
|
|
521
|
+
merges: ledgerMerges,
|
|
522
|
+
mountId: input.mount.id,
|
|
523
|
+
attemptId: input.attemptId
|
|
524
|
+
});
|
|
394
525
|
const readAtMs = Date.now();
|
|
526
|
+
const inboundMountTree = mountSubtreeObjectId(
|
|
527
|
+
workspacePath,
|
|
528
|
+
theirsHead,
|
|
529
|
+
workspaceRelativePath,
|
|
530
|
+
`current workspace head subtree for mount ${input.mount.id}`
|
|
531
|
+
);
|
|
532
|
+
const inboundCommit = requireObjectId(
|
|
533
|
+
gitText(
|
|
534
|
+
workspacePath,
|
|
535
|
+
[
|
|
536
|
+
"commit-tree",
|
|
537
|
+
graftMountTree({ workspacePath, workspaceRelativePath, basisHead: mergeBase, mountTree: inboundMountTree }),
|
|
538
|
+
"-p",
|
|
539
|
+
mergeBase,
|
|
540
|
+
"-m",
|
|
541
|
+
JSON.stringify({ type: "workspace_projection_inbound", mountId: input.mount.id, attemptId: input.attemptId })
|
|
542
|
+
],
|
|
543
|
+
`commit inbound workspace projection for mount ${input.mount.id}`
|
|
544
|
+
),
|
|
545
|
+
`Inbound workspace projection commit for mount ${input.mount.id}`
|
|
546
|
+
);
|
|
395
547
|
const merged = gitResult(workspacePath, [
|
|
396
548
|
"merge-tree",
|
|
397
549
|
"--write-tree",
|
|
398
|
-
`--merge-base=${
|
|
550
|
+
`--merge-base=${mergeBase}`,
|
|
399
551
|
"--name-only",
|
|
400
552
|
"-z",
|
|
401
553
|
"--no-messages",
|
|
402
554
|
oursCommit,
|
|
403
|
-
|
|
555
|
+
inboundCommit
|
|
404
556
|
]);
|
|
405
557
|
if (merged.exitCode !== 0 && merged.exitCode !== 1) {
|
|
406
558
|
const detail = merged.stderr.toString().trim() || merged.stdout.toString().trim() || `git exited ${merged.exitCode}`;
|
|
407
559
|
throw new Error(`Merge workspace projection for mount ${input.mount.id}: ${detail}`);
|
|
408
560
|
}
|
|
409
561
|
const records = nulRecords(merged.stdout);
|
|
410
|
-
const
|
|
411
|
-
requireObjectId(
|
|
412
|
-
if (merged.exitCode === 0)
|
|
562
|
+
const mergedTree = records.shift()?.toString() ?? "";
|
|
563
|
+
requireObjectId(mergedTree, `Merged workspace projection tree for mount ${input.mount.id}`);
|
|
564
|
+
if (merged.exitCode === 0) {
|
|
565
|
+
const resultTree = graftMountTree({
|
|
566
|
+
workspacePath,
|
|
567
|
+
workspaceRelativePath,
|
|
568
|
+
basisHead: currentHead,
|
|
569
|
+
mountTree: mountSubtreeObjectId(workspacePath, mergedTree, workspaceRelativePath, `merged subtree for mount ${input.mount.id}`)
|
|
570
|
+
});
|
|
571
|
+
return { kind: "clean", resultTree, oursCommit, projectedFiles, readAtMs, staleRewriteCleared, ledgerMergedPaths };
|
|
572
|
+
}
|
|
413
573
|
const conflictPaths = records.map((record) => record.toString()).sort();
|
|
414
574
|
return {
|
|
415
575
|
kind: "conflict",
|
|
@@ -418,7 +578,8 @@ function mergeWorkspaceProjectionMount(input) {
|
|
|
418
578
|
error: `Workspace projection for mount ${input.mount.id} conflicted with the current workspace head`,
|
|
419
579
|
projectedFiles,
|
|
420
580
|
readAtMs,
|
|
421
|
-
staleRewriteCleared
|
|
581
|
+
staleRewriteCleared,
|
|
582
|
+
ledgerMergedPaths
|
|
422
583
|
};
|
|
423
584
|
}
|
|
424
585
|
function materializeWorkspaceProjectionTree(input) {
|
package/dist/mjs/main.mjs
CHANGED
|
@@ -4330,6 +4330,8 @@ async function startWorker(options, projectRuntime = {
|
|
|
4330
4330
|
});
|
|
4331
4331
|
};
|
|
4332
4332
|
const projectBranchMountBusy = (projectId, branchName, branchPath) => !readyProjectIds.has(projectId) || projectBranchMountActivityBusy(projectId, branchName, branchPath);
|
|
4333
|
+
const projectBranchMountFenced = (projectId, branchName, branchPath) => !readyProjectIds.has(projectId) || projectConfigById.get(projectId)?.executionDisabled === true || hasProjectWorktree(branchPath) && projectWorktreeOperationInProgress(branchPath) || canonicalWorkspaceMutationIsActive(activeWorkspaceMutationTargets());
|
|
4334
|
+
const projectBranchMountProcessLive = (projectId, branchName) => projectBranchHasActiveWorkspaceTarget(projectId, branchName, activeWorkspaceMutationTargets());
|
|
4333
4335
|
const inventoryConfiguredProjectCheckouts = () => {
|
|
4334
4336
|
const inventory = { projects: [], missingCheckouts: [] };
|
|
4335
4337
|
for (const project of projectConfigById.values()) {
|
|
@@ -4415,7 +4417,11 @@ async function startWorker(options, projectRuntime = {
|
|
|
4415
4417
|
].filter((id) => typeof id === "string")
|
|
4416
4418
|
)
|
|
4417
4419
|
].join(", ") || "no live declared holder",
|
|
4418
|
-
|
|
4420
|
+
// A running job no longer pins its checkout: merge hydration folds
|
|
4421
|
+
// inbound changes into it. The plan mount below keeps the old fence
|
|
4422
|
+
// because its all-mode hydration is a plain mirror.
|
|
4423
|
+
busy: () => projectBranchMountFenced(project.projectId, branch.branchName, branchPath),
|
|
4424
|
+
processLive: () => projectBranchMountProcessLive(project.projectId, branch.branchName),
|
|
4419
4425
|
mutationToken: () => projectWorkspaceMutationToken(project.projectId, branch.branchName),
|
|
4420
4426
|
busyForRecovery: () => projectBranchMountExclusiveSyncBusy(project.projectId, branch.branchName, branchPath)
|
|
4421
4427
|
});
|
package/dist/mjs/package.json
CHANGED