How OpenCode Session Revert Functionality Works to Undo Changes
OpenCode's session revert functionality uses a three‑step workflow that marks a revert point in session metadata, restores the working tree using Git‑style snapshots, and cleans up the database to remove reverted content from the UI.
OpenCode (anomalyco/opencode) provides a robust session revert functionality that allows developers to rewind their coding sessions to previous states without losing historical data. This Git‑inspired approach captures snapshots of the working tree and manages message history through a sophisticated revert descriptor system. The implementation spans multiple source files including packages/opencode/src/session/revert.ts and packages/opencode/src/snapshot/index.ts.
The Three-Step Session Revert Workflow
The SessionRevert.revert function orchestrates the undo process through three distinct phases that ensure both the filesystem and database remain consistent.
Step 1: Marking the Revert Point in Session Metadata
The process begins in packages/opencode/src/session/revert.ts (lines 24‑80) where the revert function scans all session messages chronologically. When it encounters the target messageID or optional partID, it constructs a revert descriptor—an object containing {messageID, partID?} that identifies the earliest user message to preserve.
if ((msg.info.id === input.messageID && !input.partID) || part.id === input.partID) {
const partID = remaining.some(item => ["text","tool"].includes(item.type))
? input.partID : undefined
revert = { messageID: !partID && lastUser ? lastUser.id : msg.info.id, partID }
}
This descriptor gets stored in the session.revert column (defined in packages/opencode/src/session/session.sql.ts line 28) as JSON, enabling the session to track its reverted state persistently.
Step 2: Restoring the Working Tree with Git‑Style Snapshots
OpenCode utilizes Git‑style snapshots to restore file states. First, it captures or reuses a snapshot hash:
revert.snapshot = session.revert?.snapshot ?? (await Snapshot.track())
The Snapshot.track() method (in packages/opencode/src/snapshot/index.ts, lines 51‑77) writes the current worktree as a Git tree and returns its hash. Then, the Snapshot.revert function (lines 31‑61) performs the actual restoration:
export async function revert(patches: Patch[]) {
const files = new Set<string>()
const git = gitdir()
for (const item of patches) {
for (const file of item.files) {
if (files.has(file)) continue
const result = await $`git --git-dir ${git} --work-tree ${Instance.worktree} checkout ${item.hash} -- ${file}`
if (result.exitCode !== 0) {
await fs.unlink(file).catch(() => {})
}
files.add(file)
}
}
}
This procedure checks out each file from the snapshot commit (item.hash), effectively rewinding content to its previous state. If a file did not exist in the snapshot, the system deletes it from the worktree.
Step 3: Cleaning Up the Database and UI State
After filesystem restoration, the cleanup helper (lines 91‑136 in revert.ts) removes obsolete messages and parts from the SQLite store. It operates directly on MessageTable and PartTable, publishing MessageV2.Event.Removed and MessageV2.Event.PartRemoved events via the internal bus to synchronize the UI:
export async function cleanup(session: Session.Info) {
for (const msg of remove) {
Database.use(db => db.delete(MessageTable).where(eq(MessageTable.id, msg.info.id)).run())
await Bus.publish(MessageV2.Event.Removed, { sessionID, messageID: msg.info.id })
}
}
Deep Dive into Diff Computation and Storage
Once the workspace is rewound, OpenCode computes a human‑readable diff for the reverted message range:
const rangeMessages = all.filter(msg => msg.info.id >= revert!.messageID)
const diffs = await SessionSummary.computeDiff({ messages: rangeMessages })
await Storage.write(["session_diff", input.sessionID], diffs)
Bus.publish(Session.Event.Diff, { sessionID: input.sessionID, diff: diffs })
The system then persists the revert state with summary statistics:
return Session.setRevert({
sessionID: input.sessionID,
revert,
summary: {
additions: diffs.reduce((s, x) => s + x.additions, 0),
deletions: diffs.reduce((s, x) => s + x.deletions, 0),
files: diffs.length,
},
})
This metadata allows UI components to display "X additions / Y deletions" after a revert operation completes.
How to Unrevert and Restore Original State
The unrevert method (lines 82‑89 in revert.ts) provides an escape hatch to return to the original state before the revert occurred:
export async function unrevert(input: { sessionID: string }) {
const session = await Session.get(input.sessionID)
if (!session.revert) return session
if (session.revert.snapshot) await Snapshot.restore(session.revert.snapshot)
return Session.clearRevert(input.sessionID)
}
The Snapshot.restore function (lines 12‑20 in snapshot/index.ts) checks out the saved tree hash and writes all files back to their pre‑revert state, while Session.clearRevert removes the revert flag from the session metadata.
SDK Usage Examples
The OpenCode JavaScript SDK exposes these operations through type‑safe methods defined in packages/sdk/js/src/v2/gen/sdk.gen.ts (lines 681‑695):
// Revert to a specific message
await client.session.revert({
sessionID: "s_01",
messageID: "m_42", // Target point to revert to
// optional: partID: "p_3"
})
// Restore to original state before revert
await client.session.unrevert({ sessionID: "s_01" })
Both calls return the updated Session.Info object, including the revert field (if active) and a summary of file changes.
Summary
- OpenCode session revert functionality combines Git‑style snapshots with database cleanup to provide reliable undo capabilities.
- The
revertfunction inpackages/opencode/src/session/revert.tsmarks revert points by building a descriptor object that identifies which messages to preserve. - Snapshot operations in
packages/opencode/src/snapshot/index.tshandle the actual file restoration usinggit checkoutcommands against tracked tree hashes. - The
cleanupmethod removes obsolete database records while publishing events to keep the UI synchronized. - Unrevert functionality allows users to restore their original state by checking out the saved snapshot and clearing session metadata.
- The JavaScript SDK provides convenient
client.session.revert()andclient.session.unrevert()methods for programmatic access.
Frequently Asked Questions
How does OpenCode determine which files to restore during a revert?
OpenCode collects patch objects containing file paths and snapshot hashes during the revert point marking phase. The Snapshot.revert function iterates through these patches and executes git checkout commands for each file against the specific commit hash stored in the snapshot. If a file checkout fails (indicating the file did not exist in the snapshot), the system deletes that file from the worktree.
What is the difference between revert and unrevert in OpenCode?
Revert rewinds the session to a previous message point by restoring files from a Git‑style snapshot and removing subsequent messages from the database. Unrevert restores the session to its state before the revert occurred by checking out the saved snapshot hash and clearing the revert metadata from the session record. Unrevert essentially acts as an "undo for the undo."
Does OpenCode delete historical data when performing a session revert?
No, OpenCode does not permanently delete historical data during a revert operation. While the cleanup function removes messages and parts from the active SQLite database tables to clean up the UI, the underlying Git snapshots preserve the filesystem state. The original content remains recoverable through the snapshot hash stored in the revert descriptor, enabling the unrevert functionality.
How can I use the OpenCode SDK to revert a session programmatically?
Import the OpenCode client and call client.session.revert() with the target sessionID and messageID. Optionally specify a partID to revert to a specific part within a message rather than the entire message. To cancel the revert and return to the latest state, invoke client.session.unrevert() with the same sessionID. Both methods return the updated session information including revert status and diff summaries.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →