@ricsam/r5d-worker 0.0.95 → 0.0.97
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 +9 -0
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/workspace-git-sync.cjs +146 -14
- package/dist/cjs/workspace-merge-projection.cjs +19 -1
- package/dist/mjs/main.mjs +9 -0
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/workspace-git-sync.mjs +146 -14
- package/dist/mjs/workspace-merge-projection.mjs +19 -1
- package/dist/types/workspace-git-sync.d.ts +14 -1
- package/package.json +1 -1
package/dist/cjs/main.cjs
CHANGED
|
@@ -3824,6 +3824,7 @@ async function startWorker(options) {
|
|
|
3824
3824
|
const branchKey = projectBranchKey(project.projectId, branch.branchName);
|
|
3825
3825
|
const creatorLocalIncarnation = creatorLocalIncarnations.get(branchKey) === branch.branchId;
|
|
3826
3826
|
const preservesCheckoutPathMove = checkoutPathMoveBranches.has(branchKey);
|
|
3827
|
+
const initialPublicationSourceBranch = creatorLocalIncarnation ? branch.sourceBranchName : null;
|
|
3827
3828
|
mounts.push({
|
|
3828
3829
|
id: projectMountId(project.projectId, branch.branchName),
|
|
3829
3830
|
hydrationIncarnationKey: branch.branchId,
|
|
@@ -3833,6 +3834,7 @@ async function startWorker(options) {
|
|
|
3833
3834
|
sourceMode: "git",
|
|
3834
3835
|
hydrateDeletionMode: "git",
|
|
3835
3836
|
preserveLocalOnInitialOuterAbsence: creatorLocalIncarnation,
|
|
3837
|
+
...initialPublicationSourceBranch ? { initialPublicationSourceRelativePath: workspaceProjectRelativePath(project.projectId, initialPublicationSourceBranch) } : {},
|
|
3836
3838
|
preserveLocalOnHydrationBasisChange: preservesCheckoutPathMove,
|
|
3837
3839
|
busy: () => projectBranchMountBusy(project.projectId, branch.branchName, branchPath),
|
|
3838
3840
|
mutationToken: () => projectWorkspaceMutationToken(project.projectId, branch.branchName),
|
|
@@ -3847,6 +3849,7 @@ async function startWorker(options) {
|
|
|
3847
3849
|
sourceMode: "all",
|
|
3848
3850
|
hydrateDeletionMode: "all",
|
|
3849
3851
|
preserveLocalOnInitialOuterAbsence: creatorLocalIncarnation,
|
|
3852
|
+
...initialPublicationSourceBranch ? { initialPublicationSourceRelativePath: workspacePlanRelativePath(project.projectId, initialPublicationSourceBranch) } : {},
|
|
3850
3853
|
preserveLocalOnHydrationBasisChange: preservesCheckoutPathMove,
|
|
3851
3854
|
busy: () => projectBranchMountBusy(project.projectId, branch.branchName, branchPath),
|
|
3852
3855
|
mutationToken: () => projectWorkspaceMutationToken(project.projectId, branch.branchName),
|
|
@@ -4446,6 +4449,12 @@ async function startWorker(options) {
|
|
|
4446
4449
|
}
|
|
4447
4450
|
});
|
|
4448
4451
|
input.assertStillAdmitted?.();
|
|
4452
|
+
if (result.initialPublicationMountIds && result.initialPublicationMountIds.length > 0) {
|
|
4453
|
+
process.stderr.write(
|
|
4454
|
+
`[r5d-worker] published first checkout copies above the automatic diff-size limit, measured against their source subtrees at ${result.diffSizeBytes} bytes: ${result.initialPublicationMountIds.join(", ")}
|
|
4455
|
+
`
|
|
4456
|
+
);
|
|
4457
|
+
}
|
|
4449
4458
|
if (["no_change", "updated", "pushed"].includes(result.outcome) && !outerRemediation) {
|
|
4450
4459
|
await applyObservedProjectHeadsAfterInboundWorkspace(
|
|
4451
4460
|
observedProjectHeads,
|
package/dist/cjs/package.json
CHANGED
|
@@ -1483,13 +1483,38 @@ function hydrateWorkspaceGitMountsRaw(workspacePath, mounts, options = {}) {
|
|
|
1483
1483
|
}
|
|
1484
1484
|
return { hydratedMountIds: hydratedMountIds.sort(), skippedMountIds: skippedMountIds.sort() };
|
|
1485
1485
|
}
|
|
1486
|
+
async function selectMountsWithUnchangedOuterSubtree(workspacePath, receipt, head, mounts) {
|
|
1487
|
+
const candidates = mounts.flatMap((mount) => {
|
|
1488
|
+
const basisHead = workspaceHydrationReceiptMountBasisHead(receipt, mount);
|
|
1489
|
+
return basisHead && basisHead !== head ? [{ mount, basisHead }] : [];
|
|
1490
|
+
});
|
|
1491
|
+
if (candidates.length === 0) return /* @__PURE__ */ new Set();
|
|
1492
|
+
const objects = await resolveGitObjectIdsAsync(
|
|
1493
|
+
workspacePath,
|
|
1494
|
+
candidates.flatMap(({ mount, basisHead }) => [
|
|
1495
|
+
workspaceTreeObjectName(basisHead, mount.workspaceRelativePath),
|
|
1496
|
+
workspaceTreeObjectName(head, mount.workspaceRelativePath)
|
|
1497
|
+
])
|
|
1498
|
+
);
|
|
1499
|
+
const unchanged = /* @__PURE__ */ new Set();
|
|
1500
|
+
for (const { mount, basisHead } of candidates) {
|
|
1501
|
+
const before = objects.get(workspaceTreeObjectName(basisHead, mount.workspaceRelativePath)) ?? null;
|
|
1502
|
+
const after = objects.get(workspaceTreeObjectName(head, mount.workspaceRelativePath)) ?? null;
|
|
1503
|
+
if (before === after) unchanged.add(mount.id);
|
|
1504
|
+
}
|
|
1505
|
+
return unchanged;
|
|
1506
|
+
}
|
|
1486
1507
|
async function hydrateWorkspaceGitMountsTransactionally(input) {
|
|
1487
1508
|
const workspacePath = import_node_path.default.resolve(input.workspacePath);
|
|
1488
1509
|
const durabilityMounts = input.durabilityMounts ?? input.mounts;
|
|
1489
1510
|
const receiptMounts = input.receiptMounts ?? durabilityMounts;
|
|
1490
1511
|
const requiredMounts = input.requiredMounts ?? input.mounts;
|
|
1512
|
+
const carryMounts = input.carryMounts ?? [];
|
|
1513
|
+
if (carryMounts.length > 0 && !input.recordCurrentHead) {
|
|
1514
|
+
throw new Error("Workspace hydration can only carry unchanged mounts while recording the current head");
|
|
1515
|
+
}
|
|
1491
1516
|
const candidateMounts = [
|
|
1492
|
-
...new Map([...input.mounts, ...durabilityMounts, ...requiredMounts].map((mount) => [mount.id, mount])).values()
|
|
1517
|
+
...new Map([...input.mounts, ...durabilityMounts, ...requiredMounts, ...carryMounts].map((mount) => [mount.id, mount])).values()
|
|
1493
1518
|
];
|
|
1494
1519
|
if (input.recordCurrentHead && !await workspaceCheckoutDurabilityIsCurrentAsync(workspacePath)) {
|
|
1495
1520
|
throw new Error("Workspace checkout is not durably materialized at its current HEAD");
|
|
@@ -1505,12 +1530,13 @@ async function hydrateWorkspaceGitMountsTransactionally(input) {
|
|
|
1505
1530
|
({ id }) => !busyMountIds.has(id) && (requestedIds.has(id) || !requiredIds.has(id))
|
|
1506
1531
|
);
|
|
1507
1532
|
const unavailableRequiredIds = requiredMounts.filter(({ id }) => busyMountIds.has(id) || !requestedIds.has(id)).map(({ id }) => id);
|
|
1533
|
+
const carriedMounts = carryMounts.filter(({ id }) => !busyMountIds.has(id));
|
|
1508
1534
|
return {
|
|
1509
1535
|
busyMountIds,
|
|
1510
1536
|
hydrationMounts: hydrationMounts2,
|
|
1511
1537
|
advancedDurabilityMounts: advancedDurabilityMounts2,
|
|
1512
1538
|
skippedMountIds: [.../* @__PURE__ */ new Set([...busyMountIds, ...unavailableRequiredIds])].sort(),
|
|
1513
|
-
targetReceipt: targetHead ? receiptWithMountsAtHead(receiptBeforeTransaction, targetHead, advancedDurabilityMounts2, receiptMounts) : null
|
|
1539
|
+
targetReceipt: targetHead ? receiptWithMountsAtHead(receiptBeforeTransaction, targetHead, [...advancedDurabilityMounts2, ...carriedMounts], receiptMounts) : null
|
|
1514
1540
|
};
|
|
1515
1541
|
};
|
|
1516
1542
|
const noopCandidate = computeSelection();
|
|
@@ -1686,14 +1712,26 @@ async function changedPaths(workspacePath, baseRevision, headRevision) {
|
|
|
1686
1712
|
const base = baseRevision ?? emptyTreeHash(workspacePath);
|
|
1687
1713
|
return (await gitAsync(workspacePath, ["diff", "--name-only", "--no-renames", "-z", base, headRevision], "inspect workspace commit paths")).split("\0").filter(Boolean).sort();
|
|
1688
1714
|
}
|
|
1689
|
-
async function diffSizeBytes(workspacePath, baseRevision, headRevision, limit) {
|
|
1715
|
+
async function diffSizeBytes(workspacePath, baseRevision, headRevision, limit, pathspecs = []) {
|
|
1690
1716
|
const base = baseRevision ?? emptyTreeHash(workspacePath);
|
|
1691
|
-
const subprocess = Bun.spawn(
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1717
|
+
const subprocess = Bun.spawn(
|
|
1718
|
+
[
|
|
1719
|
+
"git",
|
|
1720
|
+
...NON_RECURSIVE_GIT_CONFIG,
|
|
1721
|
+
"diff",
|
|
1722
|
+
"--binary",
|
|
1723
|
+
"--no-ext-diff",
|
|
1724
|
+
base,
|
|
1725
|
+
headRevision,
|
|
1726
|
+
...pathspecs.length > 0 ? ["--", ...pathspecs] : []
|
|
1727
|
+
],
|
|
1728
|
+
{
|
|
1729
|
+
cwd: workspacePath,
|
|
1730
|
+
stdout: "pipe",
|
|
1731
|
+
stderr: "pipe",
|
|
1732
|
+
env: (0, import_git_process_environment.workerGitProcessEnvironment)()
|
|
1733
|
+
}
|
|
1734
|
+
);
|
|
1697
1735
|
const stderrPromise = new Response(subprocess.stderr).text();
|
|
1698
1736
|
const reader = subprocess.stdout.getReader();
|
|
1699
1737
|
let total = 0;
|
|
@@ -1713,6 +1751,73 @@ async function diffSizeBytes(workspacePath, baseRevision, headRevision, limit) {
|
|
|
1713
1751
|
if (exitCode !== 0) throw new Error(`Measure workspace diff: ${stderr || `git exited ${exitCode}`}`);
|
|
1714
1752
|
return total;
|
|
1715
1753
|
}
|
|
1754
|
+
async function resolveGitObjectIdsAsync(workspacePath, names) {
|
|
1755
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
1756
|
+
const unique = [...new Set(names)];
|
|
1757
|
+
if (unique.length === 0) return resolved;
|
|
1758
|
+
const subprocess = Bun.spawn(gitCommandArgs(["cat-file", "--batch-check"]), {
|
|
1759
|
+
cwd: workspacePath,
|
|
1760
|
+
stdin: Buffer.from(`${unique.join("\n")}
|
|
1761
|
+
`),
|
|
1762
|
+
stdout: "pipe",
|
|
1763
|
+
stderr: "pipe",
|
|
1764
|
+
env: (0, import_git_process_environment.workerGitProcessEnvironment)()
|
|
1765
|
+
});
|
|
1766
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
1767
|
+
new Response(subprocess.stdout).text(),
|
|
1768
|
+
new Response(subprocess.stderr).text(),
|
|
1769
|
+
subprocess.exited
|
|
1770
|
+
]);
|
|
1771
|
+
if (exitCode !== 0) throw new Error(`Resolve workspace objects: ${stderr.trim() || `git exited ${exitCode}`}`);
|
|
1772
|
+
const lines = stdout.split("\n").filter((line) => line.length > 0);
|
|
1773
|
+
if (lines.length !== unique.length) {
|
|
1774
|
+
throw new Error(`Resolve workspace objects: expected ${unique.length} results, received ${lines.length}`);
|
|
1775
|
+
}
|
|
1776
|
+
unique.forEach((name, index) => {
|
|
1777
|
+
const match = /^([0-9a-f]{40,64}) \S+ \d+$/.exec(lines[index]);
|
|
1778
|
+
resolved.set(name, match ? match[1] : null);
|
|
1779
|
+
});
|
|
1780
|
+
return resolved;
|
|
1781
|
+
}
|
|
1782
|
+
function workspaceTreeObjectName(revision, workspaceRelativePath) {
|
|
1783
|
+
return `${revision}:${normalizedWorkspaceMountPath(workspaceRelativePath)}`;
|
|
1784
|
+
}
|
|
1785
|
+
async function measureInitialPublicationDiffSize(input) {
|
|
1786
|
+
const candidates = input.mounts.filter(
|
|
1787
|
+
(mount) => typeof mount.initialPublicationSourceRelativePath === "string"
|
|
1788
|
+
);
|
|
1789
|
+
if (candidates.length === 0) return null;
|
|
1790
|
+
const revisions = [input.localHead, ...input.remoteHead ? [input.remoteHead] : []];
|
|
1791
|
+
const objects = await resolveGitObjectIdsAsync(
|
|
1792
|
+
input.workspacePath,
|
|
1793
|
+
candidates.flatMap(
|
|
1794
|
+
(mount) => revisions.flatMap((revision) => [
|
|
1795
|
+
workspaceTreeObjectName(revision, mount.workspaceRelativePath),
|
|
1796
|
+
workspaceTreeObjectName(revision, mount.initialPublicationSourceRelativePath)
|
|
1797
|
+
])
|
|
1798
|
+
)
|
|
1799
|
+
);
|
|
1800
|
+
const lookup = (revision, workspaceRelativePath) => revision ? objects.get(workspaceTreeObjectName(revision, workspaceRelativePath)) ?? null : null;
|
|
1801
|
+
const initialPublications = candidates.flatMap((mount) => {
|
|
1802
|
+
const published = lookup(input.localHead, mount.workspaceRelativePath);
|
|
1803
|
+
if (!published || lookup(input.remoteHead, mount.workspaceRelativePath)) return [];
|
|
1804
|
+
const source = lookup(input.localHead, mount.initialPublicationSourceRelativePath) ?? lookup(input.remoteHead, mount.initialPublicationSourceRelativePath);
|
|
1805
|
+
return [{ mount, published, source }];
|
|
1806
|
+
});
|
|
1807
|
+
if (initialPublications.length === 0) return null;
|
|
1808
|
+
let size = await input.measure(
|
|
1809
|
+
input.workspacePath,
|
|
1810
|
+
input.remoteHead,
|
|
1811
|
+
input.localHead,
|
|
1812
|
+
input.limit,
|
|
1813
|
+
initialPublications.map(({ mount }) => `:(exclude,literal)${normalizedWorkspaceMountPath(mount.workspaceRelativePath)}`)
|
|
1814
|
+
);
|
|
1815
|
+
for (const { published, source } of initialPublications) {
|
|
1816
|
+
if (size > input.limit) break;
|
|
1817
|
+
size += await input.measure(input.workspacePath, source, published, input.limit - size);
|
|
1818
|
+
}
|
|
1819
|
+
return { size, mountIds: initialPublications.map(({ mount }) => mount.id).sort() };
|
|
1820
|
+
}
|
|
1716
1821
|
function conflictPaths(workspacePath) {
|
|
1717
1822
|
const result = gitResult(workspacePath, ["diff", "--name-only", "--diff-filter=U", "-z"]);
|
|
1718
1823
|
return result.exitCode === 0 ? result.stdout.split("\0").filter(Boolean).sort() : [];
|
|
@@ -1940,12 +2045,19 @@ async function synchronizeWorkspaceGit(input) {
|
|
|
1940
2045
|
const uncoveredCompletionBasisMounts = completionReceiptMounts.filter(
|
|
1941
2046
|
(mount) => !cycleSkippedMountIds.has(mount.id) && !projectionSkippedIds.has(mount.id) && !mountIsAlreadyCovered(mount)
|
|
1942
2047
|
);
|
|
2048
|
+
const unchangedMountIds = currentHeadBeforeHydration ? await selectMountsWithUnchangedOuterSubtree(
|
|
2049
|
+
workspacePath,
|
|
2050
|
+
currentReceiptBeforeHydration,
|
|
2051
|
+
currentHeadBeforeHydration,
|
|
2052
|
+
uncoveredCompletionMounts
|
|
2053
|
+
) : /* @__PURE__ */ new Set();
|
|
1943
2054
|
const hydration = await hydrateWorkspaceGitMountsTransactionally({
|
|
1944
2055
|
workspacePath,
|
|
1945
|
-
mounts: uncoveredCompletionMounts,
|
|
1946
|
-
durabilityMounts: uncoveredCompletionBasisMounts,
|
|
2056
|
+
mounts: uncoveredCompletionMounts.filter(({ id }) => !unchangedMountIds.has(id)),
|
|
2057
|
+
durabilityMounts: uncoveredCompletionBasisMounts.filter(({ id }) => !unchangedMountIds.has(id)),
|
|
2058
|
+
carryMounts: uncoveredCompletionMounts.filter(({ id }) => unchangedMountIds.has(id)),
|
|
1947
2059
|
receiptMounts: completionReceiptMounts,
|
|
1948
|
-
requiredMounts: uncoveredRequiredLiveMounts,
|
|
2060
|
+
requiredMounts: uncoveredRequiredLiveMounts.filter(({ id }) => !unchangedMountIds.has(id)),
|
|
1949
2061
|
recordCurrentHead: true
|
|
1950
2062
|
});
|
|
1951
2063
|
for (const id of hydration.skippedMountIds) cycleSkippedMountIds.add(id);
|
|
@@ -2095,8 +2207,25 @@ async function synchronizeWorkspaceGit(input) {
|
|
|
2095
2207
|
};
|
|
2096
2208
|
}
|
|
2097
2209
|
const paths = await changedPaths(workspacePath, remoteHead, localHead);
|
|
2098
|
-
const
|
|
2210
|
+
const measureDiffSize = input.measureDiffSize ?? diffSizeBytes;
|
|
2211
|
+
let size = paths.length > 0 ? await measureDiffSize(workspacePath, remoteHead, localHead, maxDiffBytes) : 0;
|
|
2099
2212
|
input.assertStillAdmitted?.();
|
|
2213
|
+
let initialPublicationMountIds;
|
|
2214
|
+
if (paths.length > 0 && size > maxDiffBytes && !input.allowLargeDiff) {
|
|
2215
|
+
const initialPublication = await measureInitialPublicationDiffSize({
|
|
2216
|
+
workspacePath,
|
|
2217
|
+
remoteHead,
|
|
2218
|
+
localHead,
|
|
2219
|
+
mounts: input.mounts,
|
|
2220
|
+
limit: maxDiffBytes,
|
|
2221
|
+
measure: measureDiffSize
|
|
2222
|
+
});
|
|
2223
|
+
input.assertStillAdmitted?.();
|
|
2224
|
+
if (initialPublication && initialPublication.size <= maxDiffBytes) {
|
|
2225
|
+
size = initialPublication.size;
|
|
2226
|
+
initialPublicationMountIds = initialPublication.mountIds;
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2100
2229
|
if (paths.length > 0 && size > maxDiffBytes && !input.allowLargeDiff) {
|
|
2101
2230
|
const classifiedMounts = classifyMounts([...selected.active, ...selected.tombstones]);
|
|
2102
2231
|
return {
|
|
@@ -2141,7 +2270,9 @@ async function synchronizeWorkspaceGit(input) {
|
|
|
2141
2270
|
"push",
|
|
2142
2271
|
"--no-recurse-submodules"
|
|
2143
2272
|
];
|
|
2144
|
-
if (input.allowLargeDiff)
|
|
2273
|
+
if (input.allowLargeDiff || initialPublicationMountIds) {
|
|
2274
|
+
pushArgs.push(`--push-option=${WORKSPACE_GIT_CONFIRMED_LARGE_DIFF_PUSH_OPTION}`);
|
|
2275
|
+
}
|
|
2145
2276
|
pushArgs.push("origin", `HEAD:refs/heads/${WORKSPACE_GIT_BRANCH}`);
|
|
2146
2277
|
const push = await gitResultAsync(workspacePath, pushArgs);
|
|
2147
2278
|
if (push.exitCode === 0) {
|
|
@@ -2163,6 +2294,7 @@ async function synchronizeWorkspaceGit(input) {
|
|
|
2163
2294
|
diffSizeBytes: size,
|
|
2164
2295
|
affectedPaths: paths,
|
|
2165
2296
|
...completedMounts,
|
|
2297
|
+
...initialPublicationMountIds ? { initialPublicationMountIds } : {},
|
|
2166
2298
|
...verifiedAncestorHeads ? { verifiedAncestorHeads } : {},
|
|
2167
2299
|
...projectionWarning ? { error: projectionWarning } : {}
|
|
2168
2300
|
};
|
|
@@ -206,6 +206,14 @@ function indexMode(entry) {
|
|
|
206
206
|
function indexInfoRecord(mode, objectId, relativePath) {
|
|
207
207
|
return Buffer.concat([Buffer.from(`${mode} ${objectId} `), Buffer.from(relativePath), Buffer.from([0])]);
|
|
208
208
|
}
|
|
209
|
+
function basisGitlinks(workspacePath, basis) {
|
|
210
|
+
const listed = gitResult(workspacePath, ["ls-tree", "-r", "-z", `${basis.basisHead}:${basis.workspaceRelativePath}`]);
|
|
211
|
+
if (listed.exitCode !== 0) return [];
|
|
212
|
+
return nulRecords(listed.stdout).flatMap((record) => {
|
|
213
|
+
const match = /^160000 commit ([0-9a-f]{40,64})\t(.+)$/su.exec(record.toString());
|
|
214
|
+
return match ? [{ objectId: match[1], relativePath: match[2] }] : [];
|
|
215
|
+
});
|
|
216
|
+
}
|
|
209
217
|
function synthesizeMountTree(input) {
|
|
210
218
|
const sourceRoot = import_node_path.default.resolve(input.sourcePath);
|
|
211
219
|
const entries = (0, import_working_tree_mirror.inspectWorkingTree)(sourceRoot, input.sourceMode);
|
|
@@ -220,6 +228,15 @@ function synthesizeMountTree(input) {
|
|
|
220
228
|
const objectId = entry.kind === "file" ? hashRegularFile(input.workspacePath, sourcePath, entry) : hashSymlink(input.workspacePath, sourcePath, entry);
|
|
221
229
|
records.push(indexInfoRecord(indexMode(entry), objectId, relativePath));
|
|
222
230
|
}
|
|
231
|
+
if (input.gitlinkBasis && input.sourceMode === "git") {
|
|
232
|
+
for (const gitlink of basisGitlinks(input.workspacePath, input.gitlinkBasis)) {
|
|
233
|
+
const populatedPrefix = `${gitlink.relativePath}/`;
|
|
234
|
+
const populated = [...entries.keys()].some((relativePath) => relativePath.startsWith(populatedPrefix));
|
|
235
|
+
if (entries.get(gitlink.relativePath)?.kind === "directory" && !populated) {
|
|
236
|
+
records.push(indexInfoRecord("160000", gitlink.objectId, gitlink.relativePath));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
223
240
|
if (records.length > 0) {
|
|
224
241
|
git(input.workspacePath, ["update-index", "-z", "--index-info"], "populate workspace projection index", {
|
|
225
242
|
environment,
|
|
@@ -284,7 +301,8 @@ function synthesizeOursCommit(input) {
|
|
|
284
301
|
const mountTree = synthesizeMountTree({
|
|
285
302
|
workspacePath: input.workspacePath,
|
|
286
303
|
sourcePath: input.mount.sourcePath,
|
|
287
|
-
sourceMode: input.mount.sourceMode
|
|
304
|
+
sourceMode: input.mount.sourceMode,
|
|
305
|
+
gitlinkBasis: { basisHead: input.basisHead, workspaceRelativePath: input.workspaceRelativePath }
|
|
288
306
|
});
|
|
289
307
|
const rootTree = graftMountTree({
|
|
290
308
|
workspacePath: input.workspacePath,
|
package/dist/mjs/main.mjs
CHANGED
|
@@ -3831,6 +3831,7 @@ async function startWorker(options) {
|
|
|
3831
3831
|
const branchKey = projectBranchKey(project.projectId, branch.branchName);
|
|
3832
3832
|
const creatorLocalIncarnation = creatorLocalIncarnations.get(branchKey) === branch.branchId;
|
|
3833
3833
|
const preservesCheckoutPathMove = checkoutPathMoveBranches.has(branchKey);
|
|
3834
|
+
const initialPublicationSourceBranch = creatorLocalIncarnation ? branch.sourceBranchName : null;
|
|
3834
3835
|
mounts.push({
|
|
3835
3836
|
id: projectMountId(project.projectId, branch.branchName),
|
|
3836
3837
|
hydrationIncarnationKey: branch.branchId,
|
|
@@ -3840,6 +3841,7 @@ async function startWorker(options) {
|
|
|
3840
3841
|
sourceMode: "git",
|
|
3841
3842
|
hydrateDeletionMode: "git",
|
|
3842
3843
|
preserveLocalOnInitialOuterAbsence: creatorLocalIncarnation,
|
|
3844
|
+
...initialPublicationSourceBranch ? { initialPublicationSourceRelativePath: workspaceProjectRelativePath(project.projectId, initialPublicationSourceBranch) } : {},
|
|
3843
3845
|
preserveLocalOnHydrationBasisChange: preservesCheckoutPathMove,
|
|
3844
3846
|
busy: () => projectBranchMountBusy(project.projectId, branch.branchName, branchPath),
|
|
3845
3847
|
mutationToken: () => projectWorkspaceMutationToken(project.projectId, branch.branchName),
|
|
@@ -3854,6 +3856,7 @@ async function startWorker(options) {
|
|
|
3854
3856
|
sourceMode: "all",
|
|
3855
3857
|
hydrateDeletionMode: "all",
|
|
3856
3858
|
preserveLocalOnInitialOuterAbsence: creatorLocalIncarnation,
|
|
3859
|
+
...initialPublicationSourceBranch ? { initialPublicationSourceRelativePath: workspacePlanRelativePath(project.projectId, initialPublicationSourceBranch) } : {},
|
|
3857
3860
|
preserveLocalOnHydrationBasisChange: preservesCheckoutPathMove,
|
|
3858
3861
|
busy: () => projectBranchMountBusy(project.projectId, branch.branchName, branchPath),
|
|
3859
3862
|
mutationToken: () => projectWorkspaceMutationToken(project.projectId, branch.branchName),
|
|
@@ -4453,6 +4456,12 @@ async function startWorker(options) {
|
|
|
4453
4456
|
}
|
|
4454
4457
|
});
|
|
4455
4458
|
input.assertStillAdmitted?.();
|
|
4459
|
+
if (result.initialPublicationMountIds && result.initialPublicationMountIds.length > 0) {
|
|
4460
|
+
process.stderr.write(
|
|
4461
|
+
`[r5d-worker] published first checkout copies above the automatic diff-size limit, measured against their source subtrees at ${result.diffSizeBytes} bytes: ${result.initialPublicationMountIds.join(", ")}
|
|
4462
|
+
`
|
|
4463
|
+
);
|
|
4464
|
+
}
|
|
4456
4465
|
if (["no_change", "updated", "pushed"].includes(result.outcome) && !outerRemediation) {
|
|
4457
4466
|
await applyObservedProjectHeadsAfterInboundWorkspace(
|
|
4458
4467
|
observedProjectHeads,
|
package/dist/mjs/package.json
CHANGED
|
@@ -1438,13 +1438,38 @@ function hydrateWorkspaceGitMountsRaw(workspacePath, mounts, options = {}) {
|
|
|
1438
1438
|
}
|
|
1439
1439
|
return { hydratedMountIds: hydratedMountIds.sort(), skippedMountIds: skippedMountIds.sort() };
|
|
1440
1440
|
}
|
|
1441
|
+
async function selectMountsWithUnchangedOuterSubtree(workspacePath, receipt, head, mounts) {
|
|
1442
|
+
const candidates = mounts.flatMap((mount) => {
|
|
1443
|
+
const basisHead = workspaceHydrationReceiptMountBasisHead(receipt, mount);
|
|
1444
|
+
return basisHead && basisHead !== head ? [{ mount, basisHead }] : [];
|
|
1445
|
+
});
|
|
1446
|
+
if (candidates.length === 0) return /* @__PURE__ */ new Set();
|
|
1447
|
+
const objects = await resolveGitObjectIdsAsync(
|
|
1448
|
+
workspacePath,
|
|
1449
|
+
candidates.flatMap(({ mount, basisHead }) => [
|
|
1450
|
+
workspaceTreeObjectName(basisHead, mount.workspaceRelativePath),
|
|
1451
|
+
workspaceTreeObjectName(head, mount.workspaceRelativePath)
|
|
1452
|
+
])
|
|
1453
|
+
);
|
|
1454
|
+
const unchanged = /* @__PURE__ */ new Set();
|
|
1455
|
+
for (const { mount, basisHead } of candidates) {
|
|
1456
|
+
const before = objects.get(workspaceTreeObjectName(basisHead, mount.workspaceRelativePath)) ?? null;
|
|
1457
|
+
const after = objects.get(workspaceTreeObjectName(head, mount.workspaceRelativePath)) ?? null;
|
|
1458
|
+
if (before === after) unchanged.add(mount.id);
|
|
1459
|
+
}
|
|
1460
|
+
return unchanged;
|
|
1461
|
+
}
|
|
1441
1462
|
async function hydrateWorkspaceGitMountsTransactionally(input) {
|
|
1442
1463
|
const workspacePath = path.resolve(input.workspacePath);
|
|
1443
1464
|
const durabilityMounts = input.durabilityMounts ?? input.mounts;
|
|
1444
1465
|
const receiptMounts = input.receiptMounts ?? durabilityMounts;
|
|
1445
1466
|
const requiredMounts = input.requiredMounts ?? input.mounts;
|
|
1467
|
+
const carryMounts = input.carryMounts ?? [];
|
|
1468
|
+
if (carryMounts.length > 0 && !input.recordCurrentHead) {
|
|
1469
|
+
throw new Error("Workspace hydration can only carry unchanged mounts while recording the current head");
|
|
1470
|
+
}
|
|
1446
1471
|
const candidateMounts = [
|
|
1447
|
-
...new Map([...input.mounts, ...durabilityMounts, ...requiredMounts].map((mount) => [mount.id, mount])).values()
|
|
1472
|
+
...new Map([...input.mounts, ...durabilityMounts, ...requiredMounts, ...carryMounts].map((mount) => [mount.id, mount])).values()
|
|
1448
1473
|
];
|
|
1449
1474
|
if (input.recordCurrentHead && !await workspaceCheckoutDurabilityIsCurrentAsync(workspacePath)) {
|
|
1450
1475
|
throw new Error("Workspace checkout is not durably materialized at its current HEAD");
|
|
@@ -1460,12 +1485,13 @@ async function hydrateWorkspaceGitMountsTransactionally(input) {
|
|
|
1460
1485
|
({ id }) => !busyMountIds.has(id) && (requestedIds.has(id) || !requiredIds.has(id))
|
|
1461
1486
|
);
|
|
1462
1487
|
const unavailableRequiredIds = requiredMounts.filter(({ id }) => busyMountIds.has(id) || !requestedIds.has(id)).map(({ id }) => id);
|
|
1488
|
+
const carriedMounts = carryMounts.filter(({ id }) => !busyMountIds.has(id));
|
|
1463
1489
|
return {
|
|
1464
1490
|
busyMountIds,
|
|
1465
1491
|
hydrationMounts: hydrationMounts2,
|
|
1466
1492
|
advancedDurabilityMounts: advancedDurabilityMounts2,
|
|
1467
1493
|
skippedMountIds: [.../* @__PURE__ */ new Set([...busyMountIds, ...unavailableRequiredIds])].sort(),
|
|
1468
|
-
targetReceipt: targetHead ? receiptWithMountsAtHead(receiptBeforeTransaction, targetHead, advancedDurabilityMounts2, receiptMounts) : null
|
|
1494
|
+
targetReceipt: targetHead ? receiptWithMountsAtHead(receiptBeforeTransaction, targetHead, [...advancedDurabilityMounts2, ...carriedMounts], receiptMounts) : null
|
|
1469
1495
|
};
|
|
1470
1496
|
};
|
|
1471
1497
|
const noopCandidate = computeSelection();
|
|
@@ -1641,14 +1667,26 @@ async function changedPaths(workspacePath, baseRevision, headRevision) {
|
|
|
1641
1667
|
const base = baseRevision ?? emptyTreeHash(workspacePath);
|
|
1642
1668
|
return (await gitAsync(workspacePath, ["diff", "--name-only", "--no-renames", "-z", base, headRevision], "inspect workspace commit paths")).split("\0").filter(Boolean).sort();
|
|
1643
1669
|
}
|
|
1644
|
-
async function diffSizeBytes(workspacePath, baseRevision, headRevision, limit) {
|
|
1670
|
+
async function diffSizeBytes(workspacePath, baseRevision, headRevision, limit, pathspecs = []) {
|
|
1645
1671
|
const base = baseRevision ?? emptyTreeHash(workspacePath);
|
|
1646
|
-
const subprocess = Bun.spawn(
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1672
|
+
const subprocess = Bun.spawn(
|
|
1673
|
+
[
|
|
1674
|
+
"git",
|
|
1675
|
+
...NON_RECURSIVE_GIT_CONFIG,
|
|
1676
|
+
"diff",
|
|
1677
|
+
"--binary",
|
|
1678
|
+
"--no-ext-diff",
|
|
1679
|
+
base,
|
|
1680
|
+
headRevision,
|
|
1681
|
+
...pathspecs.length > 0 ? ["--", ...pathspecs] : []
|
|
1682
|
+
],
|
|
1683
|
+
{
|
|
1684
|
+
cwd: workspacePath,
|
|
1685
|
+
stdout: "pipe",
|
|
1686
|
+
stderr: "pipe",
|
|
1687
|
+
env: workerGitProcessEnvironment()
|
|
1688
|
+
}
|
|
1689
|
+
);
|
|
1652
1690
|
const stderrPromise = new Response(subprocess.stderr).text();
|
|
1653
1691
|
const reader = subprocess.stdout.getReader();
|
|
1654
1692
|
let total = 0;
|
|
@@ -1668,6 +1706,73 @@ async function diffSizeBytes(workspacePath, baseRevision, headRevision, limit) {
|
|
|
1668
1706
|
if (exitCode !== 0) throw new Error(`Measure workspace diff: ${stderr || `git exited ${exitCode}`}`);
|
|
1669
1707
|
return total;
|
|
1670
1708
|
}
|
|
1709
|
+
async function resolveGitObjectIdsAsync(workspacePath, names) {
|
|
1710
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
1711
|
+
const unique = [...new Set(names)];
|
|
1712
|
+
if (unique.length === 0) return resolved;
|
|
1713
|
+
const subprocess = Bun.spawn(gitCommandArgs(["cat-file", "--batch-check"]), {
|
|
1714
|
+
cwd: workspacePath,
|
|
1715
|
+
stdin: Buffer.from(`${unique.join("\n")}
|
|
1716
|
+
`),
|
|
1717
|
+
stdout: "pipe",
|
|
1718
|
+
stderr: "pipe",
|
|
1719
|
+
env: workerGitProcessEnvironment()
|
|
1720
|
+
});
|
|
1721
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
1722
|
+
new Response(subprocess.stdout).text(),
|
|
1723
|
+
new Response(subprocess.stderr).text(),
|
|
1724
|
+
subprocess.exited
|
|
1725
|
+
]);
|
|
1726
|
+
if (exitCode !== 0) throw new Error(`Resolve workspace objects: ${stderr.trim() || `git exited ${exitCode}`}`);
|
|
1727
|
+
const lines = stdout.split("\n").filter((line) => line.length > 0);
|
|
1728
|
+
if (lines.length !== unique.length) {
|
|
1729
|
+
throw new Error(`Resolve workspace objects: expected ${unique.length} results, received ${lines.length}`);
|
|
1730
|
+
}
|
|
1731
|
+
unique.forEach((name, index) => {
|
|
1732
|
+
const match = /^([0-9a-f]{40,64}) \S+ \d+$/.exec(lines[index]);
|
|
1733
|
+
resolved.set(name, match ? match[1] : null);
|
|
1734
|
+
});
|
|
1735
|
+
return resolved;
|
|
1736
|
+
}
|
|
1737
|
+
function workspaceTreeObjectName(revision, workspaceRelativePath) {
|
|
1738
|
+
return `${revision}:${normalizedWorkspaceMountPath(workspaceRelativePath)}`;
|
|
1739
|
+
}
|
|
1740
|
+
async function measureInitialPublicationDiffSize(input) {
|
|
1741
|
+
const candidates = input.mounts.filter(
|
|
1742
|
+
(mount) => typeof mount.initialPublicationSourceRelativePath === "string"
|
|
1743
|
+
);
|
|
1744
|
+
if (candidates.length === 0) return null;
|
|
1745
|
+
const revisions = [input.localHead, ...input.remoteHead ? [input.remoteHead] : []];
|
|
1746
|
+
const objects = await resolveGitObjectIdsAsync(
|
|
1747
|
+
input.workspacePath,
|
|
1748
|
+
candidates.flatMap(
|
|
1749
|
+
(mount) => revisions.flatMap((revision) => [
|
|
1750
|
+
workspaceTreeObjectName(revision, mount.workspaceRelativePath),
|
|
1751
|
+
workspaceTreeObjectName(revision, mount.initialPublicationSourceRelativePath)
|
|
1752
|
+
])
|
|
1753
|
+
)
|
|
1754
|
+
);
|
|
1755
|
+
const lookup = (revision, workspaceRelativePath) => revision ? objects.get(workspaceTreeObjectName(revision, workspaceRelativePath)) ?? null : null;
|
|
1756
|
+
const initialPublications = candidates.flatMap((mount) => {
|
|
1757
|
+
const published = lookup(input.localHead, mount.workspaceRelativePath);
|
|
1758
|
+
if (!published || lookup(input.remoteHead, mount.workspaceRelativePath)) return [];
|
|
1759
|
+
const source = lookup(input.localHead, mount.initialPublicationSourceRelativePath) ?? lookup(input.remoteHead, mount.initialPublicationSourceRelativePath);
|
|
1760
|
+
return [{ mount, published, source }];
|
|
1761
|
+
});
|
|
1762
|
+
if (initialPublications.length === 0) return null;
|
|
1763
|
+
let size = await input.measure(
|
|
1764
|
+
input.workspacePath,
|
|
1765
|
+
input.remoteHead,
|
|
1766
|
+
input.localHead,
|
|
1767
|
+
input.limit,
|
|
1768
|
+
initialPublications.map(({ mount }) => `:(exclude,literal)${normalizedWorkspaceMountPath(mount.workspaceRelativePath)}`)
|
|
1769
|
+
);
|
|
1770
|
+
for (const { published, source } of initialPublications) {
|
|
1771
|
+
if (size > input.limit) break;
|
|
1772
|
+
size += await input.measure(input.workspacePath, source, published, input.limit - size);
|
|
1773
|
+
}
|
|
1774
|
+
return { size, mountIds: initialPublications.map(({ mount }) => mount.id).sort() };
|
|
1775
|
+
}
|
|
1671
1776
|
function conflictPaths(workspacePath) {
|
|
1672
1777
|
const result = gitResult(workspacePath, ["diff", "--name-only", "--diff-filter=U", "-z"]);
|
|
1673
1778
|
return result.exitCode === 0 ? result.stdout.split("\0").filter(Boolean).sort() : [];
|
|
@@ -1895,12 +2000,19 @@ async function synchronizeWorkspaceGit(input) {
|
|
|
1895
2000
|
const uncoveredCompletionBasisMounts = completionReceiptMounts.filter(
|
|
1896
2001
|
(mount) => !cycleSkippedMountIds.has(mount.id) && !projectionSkippedIds.has(mount.id) && !mountIsAlreadyCovered(mount)
|
|
1897
2002
|
);
|
|
2003
|
+
const unchangedMountIds = currentHeadBeforeHydration ? await selectMountsWithUnchangedOuterSubtree(
|
|
2004
|
+
workspacePath,
|
|
2005
|
+
currentReceiptBeforeHydration,
|
|
2006
|
+
currentHeadBeforeHydration,
|
|
2007
|
+
uncoveredCompletionMounts
|
|
2008
|
+
) : /* @__PURE__ */ new Set();
|
|
1898
2009
|
const hydration = await hydrateWorkspaceGitMountsTransactionally({
|
|
1899
2010
|
workspacePath,
|
|
1900
|
-
mounts: uncoveredCompletionMounts,
|
|
1901
|
-
durabilityMounts: uncoveredCompletionBasisMounts,
|
|
2011
|
+
mounts: uncoveredCompletionMounts.filter(({ id }) => !unchangedMountIds.has(id)),
|
|
2012
|
+
durabilityMounts: uncoveredCompletionBasisMounts.filter(({ id }) => !unchangedMountIds.has(id)),
|
|
2013
|
+
carryMounts: uncoveredCompletionMounts.filter(({ id }) => unchangedMountIds.has(id)),
|
|
1902
2014
|
receiptMounts: completionReceiptMounts,
|
|
1903
|
-
requiredMounts: uncoveredRequiredLiveMounts,
|
|
2015
|
+
requiredMounts: uncoveredRequiredLiveMounts.filter(({ id }) => !unchangedMountIds.has(id)),
|
|
1904
2016
|
recordCurrentHead: true
|
|
1905
2017
|
});
|
|
1906
2018
|
for (const id of hydration.skippedMountIds) cycleSkippedMountIds.add(id);
|
|
@@ -2050,8 +2162,25 @@ async function synchronizeWorkspaceGit(input) {
|
|
|
2050
2162
|
};
|
|
2051
2163
|
}
|
|
2052
2164
|
const paths = await changedPaths(workspacePath, remoteHead, localHead);
|
|
2053
|
-
const
|
|
2165
|
+
const measureDiffSize = input.measureDiffSize ?? diffSizeBytes;
|
|
2166
|
+
let size = paths.length > 0 ? await measureDiffSize(workspacePath, remoteHead, localHead, maxDiffBytes) : 0;
|
|
2054
2167
|
input.assertStillAdmitted?.();
|
|
2168
|
+
let initialPublicationMountIds;
|
|
2169
|
+
if (paths.length > 0 && size > maxDiffBytes && !input.allowLargeDiff) {
|
|
2170
|
+
const initialPublication = await measureInitialPublicationDiffSize({
|
|
2171
|
+
workspacePath,
|
|
2172
|
+
remoteHead,
|
|
2173
|
+
localHead,
|
|
2174
|
+
mounts: input.mounts,
|
|
2175
|
+
limit: maxDiffBytes,
|
|
2176
|
+
measure: measureDiffSize
|
|
2177
|
+
});
|
|
2178
|
+
input.assertStillAdmitted?.();
|
|
2179
|
+
if (initialPublication && initialPublication.size <= maxDiffBytes) {
|
|
2180
|
+
size = initialPublication.size;
|
|
2181
|
+
initialPublicationMountIds = initialPublication.mountIds;
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2055
2184
|
if (paths.length > 0 && size > maxDiffBytes && !input.allowLargeDiff) {
|
|
2056
2185
|
const classifiedMounts = classifyMounts([...selected.active, ...selected.tombstones]);
|
|
2057
2186
|
return {
|
|
@@ -2096,7 +2225,9 @@ async function synchronizeWorkspaceGit(input) {
|
|
|
2096
2225
|
"push",
|
|
2097
2226
|
"--no-recurse-submodules"
|
|
2098
2227
|
];
|
|
2099
|
-
if (input.allowLargeDiff)
|
|
2228
|
+
if (input.allowLargeDiff || initialPublicationMountIds) {
|
|
2229
|
+
pushArgs.push(`--push-option=${WORKSPACE_GIT_CONFIRMED_LARGE_DIFF_PUSH_OPTION}`);
|
|
2230
|
+
}
|
|
2100
2231
|
pushArgs.push("origin", `HEAD:refs/heads/${WORKSPACE_GIT_BRANCH}`);
|
|
2101
2232
|
const push = await gitResultAsync(workspacePath, pushArgs);
|
|
2102
2233
|
if (push.exitCode === 0) {
|
|
@@ -2118,6 +2249,7 @@ async function synchronizeWorkspaceGit(input) {
|
|
|
2118
2249
|
diffSizeBytes: size,
|
|
2119
2250
|
affectedPaths: paths,
|
|
2120
2251
|
...completedMounts,
|
|
2252
|
+
...initialPublicationMountIds ? { initialPublicationMountIds } : {},
|
|
2121
2253
|
...verifiedAncestorHeads ? { verifiedAncestorHeads } : {},
|
|
2122
2254
|
...projectionWarning ? { error: projectionWarning } : {}
|
|
2123
2255
|
};
|
|
@@ -170,6 +170,14 @@ function indexMode(entry) {
|
|
|
170
170
|
function indexInfoRecord(mode, objectId, relativePath) {
|
|
171
171
|
return Buffer.concat([Buffer.from(`${mode} ${objectId} `), Buffer.from(relativePath), Buffer.from([0])]);
|
|
172
172
|
}
|
|
173
|
+
function basisGitlinks(workspacePath, basis) {
|
|
174
|
+
const listed = gitResult(workspacePath, ["ls-tree", "-r", "-z", `${basis.basisHead}:${basis.workspaceRelativePath}`]);
|
|
175
|
+
if (listed.exitCode !== 0) return [];
|
|
176
|
+
return nulRecords(listed.stdout).flatMap((record) => {
|
|
177
|
+
const match = /^160000 commit ([0-9a-f]{40,64})\t(.+)$/su.exec(record.toString());
|
|
178
|
+
return match ? [{ objectId: match[1], relativePath: match[2] }] : [];
|
|
179
|
+
});
|
|
180
|
+
}
|
|
173
181
|
function synthesizeMountTree(input) {
|
|
174
182
|
const sourceRoot = path.resolve(input.sourcePath);
|
|
175
183
|
const entries = inspectWorkingTree(sourceRoot, input.sourceMode);
|
|
@@ -184,6 +192,15 @@ function synthesizeMountTree(input) {
|
|
|
184
192
|
const objectId = entry.kind === "file" ? hashRegularFile(input.workspacePath, sourcePath, entry) : hashSymlink(input.workspacePath, sourcePath, entry);
|
|
185
193
|
records.push(indexInfoRecord(indexMode(entry), objectId, relativePath));
|
|
186
194
|
}
|
|
195
|
+
if (input.gitlinkBasis && input.sourceMode === "git") {
|
|
196
|
+
for (const gitlink of basisGitlinks(input.workspacePath, input.gitlinkBasis)) {
|
|
197
|
+
const populatedPrefix = `${gitlink.relativePath}/`;
|
|
198
|
+
const populated = [...entries.keys()].some((relativePath) => relativePath.startsWith(populatedPrefix));
|
|
199
|
+
if (entries.get(gitlink.relativePath)?.kind === "directory" && !populated) {
|
|
200
|
+
records.push(indexInfoRecord("160000", gitlink.objectId, gitlink.relativePath));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
187
204
|
if (records.length > 0) {
|
|
188
205
|
git(input.workspacePath, ["update-index", "-z", "--index-info"], "populate workspace projection index", {
|
|
189
206
|
environment,
|
|
@@ -248,7 +265,8 @@ function synthesizeOursCommit(input) {
|
|
|
248
265
|
const mountTree = synthesizeMountTree({
|
|
249
266
|
workspacePath: input.workspacePath,
|
|
250
267
|
sourcePath: input.mount.sourcePath,
|
|
251
|
-
sourceMode: input.mount.sourceMode
|
|
268
|
+
sourceMode: input.mount.sourceMode,
|
|
269
|
+
gitlinkBasis: { basisHead: input.basisHead, workspaceRelativePath: input.workspaceRelativePath }
|
|
252
270
|
});
|
|
253
271
|
const rootTree = graftMountTree({
|
|
254
272
|
workspacePath: input.workspacePath,
|
|
@@ -26,6 +26,13 @@ export type WorkspaceGitMount = {
|
|
|
26
26
|
* This is transient and must clear after that move is published.
|
|
27
27
|
*/
|
|
28
28
|
preserveLocalOnHydrationBasisChange?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Workspace-relative path of the subtree this creator-local mount was copied
|
|
31
|
+
* from (its source branch checkout). While the mount's own outer subtree is
|
|
32
|
+
* still unpublished, the automatic diff-size guard measures it against that
|
|
33
|
+
* source subtree instead of counting the whole copied tree as new content.
|
|
34
|
+
*/
|
|
35
|
+
initialPublicationSourceRelativePath?: string;
|
|
29
36
|
/** Remove the outer-workspace subtree when the mounted source was intentionally deleted. */
|
|
30
37
|
deleteWhenSourceMissing?: boolean;
|
|
31
38
|
busy?: () => boolean;
|
|
@@ -51,10 +58,16 @@ export type WorkspaceGitSyncResult = {
|
|
|
51
58
|
remoteHead: string | null;
|
|
52
59
|
publishedHead: string | null;
|
|
53
60
|
rebaseCount: number;
|
|
61
|
+
/**
|
|
62
|
+
* Bytes the automatic publication guard evaluated. A creator-local branch's
|
|
63
|
+
* first publication counts only its divergence from the source subtree.
|
|
64
|
+
*/
|
|
54
65
|
diffSizeBytes: number;
|
|
55
66
|
affectedPaths: string[];
|
|
56
67
|
activeMountIds: string[];
|
|
57
68
|
skippedMountIds: string[];
|
|
69
|
+
/** Creator-local mounts whose first outer publication was measured against their source subtree. */
|
|
70
|
+
initialPublicationMountIds?: string[];
|
|
58
71
|
conflictPaths?: string[];
|
|
59
72
|
conflictSnapshotRefs?: {
|
|
60
73
|
local: string;
|
|
@@ -152,7 +165,7 @@ export declare function resetWorkspaceGit(input: {
|
|
|
152
165
|
activeMountIds: string[];
|
|
153
166
|
skippedMountIds: string[];
|
|
154
167
|
}>;
|
|
155
|
-
declare function diffSizeBytes(workspacePath: string, baseRevision: string | null, headRevision: string, limit: number): Promise<number>;
|
|
168
|
+
declare function diffSizeBytes(workspacePath: string, baseRevision: string | null, headRevision: string, limit: number, pathspecs?: readonly string[]): Promise<number>;
|
|
156
169
|
export declare function synchronizeWorkspaceGit(input: {
|
|
157
170
|
attemptId?: string;
|
|
158
171
|
workerLabel: string;
|