Catch database migration conflicts before the merge, not after the database is broken.
migguard is a fast, zero-database-connection CLI and CI merge gatekeeper that detects branching migration conflicts across Django, Alembic, Prisma, Rails, and Knex.
On any team with two or more developers, two people create migrations on separate feature branches. The branches merge into main.
- In Django/Alembic, the migration graph now has two heads.
- In Prisma/Rails/Knex, timestamps are out-of-order or duplicate.
Nobody finds out until someone runs the migration in staging or production—when the database schema enters an inconsistent state. Existing ORM CLIs only catch this after you've already merged and attempted to migrate.
migguard catches conflicts before the merge in pre-commit and CI.
| Framework | Engine | Detection Mechanism | Resolution Command |
|---|---|---|---|
| Django | Engine A (DAG) | Per-app leaf node detection (dependencies / run_before) |
python manage.py makemigrations --merge |
| Alembic | Engine A (DAG) | Multi-head DAG detection (revision / down_revision) |
alembic merge heads |
| Prisma | Engine B (Timestamp) | Folder timestamp ordering vs git merge-base | npx prisma migrate resolve |
| Rails | Engine B (Timestamp) | 14-digit timestamp filenames & schema.rb merge conflicts |
rails db:migrate:status |
| Knex | Engine B (Timestamp) | Lexicographical timestamp sorting vs git merge-base | Rename migration timestamp |
Run directly via npx in any repository:
npx migguardOr install as a dev dependency:
# npm
npm install -D migguard
# pnpm
pnpm add -D migguard
# yarn
yarn add -D migguardmigguard · django · 2 apps · 34 migrations
✗ Conflict: multiple leaf nodes in app `payments` (2 unmerged migration heads)
payments/migrations/0013_add_refund_status.py
payments/migrations/0013_add_currency_field.py
→ python manage.py makemigrations --merge
1 conflict · 33 migrations OK · 0.2s
Add migguard to your GitHub pull request workflow to block broken migrations before they reach main:
# .github/workflows/migrations.yml
name: Migration Check
on:
pull_request:
branches: [main, master]
jobs:
check-migrations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history needed for merge-base detection
- name: Run migguard
uses: poorvith-mp/migguard@v0.1.0
with:
target: mainEnsure developers never commit branching migration heads locally:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/poorvith-mp/migguard
rev: v0.1.0
hooks:
- id: migguard
files: (migrations/|db/migrate/|prisma/migrations/)Usage: migguard [options]
Catch database migration conflicts before the merge, not after the database is broken.
Options:
-V, --version output the version number
-t, --target <branch> Explicit comparison target branch for timestamp-based ORMs (default: upstream or main)
-f, --framework <name> Skip detection and force a specific framework (django, alembic, prisma, rails, knex)
--json Output results in JSON format for automation
--strict Treat warnings as failures (exit code 1)
-q, --quiet Only print conflicts, suppress warnings and clean statuses
-h, --help display help for command
| Code | Meaning |
|---|---|
0 |
Clean: All migrations linear, no conflicts found. |
1 |
Conflict: Branching heads or out-of-order timestamps detected. |
2 |
Warning: Detection uncertain / unparseable file (promoted to 1 with --strict). |
migguard uses a two-engine design tailored to how ORMs manage migrations:
Migration files contain explicit parent pointers (dependencies in Django, down_revision in Alembic). migguard builds the dependency graph directly from source files and identifies leaf nodes. Multiple leaves = multiple unmerged heads.
- Handles Alembic
down_revisiontuple merge points without false positives. - Handles Django squashed migrations (
replaces) cleanly. - Checks
django-linear-migrationsmax_migration.txtfast path.
Migration files contain no parent pointers; ordering is implied by filename timestamps. migguard identifies the git merge-base with your target branch and compares files added on each side to detect collisions or out-of-order execution before merge.
- 🛡️ Zero Database Connection:
migguardinspects files and git history only. No database credentials in CI. - 🔒 Zero Code Execution: Python and Ruby files are parsed textually and structurally. No
eval(), no subprocess execution of repo code. - ⚡ No Telemetry / Offline: Zero external network calls.
- 🛑 Read-Only:
migguardnever modifies or rewrites your migration files.
MIT © Poorvith M P