fix: speed up multi-file apply-patch

This commit is contained in:
Codex
2026-06-06 23:12:51 +00:00
parent 27acd2a98f
commit b81585bcab
4 changed files with 319 additions and 3 deletions
+66 -1
View File
@@ -166,6 +166,21 @@ async function applyPatchV2FixtureAttempt(patch: string, files: Record<string, s
const start = blockIndex * blockSize;
return { exitCode: 0, stdout: content.subarray(start, start + blockSize).toString("base64"), stderr: "" };
}
if (operation === "read-bulk-b64") {
const targets = command.slice(5);
const records: string[] = [];
for (const item of targets) {
if (!state.has(item)) return { exitCode: 1, stdout: "", stderr: `missing ${item}` };
const content = Buffer.from(state.get(item) ?? "", "utf8");
records.push([
Buffer.from(item, "utf8").toString("base64"),
String(content.length),
sha256Hex(content),
content.toString("base64"),
].join(" "));
}
return { exitCode: 0, stdout: `UNIDESK_APPLY_PATCH_V2_BULK_READ ${targets.length}\n${records.join("\n")}\n`, stderr: "" };
}
if (operation === "write-b64-argv") {
const expectedBytes = Number(command[6] ?? "-1");
const expectedSha256 = command[7] ?? "";
@@ -186,6 +201,33 @@ async function applyPatchV2FixtureAttempt(patch: string, files: Record<string, s
state.set(target, content);
return { exitCode: 0, stdout: "", stderr: "" };
}
if (operation === "apply-replacements-bulk-stdin") {
const expectedCount = Number(command[5] ?? "-1");
const records = (input ?? "").split(/\r?\n/u).filter((line) => line.trim().length > 0);
if (records.length !== expectedCount) return { exitCode: 23, stdout: "", stderr: "mock bulk replacement record count mismatch" };
for (const record of records) {
const fields = record.split(/\s+/u);
if (fields.length !== 6) return { exitCode: 23, stdout: "", stderr: "mock bulk replacement malformed record" };
const [pathB64, originalBytesText, originalSha256, finalBytesText, finalSha256, replacementsText] = fields;
const targetPath = Buffer.from(pathB64 ?? "", "base64").toString("utf8");
const original = state.get(targetPath);
if (original === undefined) return { exitCode: 1, stdout: "", stderr: `missing ${targetPath}` };
const originalBuffer = Buffer.from(original, "utf8");
if (originalBuffer.length !== Number(originalBytesText) || sha256Hex(original) !== originalSha256) return { exitCode: 23, stdout: "", stderr: "mock bulk replacement original integrity mismatch" };
const lines = original.split("\n");
if (lines.at(-1) === "") lines.pop();
for (const item of (replacementsText ?? "").split(";").filter(Boolean).reverse()) {
const [startText, oldLengthText, newB64] = item.split(",", 3);
const newLines = Buffer.from(newB64 ?? "", "base64").toString("utf8").split("\n");
if (newLines.at(-1) === "") newLines.pop();
lines.splice(Number(startText), Number(oldLengthText), ...newLines);
}
const updated = lines.length === 0 ? "" : `${lines.join("\n")}\n`;
if (Buffer.byteLength(updated, "utf8") !== Number(finalBytesText) || sha256Hex(updated) !== finalSha256) return { exitCode: 23, stdout: "", stderr: "mock bulk replacement final integrity mismatch" };
state.set(targetPath, updated);
}
return { exitCode: 0, stdout: "", stderr: "" };
}
if (operation === "write-b64-begin") {
pendingWrites.set(`${target}\0${command[6] ?? ""}`, "");
return { exitCode: 0, stdout: "", stderr: "" };
@@ -800,6 +842,29 @@ export async function runSshArgvGuidanceContract(): Promise<JsonRecord> {
assertCondition((unifiedTiming.remoteOperationCounts.stat ?? 0) >= 1 && (unifiedTiming.remoteOperationCounts["read-b64-block"] ?? 0) >= 1 && Object.keys(unifiedTiming.remoteOperationCounts).some((key) => key.startsWith("write-b64")), "v2 timing summary should classify stat/read/write operations", unifiedTiming);
assertCondition(unifiedHeaderLineRangeV2.stdout.startsWith("Success. Updated the following files:"), "v2 timing summary must not change Codex-compatible success stdout", unifiedHeaderLineRangeV2.stdout);
const bulkPatchLines = ["*** Begin Patch"];
const bulkFiles: Record<string, string> = {};
for (let fileIndex = 0; fileIndex < 4; fileIndex += 1) {
const fileName = `bulk-${fileIndex}.txt`;
bulkFiles[fileName] = Array.from({ length: 120 }, (_, lineIndex) => `file=${fileIndex} line=${lineIndex} value=alpha`).join("\n") + "\n";
bulkPatchLines.push(`*** Update File: ${fileName}`);
bulkPatchLines.push("@@");
bulkPatchLines.push(` file=${fileIndex} line=39 value=alpha`);
bulkPatchLines.push(`-file=${fileIndex} line=40 value=alpha`);
bulkPatchLines.push(`+file=${fileIndex} line=40 value=beta`);
bulkPatchLines.push(` file=${fileIndex} line=41 value=alpha`);
}
bulkPatchLines.push("*** End Patch", "");
const bulkV2 = await applyPatchV2FixtureAttempt(bulkPatchLines.join("\n"), bulkFiles, { stderrOutput: true });
assertCondition(bulkV2.exitCode === 0 && bulkV2.error === null, "v2 multi-file update patch should succeed through the bulk path", bulkV2);
assertCondition(bulkV2.commands.filter((command) => command.startsWith("read-bulk-b64")).length === 1 && bulkV2.commands.filter((command) => command.startsWith("apply-replacements-bulk-stdin")).length === 1, "v2 multi-file updates should collapse remote IO to one bulk read and one line-level bulk apply", bulkV2.commands);
assertCondition(!bulkV2.commands.some((command) => command.startsWith("stat") || command.startsWith("read-b64-block") || command.startsWith("write-b64-stdin")), "v2 bulk path should avoid per-file stat/read/write operations", bulkV2.commands);
for (let fileIndex = 0; fileIndex < 4; fileIndex += 1) {
assertCondition(bulkV2.files[`bulk-${fileIndex}.txt`]?.includes(`file=${fileIndex} line=40 value=beta`), "v2 bulk path should write all changed files", { fileIndex, files: bulkV2.files });
}
const bulkTiming = applyPatchTimingFromStderr(bulkV2.stderr);
assertCondition(bulkTiming.remoteOperationCount === 2 && bulkTiming.remoteOperationCounts["read-bulk-b64"] === 1 && bulkTiming.remoteOperationCounts["apply-replacements-bulk-stdin"] === 1, "v2 timing summary should classify bulk read and line-level apply operations", bulkTiming);
const unprefixedUpdateContextV2 = await applyPatchV2FixtureAttempt([
"*** Begin Patch",
"*** Update File: internal/cloud/access-control.ts",
@@ -1018,7 +1083,7 @@ export async function runSshArgvGuidanceContract(): Promise<JsonRecord> {
assertCondition(failedCompoundV2.error !== null, "v2 compound patch should fail when a later hunk does not match", failedCompoundV2);
assertCondition(failedCompoundV2.files["first.txt"] === "new first\n", "v2 should match Codex apply_patch by preserving earlier committed changes when a later hunk fails", failedCompoundV2);
assertCondition(failedCompoundV2.files["second.txt"] === "old second\n", "v2 must leave later failed files unchanged", failedCompoundV2);
assertCondition(failedCompoundV2.commands.some((command) => command.startsWith("write-b64")), "v2 should commit preceding operations in patch order like Codex apply_patch", failedCompoundV2.commands);
assertCondition(failedCompoundV2.commands.some((command) => command.startsWith("write-b64") || command.startsWith("apply-replacements-bulk-stdin")), "v2 should commit preceding operations in patch order like Codex apply_patch", failedCompoundV2.commands);
assertCondition(
Array.isArray((failedCompoundV2.error as { details?: { partialChanges?: unknown } })?.details?.partialChanges)
&& ((failedCompoundV2.error as { details?: { partialChanges?: string[] } }).details?.partialChanges ?? []).includes("M first.txt"),