Dolt Branch Management for Datasets: CLI and SQL Workflows Explained

Dolt implements Git-like branch management for datasets through a unified storage layer where branches are named references pointing to commits, accessible via CLI commands, SQL stored procedures, and the Go API.

Dolt branch management for datasets enables version-controlled database workflows where every branch represents an independent line of development. In the dolthub/dolt repository, branches function as mutable pointers to commits—stored as refs/heads/<branch-name>—while the SQL engine exposes them through the dolt_branches system table and stored procedures. This architecture ensures that whether you invoke dolt branch from the command line or execute CALL dolt_branch() from a SQL client, both operations manipulate the same underlying ref store and dataset abstractions.

How Dolt Branches Work Internally

Dolt treats a branch exactly like Git: it is a named reference that points to a commit (the head of the branch). However, Dolt extends this concept to SQL databases through a dataset abstraction that bridges Git semantics with relational data.

The Ref-Dataset Relationship

At the storage layer, every branch is a ref.DoltRef object with the form refs/heads/<branch-name> that stores a commit hash. The head of a branch is represented as a dataset whose name matches the branch name. According to the implementation in go/store/datas/dataset.go, the Dataset type represents the mutable root of a branch and provides methods like MaybeHead and Commit to read or update the head commit.

The resolution from branch name to dataset occurs through go/store/spec/spec.go, where Spec.ForDataset("mybranch") parses the identifier and returns a live Dataset object. This abstraction is used uniformly by the SQL engine, CLI, and Go API to ensure consistent access patterns.

The Dolt Branches System Table

