diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index e8db405fddd..47c72e9499c 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -152,6 +152,7 @@ export namespace Session { partID: z.string().optional(), snapshot: z.string().optional(), diff: z.string().optional(), + files: z.string().array().optional(), }) .optional(), }) diff --git a/packages/opencode/src/session/revert.ts b/packages/opencode/src/session/revert.ts index ef9c7e2aace..0d355bb2e39 100644 --- a/packages/opencode/src/session/revert.ts +++ b/packages/opencode/src/session/revert.ts @@ -57,6 +57,7 @@ export namespace SessionRevert { if (revert) { const session = await Session.get(input.sessionID) revert.snapshot = session.revert?.snapshot ?? (await Snapshot.track()) + revert.files = [...new Set(patches.flatMap((p) => p.files))] await Snapshot.revert(patches) if (revert.snapshot) revert.diff = await Snapshot.diff(revert.snapshot) const rangeMessages = all.filter((msg) => msg.info.id >= revert!.messageID) @@ -84,7 +85,7 @@ export namespace SessionRevert { SessionPrompt.assertNotBusy(input.sessionID) const session = await Session.get(input.sessionID) if (!session.revert) return session - if (session.revert.snapshot) await Snapshot.restore(session.revert.snapshot) + if (session.revert.snapshot) await Snapshot.restore(session.revert.snapshot, session.revert.files) return Session.clearRevert(input.sessionID) } diff --git a/packages/opencode/src/snapshot/index.ts b/packages/opencode/src/snapshot/index.ts index cf254b4cef7..229e5010658 100644 --- a/packages/opencode/src/snapshot/index.ts +++ b/packages/opencode/src/snapshot/index.ts @@ -112,9 +112,38 @@ export namespace Snapshot { } } - export async function restore(snapshot: string) { - log.info("restore", { commit: snapshot }) + export async function restore(snapshot: string, files?: string[]) { + log.info("restore", { commit: snapshot, files: files?.length }) const git = gitdir() + + if (files?.length) { + const restored = new Set() + for (const file of files) { + if (restored.has(file)) continue + const result = + await $`git -c core.longpaths=true -c core.symlinks=true --git-dir ${git} --work-tree ${Instance.worktree} checkout ${snapshot} -- ${file}` + .quiet() + .cwd(Instance.worktree) + .nothrow() + if (result.exitCode !== 0) { + const relative = path.relative(Instance.worktree, file) + const check = + await $`git -c core.longpaths=true -c core.symlinks=true --git-dir ${git} --work-tree ${Instance.worktree} ls-tree ${snapshot} -- ${relative}` + .quiet() + .cwd(Instance.worktree) + .nothrow() + if (check.exitCode === 0 && check.text().trim()) { + log.info("file existed in snapshot but checkout failed, keeping", { file }) + } else { + log.info("file did not exist in snapshot, deleting", { file }) + await fs.unlink(file).catch(() => {}) + } + } + restored.add(file) + } + return + } + const result = await $`git -c core.longpaths=true -c core.symlinks=true --git-dir ${git} --work-tree ${Instance.worktree} read-tree ${snapshot} && git -c core.longpaths=true -c core.symlinks=true --git-dir ${git} --work-tree ${Instance.worktree} checkout-index -a -f` .quiet()