# How GitButler Oplog Revert Operations Restore Repository State

> Discover how GitButler oplog revert operations restore repository state. Learn how GitButler loads snapshots to reconstruct your entire repository, ensuring a full recovery.

- Repository: [GitButler/gitbutler](https://github.com/gitbutlerapp/gitbutler)
- Tags: internals
- Published: 2026-02-16

---

**GitButler oplog revert operations work by loading a previously saved snapshot—a Git tree containing the worktree, index, conflicts, and virtual branch metadata—and reconstructing the entire repository state from that snapshot, then recording the revert itself as a new snapshot.**

The `gitbutlerapp/gitbutler` codebase implements a robust undo system through its operations log (oplog). Unlike traditional Git resets, GitButler oplog revert operations provide a declarative, auditable mechanism for restoring complex repository states including virtual branches and conflict markers.

## What Is the GitButler Oplog?

The oplog is an append-only log where every user action—commits, branch-stack changes, conflict resolutions, and reverts—is stored as an immutable **snapshot**. Each snapshot is a Git tree object that encapsulates the complete state of the repository at that moment.

According to the source code in [`crates/gitbutler-oplog/src/oplog.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-oplog/src/oplog.rs), a snapshot captures:

- `worktree/` – Current worktree files
- `index/` – Staging area state
- `target_tree/` – The target commit tree
- `conflicts/` – Conflict state metadata
- Virtual branch metadata ([`virtual_branches.toml`](https://github.com/gitbutlerapp/gitbutler/blob/main/virtual_branches.toml) and per-branch sub-trees)

## How Snapshots Capture Repository State

Before understanding GitButler oplog revert operations, it is essential to understand how snapshots serialize repository state. The `prepare_snapshot` function in [`crates/gitbutler-oplog/src/oplog.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-oplog/src/oplog.rs) traverses the working directory, index, and virtual branch state to construct a Git tree object.

Virtual branches—GitButler's core abstraction for stacked changes—are serialized into the snapshot via [`virtual_branches.toml`](https://github.com/gitbutlerapp/gitbutler/blob/main/virtual_branches.toml) and individual branch trees. This ensures that complex branch-stack configurations are preserved atomically.

## The GitButler Oplog Revert Process Step-by-Step

When a user triggers a revert, the `restore_snapshot` function (lines 64-71 in [`crates/gitbutler-oplog/src/oplog.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-oplog/src/oplog.rs)) executes an eight-step restoration process:

### 1. Load the Snapshot Tree

The revert operation begins by loading the Git tree associated with the target snapshot SHA. This tree contains all serialized state components needed for reconstruction.

### 2. Restore Conflict State

The `restore_conflicts_tree` function (lines 73-80) extracts the `conflicts/` subtree and writes conflict markers back to `.git/base_merge_parent` and `.git/conflicts`. This resurrects the exact conflict resolution state present at snapshot creation.

### 3. Recreate Virtual Branch Commits

Snapshots may contain virtual branch commits that do not exist in the main object database. The system deserializes stored commit data using `deserialize_commit` (lines 45-57) and writes commit objects into the repository if they are absent. This ensures branch stacks remain intact even if commits were garbage collected.

### 4. Update the Workspace Branch

If the snapshot contains a `workspace` branch, the system resets HEAD, deletes the old `gitbutler/workspace` reference, and recreates it at the restored commit (lines 60-78). This synchronizes GitButler's internal workspace pointer with the historical state.

### 5. Checkout the Worktree

The worktree restoration checks the `cv3` feature flag to determine the checkout method:

- **With `cv3` enabled**: Uses `but_core::worktree::safe_checkout_from_head` (lines 94-100) for fast, atomic worktree updates
- **Legacy mode**: Falls back to the `git2` checkout builder (lines 102-107)

Both methods restore working directory files to their snapshot state.

### 6. Restore Virtual Branch Metadata

The system writes the saved [`virtual_branches.toml`](https://github.com/gitbutlerapp/gitbutler/blob/main/virtual_branches.toml) blob back to the project data directory and re-syncs reference pointers (lines 109-122). This resurrects the exact virtual branch configuration, including branch names, dependencies, and stack ordering.

### 7. Restore the Index

The staging area is reconstructed from the `index/` tree (lines 124-132), ensuring that staged changes present in the snapshot are accurately reflected in the restored state.

### 8. Record the Revert Operation

Finally, `commit_snapshot` (lines 162-169) creates a new snapshot documenting the revert itself. This new snapshot becomes the head of the oplog, maintaining an auditable chain of all operations including undos.

## Code Example: Reverting to a Snapshot

The following Rust example demonstrates how to programmatically execute GitButler oplog revert operations using the `OplogExt` trait:

```rust
use but_ctx::{Context, access::RepoExclusive};
use gitbutler_oplog::OplogExt;
use git2::Oid;

/// Reverts the repository to a specific snapshot SHA.
fn revert_to_snapshot(ctx: &Context, snapshot: Oid) -> anyhow::Result<Oid> {
    // Acquire exclusive access to prevent concurrent modifications
    let mut guard = ctx.exclusive_worktree_access();

    // Execute the revert operation
    // This internally calls restore_snapshot and creates a new revert snapshot
    let new_snapshot_sha = ctx.restore_snapshot(snapshot, &mut guard)?;

    println!("Reverted to {} – new snapshot SHA: {}", snapshot, new_snapshot_sha);
    Ok(new_snapshot_sha)
}

```

To create a snapshot that can be reverted to later:

```rust
use but_ctx::{Context, access::RepoExclusive};
use gitbutler_oplog::{OplogExt, SnapshotDetails, OperationKind};

fn create_revertible_snapshot(ctx: &Context) -> anyhow::Result<Oid> {
    let mut guard = ctx.exclusive_worktree_access();

    let details = SnapshotDetails {
        version: Default::default(),
        operation: OperationKind::UserAction,
        title: "Critical changes before refactor".into(),
        body: None,
        trailers: vec![],
    };

    ctx.create_snapshot(details, &mut guard)
}

```

## Key Source Files for Oplog Operations

Understanding GitButler oplog revert operations requires familiarity with these specific files in the `gitbutlerapp/gitbutler` repository:

| File | Role |
|------|------|
| [`crates/gitbutler-oplog/src/oplog.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-oplog/src/oplog.rs) | Core implementation containing `restore_snapshot`, `prepare_snapshot`, `commit_snapshot`, and conflict restoration logic. |
| [`crates/gitbutler-oplog/src/entry.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-oplog/src/entry.rs) | Defines `SnapshotDetails`, `OperationKind`, and metadata structures stored with each snapshot. |
| [`crates/gitbutler-oplog/src/state.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-oplog/src/state.rs) | Manages the oplog head pointer in [`operations-log.toml`](https://github.com/gitbutlerapp/gitbutler/blob/main/operations-log.toml) and persistent state storage. |
| [`apps/desktop/src/lib/history/oplogService.svelte.ts`](https://github.com/gitbutlerapp/gitbutler/blob/main/apps/desktop/src/lib/history/oplogService.svelte.ts) | Frontend service that invokes Rust Oplog APIs from the Svelte-based desktop UI. |
| [`crates/but/src/command/legacy/oplog.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/but/src/command/legacy/oplog.rs) | CLI command wrappers exposing oplog functionality to the legacy command interface. |

## Summary

GitButler oplog revert operations provide a deterministic mechanism for restoring complex repository states through the following key mechanisms:

- **Snapshot-based architecture**: Every operation is stored as an immutable Git tree containing worktree, index, conflicts, and virtual branch metadata
- **Declarative restoration**: The `restore_snapshot` function in [`crates/gitbutler-oplog/src/oplog.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-oplog/src/oplog.rs) reconstructs state by replaying stored snapshots rather than performing Git resets
- **Virtual branch preservation**: The system recreates missing commits and restores [`virtual_branches.toml`](https://github.com/gitbutlerapp/gitbutler/blob/main/virtual_branches.toml) to maintain branch stack integrity
- **Audit trail maintenance**: Every revert creates a new snapshot, ensuring the oplog remains a complete, reversible history of all user actions

## Frequently Asked Questions

### How does GitButler's oplog differ from Git's reflog?

GitButler's oplog stores **complete state snapshots** as Git trees, while Git's reflog only records reference updates. The oplog captures virtual branch metadata, conflict states, and worktree contents, enabling full repository restoration rather than just reference movement. Additionally, oplog entries are immutable snapshots identified by SHA, whereas reflog entries expire and are not portable.

### Can I revert to a snapshot if the original commits were garbage collected?

Yes. During `restore_snapshot`, GitButler executes `deserialize_commit` (lines 45-57 in [`crates/gitbutler-oplog/src/oplog.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-oplog/src/oplog.rs)) to recreate any virtual branch commits that exist only in the snapshot but are missing from the object database. This ensures branch stacks remain intact even after aggressive garbage collection.

### What happens to uncommitted worktree changes during a revert?

The revert process checks out the saved `worktree/` tree from the snapshot. If the `cv3` feature flag is enabled, GitButler uses `but_core::worktree::safe_checkout_from_head` for atomic worktree updates; otherwise, it falls back to the `git2` checkout builder (lines 94-107 in [`crates/gitbutler-oplog/src/oplog.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-oplog/src/oplog.rs)). Any current uncommitted changes are replaced by the snapshot's worktree state.

### Is the revert operation itself recorded in the oplog?

Yes. After restoring state, `commit_snapshot` (lines 162-169 in [`crates/gitbutler-oplog/src/oplog.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-oplog/src/oplog.rs)) creates a new snapshot documenting the revert operation. This new snapshot becomes the head of the oplog, maintaining a complete audit trail that includes the revert itself, allowing users to undo the undo if necessary.