The SQL engine exposes branch metadata through dolt_branches, a virtual table defined in go/libraries/doltcore/sqle/tables.go. Unlike physical tables, dolt_branches is built on-the-fly by the BranchRowIter iterator, which scans all refs/heads/* entries in the ref store. This guarantees that any branch created via the CLI immediately appears in SQL queries without synchronization delays.

Branch Operations Across Interfaces

Dolt provides three equivalent interfaces for branch management: the CLI (dolt branch), SQL stored procedures (CALL dolt_branch(...)), and the Go API (go/libraries/doltcore/env/actions/branch.go). All three layers invoke the same core functions, ensuring behavioral consistency.

Creating Branches

To create a branch from the current HEAD or a specific start point:

CLI:

dolt branch feature1
dolt branch -b feature1 main

SQL:

CALL dolt_branch('feature1');
CALL dolt_branch('feature1', 'main');

Go API:

import "github.com/dolthub/dolt/go/libraries/doltcore/env/actions"

// Create from current HEAD
err := actions.CreateBranchOnDB(ctx, db, "feature1", "", false, headRef, rsc)

// Create from specific branch
err := actions.CreateBranchOnDB(ctx, db, "feature1", "main", false, headRef, rsc)

Behind the scenes, CreateBranchOnDB in go/libraries/doltcore/env/actions/branch.go validates the branch name using ValidateDatasetId, creates a new ref.DoltRef pointing to the start commit, and writes it to the ref store atomically.

Renaming Branches

Rename operations move the reference while preserving the commit history:

CLI:

dolt branch -m old_name new_name

SQL:

CALL dolt_branch('-m', 'old_name', 'new_name');

Go API:

err := actions.RenameBranch(ctx, dbData, "old_name", "new_name", nil, false, rsc)

The RenameBranch function ensures the target name does not already exist, then renames the ref from refs/heads/old_name to refs/heads/new_name in the ref store.

Copying Branches

Branch copying creates a new reference pointing to the same commit as the source:

CLI:

dolt branch --copy dev dev_backup

SQL:

CALL dolt_branch('--copy', 'dev', 'dev_backup');

Go API:

err := actions.CopyBranch(ctx, env, "dev", "dev_backup", false)

As implemented in actions/branch.go, CopyBranch loads the source branch's head commit and creates a new ref refs/heads/dev_backup that points to the same hash, performing the operation atomically.

Deleting Branches with Safety Checks

Dolt prevents accidental data loss by validating that a branch is fully merged before deletion, unless the force flag is used:

CLI:


# Safe delete (requires merged status)

dolt branch -d feature1

# Force delete

dolt branch -D feature1

SQL:

-- Safe delete
CALL dolt_branch('-d', 'feature1');

-- Force delete
CALL dolt_branch('-D', 'feature1');

Go API:

opts := actions.BranchDeleteOptions{Force: false}
err := actions.DeleteBranch(ctx, dbData, "feature1", opts, nil, rsc)

The DeleteBranch function in go/libraries/doltcore/env/actions/branch.go first calls validateBranchMergedIntoCurrentWorkingBranch to verify that the branch has been merged into the current working branch. If the check fails and Force is false, the operation returns an error. Passing the force flag (-D or Force: true) bypasses this validation.

Listing Branches via System Tables

Query branch metadata using the virtual table:

SELECT name, head_commit_hash, head_commit_date
FROM dolt_branches
ORDER BY name;

This query executes against doltBranchesTable in go/libraries/doltcore/sqle/tables.go, where BranchRowIter iterates over all refs/heads/* references and extracts commit metadata without requiring physical table storage.

Key Source Files

Understanding the implementation details requires examining these specific files in the dolthub/dolt repository:

Summary

  • Unified storage layer – Dolt branch management for datasets uses a single ref store (refs/heads/<name>) accessed by the CLI, SQL engine, and Go API, ensuring immediate consistency across interfaces.

  • Dataset abstraction – Branches are first-class datasets identified by name, resolved through Spec.ForDataset() and manipulated via the Dataset type in go/store/datas/dataset.go.

  • Virtual metadata – The dolt_branches table is built on-the-fly from the ref store by BranchRowIter, eliminating synchronization lag between branch creation and visibility in SQL.

  • Safety mechanisms – Delete operations invoke validateBranchMergedIntoCurrentWorkingBranch to prevent data loss, with force flags available for intentional deletion of unmerged branches.

  • Atomic operations – All branch mutations (create, rename, copy, delete) update the ref store atomically, maintaining ACID properties for database versioning workflows.

Frequently Asked Questions

How does Dolt branch management differ from Git branch management?

Dolt branch management mirrors Git's semantics—branches are named references to commits—but extends them to SQL databases through the dataset abstraction. While Git operates on file trees, Dolt's branches in go/store/datas/dataset.go point to root hashes of database tables, enabling CALL dolt_branch() operations within SQL transactions and exposing branch metadata via the dolt_branches system table.

Can I create a branch from a specific commit or only from branch names?

You can create branches from both specific commits and existing branch names. The dolt_branch stored procedure and CLI accept an optional start-point parameter. According to go/libraries/doltcore/env/actions/branch.go, CreateBranchOnDB accepts a startPt string that can resolve to any commit hash or branch name, creating the new refs/heads/<branch> reference pointing to that specific commit.

Why does the dolt_branches table show branches immediately after CLI creation?

The dolt_branches table is a virtual table implemented in go/libraries/doltcore/sqle/tables.go rather than a physical table. The BranchRowIter scans the ref store dynamically for all refs/heads/* entries whenever a query executes. Because both the CLI and SQL engine write to the same ref store in go/store/spec/spec.go, changes are visible immediately without cache invalidation or replication delays.

What happens when I force-delete a branch that isn't merged?

When force-deleting via dolt branch -D or CALL dolt_branch('-D', 'name'), Dolt skips the merge validation performed by validateBranchMergedIntoCurrentWorkingBranch in go/libraries/doltcore/env/actions/branch.go. The DeleteBranch function removes the refs/heads/<name> reference from the store, making the branch unreachable from standard queries (though the commit data persists until garbage collection). This operation is irreversible via standard branch commands, requiring reflog or backup recovery if needed.

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 →