Database Branching
Database Branching
Applies to: HeliosDB Nano 4.31.1
Database branching in HeliosDB Nano works like Git branches for your data. Create isolated copies of your database for development, testing, or experimentation without duplicating storage.
One place the Git analogy breaks down: merge has no conflict detection. See Merging Branches before you plan a workflow around it.
Overview
Branches are lightweight, copy-on-write database snapshots that share unchanged data with their parent. This makes creating branches nearly instantaneous regardless of database size.
Creating Branches
From Current State
-- Create a branch from the current state of mainCREATE DATABASE BRANCH dev FROM main;
-- Or use AS OF NOW explicitlyCREATE DATABASE BRANCH dev FROM main AS OF NOW;From a Point in Time
-- Branch from a specific timestampCREATE DATABASE BRANCH hotfix FROM mainAS OF TIMESTAMP '2025-01-15 00:00:00';
-- Branch from a specific transactionCREATE DATABASE BRANCH feature FROM mainAS OF TRANSACTION 12345;Switching Branches
SQL Syntax
USE BRANCH dev;REPL Command
\use devCheck Current Branch
\show branchListing Branches
REPL
\branchesSQL
SELECT * FROM pg_database_branches();Output includes:
name- Branch nameparent- Parent branch namecreated_at- Creation timestampcommit_count- Number of commitsis_current- Whether this is the active branch
Merging Branches
MERGE DATABASE BRANCH does move rows — it is not a no-op. But it is
last-writer-wins and performs no conflict detection: rows changed on both
branches are overwritten by the merge, and no conflict is reported. Treat it as
“apply the source branch’s rows onto the target”, not as a three-way merge.
Basic Merge
MERGE DATABASE BRANCH dev INTO main;Options
| Option | Status |
|---|---|
delete_branch_after = true | Supported — drops the source branch after a successful merge |
conflict_resolution = 'fail' | 'target_wins' | 'branch_wins' | Not implemented — the statement returns an error |
-- Returns an error, by design:MERGE DATABASE BRANCH dev INTO mainWITH (conflict_resolution = 'branch_wins');MERGE BRANCH conflict_resolution is not implemented: merges arelast-writer-wins and conflicts are never detected. Remove the option tomerge with those semantics.The option is parsed but the storage engine never detects a conflict or applies
a strategy, so accepting it would be a silent no-op — you would ask for
target_wins, get branch_wins, and be told the merge completed with zero
conflicts. It now fails loudly instead of lying, and will stay that way until
conflict detection exists.
Safe pattern for anything conflict-sensitive
Branch, validate, discard, then re-apply the validated SQL to main:
CREATE DATABASE BRANCH trial FROM main;USE BRANCH trial;-- experiment, inspect, verifyUSE BRANCH main;DROP DATABASE BRANCH trial;-- then run the statements you actually validated against mainDropping Branches
DROP DATABASE BRANCH dev;
-- With IF EXISTS to avoid errorsDROP DATABASE BRANCH IF EXISTS dev;Use Cases
Development Environment
-- Create dev branchCREATE DATABASE BRANCH dev FROM main;USE BRANCH dev;
-- Make experimental changesALTER TABLE users ADD COLUMN preferences JSONB;INSERT INTO users (name, preferences) VALUES ('Test', '{}');
-- If changes work, merge back (last-writer-wins: rows changed on both-- branches are overwritten without a conflict report)USE BRANCH main;MERGE DATABASE BRANCH dev INTO main;Feature Testing
-- Create branch for testing a featureCREATE DATABASE BRANCH feature_x FROM main;USE BRANCH feature_x;
-- Run integration tests with modified dataDELETE FROM users WHERE test_account = true;-- Tests run here...
-- Discard when done (no merge needed)USE BRANCH main;DROP DATABASE BRANCH feature_x;Point-in-Time Recovery
-- Create branch from before the incidentCREATE DATABASE BRANCH recovery FROM mainAS OF TIMESTAMP '2025-01-15 00:00:00';
-- Verify data is correctUSE BRANCH recovery;SELECT COUNT(*) FROM orders;
-- If good, make it the new main-- (requires careful planning in production)How It Works
- Copy-on-Write: Branches share data pages with their parent. Only modified pages are copied.
- Snapshot Isolation: Each branch has its own transaction log and snapshot.
- Minimal Overhead: Creating a branch is O(1) regardless of data size.
Best Practices
- Name branches descriptively:
feature_auth_refactor,bugfix_order_calc - Keep branches short-lived: Merge or delete when done
- Test merges on a copy first: Create a test branch to verify merge results
- Use timestamps for recovery branches: Makes it clear what state you’re restoring
Limitations
- No conflict detection on merge.
MERGE DATABASE BRANCHis last-writer-wins;conflict_resolution=is not implemented and returns an error (see above). - Whole-database scope. A branch covers the whole database, not an individual table or schema.
- Not a security boundary. Branching is storage-level copy-on-write isolation. It does not sandbox anything outside the database — an agent working on a branch can still make external side effects — and it is not an access-control mechanism.
- Circular branch relationships are not allowed.
- Branch names must be unique.
Related
- Time-Travel Queries - Query historical data without branches
- REPL Commands -
\branches,\usecommands