@ricsam/r5d-worker 0.0.57 → 0.0.59
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/README.md +1 -1
- package/dist/cjs/main.cjs +717 -140
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/workspace-convergence.cjs +320 -0
- package/dist/cjs/workspace-incident-state.cjs +32 -3
- package/dist/cjs/workspace-manifest-admission.cjs +70 -0
- package/dist/cjs/workspace-sync.cjs +259 -69
- package/dist/mjs/main.mjs +736 -143
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/workspace-convergence.mjs +275 -0
- package/dist/mjs/workspace-incident-state.mjs +31 -3
- package/dist/mjs/workspace-manifest-admission.mjs +35 -0
- package/dist/mjs/workspace-sync.mjs +256 -68
- package/dist/types/workspace-convergence.d.ts +102 -0
- package/dist/types/workspace-incident-state.d.ts +15 -1
- package/dist/types/workspace-manifest-admission.d.ts +27 -0
- package/dist/types/workspace-sync.d.ts +31 -2
- package/package.json +1 -1
|
@@ -32,11 +32,13 @@ __export(workspace_sync_exports, {
|
|
|
32
32
|
WORKSPACE_BRANCH: () => WORKSPACE_BRANCH,
|
|
33
33
|
WORKSPACE_INTENT_REF_PREFIX: () => WORKSPACE_INTENT_REF_PREFIX,
|
|
34
34
|
WORKSPACE_NATIVE_PLANS_RELATIVE_PATH: () => WORKSPACE_NATIVE_PLANS_RELATIVE_PATH,
|
|
35
|
-
|
|
35
|
+
WORKSPACE_PUBLICATION_REF_PREFIX: () => WORKSPACE_PUBLICATION_REF_PREFIX,
|
|
36
36
|
WorkspaceSyncSingleFlight: () => WorkspaceSyncSingleFlight,
|
|
37
37
|
assertCanonicalCheckoutIndexMatchesWorktree: () => assertCanonicalCheckoutIndexMatchesWorktree,
|
|
38
38
|
assertWorkspaceQuarantineRef: () => assertWorkspaceQuarantineRef,
|
|
39
39
|
calculateWorkspaceDiffFingerprint: () => calculateWorkspaceDiffFingerprint,
|
|
40
|
+
calculateWorkspaceLocalDiffFingerprint: () => calculateWorkspaceLocalDiffFingerprint,
|
|
41
|
+
convergeWorkspaceHead: () => convergeWorkspaceHead,
|
|
40
42
|
encodeWorkspaceBranch: () => encodeWorkspaceBranch,
|
|
41
43
|
mirrorShadowWorkspaceToVisible: () => mirrorShadowWorkspaceToVisible,
|
|
42
44
|
mirrorVisibleWorkspaceToShadow: () => mirrorVisibleWorkspaceToShadow,
|
|
@@ -54,8 +56,8 @@ var import_managed_paths = require("./managed-paths.cjs");
|
|
|
54
56
|
var import_workspace_mutation_gate = require("./workspace-mutation-gate.cjs");
|
|
55
57
|
const WORKSPACE_BRANCH = "main";
|
|
56
58
|
const WORKSPACE_INTENT_REF_PREFIX = "refs/r5d/workspace-intents/";
|
|
59
|
+
const WORKSPACE_PUBLICATION_REF_PREFIX = "refs/r5d/workspace-publications/";
|
|
57
60
|
const MAX_WORKSPACE_SYNC_DIFF_BYTES = 5 * 1024 * 1024;
|
|
58
|
-
const WORKSPACE_PERIODIC_SCAN_INTERVAL_MS = 5e3;
|
|
59
61
|
const MAX_REPORTED_WORKSPACE_PATHS = 2e3;
|
|
60
62
|
const MAX_REPORTED_WORKSPACE_STATUS_BYTES = 256 * 1024;
|
|
61
63
|
const MAX_MIRROR_COMPARISON_CACHE_ENTRIES = 2e5;
|
|
@@ -960,7 +962,7 @@ function hydrateProjectFromShadowOptimistically(input, relativeRoot, visibleRoot
|
|
|
960
962
|
expectedCheckoutSnapshot = checkoutGitSnapshot(visibleRoot);
|
|
961
963
|
return expectedCheckoutSnapshot !== null;
|
|
962
964
|
};
|
|
963
|
-
|
|
965
|
+
const currentGitlinks = new Map(checkoutGitlinks(visibleRoot).map((entry) => [entry.filePath, entry]));
|
|
964
966
|
const changedPaths = [.../* @__PURE__ */ new Set([...preimage.keys(), ...desired.keys()])].filter(
|
|
965
967
|
(filePath) => !hydrationEntriesEqual(preimage.get(filePath), desired.get(filePath))
|
|
966
968
|
);
|
|
@@ -1582,6 +1584,33 @@ function resetUncommittedShadowSnapshot(input) {
|
|
|
1582
1584
|
}
|
|
1583
1585
|
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean interrupted workspace snapshot");
|
|
1584
1586
|
}
|
|
1587
|
+
function activeWorkspaceRebase(input) {
|
|
1588
|
+
for (const name of ["rebase-merge", "rebase-apply"]) {
|
|
1589
|
+
const resolved = runGitResult(input, input.shadowRoot, ["rev-parse", "--git-path", name]);
|
|
1590
|
+
if (resolved.exitCode !== 0 || !resolved.stdout) continue;
|
|
1591
|
+
const gitPath = import_node_path.default.isAbsolute(resolved.stdout) ? resolved.stdout : import_node_path.default.resolve(input.shadowRoot, resolved.stdout);
|
|
1592
|
+
if (import_node_fs.default.existsSync(gitPath)) return name;
|
|
1593
|
+
}
|
|
1594
|
+
return null;
|
|
1595
|
+
}
|
|
1596
|
+
function remediationRebaseInProgressResult(input, result) {
|
|
1597
|
+
if (input.trigger.type !== "remediation" && input.trigger.type !== "remediation_confirm") return null;
|
|
1598
|
+
const rebaseKind = activeWorkspaceRebase(input);
|
|
1599
|
+
if (!rebaseKind) return null;
|
|
1600
|
+
const conflictPaths = runGitResult(input, input.shadowRoot, ["diff", "--name-only", "--diff-filter=U", "-z"]).stdout.split("\0").filter(Boolean).sort();
|
|
1601
|
+
const status = reportedStatus(gitStatus(input));
|
|
1602
|
+
return {
|
|
1603
|
+
...result,
|
|
1604
|
+
outcome: "conflict_blocked",
|
|
1605
|
+
expectedHead: revParse(input, `origin/${WORKSPACE_BRANCH}`),
|
|
1606
|
+
candidateHead: revParse(input, "ORIG_HEAD") ?? revParse(input, "HEAD") ?? void 0,
|
|
1607
|
+
rebaseCount: 1,
|
|
1608
|
+
gitStatus: status,
|
|
1609
|
+
affectedProjects: affectedProjects(conflictPaths),
|
|
1610
|
+
affectedPaths: reportedPaths(conflictPaths),
|
|
1611
|
+
error: `Workspace conflict remediation has an in-progress ${rebaseKind}; the index and worktree were preserved until the resolver completes or aborts it.`
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1585
1614
|
function fastForwardCleanShadowForNewCheckouts(input) {
|
|
1586
1615
|
if (!input.newVisibleCheckouts?.length) return null;
|
|
1587
1616
|
runGit(input, input.shadowRoot, ["add", "-A"], "stage existing workspace changes before checkout hydration");
|
|
@@ -1727,9 +1756,12 @@ function commitMessage(input) {
|
|
|
1727
1756
|
});
|
|
1728
1757
|
}
|
|
1729
1758
|
function assertWorkspaceQuarantineRef(quarantineRef) {
|
|
1730
|
-
const
|
|
1731
|
-
|
|
1732
|
-
|
|
1759
|
+
const prefix = quarantineRef.startsWith(WORKSPACE_PUBLICATION_REF_PREFIX) ? WORKSPACE_PUBLICATION_REF_PREFIX : WORKSPACE_INTENT_REF_PREFIX;
|
|
1760
|
+
const suffix = quarantineRef.slice(prefix.length);
|
|
1761
|
+
if (!quarantineRef.startsWith(prefix) || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(suffix)) {
|
|
1762
|
+
throw new Error(
|
|
1763
|
+
`Workspace candidate ref must be below ${WORKSPACE_PUBLICATION_REF_PREFIX} or ${WORKSPACE_INTENT_REF_PREFIX}`
|
|
1764
|
+
);
|
|
1733
1765
|
}
|
|
1734
1766
|
const result = Bun.spawnSync(["git", "check-ref-format", quarantineRef], {
|
|
1735
1767
|
stdout: "pipe",
|
|
@@ -1801,17 +1833,105 @@ function convergeRedundantInboundCandidate(input, result) {
|
|
|
1801
1833
|
affectedPaths: []
|
|
1802
1834
|
};
|
|
1803
1835
|
}
|
|
1836
|
+
function preserveWorkspaceConflictCandidate(input, observed, options) {
|
|
1837
|
+
try {
|
|
1838
|
+
if (!options.baseRevision) throw new Error("the preserved workspace does not have an old-base commit");
|
|
1839
|
+
runGit(input, input.shadowRoot, ["reset", "--hard", options.baseRevision], "restore old-base workspace conflict candidate");
|
|
1840
|
+
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean old-base workspace conflict candidate");
|
|
1841
|
+
if (!options.preserveShadowCandidate) {
|
|
1842
|
+
const projection = mirrorVisibleWorkspaceToShadow(input);
|
|
1843
|
+
restoreUnscopedCanonicalCheckoutSubtrees(input);
|
|
1844
|
+
const stagePathspecs = projection.opaqueRoots.flatMap((root) => [`:(exclude,literal)${root}`]);
|
|
1845
|
+
runGit(input, input.shadowRoot, ["add", "-A", "--", ".", ...stagePathspecs], "stage preserved workspace conflict candidate");
|
|
1846
|
+
forceStageCanonicalCheckoutFiles(input, projection.entries, projection.opaqueRoots);
|
|
1847
|
+
if (stagedPaths(input).length > 0) {
|
|
1848
|
+
runGit(input, input.shadowRoot, ["commit", "-m", commitMessage(input)], "commit preserved workspace conflict candidate");
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
const candidateHead = revParse(input, "HEAD");
|
|
1852
|
+
if (!candidateHead) throw new Error("the preserved workspace conflict candidate does not have a commit");
|
|
1853
|
+
assertUnscopedCanonicalCheckoutSubtreesMatchRemote(input, candidateHead);
|
|
1854
|
+
const upload = uploadWorkspaceCandidate(input, candidateHead);
|
|
1855
|
+
if (upload.exitCode !== 0) {
|
|
1856
|
+
throw new Error(upload.stderr || upload.stdout || "workspace conflict candidate upload failed");
|
|
1857
|
+
}
|
|
1858
|
+
const conflictPaths = [...new Set(options.conflictPaths)].sort();
|
|
1859
|
+
return {
|
|
1860
|
+
...observed,
|
|
1861
|
+
outcome: "conflict_blocked",
|
|
1862
|
+
expectedHead: options.expectedHead,
|
|
1863
|
+
candidateHead,
|
|
1864
|
+
quarantineRef: input.quarantineRef,
|
|
1865
|
+
...options.publishedHead ? { publishedHead: options.publishedHead } : {},
|
|
1866
|
+
rebaseCount: options.rebaseCount ?? 0,
|
|
1867
|
+
affectedProjects: affectedProjects(conflictPaths),
|
|
1868
|
+
affectedPaths: reportedPaths(conflictPaths),
|
|
1869
|
+
error: options.error,
|
|
1870
|
+
gitStatus: gitStatus(input)
|
|
1871
|
+
};
|
|
1872
|
+
} catch (error) {
|
|
1873
|
+
return {
|
|
1874
|
+
...observed,
|
|
1875
|
+
outcome: "failed",
|
|
1876
|
+
rebaseCount: options.rebaseCount ?? 0,
|
|
1877
|
+
affectedProjects: affectedProjects(options.conflictPaths),
|
|
1878
|
+
affectedPaths: reportedPaths(options.conflictPaths),
|
|
1879
|
+
error: `Workspace synchronization found a conflict but could not preserve a durable candidate: ${error instanceof Error ? error.message : String(error)}`,
|
|
1880
|
+
gitStatus: gitStatus(input)
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
function preserveLargeDiffCandidate(input, observed, error) {
|
|
1885
|
+
try {
|
|
1886
|
+
if (stagedPaths(input).length > 0) {
|
|
1887
|
+
runGit(input, input.shadowRoot, ["commit", "-m", commitMessage(input)], "commit blocked workspace candidate");
|
|
1888
|
+
}
|
|
1889
|
+
const candidateHead = revParse(input, "HEAD");
|
|
1890
|
+
if (!candidateHead) throw new Error("the blocked workspace candidate does not have a commit");
|
|
1891
|
+
assertUnscopedCanonicalCheckoutSubtreesMatchRemote(input, candidateHead);
|
|
1892
|
+
const upload = uploadWorkspaceCandidate(input, candidateHead);
|
|
1893
|
+
if (upload.exitCode !== 0) {
|
|
1894
|
+
throw new Error(upload.stderr || upload.stdout || "blocked workspace candidate upload failed");
|
|
1895
|
+
}
|
|
1896
|
+
return {
|
|
1897
|
+
...observed,
|
|
1898
|
+
outcome: "large_diff_blocked",
|
|
1899
|
+
expectedHead: revParse(input, `origin/${WORKSPACE_BRANCH}`),
|
|
1900
|
+
candidateHead,
|
|
1901
|
+
quarantineRef: input.quarantineRef,
|
|
1902
|
+
error,
|
|
1903
|
+
gitStatus: gitStatus(input)
|
|
1904
|
+
};
|
|
1905
|
+
} catch (candidateError) {
|
|
1906
|
+
return {
|
|
1907
|
+
...observed,
|
|
1908
|
+
outcome: "failed",
|
|
1909
|
+
error: `Workspace safety guard blocked publication but could not preserve a durable candidate: ${candidateError instanceof Error ? candidateError.message : String(candidateError)}`,
|
|
1910
|
+
gitStatus: gitStatus(input)
|
|
1911
|
+
};
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1804
1914
|
async function synchronizeWorkspace(rawInput) {
|
|
1805
1915
|
let input = { ...rawInput, attemptId: rawInput.attemptId ?? crypto.randomUUID() };
|
|
1806
1916
|
try {
|
|
1807
1917
|
input = { ...input, projects: workspaceProjectsForSync(input.projects, input.trigger) };
|
|
1918
|
+
const isCanonicalRemediationSync = input.skipVisibleMirror === true && (input.trigger.type === "remediation" || input.trigger.type === "remediation_confirm");
|
|
1808
1919
|
let mirroredGitlinkProjection = { entries: [], opaqueRoots: [], visiblePreimages: /* @__PURE__ */ new Map() };
|
|
1809
1920
|
let fastForwardedFromHead = null;
|
|
1810
1921
|
const shadowWasCreated = ensureShadowWorkspace(input);
|
|
1811
1922
|
let hydrationPreimages = input.skipVisibleMirror ? hydrationPreimagesFromShadowRevision(input, shadowWasCreated ? null : revParse(input, "HEAD")) : /* @__PURE__ */ new Map();
|
|
1812
1923
|
const remoteHeadAtStart = revParse(input, `origin/${WORKSPACE_BRANCH}`);
|
|
1813
1924
|
const result = baseResult(input, remoteHeadAtStart);
|
|
1814
|
-
|
|
1925
|
+
if (input.expectedCanonicalHead !== void 0 && remoteHeadAtStart !== input.expectedCanonicalHead) {
|
|
1926
|
+
return {
|
|
1927
|
+
...result,
|
|
1928
|
+
outcome: "failed",
|
|
1929
|
+
expectedHead: input.expectedCanonicalHead,
|
|
1930
|
+
publishedHead: remoteHeadAtStart ?? void 0,
|
|
1931
|
+
error: `canonical_head_advanced: expected ${input.expectedCanonicalHead ?? "unborn"}, fetched ${remoteHeadAtStart ?? "unborn"}`
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
const inboundConflictsAtStart = input.skipVisibleMirror && !isCanonicalRemediationSync && remoteHeadAtStart ? visibleHydrationConflictPaths(input, hydrationPreimages, `origin/${WORKSPACE_BRANCH}`) : [];
|
|
1815
1935
|
if (input.resetToCanonical) {
|
|
1816
1936
|
if (input.trigger.canonicalCheckoutOnly) {
|
|
1817
1937
|
throw new Error("A canonical-checkout-only synchronization cannot reset the authoritative resolver checkout");
|
|
@@ -1825,6 +1945,8 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1825
1945
|
gitStatus: gitStatus(input)
|
|
1826
1946
|
};
|
|
1827
1947
|
}
|
|
1948
|
+
const inProgressRemediation = remediationRebaseInProgressResult(input, result);
|
|
1949
|
+
if (inProgressRemediation) return inProgressRemediation;
|
|
1828
1950
|
if (!input.skipVisibleMirror) resetUncommittedShadowSnapshot(input);
|
|
1829
1951
|
restoreUnscopedCanonicalCheckoutSubtrees(input);
|
|
1830
1952
|
if (!input.skipVisibleMirror) {
|
|
@@ -1857,18 +1979,18 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1857
1979
|
const destructiveReductions = destructiveCheckoutReductions(input, baseRevision);
|
|
1858
1980
|
if (destructiveReductions.length > 0 && !input.confirmedLargeDiff) {
|
|
1859
1981
|
const summary = destructiveReductions.map(({ root, trackedBefore, trackedAfter }) => `${root} (${trackedBefore} -> ${trackedAfter} tracked files)`).join(", ");
|
|
1860
|
-
return
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1982
|
+
return preserveLargeDiffCandidate(
|
|
1983
|
+
input,
|
|
1984
|
+
observed,
|
|
1985
|
+
`Workspace safety guard blocked a destructive checkout reduction: ${summary}`
|
|
1986
|
+
);
|
|
1865
1987
|
}
|
|
1866
1988
|
if (paths.length > 0 && diffSizeBytes > MAX_WORKSPACE_SYNC_DIFF_BYTES && !input.confirmedLargeDiff && !input.trigger.canonicalCheckoutOnly) {
|
|
1867
|
-
return
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1989
|
+
return preserveLargeDiffCandidate(
|
|
1990
|
+
input,
|
|
1991
|
+
observed,
|
|
1992
|
+
`Workspace safety guard blocked a ${diffSizeBytes}-byte diff above the ${MAX_WORKSPACE_SYNC_DIFF_BYTES}-byte automatic publication limit.`
|
|
1993
|
+
);
|
|
1872
1994
|
}
|
|
1873
1995
|
if (stagedWorkingPaths.length > 0) {
|
|
1874
1996
|
runGit(input, input.shadowRoot, ["commit", "-m", commitMessage(input)], "commit workspace changes");
|
|
@@ -1880,15 +2002,12 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1880
2002
|
const hasUnpushedCommit = candidateHead ? !hasRemoteMain || !tryGit(input, input.shadowRoot, ["merge-base", "--is-ancestor", candidateHead, `origin/${WORKSPACE_BRANCH}`]) : false;
|
|
1881
2003
|
if (hasUnpushedCommit && inboundConflictsAtStart.length > 0) {
|
|
1882
2004
|
const conflictPaths = [.../* @__PURE__ */ new Set([...paths, ...inboundConflictsAtStart])].sort();
|
|
1883
|
-
return {
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
error: "Inbound workspace update overlaps local visible changes; the old-base candidate was preserved for reconciliation.",
|
|
1890
|
-
gitStatus: gitStatus(input)
|
|
1891
|
-
};
|
|
2005
|
+
return preserveWorkspaceConflictCandidate(input, observed, {
|
|
2006
|
+
baseRevision: candidateHead,
|
|
2007
|
+
expectedHead: revParse(input, `origin/${WORKSPACE_BRANCH}`),
|
|
2008
|
+
conflictPaths,
|
|
2009
|
+
error: "Inbound workspace update overlaps local visible changes; the old-base candidate was preserved for reconciliation."
|
|
2010
|
+
});
|
|
1892
2011
|
}
|
|
1893
2012
|
if (!hasUnpushedCommit) {
|
|
1894
2013
|
const localHead = revParse(input, "HEAD");
|
|
@@ -1897,22 +2016,21 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1897
2016
|
runGit(input, input.shadowRoot, ["reset", "--hard", `origin/${WORKSPACE_BRANCH}`], "update workspace from canonical state");
|
|
1898
2017
|
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean updated workspace");
|
|
1899
2018
|
assertPublishedCanonicalCheckoutTree(input, currentRemoteHead, sampledCanonicalCheckoutTreeHash);
|
|
1900
|
-
|
|
1901
|
-
|
|
2019
|
+
if (!isCanonicalRemediationSync) {
|
|
2020
|
+
mirrorShadowWorkspaceToVisible(input, mirroredGitlinkProjection.opaqueRoots, hydrationPreimages);
|
|
2021
|
+
}
|
|
2022
|
+
const inboundConflicts = input.trigger.canonicalCheckoutOnly || isCanonicalRemediationSync ? [] : localHead ? hydrationConflictPaths(input, hydrationPreimages, localHead, currentRemoteHead) : visibleHydrationConflictPaths(input, hydrationPreimages, currentRemoteHead);
|
|
1902
2023
|
if (inboundConflicts.length > 0 && localHead) {
|
|
1903
2024
|
runGit(input, input.shadowRoot, ["reset", "--hard", localHead], "preserve old workspace base after inbound conflict");
|
|
1904
2025
|
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean preserved old workspace base");
|
|
1905
2026
|
const conflictPaths = [.../* @__PURE__ */ new Set([...paths, ...inboundConflicts])].sort();
|
|
1906
|
-
return {
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
candidateHead: candidateHead ?? void 0,
|
|
2027
|
+
return preserveWorkspaceConflictCandidate(input, observed, {
|
|
2028
|
+
baseRevision: localHead,
|
|
2029
|
+
expectedHead: currentRemoteHead,
|
|
1910
2030
|
publishedHead: currentRemoteHead,
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
gitStatus: gitStatus(input)
|
|
1915
|
-
};
|
|
2031
|
+
conflictPaths,
|
|
2032
|
+
error: "Inbound workspace update overlaps local visible changes; disjoint updates were hydrated and the old base was preserved for reconciliation."
|
|
2033
|
+
});
|
|
1916
2034
|
}
|
|
1917
2035
|
return {
|
|
1918
2036
|
...observed,
|
|
@@ -1925,8 +2043,10 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1925
2043
|
const authoritativeHead = currentRemoteHead ?? localHead;
|
|
1926
2044
|
if (authoritativeHead) {
|
|
1927
2045
|
assertPublishedCanonicalCheckoutTree(input, authoritativeHead, sampledCanonicalCheckoutTreeHash);
|
|
1928
|
-
|
|
1929
|
-
|
|
2046
|
+
if (!isCanonicalRemediationSync) {
|
|
2047
|
+
mirrorShadowWorkspaceToVisible(input, mirroredGitlinkProjection.opaqueRoots, hydrationPreimages);
|
|
2048
|
+
}
|
|
2049
|
+
const hydrationConflicts2 = input.trigger.canonicalCheckoutOnly || isCanonicalRemediationSync ? [] : fastForwardedFromHead ? hydrationConflictPaths(input, hydrationPreimages, fastForwardedFromHead, authoritativeHead) : visibleHydrationConflictPaths(input, hydrationPreimages, authoritativeHead);
|
|
1930
2050
|
if (hydrationConflicts2.length > 0 && fastForwardedFromHead) {
|
|
1931
2051
|
runGit(
|
|
1932
2052
|
input,
|
|
@@ -1935,16 +2055,13 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1935
2055
|
"preserve old workspace base after new-checkout fast-forward conflict"
|
|
1936
2056
|
);
|
|
1937
2057
|
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean preserved new-checkout workspace base");
|
|
1938
|
-
return {
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
candidateHead: candidateHead ?? void 0,
|
|
2058
|
+
return preserveWorkspaceConflictCandidate(input, observed, {
|
|
2059
|
+
baseRevision: fastForwardedFromHead,
|
|
2060
|
+
expectedHead: currentRemoteHead,
|
|
1942
2061
|
publishedHead: currentRemoteHead ?? void 0,
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
gitStatus: gitStatus(input)
|
|
1947
|
-
};
|
|
2062
|
+
conflictPaths: hydrationConflicts2,
|
|
2063
|
+
error: "Workspace fast-forward for a new checkout overlaps local visible changes; compatible updates were hydrated and the old base was preserved for reconciliation."
|
|
2064
|
+
});
|
|
1948
2065
|
}
|
|
1949
2066
|
}
|
|
1950
2067
|
return {
|
|
@@ -1964,17 +2081,14 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1964
2081
|
const conflicted = runGitResult(input, input.shadowRoot, ["diff", "--name-only", "--diff-filter=U", "-z"]);
|
|
1965
2082
|
const conflictPaths = [.../* @__PURE__ */ new Set([...paths, ...conflicted.stdout.split("\0").filter(Boolean)])].sort();
|
|
1966
2083
|
runGit(input, input.shadowRoot, ["rebase", "--abort"], "preserve workspace candidate after rebase conflict");
|
|
1967
|
-
return {
|
|
1968
|
-
|
|
1969
|
-
outcome: "failed",
|
|
2084
|
+
return preserveWorkspaceConflictCandidate(input, observed, {
|
|
2085
|
+
baseRevision: candidateHead,
|
|
1970
2086
|
expectedHead,
|
|
1971
|
-
candidateHead: candidateHead ?? void 0,
|
|
1972
2087
|
rebaseCount,
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
error: `Workspace candidate conflicts with canonical head ${expectedHead}; local changes were preserved for reconciliation
|
|
1976
|
-
|
|
1977
|
-
};
|
|
2088
|
+
preserveShadowCandidate: isCanonicalRemediationSync,
|
|
2089
|
+
conflictPaths,
|
|
2090
|
+
error: `Workspace candidate conflicts with canonical head ${expectedHead}; local changes were preserved for reconciliation.`
|
|
2091
|
+
});
|
|
1978
2092
|
}
|
|
1979
2093
|
candidateHead = revParse(input, "HEAD");
|
|
1980
2094
|
assertPublishedCanonicalCheckoutTree(input, "HEAD", sampledCanonicalCheckoutTreeHash);
|
|
@@ -1990,8 +2104,10 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1990
2104
|
};
|
|
1991
2105
|
}
|
|
1992
2106
|
assertPublishedCanonicalCheckoutTree(input, candidateHead, sampledCanonicalCheckoutTreeHash);
|
|
1993
|
-
|
|
1994
|
-
|
|
2107
|
+
if (!isCanonicalRemediationSync) {
|
|
2108
|
+
mirrorShadowWorkspaceToVisible(input, mirroredGitlinkProjection.opaqueRoots, hydrationPreimages);
|
|
2109
|
+
}
|
|
2110
|
+
const hydrationConflicts = input.trigger.canonicalCheckoutOnly || isCanonicalRemediationSync ? [] : candidateHeadBeforeRebase ? hydrationConflictPaths(input, hydrationPreimages, candidateHeadBeforeRebase, "HEAD") : visibleHydrationConflictPaths(input, hydrationPreimages, "HEAD");
|
|
1995
2111
|
if (hydrationConflicts.length > 0 && candidateHeadBeforeRebase) {
|
|
1996
2112
|
runGit(
|
|
1997
2113
|
input,
|
|
@@ -2001,17 +2117,13 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
2001
2117
|
);
|
|
2002
2118
|
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean old-base workspace candidate");
|
|
2003
2119
|
const conflictPaths = [.../* @__PURE__ */ new Set([...paths, ...hydrationConflicts])].sort();
|
|
2004
|
-
return {
|
|
2005
|
-
|
|
2006
|
-
outcome: "failed",
|
|
2120
|
+
return preserveWorkspaceConflictCandidate(input, observed, {
|
|
2121
|
+
baseRevision: candidateHeadBeforeRebase,
|
|
2007
2122
|
expectedHead,
|
|
2008
|
-
candidateHead,
|
|
2009
2123
|
rebaseCount,
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
gitStatus: gitStatus(input)
|
|
2014
|
-
};
|
|
2124
|
+
conflictPaths,
|
|
2125
|
+
error: "Workspace changed while its candidate was reconciling; visible changes and the old-base candidate were preserved."
|
|
2126
|
+
});
|
|
2015
2127
|
}
|
|
2016
2128
|
const upload = uploadWorkspaceCandidate(input, candidateHead);
|
|
2017
2129
|
if (upload.exitCode !== 0) {
|
|
@@ -2043,6 +2155,79 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
2043
2155
|
};
|
|
2044
2156
|
}
|
|
2045
2157
|
}
|
|
2158
|
+
async function convergeWorkspaceHead(rawInput, options) {
|
|
2159
|
+
const input = {
|
|
2160
|
+
...rawInput,
|
|
2161
|
+
projects: workspaceProjectsForSync(rawInput.projects, rawInput.trigger)
|
|
2162
|
+
};
|
|
2163
|
+
if (input.trigger.canonicalCheckoutOnly) {
|
|
2164
|
+
throw new Error("Ordinary inbound convergence cannot target a canonical resolver checkout");
|
|
2165
|
+
}
|
|
2166
|
+
ensureShadowWorkspace(input);
|
|
2167
|
+
const fetchedLocalHead = revParse(input, "HEAD");
|
|
2168
|
+
const fetchedCanonicalHead = revParse(input, `origin/${WORKSPACE_BRANCH}`);
|
|
2169
|
+
if (!options.hydrateVisible) {
|
|
2170
|
+
return {
|
|
2171
|
+
localHead: fetchedLocalHead,
|
|
2172
|
+
canonicalHead: fetchedCanonicalHead,
|
|
2173
|
+
localChanges: false,
|
|
2174
|
+
hydrated: false,
|
|
2175
|
+
hydrationConflicts: []
|
|
2176
|
+
};
|
|
2177
|
+
}
|
|
2178
|
+
resetUncommittedShadowSnapshot(input);
|
|
2179
|
+
restoreUnscopedCanonicalCheckoutSubtrees(input);
|
|
2180
|
+
const localHead = fetchedLocalHead;
|
|
2181
|
+
const canonicalHead = fetchedCanonicalHead;
|
|
2182
|
+
const projection = mirrorVisibleWorkspaceToShadow(input);
|
|
2183
|
+
runGit(
|
|
2184
|
+
input,
|
|
2185
|
+
input.shadowRoot,
|
|
2186
|
+
["add", "-A", "--", ".", ...projection.opaqueRoots.map((root) => `:(exclude,literal)${root}`)],
|
|
2187
|
+
"stage inbound workspace observation"
|
|
2188
|
+
);
|
|
2189
|
+
forceStageCanonicalCheckoutFiles(input, projection.entries, projection.opaqueRoots);
|
|
2190
|
+
const stagedTree = runGit(input, input.shadowRoot, ["write-tree"], "write inbound workspace observation tree");
|
|
2191
|
+
const headTree = localHead ? revParse(input, `${localHead}^{tree}`) : null;
|
|
2192
|
+
const localChanges = stagedTree !== headTree;
|
|
2193
|
+
if (!options.hydrateVisible || localChanges || !canonicalHead || localHead === canonicalHead) {
|
|
2194
|
+
resetUncommittedShadowSnapshot(input);
|
|
2195
|
+
return { localHead, canonicalHead, localChanges, hydrated: false, hydrationConflicts: [] };
|
|
2196
|
+
}
|
|
2197
|
+
const preimages = projection.visiblePreimages ?? hydrationPreimagesFromShadowRevision(input, localHead);
|
|
2198
|
+
const hydrationConflicts = localHead ? hydrationConflictPaths(input, preimages, localHead, canonicalHead) : [];
|
|
2199
|
+
if (hydrationConflicts.length > 0) {
|
|
2200
|
+
resetUncommittedShadowSnapshot(input);
|
|
2201
|
+
return { localHead, canonicalHead, localChanges: true, hydrated: false, hydrationConflicts };
|
|
2202
|
+
}
|
|
2203
|
+
runGit(input, input.shadowRoot, ["reset", "--hard", canonicalHead], "fast-forward workspace to available canonical head");
|
|
2204
|
+
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean fast-forwarded workspace");
|
|
2205
|
+
mirrorShadowWorkspaceToVisible(input, projection.opaqueRoots, preimages);
|
|
2206
|
+
return { localHead: canonicalHead, canonicalHead, localChanges: false, hydrated: true, hydrationConflicts: [] };
|
|
2207
|
+
}
|
|
2208
|
+
function calculateWorkspaceLocalDiffFingerprint(rawInput) {
|
|
2209
|
+
const input = {
|
|
2210
|
+
...rawInput,
|
|
2211
|
+
projects: workspaceProjectsForSync(rawInput.projects, rawInput.trigger)
|
|
2212
|
+
};
|
|
2213
|
+
if (!import_node_fs.default.existsSync(import_node_path.default.join(input.shadowRoot, ".git"))) {
|
|
2214
|
+
throw new Error("Cannot inspect local workspace changes before first bootstrap");
|
|
2215
|
+
}
|
|
2216
|
+
resetUncommittedShadowSnapshot(input);
|
|
2217
|
+
restoreUnscopedCanonicalCheckoutSubtrees(input);
|
|
2218
|
+
const projection = mirrorVisibleWorkspaceToShadow(input);
|
|
2219
|
+
runGit(
|
|
2220
|
+
input,
|
|
2221
|
+
input.shadowRoot,
|
|
2222
|
+
["add", "-A", "--", ".", ...projection.opaqueRoots.map((root) => `:(exclude,literal)${root}`)],
|
|
2223
|
+
"stage local workspace fingerprint"
|
|
2224
|
+
);
|
|
2225
|
+
forceStageCanonicalCheckoutFiles(input, projection.entries, projection.opaqueRoots);
|
|
2226
|
+
const stagedTree = runGit(input, input.shadowRoot, ["write-tree"], "write local workspace fingerprint tree");
|
|
2227
|
+
const headTree = revParse(input, "HEAD^{tree}");
|
|
2228
|
+
resetUncommittedShadowSnapshot(input);
|
|
2229
|
+
return stagedTree === headTree ? (0, import_node_crypto.createHash)("sha256").update("").digest("hex") : (0, import_node_crypto.createHash)("sha256").update(stagedTree).digest("hex");
|
|
2230
|
+
}
|
|
2046
2231
|
function calculateWorkspaceDiffFingerprint(rawInput) {
|
|
2047
2232
|
const input = {
|
|
2048
2233
|
...rawInput,
|
|
@@ -2096,6 +2281,9 @@ class WorkspaceSyncSingleFlight {
|
|
|
2096
2281
|
fingerprintPrepared(prepare) {
|
|
2097
2282
|
return this.enqueue(() => calculateWorkspaceDiffFingerprint(prepare()));
|
|
2098
2283
|
}
|
|
2284
|
+
runExclusive(operation) {
|
|
2285
|
+
return this.enqueue(operation);
|
|
2286
|
+
}
|
|
2099
2287
|
runMutation(operation) {
|
|
2100
2288
|
return this.mutationGate.runMutation(operation);
|
|
2101
2289
|
}
|
|
@@ -2112,11 +2300,13 @@ class WorkspaceSyncSingleFlight {
|
|
|
2112
2300
|
WORKSPACE_BRANCH,
|
|
2113
2301
|
WORKSPACE_INTENT_REF_PREFIX,
|
|
2114
2302
|
WORKSPACE_NATIVE_PLANS_RELATIVE_PATH,
|
|
2115
|
-
|
|
2303
|
+
WORKSPACE_PUBLICATION_REF_PREFIX,
|
|
2116
2304
|
WorkspaceSyncSingleFlight,
|
|
2117
2305
|
assertCanonicalCheckoutIndexMatchesWorktree,
|
|
2118
2306
|
assertWorkspaceQuarantineRef,
|
|
2119
2307
|
calculateWorkspaceDiffFingerprint,
|
|
2308
|
+
calculateWorkspaceLocalDiffFingerprint,
|
|
2309
|
+
convergeWorkspaceHead,
|
|
2120
2310
|
encodeWorkspaceBranch,
|
|
2121
2311
|
mirrorShadowWorkspaceToVisible,
|
|
2122
2312
|
mirrorVisibleWorkspaceToShadow,
|