From 8abfd71684a1a0fccc55575d6f8be775a835f6a4 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Tue, 17 Feb 2026 19:51:23 -0600 Subject: [PATCH] refactor(apply_patch): avoid redundant file reads during update verification Make patch derivation async and allow callers to provide existing file content so apply_patch update checks reuse already-loaded text instead of reading the same file twice. --- packages/opencode/src/patch/index.ts | 27 ++++++++++++----------- packages/opencode/src/tool/apply_patch.ts | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/opencode/src/patch/index.ts b/packages/opencode/src/patch/index.ts index 0efeff544f..5d0d53e61b 100644 --- a/packages/opencode/src/patch/index.ts +++ b/packages/opencode/src/patch/index.ts @@ -1,7 +1,6 @@ import z from "zod" import * as path from "path" import * as fs from "fs/promises" -import { readFileSync } from "fs" import { Log } from "../util/log" export namespace Patch { @@ -308,16 +307,18 @@ export namespace Patch { content: string } - export function deriveNewContentsFromChunks(filePath: string, chunks: UpdateFileChunk[]): ApplyPatchFileUpdate { - // Read original file content - let originalContent: string - try { - originalContent = readFileSync(filePath, "utf-8") - } catch (error) { - throw new Error(`Failed to read file ${filePath}: ${error}`) - } + export async function deriveNewContentsFromChunks( + filePath: string, + chunks: UpdateFileChunk[], + originalContent?: string, + ): Promise { + const content = + originalContent ?? + (await fs.readFile(filePath, "utf-8").catch((error) => { + throw new Error(`Failed to read file ${filePath}: ${error}`) + })) - let originalLines = originalContent.split("\n") + let originalLines = content.split("\n") // Drop trailing empty element for consistent line counting if (originalLines.length > 0 && originalLines[originalLines.length - 1] === "") { @@ -335,7 +336,7 @@ export namespace Patch { const newContent = newLines.join("\n") // Generate unified diff - const unifiedDiff = generateUnifiedDiff(originalContent, newContent) + const unifiedDiff = generateUnifiedDiff(content, newContent) return { unified_diff: unifiedDiff, @@ -545,7 +546,7 @@ export namespace Patch { break case "update": - const fileUpdate = deriveNewContentsFromChunks(hunk.path, hunk.chunks) + const fileUpdate = await deriveNewContentsFromChunks(hunk.path, hunk.chunks) if (hunk.move_path) { // Handle file move @@ -641,7 +642,7 @@ export namespace Patch { case "update": const updatePath = path.resolve(effectiveCwd, hunk.path) try { - const fileUpdate = deriveNewContentsFromChunks(updatePath, hunk.chunks) + const fileUpdate = await deriveNewContentsFromChunks(updatePath, hunk.chunks) changes.set(resolvedPath, { type: "update", unified_diff: fileUpdate.unified_diff, diff --git a/packages/opencode/src/tool/apply_patch.ts b/packages/opencode/src/tool/apply_patch.ts index 1344467c71..62c26bb75d 100644 --- a/packages/opencode/src/tool/apply_patch.ts +++ b/packages/opencode/src/tool/apply_patch.ts @@ -101,7 +101,7 @@ export const ApplyPatchTool = Tool.define("apply_patch", { // Apply the update chunks to get new content try { - const fileUpdate = Patch.deriveNewContentsFromChunks(filePath, hunk.chunks) + const fileUpdate = await Patch.deriveNewContentsFromChunks(filePath, hunk.chunks, oldContent) newContent = fileUpdate.content } catch (error) { throw new Error(`apply_patch verification failed: ${error}`)