@ricsam/r5d-worker 0.0.97 → 0.0.100
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 +37 -5
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/working-tree-mirror.cjs +256 -4
- package/dist/cjs/workspace-git-sync.cjs +179 -87
- package/dist/mjs/main.mjs +37 -5
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/working-tree-mirror.mjs +250 -3
- package/dist/mjs/workspace-git-sync.mjs +188 -88
- package/dist/types/main.d.ts +181 -0
- package/dist/types/working-tree-mirror.d.ts +80 -1
- package/dist/types/workspace-git-sync.d.ts +25 -0
- package/package.json +1 -1
package/dist/cjs/main.cjs
CHANGED
|
@@ -97,9 +97,35 @@ function workerServerHeartbeatAction(input) {
|
|
|
97
97
|
if (input.watchdogSweepDelayed || input.probeElapsedMs === void 0) return "start_probe";
|
|
98
98
|
return input.probeElapsedMs >= input.timeoutMs ? "terminate" : "wait";
|
|
99
99
|
}
|
|
100
|
+
const WORKSPACE_HYDRATION_LEASE_OPERATION_PREFIX = "hydration:";
|
|
101
|
+
function createWorkspaceHydrationLeaseHooks(send, options = {}) {
|
|
102
|
+
const yieldToEventLoop = options.yieldToEventLoop ?? (() => new Promise((resolve) => setImmediate(resolve)));
|
|
103
|
+
const log = options.log ?? ((line) => process.stdout.write(`${line}
|
|
104
|
+
`));
|
|
105
|
+
const now = options.now ?? (() => performance.now());
|
|
106
|
+
const operationId = (context) => `${WORKSPACE_HYDRATION_LEASE_OPERATION_PREFIX}${context.transactionId}`;
|
|
107
|
+
const startedAtMs = /* @__PURE__ */ new Map();
|
|
108
|
+
return {
|
|
109
|
+
async beforeTransaction(context) {
|
|
110
|
+
send({ type: "heartbeat_lease", operationId: operationId(context), kind: "hydration", active: true });
|
|
111
|
+
startedAtMs.set(context.transactionId, now());
|
|
112
|
+
log(`[r5d-worker] hydrating ${context.mountIds.length} workspace mount(s) under hydration lease ${context.transactionId}`);
|
|
113
|
+
await yieldToEventLoop();
|
|
114
|
+
},
|
|
115
|
+
afterTransaction(context) {
|
|
116
|
+
const startedAt = startedAtMs.get(context.transactionId);
|
|
117
|
+
startedAtMs.delete(context.transactionId);
|
|
118
|
+
send({ type: "heartbeat_lease", operationId: operationId(context), kind: "hydration", active: false });
|
|
119
|
+
log(
|
|
120
|
+
`[r5d-worker] released hydration lease ${context.transactionId} after ${startedAt === void 0 ? "?" : Math.round(now() - startedAt)}ms`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
100
125
|
const workerLifecycleTestHarness = {
|
|
101
126
|
boundedWorkerLifecycleProgress,
|
|
102
|
-
workerServerHeartbeatAction
|
|
127
|
+
workerServerHeartbeatAction,
|
|
128
|
+
createWorkspaceHydrationLeaseHooks
|
|
103
129
|
};
|
|
104
130
|
class ProjectWorkspaceConfigurationDeferredError extends Error {
|
|
105
131
|
}
|
|
@@ -4370,6 +4396,7 @@ async function startWorker(options) {
|
|
|
4370
4396
|
error: error instanceof Error ? error.message : String(error)
|
|
4371
4397
|
};
|
|
4372
4398
|
};
|
|
4399
|
+
const workspaceHydrationTransactionHooks = createWorkspaceHydrationLeaseHooks((message) => sendWorkerMessageFromCurrentSource(ws, message));
|
|
4373
4400
|
const performWorkspaceSync = async (input) => {
|
|
4374
4401
|
if (!workspaceRemoteUrl || !workspaceCredentialHelper || !workspaceCredentialUsername || !workspaceGitIdentity) {
|
|
4375
4402
|
throw new Error("Worker workspace configuration has not been received");
|
|
@@ -4392,7 +4419,8 @@ async function startWorker(options) {
|
|
|
4392
4419
|
credentialHelper: workspaceCredentialHelper,
|
|
4393
4420
|
credentialUsername: workspaceCredentialUsername,
|
|
4394
4421
|
gitIdentity: workspaceGitIdentity,
|
|
4395
|
-
mounts: configuredMounts
|
|
4422
|
+
mounts: configuredMounts,
|
|
4423
|
+
hydrationHooks: workspaceHydrationTransactionHooks
|
|
4396
4424
|
});
|
|
4397
4425
|
await applyObservedProjectHeadsAfterInboundWorkspace(observedProjectHeads2, reset.activeMountIds, true);
|
|
4398
4426
|
return {
|
|
@@ -4446,7 +4474,8 @@ async function startWorker(options) {
|
|
|
4446
4474
|
assertStillAdmitted: input.assertStillAdmitted,
|
|
4447
4475
|
afterWorkspacePublished: outerRemediation ? void 0 : async ({ activeMountIds, publishedHead }) => {
|
|
4448
4476
|
await pushChangedProjectHeads(activeMountIds, publishedHead, publicationHeads, expectedMirrorHeads, inboundMoveMountIds);
|
|
4449
|
-
}
|
|
4477
|
+
},
|
|
4478
|
+
hydrationHooks: workspaceHydrationTransactionHooks
|
|
4450
4479
|
});
|
|
4451
4480
|
input.assertStillAdmitted?.();
|
|
4452
4481
|
if (result.initialPublicationMountIds && result.initialPublicationMountIds.length > 0) {
|
|
@@ -5040,7 +5069,10 @@ async function startWorker(options) {
|
|
|
5040
5069
|
sourcePath: recoverySourceOverrides.get(mount.id) ?? mount.sourcePath,
|
|
5041
5070
|
busy: mount.busyForRecovery ?? mount.busy
|
|
5042
5071
|
}));
|
|
5043
|
-
await (0, import_workspace_git_sync.recoverWorkspaceGitHydration)(workspaceShadowRoot, recoveryMounts, {
|
|
5072
|
+
await (0, import_workspace_git_sync.recoverWorkspaceGitHydration)(workspaceShadowRoot, recoveryMounts, {
|
|
5073
|
+
preserveStaleBases: true,
|
|
5074
|
+
hydrationHooks: workspaceHydrationTransactionHooks
|
|
5075
|
+
});
|
|
5044
5076
|
for (const project of message.projects) {
|
|
5045
5077
|
for (const { branchName } of project.preserveOnlyBranches) {
|
|
5046
5078
|
if (stillPendingCreatedBranches.has((0, import_workspace_automatic_sync_policy.pendingCreatedBranchKey)(project.projectId, branchName))) {
|
|
@@ -5084,7 +5116,7 @@ async function startWorker(options) {
|
|
|
5084
5116
|
// layouts can be marked ready. No process can resolve an unready
|
|
5085
5117
|
// target, and preserving the fetched outer tree here prevents clean
|
|
5086
5118
|
// checkout initialization from erasing synchronized dirt.
|
|
5087
|
-
{ ignoreBusy: true }
|
|
5119
|
+
{ ignoreBusy: true, hydrationHooks: workspaceHydrationTransactionHooks }
|
|
5088
5120
|
);
|
|
5089
5121
|
await ensureConfiguredProjects((progress) => {
|
|
5090
5122
|
sendLifecycleProgress({ phase: "configuring", operationId: configurationOperationId, ...progress });
|
package/dist/cjs/package.json
CHANGED
|
@@ -29,8 +29,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
29
29
|
var working_tree_mirror_exports = {};
|
|
30
30
|
__export(working_tree_mirror_exports, {
|
|
31
31
|
assertWorkingTreeHasNoPortableGitMetadataAliases: () => assertWorkingTreeHasNoPortableGitMetadataAliases,
|
|
32
|
+
captureWorkingTreeMirrorPlan: () => captureWorkingTreeMirrorPlan,
|
|
33
|
+
fsyncWorkingTreePaths: () => fsyncWorkingTreePaths,
|
|
32
34
|
inspectWorkingTree: () => inspectWorkingTree,
|
|
33
|
-
mirrorWorkingTree: () => mirrorWorkingTree
|
|
35
|
+
mirrorWorkingTree: () => mirrorWorkingTree,
|
|
36
|
+
normalizeWorkingTreeRelativePath: () => normalizeWorkingTreeRelativePath,
|
|
37
|
+
planWorkingTreeMirror: () => planWorkingTreeMirror,
|
|
38
|
+
restoreWorkingTreeMirrorScope: () => restoreWorkingTreeMirrorScope
|
|
34
39
|
});
|
|
35
40
|
module.exports = __toCommonJS(working_tree_mirror_exports);
|
|
36
41
|
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
@@ -186,14 +191,55 @@ function addParentDirectories(entries, root) {
|
|
|
186
191
|
}
|
|
187
192
|
}
|
|
188
193
|
}
|
|
189
|
-
function
|
|
194
|
+
function gitlinkPaths(root) {
|
|
195
|
+
const result = Bun.spawnSync(
|
|
196
|
+
[
|
|
197
|
+
"git",
|
|
198
|
+
"-c",
|
|
199
|
+
"submodule.recurse=false",
|
|
200
|
+
"-c",
|
|
201
|
+
"fetch.recurseSubmodules=false",
|
|
202
|
+
"-c",
|
|
203
|
+
"push.recurseSubmodules=false",
|
|
204
|
+
"ls-files",
|
|
205
|
+
"-z",
|
|
206
|
+
"--stage",
|
|
207
|
+
"--",
|
|
208
|
+
"."
|
|
209
|
+
],
|
|
210
|
+
{
|
|
211
|
+
cwd: root,
|
|
212
|
+
stdout: "pipe",
|
|
213
|
+
stderr: "pipe",
|
|
214
|
+
env: (0, import_git_process_environment.workerGitProcessEnvironment)()
|
|
215
|
+
}
|
|
216
|
+
);
|
|
217
|
+
if (result.exitCode !== 0) {
|
|
218
|
+
throw new Error(`Inspect Git index gitlinks: ${result.stderr.toString().trim() || `git exited ${result.exitCode}`}`);
|
|
219
|
+
}
|
|
220
|
+
const gitlinks = /* @__PURE__ */ new Set();
|
|
221
|
+
for (const record of result.stdout.toString().split("\0")) {
|
|
222
|
+
const match = /^160000 [0-9a-f]+ [0-3]\t(.+)$/su.exec(record);
|
|
223
|
+
if (!match) continue;
|
|
224
|
+
const normalized = normalizeRelativePath(match[1]);
|
|
225
|
+
if (normalized) gitlinks.add(normalized);
|
|
226
|
+
}
|
|
227
|
+
return gitlinks;
|
|
228
|
+
}
|
|
229
|
+
function directoryIsEmpty(root, relativePath) {
|
|
230
|
+
const inspected = inspectRelativePathWithoutFollowingAncestors(root, relativePath);
|
|
231
|
+
return inspected.kind === "entry" && inspected.stat.isDirectory() && import_node_fs.default.readdirSync(inspected.absolutePath).length === 0;
|
|
232
|
+
}
|
|
233
|
+
function inspectWorkingTree(root, mode, options = {}) {
|
|
190
234
|
const entries = /* @__PURE__ */ new Map();
|
|
191
235
|
root = import_node_path.default.resolve(root);
|
|
192
236
|
if (!inspectDirectoryRoot(root, { allowMissing: true, label: "Working-tree root" })) return entries;
|
|
193
237
|
if (mode === "all") {
|
|
194
238
|
for (const child of import_node_fs.default.readdirSync(root).sort()) addEntry(entries, root, child, true);
|
|
195
239
|
} else {
|
|
240
|
+
const omittedGitlinks = options.omitUnpopulatedGitlinks ? gitlinkPaths(root) : null;
|
|
196
241
|
for (const relativePath of gitEligibleRoots(root)) {
|
|
242
|
+
if (omittedGitlinks?.has(relativePath) && directoryIsEmpty(root, relativePath)) continue;
|
|
197
243
|
addEntry(entries, root, relativePath, true);
|
|
198
244
|
}
|
|
199
245
|
}
|
|
@@ -367,7 +413,9 @@ function mirrorWorkingTree(input) {
|
|
|
367
413
|
}
|
|
368
414
|
inspectDirectoryRoot(targetRoot, { allowMissing: false, label: "Working-tree target root" });
|
|
369
415
|
const desired = inspectWorkingTree(sourceRoot, input.sourceMode);
|
|
370
|
-
const existing = inspectWorkingTree(targetRoot, input.deletionMode === "git" ? "git" : "all"
|
|
416
|
+
const existing = inspectWorkingTree(targetRoot, input.deletionMode === "git" ? "git" : "all", {
|
|
417
|
+
omitUnpopulatedGitlinks: input.deletionMode === "git"
|
|
418
|
+
});
|
|
371
419
|
for (const relativePath of [...existing.keys()].sort((left, right) => right.split("/").length - left.split("/").length)) {
|
|
372
420
|
if (!desired.has(relativePath)) removeEntry(targetRoot, relativePath);
|
|
373
421
|
}
|
|
@@ -378,9 +426,213 @@ function mirrorWorkingTree(input) {
|
|
|
378
426
|
}
|
|
379
427
|
return { paths: [...desired.keys()].filter((relativePath) => desired.get(relativePath)?.kind !== "directory").sort() };
|
|
380
428
|
}
|
|
429
|
+
function normalizeWorkingTreeRelativePath(value) {
|
|
430
|
+
return normalizeRelativePath(value);
|
|
431
|
+
}
|
|
432
|
+
const MIRROR_TEMPORARY_FILE_PATTERN = /^\.r5d-working-tree-mirror-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.tmp$/u;
|
|
433
|
+
function relativeDepth(relativePath) {
|
|
434
|
+
return relativePath.split("/").length;
|
|
435
|
+
}
|
|
436
|
+
function shallowestFirst(left, right) {
|
|
437
|
+
return relativeDepth(left) - relativeDepth(right) || left.localeCompare(right);
|
|
438
|
+
}
|
|
439
|
+
function deepestFirst(left, right) {
|
|
440
|
+
return relativeDepth(right) - relativeDepth(left) || left.localeCompare(right);
|
|
441
|
+
}
|
|
442
|
+
function hasAncestorIn(relativePath, ancestors) {
|
|
443
|
+
let parent = import_node_path.default.posix.dirname(relativePath);
|
|
444
|
+
while (parent !== ".") {
|
|
445
|
+
if (ancestors.has(parent)) return true;
|
|
446
|
+
parent = import_node_path.default.posix.dirname(parent);
|
|
447
|
+
}
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
function treeEntryFromStat(stat, absolutePath) {
|
|
451
|
+
if (stat.isSymbolicLink()) return { kind: "symlink", mode: stat.mode, target: import_node_fs.default.readlinkSync(absolutePath) };
|
|
452
|
+
if (stat.isFile()) return { kind: "file", mode: stat.mode, size: stat.size };
|
|
453
|
+
if (stat.isDirectory()) return { kind: "directory", mode: stat.mode };
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
function fsyncPath(targetPath, flags) {
|
|
457
|
+
const descriptor = import_node_fs.default.openSync(targetPath, flags);
|
|
458
|
+
try {
|
|
459
|
+
import_node_fs.default.fsyncSync(descriptor);
|
|
460
|
+
} finally {
|
|
461
|
+
import_node_fs.default.closeSync(descriptor);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
function planWorkingTreeMirror(input) {
|
|
465
|
+
const sourceRoot = import_node_path.default.resolve(input.sourceRoot);
|
|
466
|
+
const targetRoot = import_node_path.default.resolve(input.targetRoot);
|
|
467
|
+
const desired = inspectWorkingTree(sourceRoot, input.sourceMode);
|
|
468
|
+
const existing = inspectWorkingTree(targetRoot, input.deletionMode === "git" ? "git" : "all", {
|
|
469
|
+
omitUnpopulatedGitlinks: input.deletionMode === "git"
|
|
470
|
+
});
|
|
471
|
+
const replaced = /* @__PURE__ */ new Map();
|
|
472
|
+
const absent = [];
|
|
473
|
+
for (const [relativePath, entry] of existing) {
|
|
474
|
+
if (!desired.has(relativePath)) replaced.set(relativePath, entry);
|
|
475
|
+
}
|
|
476
|
+
for (const [relativePath, entry] of desired) {
|
|
477
|
+
const target = inspectRelativePathWithoutFollowingAncestors(targetRoot, relativePath);
|
|
478
|
+
if (target.kind !== "entry") {
|
|
479
|
+
absent.push(relativePath);
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
const { absolutePath, stat } = target;
|
|
483
|
+
if (entry.kind === "directory") {
|
|
484
|
+
if (stat.isDirectory()) continue;
|
|
485
|
+
} else if (entry.kind === "symlink") {
|
|
486
|
+
if (stat.isSymbolicLink() && import_node_fs.default.readlinkSync(absolutePath) === entry.target) continue;
|
|
487
|
+
} else if (stat.isFile() && filesEqual(import_node_path.default.join(sourceRoot, ...relativePath.split("/")), absolutePath, entry)) {
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
const current = treeEntryFromStat(stat, absolutePath);
|
|
491
|
+
if (current) replaced.set(relativePath, current);
|
|
492
|
+
else absent.push(relativePath);
|
|
493
|
+
}
|
|
494
|
+
const replacedDirectories = new Set(
|
|
495
|
+
[...replaced].filter(([, entry]) => entry.kind === "directory").map(([relativePath]) => relativePath)
|
|
496
|
+
);
|
|
497
|
+
for (const relativePath of [...replaced.keys()]) {
|
|
498
|
+
if (hasAncestorIn(relativePath, replacedDirectories)) replaced.delete(relativePath);
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
desired,
|
|
502
|
+
replaced,
|
|
503
|
+
absent: absent.filter((relativePath) => !hasAncestorIn(relativePath, replacedDirectories)).sort(shallowestFirst)
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
function captureWorkingTreeMirrorPlan(input) {
|
|
507
|
+
const targetRoot = import_node_path.default.resolve(input.targetRoot);
|
|
508
|
+
const snapshotRoot = import_node_path.default.resolve(input.snapshotRoot);
|
|
509
|
+
const durability = input.durability ?? "per_entry";
|
|
510
|
+
inspectDirectoryRoot(targetRoot, { allowMissing: false, label: "Working-tree target root" });
|
|
511
|
+
if (!inspectDirectoryRoot(snapshotRoot, { allowMissing: true, label: "Working-tree snapshot root" })) {
|
|
512
|
+
import_node_fs.default.mkdirSync(snapshotRoot, { recursive: true });
|
|
513
|
+
}
|
|
514
|
+
const trees = [];
|
|
515
|
+
for (const [relativePath, entry] of [...input.plan.replaced.entries()].sort(([left], [right]) => shallowestFirst(left, right))) {
|
|
516
|
+
if (entry.kind !== "directory") {
|
|
517
|
+
copyEntry(targetRoot, snapshotRoot, relativePath, entry, durability);
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
const source = inspectRelativePathWithoutFollowingAncestors(targetRoot, relativePath);
|
|
521
|
+
if (source.kind !== "entry" || !source.stat.isDirectory()) {
|
|
522
|
+
throw new Error(`Working-tree source changed while it was being mirrored: ${import_node_path.default.join(targetRoot, ...relativePath.split("/"))}`);
|
|
523
|
+
}
|
|
524
|
+
ensureDirectory(snapshotRoot, relativePath, entry.mode);
|
|
525
|
+
mirrorWorkingTree({
|
|
526
|
+
sourceRoot: source.absolutePath,
|
|
527
|
+
targetRoot: import_node_path.default.join(snapshotRoot, ...relativePath.split("/")),
|
|
528
|
+
sourceMode: "all",
|
|
529
|
+
deletionMode: "all",
|
|
530
|
+
durability
|
|
531
|
+
});
|
|
532
|
+
trees.push(relativePath);
|
|
533
|
+
}
|
|
534
|
+
return { absent: [...input.plan.absent].sort(shallowestFirst), trees: trees.sort(shallowestFirst) };
|
|
535
|
+
}
|
|
536
|
+
function restoreWorkingTreeMirrorScope(input) {
|
|
537
|
+
const snapshotRoot = import_node_path.default.resolve(input.snapshotRoot);
|
|
538
|
+
const targetRoot = import_node_path.default.resolve(input.targetRoot);
|
|
539
|
+
inspectDirectoryRoot(snapshotRoot, { allowMissing: false, label: "Working-tree snapshot root" });
|
|
540
|
+
if (!inspectDirectoryRoot(targetRoot, { allowMissing: true, label: "Working-tree target root" })) {
|
|
541
|
+
import_node_fs.default.mkdirSync(targetRoot, { recursive: true });
|
|
542
|
+
}
|
|
543
|
+
const normalize = (relativePath) => {
|
|
544
|
+
const normalized = normalizeRelativePath(relativePath);
|
|
545
|
+
if (!normalized) throw new Error(`Invalid working-tree scope path: ${relativePath}`);
|
|
546
|
+
return normalized;
|
|
547
|
+
};
|
|
548
|
+
const absent = input.scope.absent.map(normalize);
|
|
549
|
+
const trees = new Set(input.scope.trees.map(normalize));
|
|
550
|
+
const captured = inspectWorkingTree(snapshotRoot, "all");
|
|
551
|
+
for (const tree of trees) {
|
|
552
|
+
if (captured.get(tree)?.kind !== "directory") {
|
|
553
|
+
throw new Error(`Working-tree snapshot does not hold the captured directory: ${tree}`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
const touched = /* @__PURE__ */ new Set();
|
|
557
|
+
for (const relativePath of [...absent].sort(deepestFirst)) {
|
|
558
|
+
const inspected = inspectRelativePathWithoutFollowingAncestors(targetRoot, relativePath);
|
|
559
|
+
if (inspected.kind === "entry") import_node_fs.default.rmSync(inspected.absolutePath, { recursive: true, force: true });
|
|
560
|
+
touched.add(relativePath);
|
|
561
|
+
}
|
|
562
|
+
const writtenDirectories = /* @__PURE__ */ new Set();
|
|
563
|
+
for (const relativePath of absent) writtenDirectories.add(import_node_path.default.posix.dirname(relativePath));
|
|
564
|
+
for (const [relativePath, entry] of captured) {
|
|
565
|
+
if (entry.kind !== "directory" && !hasAncestorIn(relativePath, trees)) writtenDirectories.add(import_node_path.default.posix.dirname(relativePath));
|
|
566
|
+
}
|
|
567
|
+
for (const directory of writtenDirectories) {
|
|
568
|
+
let absoluteDirectory = targetRoot;
|
|
569
|
+
if (directory !== ".") {
|
|
570
|
+
const inspected = inspectRelativePathWithoutFollowingAncestors(targetRoot, directory);
|
|
571
|
+
if (inspected.kind !== "entry" || !inspected.stat.isDirectory()) continue;
|
|
572
|
+
absoluteDirectory = inspected.absolutePath;
|
|
573
|
+
}
|
|
574
|
+
for (const name of import_node_fs.default.readdirSync(absoluteDirectory)) {
|
|
575
|
+
if (!MIRROR_TEMPORARY_FILE_PATTERN.test(name)) continue;
|
|
576
|
+
const temporaryPath = import_node_path.default.join(absoluteDirectory, name);
|
|
577
|
+
if (!import_node_fs.default.lstatSync(temporaryPath).isFile()) continue;
|
|
578
|
+
import_node_fs.default.rmSync(temporaryPath, { force: true });
|
|
579
|
+
touched.add(directory === "." ? name : import_node_path.default.posix.join(directory, name));
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
for (const [relativePath, entry] of [...captured.entries()].sort(([left], [right]) => shallowestFirst(left, right))) {
|
|
583
|
+
touched.add(relativePath);
|
|
584
|
+
if (hasAncestorIn(relativePath, trees)) continue;
|
|
585
|
+
if (!trees.has(relativePath)) {
|
|
586
|
+
copyEntry(snapshotRoot, targetRoot, relativePath, entry, "per_entry");
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
const current = inspectRelativePathWithoutFollowingAncestors(targetRoot, relativePath);
|
|
590
|
+
if (current.kind === "blocked") {
|
|
591
|
+
throw new Error(`Refusing to restore a working-tree directory through a symlink or non-directory: ${current.blockingPath}`);
|
|
592
|
+
}
|
|
593
|
+
if (current.kind === "entry" && !current.stat.isDirectory()) {
|
|
594
|
+
import_node_fs.default.rmSync(current.absolutePath, { recursive: true, force: true });
|
|
595
|
+
}
|
|
596
|
+
ensureDirectory(targetRoot, relativePath, entry.mode);
|
|
597
|
+
mirrorWorkingTree({
|
|
598
|
+
sourceRoot: import_node_path.default.join(snapshotRoot, ...relativePath.split("/")),
|
|
599
|
+
targetRoot: import_node_path.default.join(targetRoot, ...relativePath.split("/")),
|
|
600
|
+
sourceMode: "all",
|
|
601
|
+
deletionMode: "all"
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
return { paths: [...touched].sort(shallowestFirst) };
|
|
605
|
+
}
|
|
606
|
+
function fsyncWorkingTreePaths(root, relativePaths) {
|
|
607
|
+
root = import_node_path.default.resolve(root);
|
|
608
|
+
inspectDirectoryRoot(root, { allowMissing: false, label: "Working-tree root" });
|
|
609
|
+
const directories = /* @__PURE__ */ new Set(["."]);
|
|
610
|
+
for (const relativePath of relativePaths) {
|
|
611
|
+
const normalized = normalizeRelativePath(relativePath);
|
|
612
|
+
if (!normalized) throw new Error(`Invalid working-tree path: ${relativePath}`);
|
|
613
|
+
directories.add(import_node_path.default.posix.dirname(normalized));
|
|
614
|
+
const inspected = inspectRelativePathWithoutFollowingAncestors(root, normalized);
|
|
615
|
+
if (inspected.kind !== "entry") continue;
|
|
616
|
+
if (inspected.stat.isDirectory()) directories.add(normalized);
|
|
617
|
+
else if (inspected.stat.isFile()) fsyncPath(inspected.absolutePath, import_node_fs.default.constants.O_RDONLY | import_node_fs.default.constants.O_NOFOLLOW);
|
|
618
|
+
}
|
|
619
|
+
for (const directory of directories) {
|
|
620
|
+
if (directory === ".") {
|
|
621
|
+
fsyncPath(root, import_node_fs.default.constants.O_RDONLY);
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
const inspected = inspectRelativePathWithoutFollowingAncestors(root, directory);
|
|
625
|
+
if (inspected.kind === "entry" && inspected.stat.isDirectory()) fsyncPath(inspected.absolutePath, import_node_fs.default.constants.O_RDONLY);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
381
628
|
// Annotate the CommonJS export names for ESM import in node:
|
|
382
629
|
0 && (module.exports = {
|
|
383
630
|
assertWorkingTreeHasNoPortableGitMetadataAliases,
|
|
631
|
+
captureWorkingTreeMirrorPlan,
|
|
632
|
+
fsyncWorkingTreePaths,
|
|
384
633
|
inspectWorkingTree,
|
|
385
|
-
mirrorWorkingTree
|
|
634
|
+
mirrorWorkingTree,
|
|
635
|
+
normalizeWorkingTreeRelativePath,
|
|
636
|
+
planWorkingTreeMirror,
|
|
637
|
+
restoreWorkingTreeMirrorScope
|
|
386
638
|
});
|