# How OpenCode Session Revert Functionality Works to Undo Changes

> Discover how OpenCode's session revert functionality undoes changes with its three-step workflow: marking revert points, restoring the working tree with snapshots, and cleaning the database for a seamless experience.

- Repository: [Anomaly/opencode](https://github.com/anomalyco/opencode)
- Tags: internals
- Published: 2026-02-19

---

**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`](https://github.com/anomalyco/opencode/blob/main/packages/opencode/src/session/revert.ts) and [`packages/opencode/src/snapshot/index.ts`](https://github.com/anomalyco/opencode/blob/main/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`](https://github.com/anomalyco/opencode/blob/main/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.

```typescript
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`](https://github.com/anomalyco/opencode/blob/main/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:

```typescript
revert.snapshot = session.revert?.snapshot ?? (await Snapshot.track())

```

The `Snapshot.track()` method (in **[`packages/opencode/src/snapshot/index.ts`](https://github.com/anomalyco/opencode/blob/main/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:

```typescript
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`](https://github.com/anomalyco/opencode/blob/main/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:

```typescript
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:

```typescript
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:

```typescript
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`](https://github.com/anomalyco/opencode/blob/main/revert.ts)) provides an escape hatch to return to the original state before the revert occurred:

```typescript
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`](https://github.com/anomalyco/opencode/blob/main/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`](https://github.com/anomalyco/opencode/blob/main/packages/sdk/js/src/v2/gen/sdk.gen.ts)** (lines 681‑695):

```typescript
// 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 **`revert`** function in [`packages/opencode/src/session/revert.ts`](https://github.com/anomalyco/opencode/blob/main/packages/opencode/src/session/revert.ts) marks revert points by building a descriptor object that identifies which messages to preserve.
- **Snapshot operations** in [`packages/opencode/src/snapshot/index.ts`](https://github.com/anomalyco/opencode/blob/main/packages/opencode/src/snapshot/index.ts) handle the actual file restoration using `git checkout` commands against tracked tree hashes.
- The **`cleanup`** method 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()` and `client.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.