Git branching for your PostgreSQL database.
- The Problem
- The Solution
- How It Works
- Installation
- Quick Start
- Commands
- Schema Diff
- Schema Merge (Beta)
- Automatic Branch Switching
- Remotes
- Caveats
You're working on a feature branch. You run migrations. Life is good.
Then you switch back to main.
Your database is now in a broken state. The migrations from your feature branch are still applied. Your schema doesn't match your code. Nothing works.
Your options:
- Drop the database and re-seed (slow, painful)
- Manually roll back migrations (error-prone, annoying)
- Maintain multiple databases and remember to switch connection strings (who has time for that)
None of these are good.
pgbranch init -d myapp_dev
pgbranch branch main # snapshot your clean main state
# ... switch to feature branch, run migrations, break things ...
pgbranch branch feature-x # save this state too
pgbranch checkout main # instantly back to clean stateThat's it. Your database now has branches. Just like git.
PostgreSQL has a feature called template databases. When you create a database from a template, it does a file-level copy. It's fast.
pgbranch uses this to create instant snapshots of your database. When you "checkout" a branch, it drops your working database and recreates it from the snapshot.
No pg_dump. No restore. No waiting.
go install github.com/le-vlad/pgbranch/cmd/pgbranch@latest# Initialize for your dev database
pgbranch init -d myapp_dev
# Create a branch from current state
pgbranch branch main
# Do some work, run migrations, whatever
# Then save that state
pgbranch branch feature-auth
# Go back to main
pgbranch checkout main
# Your database is now exactly as it was beforepgbranch init -d <database> Initialize pgbranch
pgbranch branch List all branches
pgbranch branch <name> Create a branch from current state
pgbranch checkout <name> Switch to a branch
pgbranch delete <name> Delete a branch
pgbranch status Show current branch and info
pgbranch log Show all branches with details
pgbranch hook install Install git hook for auto-switching
pgbranch hook uninstall Remove the git hook
pgbranch diff <branch1> [branch2] Compare schemas between branches
pgbranch merge <source> <target> Merge schema changes (Beta)
-d, --database Database name (required)
-H, --host PostgreSQL host (default: localhost)
-p, --port PostgreSQL port (default: 5432)
-U, --user PostgreSQL user (default: postgres)
-W, --password PostgreSQL password
Compare the schema between two database branches to see what changed.
# Compare two branches
pgbranch diff main feature-auth
# Compare a branch against current working database
pgbranch diff main
# Show summary statistics only
pgbranch diff main feature-auth --stat
# Show SQL statements needed to migrate
pgbranch diff main feature-auth --sql- Tables: Created, dropped
- Columns: Added, removed, type changes, nullability, defaults
- Indexes: Created, dropped, modified
- Constraints: Primary keys, foreign keys, unique, check constraints
- Enums: Created, dropped, new values added
- Functions: Created, dropped, body changes
Comparing 'main' → 'feature-auth'
+ TABLE public.sessions
id uuid NOT NULL
user_id uuid NOT NULL
expires_at timestamp with time zone NOT NULL
~ TABLE public.users
+ COLUMN last_login timestamp with time zone
~ COLUMN email: type varchar(100) → varchar(255)
+ INDEX idx_sessions_user_id on public.sessions(user_id)
Summary:
+ 5 addition(s)
~ 1 modification(s)
Changes are color-coded: + green (additions), - red (deletions), ~ yellow (modifications). Destructive changes are flagged with a warning indicator.
Beta: This feature is in beta. Use with caution and always backup important data.
Merge schema changes from one branch into another. This applies the schema diff as actual DDL statements to the target branch.
# Merge feature branch into main
pgbranch merge feature-auth main
# Preview changes without applying (dry run)
pgbranch merge feature-auth main --dry-run
# Generate a migration file instead of applying directly
pgbranch merge feature-auth main --migration-file
# Specify custom migration directory
pgbranch merge feature-auth main --migration-file --migration-dir ./db/migrations
# Force merge without confirmation prompts
pgbranch merge feature-auth main --force- Dry run mode: Preview all SQL statements before applying
- Destructive change warnings: Explicitly warns about
DROP TABLE,DROP COLUMN, and other data-loss operations - Confirmation prompts: Requires explicit confirmation for destructive changes
- Validation: Checks for potential issues before applying
Instead of applying changes directly, generate a timestamped SQL migration file:
pgbranch merge feature-auth main --migration-file
# Creates: migrations/20240115143022_merge_feature_auth.sqlThe generated file includes:
- Header with source/target branch info
- All DDL statements in correct dependency order
- Comments for destructive operations
- PostgreSQL (with
psql,createdb,dropdbin PATH) - Go 1.21+ (for installation)
When you run pgbranch branch feature-x on a database called myapp_dev, it creates a new database called myapp_dev_pgbranch_feature_x. That's your snapshot.
Your working database stays as myapp_dev. When you checkout, it gets replaced with a copy of the snapshot.
Tired of manually running pgbranch checkout every time you switch git branches? Install the git hook:
pgbranch hook installNow whenever you run git checkout feature-x, pgbranch will automatically switch your database to the feature-x branch (if it exists).
To remove the hook:
pgbranch hook uninstallShare database snapshots across machines or with your team using remote storage backends.
| Backend | URL Format | Status |
|---|---|---|
| Filesystem | /path/to/dir or file:///path/to/dir |
Supported |
| AWS S3 | s3://bucket/prefix |
Supported |
| MinIO | s3://bucket/prefix (S3-compatible) |
Supported |
| Cloudflare R2 | r2://account-id/bucket/prefix |
Supported |
| Google Cloud Storage | gs://bucket/prefix |
Planned |
| Azure Blob Storage | - | Planned |
pgbranch remote add <name> <url> Add a remote
pgbranch remote list List configured remotes
pgbranch remote remove <name> Remove a remote
pgbranch remote set-default <name> Set default remote
pgbranch remote ls-remote List branches on remote
pgbranch remote delete <branch> Delete branch from remote
pgbranch push <branch> Push branch to remote
pgbranch pull <branch> Pull branch from remote
# Add a filesystem remote (local network share, mounted drive, etc.)
pgbranch remote add origin /shared/snapshots
# Add an S3 remote (will prompt for credentials)
pgbranch remote add origin s3://my-bucket/pgbranch
# Add a Cloudflare R2 remote
pgbranch remote add origin r2://account-id/my-bucket/pgbranch
# Skip credential prompts and use environment variables instead
pgbranch remote add origin s3://my-bucket/pgbranch --no-credentials# Push a local branch to the remote
pgbranch push main
# Push with a description
pgbranch push main --description "Clean schema with seed data"
# Force overwrite if branch exists on remote
pgbranch push main --force
# Pull a branch from remote
pgbranch pull main
# Pull with a different local name
pgbranch pull main --as main-backup
# Force overwrite if local branch exists
pgbranch pull main --forceFor S3 and R2 remotes, pgbranch will prompt for your access key and secret key. These credentials are encrypted and stored in your project's .pgbranch.json config.
To use environment variables instead (useful for CI/CD), add the remote with --no-credentials:
pgbranch remote add origin s3://bucket/prefix --no-credentialsThen set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in your environment.
- This is for local development only. Don't use this in production.
- Checkout will drop your working database. Uncommitted changes are gone.
- Snapshots are full database copies. They take disk space.
- Active connections to the database will be terminated on checkout.
MIT