GitButler CLI Command Structure: Architecture and Implementation Guide

The GitButler CLI uses a parse-context-dispatch architecture where clap handles argument parsing, but_ctx::Context manages repository state, and command modules in command/vbranch.rs and command/project.rs implement the core logic.

The GitButler command-line interface is implemented in the gitbutler-cli crate as a thin wrapper around the core library. Understanding the GitButler CLI command structure reveals how the tool bridges user input with the virtual branch management system through a clean separation of concerns.

CLI Architecture Overview

The pipeline consists of five distinct phases that transform raw command-line input into repository operations:

  • Argument definition – All CLI flags and subcommands are declared with clap macros inside Args and nested enums in crates/gitbutler-cli/src/args.rs.
  • Parsingclap::Parser::parse() turns the raw command line into an Args struct at line 14 of main.rs.
  • Application context – A but_ctx::Context is discovered from the current directory (or a supplied path) and holds the repo, workspace, and configuration (lines 24-25 of main.rs).
  • Dispatch – The match args.cmd block routes the request to the appropriate command module (vbranch or project) at lines 22-48 of main.rs.
  • Command implementation – Each subcommand is a small, focused function that works against the Context in command/vbranch.rs or command/project.rs.

Command Hierarchy and Structure

Top-Level Commands

The CLI exposes two primary command groups defined in the Subcommands enum:

  • branch (aliased as vbranch): Manages virtual branches (stacks) with subcommands including list, apply, commit, series, and create.
  • project: Handles repository registration with subcommands list and add.

Subcommand Organization

The hierarchy is defined in crates/gitbutler-cli/src/args.rs using nested enums. The Subcommands enum branches into Branch(vbranch::Platform) or Project(project::Platform), each containing their own SubCommands variants. This structure allows the CLI to parse commands like gitbutler-cli branch create feature/login by matching the Branch variant, then the Create subcommand within the vbranch::Platform enum.

Virtual Branch Commands Implementation

Core Operations

The command/vbranch.rs module implements all stack manipulation logic through focused functions that map directly to CLI subcommands:

  • list: Enumerates stacks via VirtualBranchesHandle::list_all_stacks() and prints one-line summaries for each virtual branch.
  • create: Invokes gitbutler_branch_actions::create_virtual_branch with a BranchCreateRequest to initialize new stacks.
  • apply: Supports two distinct modes—apply_by_name activates existing virtual branches by name, while apply_from_branch converts real Git branches into virtual stacks when the --branch flag is provided.
  • commit: Gathers worktree changes, builds DiffSpec objects, and executes the legacy commit engine via commit_engine::create_commit_simple.
  • series: Loads an existing stack via stack_by_name and invokes stack.add_series_top_of_stack to manage patch series within stacks.

Context and Worktree Safety

Every command receives a but_ctx::Context object that abstracts repository access and project metadata. Before modifying state, commands acquire an exclusive worktree guard to prevent race conditions:

let mut guard = ctx.exclusive_worktree_access();
let outcome = ctx.branch_manager().create_virtual_branch_from_branch(
    ...,
    guard.write_permission()
)?;

This pattern appears in apply_by_name, apply_from_branch, and create functions (lines 56-68 of vbranch.rs), ensuring safe concurrent access to the Git repository.

Project Management Commands

The command/project.rs module handles repository registration and discovery:

  • list: Calls gitbutler_project::dangerously_list_projects_without_migration() to display known projects with their IDs, titles, and paths.
  • add: Normalizes the supplied path to a canonical Git worktree (lines 24-28), registers it via gitbutler_project::add_at_app_data_dir, and optionally sets a base remote branch through gitbutler_branch_actions::set_base_branch when the --switch-to-workspace flag is provided.

Repository discovery occurs at command/project.rs lines 24-28, where the path is resolved against the filesystem.

Context, Tracing, and Debug Output

The CLI supports diagnostic visibility through the --trace flag, which initializes the tracing crate in main.rs (line 75). When enabled, commands serialize their results to JSON via debug_print in command/mod.rs, providing structured output for debugging and scripting integration.

Usage Examples

Listing all virtual branches in the current project:

gitbutler-cli branch

Creating a new virtual branch named feature/auth:

gitbutler-cli branch create feature/auth

Applying an existing real branch as a virtual stack:

gitbutler-cli branch apply -b main feature/login

Registering a new repository with a specific upstream:

gitbutler-cli project add -s refs/remotes/origin/main /path/to/repo

Summary

  • The GitButler CLI uses a parse-context-dispatch architecture with clap for argument parsing and but_ctx::Context for state management.
  • Commands are organized into branch (vbranch) and project groups, implemented in command/vbranch.rs and command/project.rs.
  • Virtual branch operations include listing, creating, applying, committing, and managing series within stacks.
  • Worktree safety is enforced through exclusive access guards before any repository mutation.
  • Project registration handles repository discovery and optional base branch configuration.

Frequently Asked Questions

How does the GitButler CLI handle argument parsing?

The CLI uses the clap crate with derive macros defined in crates/gitbutler-cli/src/args.rs. The Args struct represents the top-level command, while nested enums define subcommands for branch and project operations. Parsing occurs in main.rs via clap::Parser::parse().

What is the purpose of the but_ctx::Context in GitButler CLI commands?

The Context object abstracts repository access, project metadata, and workspace state. It provides the Git repository handle, project data directory paths, and manages exclusive worktree access through guard objects. This ensures thread-safe operations and consistent state across commands.

How does the CLI ensure safe concurrent access to the Git repository?

Before executing write operations, commands acquire an exclusive worktree guard using ctx.exclusive_worktree_access(). This guard provides a write permission token that must be passed to mutation methods in the branch manager. The pattern prevents race conditions when multiple CLI instances or the GUI application access the same repository.

What is the difference between apply_by_name and apply_from_branch in the virtual branch commands?

apply_by_name looks up and activates a virtual branch (stack) by its virtual name, while apply_from_branch converts an existing real Git branch into a virtual stack. The latter is triggered when the --branch flag is passed to the apply subcommand, allowing users to migrate existing branches into GitButler's virtual branch system.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →