# How GitButler's Virtual Branch System Works: Architecture and Implementation

> Discover how GitButler's virtual branch system works. Explore its architecture, lightweight branches, and TOML metadata storage for efficient Git workflows.

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

---

**GitButler's virtual branch system implements lightweight, stackable branches as an overlay on standard Git repositories, storing metadata in TOML files while maintaining hidden Git references for each branch tip.**

GitButler's virtual branch system (also referred to as *stacks*) provides a way to manage multiple parallel lines of work without polluting the standard Git reference namespace. Unlike traditional Git branches that exist as refs in `.git/refs/heads/`, virtual branches persist their state in a dedicated TOML configuration file while using hidden references only for the current commit tips. This architecture allows developers to maintain numerous work-in-progress branches while keeping the underlying Git repository clean.

## Architecture Overview

The virtual branch system is organized into three distinct layers that separate persistence concerns from operational logic.

### State Persistence Layer

Located in [`crates/gitbutler-stack/src/state.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-stack/src/state.rs), this layer handles the [`virtual_branches.toml`](https://github.com/gitbutlerapp/gitbutler/blob/main/virtual_branches.toml) file that stores all virtual branch metadata. The `VirtualBranchesHandle` struct provides the interface for reading and writing this state, including methods like `read_file` and `write_file` that deserialize and serialize the `VirtualBranches` struct.

### In-Memory Model

Defined in [`crates/gitbutler-stack/src/stack.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-stack/src/stack.rs), the `Stack` struct represents a virtual branch containing one or more `StackBranch` heads. Each `StackBranch` (defined in [`crates/gitbutler-stack/src/stack_branch.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-stack/src/stack_branch.rs)) acts as a mutable pointer to a commit or patch and manages the underlying Git reference through methods like `set_real_reference` and `rename_real_reference`.

### Operations and API

High-level functions for creating, amending, applying, and deleting virtual branches reside in [`crates/gitbutler-branch-actions/src/branch_manager/branch_creation.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-branch-actions/src/branch_manager/branch_creation.rs) and [`crates/gitbutler-branch-actions/src/lib.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-branch-actions/src/lib.rs). The RPC facade in [`crates/but-api/src/legacy/virtual_branches.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/but-api/src/legacy/virtual_branches.rs) exposes these operations to the UI and CLI.

## Persistent Storage with VirtualBranchesHandle

The `VirtualBranchesHandle` struct in [`crates/gitbutler-stack/src/state.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-stack/src/state.rs) manages the [`virtual_branches.toml`](https://github.com/gitbutlerapp/gitbutler/blob/main/virtual_branches.toml) file located in the project directory. This file contains the default target branch configuration and an array of branch entries.

```rust
use gitbutler_stack::VirtualBranchesHandle;

let handle = VirtualBranchesHandle::new(project_path);
let state = handle.read_file()?;

```

The TOML structure includes a `default_target` specifying the base branch and commit SHA, plus a `branches` array containing UUIDs, ordering indices, workspace status flags, and head configurations for each virtual branch.

## The Stack and StackBranch Data Model

A virtual branch is represented by the `Stack` struct in [`crates/gitbutler-stack/src/stack.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-stack/src/stack.rs). Each stack maintains an ordered list of heads through the `heads: Vec<StackBranch>` field.

```rust
pub struct Stack {
    pub id: StackId,                     // Unique UUID
    pub source_refname: Option<Refname>, // Optional upstream branch
    pub upstream: Option<RemoteRefname>, // Remote tracking
    pub order: usize,                    // UI ordering
    pub in_workspace: bool,              // Applied status
    pub heads: Vec<StackBranch>,         // Series of commits/patches
}

```

Each `StackBranch` in [`crates/gitbutler-stack/src/stack_branch.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-stack/src/stack_branch.rs) manages the actual Git reference through helper methods like `set_real_reference` and `rename_real_reference`, ensuring the hidden ref stays synchronized with the stored OID.

## Creating Virtual Branches

The creation flow in [`crates/gitbutler-branch-actions/src/branch_manager/branch_creation.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-branch-actions/src/branch_manager/branch_creation.rs) follows a specific sequence when `create_virtual_branch` is called:

1. Load the default target from `VirtualBranchesHandle`
2. Generate a unique branch name and compute the next order index
3. Shift existing stacks to accommodate the new order
4. Initialize an empty `Stack` with a single `StackBranch` pointing to the target SHA
5. Persist via `VirtualBranchesHandle::set_stack`
6. Create the hidden Git reference via `add_branch_reference`
7. Update the workspace commit via `update_workspace_commit`

```rust
use gitbutler_branch_actions::create_virtual_branch;
use gitbutler_branch::BranchCreateRequest;

let request = BranchCreateRequest {
    name: Some("feature-x".into()),
    order: None,
    ..Default::default()
};

let stack_entry = create_virtual_branch(&mut ctx, &request)?;
println!("New virtual branch ID: {}", stack_entry.id);

```

## Applying and Unapplying Branches

Virtual branches can be toggled between active (in workspace) and inactive states through the workspace integration layer in [`crates/gitbutler-workspace/src/lib.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-workspace/src/lib.rs).

**Applying** a branch via `but_workspace::branch::apply`:
- Checks out the stack's tip commit into the worktree
- Sets `in_workspace = true` for the stack
- Persists the updated state via `VirtualBranchesHandle::set_stack`

**Unapplying** via `gitbutler_branch_actions::unapply_stack`:
- Removes working tree changes associated with the branch
- Sets `in_workspace = false`
- Triggers garbage collection for unreachable stacks

## Amending and Modifying Commits

The amend operation in [`crates/but-api/src/legacy/virtual_branches.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/but-api/src/legacy/virtual_branches.rs) allows rewriting the tip of a virtual branch without affecting the underlying TOML metadata structure.

```rust
use gitbutler_branch_actions::amend;
use gix::ObjectId;

let new_head_oid: ObjectId = /* new commit SHA */;
let diff_specs = vec![/* file changes as DiffSpec */];

let new_oid = amend(&mut ctx, stack_id, new_head_oid, diff_specs)?;

```

This delegates to the branch actions layer, which updates the `StackBranch` head and synchronizes the hidden Git reference through the `set_real_reference` helper.

## Garbage Collection

Inactive virtual branches that have no reachable commits are automatically cleaned up to prevent metadata bloat. The `VirtualBranchesHandle::garbage_collect` method in [`crates/gitbutler-stack/src/state.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-stack/src/state.rs) scans all stacks listed in [`virtual_branches.toml`](https://github.com/gitbutlerapp/gitbutler/blob/main/virtual_branches.toml), verifies whether their head commits are reachable from the default target branch, and deletes orphaned entries that are no longer accessible.

## Summary

- GitButler's virtual branch system uses a **TOML-based state file** ([`virtual_branches.toml`](https://github.com/gitbutlerapp/gitbutler/blob/main/virtual_branches.toml)) to store branch metadata separately from standard Git refs
- The architecture separates concerns into **persistence** (`VirtualBranchesHandle`), **in-memory models** (`Stack`, `StackBranch`), and **high-level operations**
- Virtual branches exist as **hidden Git references** only when active, keeping the `.git/refs/heads/` namespace clean
- **Creation, application, and amendment** operations maintain synchronization between the TOML state and underlying Git objects through the branch actions API
- **Garbage collection** automatically removes unreachable virtual branches to prevent metadata accumulation

## Frequently Asked Questions

### What is the difference between a virtual branch and a regular Git branch?

Regular Git branches exist as references in `.git/refs/heads/` and are part of the standard Git object model. Virtual branches in GitButler are lightweight overlays stored in a TOML configuration file that only create hidden Git references when actively applied to the workspace. This allows you to maintain many parallel workstreams without cluttering your Git ref namespace or affecting repository history until you choose to integrate changes.

### Where does GitButler store virtual branch metadata?

GitButler stores all virtual branch metadata in a file named [`virtual_branches.toml`](https://github.com/gitbutlerapp/gitbutler/blob/main/virtual_branches.toml) located in the project directory. This file contains the default target branch configuration, branch UUIDs, ordering indices, workspace status flags, and head configurations. The `VirtualBranchesHandle` struct in [`crates/gitbutler-stack/src/state.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-stack/src/state.rs) manages all read and write operations to this file, ensuring atomic updates and consistency with the underlying Git repository.

### How does GitButler handle virtual branch garbage collection?

The system automatically removes virtual branches that are no longer in the workspace and have unreachable commits. The `VirtualBranchesHandle::garbage_collect` method in [`crates/gitbutler-stack/src/state.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/gitbutler-stack/src/state.rs) scans all stacks listed in [`virtual_branches.toml`](https://github.com/gitbutlerapp/gitbutler/blob/main/virtual_branches.toml), verifies whether their head commits are reachable from the default target branch, and deletes orphaned entries. This prevents metadata accumulation from abandoned workstreams while preserving active or reachable branch history.

### Can virtual branches be converted to regular Git branches?

Yes, virtual branches can be converted to regular Git branches through GitButler's integration features. When you apply a virtual branch to the workspace, GitButler creates a hidden Git reference that points to the branch tip using `add_branch_reference` and `set_real_reference` methods. You can then push this reference to a remote or convert it to a standard branch reference. The `StackBranch` struct in [`stack_branch.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/stack_branch.rs) provides the reference management helpers that ensure the virtual state remains synchronized with actual Git objects.