Like `diff` but for PostgreSQL schemas — actively maintained fork
Project description
migra — PostgreSQL Schema Diff Tool
The actively maintained fork of djrobstep/migra.
migra compares two PostgreSQL database schemas and generates the SQL
migration script needed to transform one into the other. Drop it into
your CI pipeline and stop writing ALTER TABLE by hand.
Why This Fork
The original migra was officially deprecated in 2024. This fork picks
up where it left off — fixing known issues, adding Python 3.12+ support,
and extending coverage for advanced PostgreSQL features.
If you were using djrobstep/migra, this is your drop-in continuation.
Nothing has changed about how the tool works. We're just keeping the
lights on and making it better.
A note on naming: This is an independent community fork. The CLI
command remains migra for drop-in backward compatibility with
existing scripts and pipelines. The package name is migradiff to
distinguish it from the deprecated upstream. If you are looking for
the original djrobstep/migra, it is archived at
https://github.com/djrobstep/migra.
Quickstart
Install
pip install migradiff
Requires Python 3.10+ and a running PostgreSQL instance (12+).
To install from source:
git clone https://github.com/migradiff/migra
cd migra
pip install -e .
Note: PyPI package coming with v1.1.0.
Basic Usage
Point migra at two database connections and it outputs the DDL needed to migrate from one to the other:
migra \
postgresql://user:pass@localhost/db_production \
postgresql://user:pass@localhost/db_branch \
--unsafe
Output is plain SQL — pipe it, review it, apply it:
migra postgres://db_a postgres://db_b > migration.sql
psql postgres://db_production < migration.sql
Schema Dumps (No Live Connection Required)
If you can't or don't want to point migra at a live database, use
pg_dump -s to generate a schema dump and diff that instead:
pg_dump -s postgres://db_production > schema_a.sql
pg_dump -s postgres://db_branch > schema_b.sql
migra --from-file schema_a.sql schema_b.sql
This is the recommended approach for CI pipelines and security-conscious environments — no production credentials required.
Migrations Directory (No Live Branch Database Required)
If your target state is defined by a folder of migration files:
migra --from-migrations-dir ./migrations postgres://db_production
MigraDiff applies the migrations to an ephemeral database and diffs the result. Supports Supabase, Flyway, and standard numeric naming conventions.
Scoped to a Schema
# Single schema
migra --schema myschema postgres://db_a postgres://db_b
# Multiple schemas (comma-separated)
migra --schema public,reporting postgres://db_a postgres://db_b
JSON Output
For programmatic consumption or CI pipelines:
migra --output json postgres://db_a postgres://db_b
Output includes per-statement risk classification (safe, warning,
destructive) and a summary with overall risk level.
AI-Powered Explanation (Optional)
MigraDiff can explain any migration in plain English — what each change does, what risks it carries, and safer alternatives for destructive operations.
migra --explain postgres://db_a postgres://db_b
Output:
--- Migration SQL ---
ALTER TABLE public.users ADD COLUMN email text;
DROP TABLE public.legacy_sessions;
--- AI Explanation ---
This migration makes 2 changes to your database:
1. SAFE: Adds an email column (text) to the users table.
No existing data is affected.
2. ⚠ DESTRUCTIVE: Drops the legacy_sessions table entirely.
All data in this table will be permanently lost.
Consider archiving before dropping.
Overall risk: HIGH
Powered by Claude (Anthropic). Bring your own API key — no data is sent to MigraDiff servers.
Setup
Install the AI extras:
pip install migradiff[ai]
Configure your API key once:
migra --setup-ai
Or set the environment variable:
export ANTHROPIC_API_KEY=sk-ant-...
Get an API key at https://console.anthropic.com
Development Setup
The test suite requires a running PostgreSQL instance. The easiest way to get one is via Docker Compose:
docker compose up -d
This starts a Postgres 16 container on localhost:5432 with trust authentication. No password required.
To stop it:
docker compose down
Data persists between restarts via the migradiff-pgdata volume.
To reset completely:
docker compose down -v
Docker
No Python environment? Use the official image:
docker run --rm ghcr.io/migradiff/migra \
postgres://db_a postgres://db_b
GitHub Actions
Add schema diffing to your pull request workflow:
- uses: migradiff/migra@v1
with:
base_url: ${{ secrets.DB_PRODUCTION_URL }}
head_url: ${{ secrets.DB_BRANCH_URL }}
Fail the build automatically if destructive operations are detected:
- uses: migradiff/migra@v1
with:
base_url: ${{ secrets.DB_PRODUCTION_URL }}
head_url: ${{ secrets.DB_BRANCH_URL }}
fail_on_destructive: "true"
Use schema dump files instead of live connections:
- uses: migradiff/migra@v1
with:
base_file: schema_production.sql
head_file: schema_branch.sql
See docs/action-usage.md for full configuration options.
Pre-commit Hook
# .pre-commit-config.yaml
repos:
- repo: https://github.com/migradiff/migra
rev: v1.1.0
hooks:
- id: migra
See pre-commit-config.example.yaml in the repo root for full
configuration options.
What migra Understands
- Tables, columns, constraints, indexes
- Views and materialized views
- Functions and stored procedures
- Sequences
- Enums, composite types, domains
- Row-Level Security (RLS) policies
- Foreign data wrappers
- Column-level privileges
- Partitioned tables
Improvements Over Upstream
| Area | Upstream (deprecated) | This Fork |
|---|---|---|
| Python 3.12+ | Deprecation warnings | Clean — no warnings |
| RLS policies | Partial, equality bug | Full CREATE/DROP, partition support |
| Error messages | Cryptic on unsupported types | Actionable with object name and issue link |
| --schema flag | Edge cases in multi-schema DBs | Comma-separated, cross-schema dependencies resolved |
| pg_dump input | Not supported | First-class --from-file mode |
| JSON output | Not supported | --output json with risk classification |
| Docker image | None | ghcr.io/migradiff/migra |
| GitHub Action | None | migradiff/migra-action |
| Pre-commit hook | None | .pre-commit-hooks.yaml |
| Dev environment | Manual Docker commands | docker compose up -d |
| AI explanation | None | --explain flag with Claude — plain English diff explanation, risk analysis, safer alternatives |
See CHANGELOG.md for the full fix history.
Known Limitations
migra generates the SQL diff — it does not apply it. Review every
generated script before running against production. Destructive
operations (DROP TABLE, DROP COLUMN) are flagged in JSON output
mode but not blocked in plain SQL mode.
migra requires a live PostgreSQL connection to introspect schemas,
or schema dump files via --from-file. It does not parse raw DDL text.
Contributing
Bug reports and PRs are welcome. If you're fixing something that was
reported upstream in djrobstep/migra, reference that issue number
in your PR — it helps us track what the community most needs fixed.
git clone https://github.com/migradiff/migra
cd migra
docker compose up -d
pip install -e ".[dev]"
pytest
License
MIT. See LICENSE.
Acknowledgements
This project is a fork of djrobstep/migra, created and originally maintained by Robert Lechte. The core diffing engine is his work. We are grateful for it.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file migradiff-1.3.0.tar.gz.
File metadata
- Download URL: migradiff-1.3.0.tar.gz
- Upload date:
- Size: 18.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e66532ef04ad967076ce5b35a54c1c0270e2db0fa747c9dadbc4e38510de8f7b
|
|
| MD5 |
d98452d0115cba66ef28da5ef046f4c3
|
|
| BLAKE2b-256 |
ce645b036e612f14728ca88376027bf7dcdd11fc6973706156cdfea444d5ab99
|
File details
Details for the file migradiff-1.3.0-py3-none-any.whl.
File metadata
- Download URL: migradiff-1.3.0-py3-none-any.whl
- Upload date:
- Size: 20.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
633ef8a0312a46d3ef46deff78bf06bacefb82dea600d118e8850466061242d0
|
|
| MD5 |
10c6a865f56c97a944b75f320ae2307a
|
|
| BLAKE2b-256 |
1839b7de6408a0781accbf8359e41b8f3fe1d3f85019f1f93750da4fc5fd3d9c
|