Cheatsheet for handling Alembic branches.
How branches form
Two devs branch off the same parent rev. After both merge to main: two heads.
abc123
├── def456 (Alice)
└── ghi789 (Bob)
Detect
alembic heads
# Multiple lines = branched
Merge
alembic merge -m "merge X+Y" def456 ghi789
Creates a merge revision with two parents.
abc123
├── def456 ─┐
│ ├── merge (j1k2l3)
└── ghi789 ─┘
Merge migration content
Usually empty (just a graph fix):
def upgrade(): pass
def downgrade(): pass
If the two branches conflict (e.g., both renamed the same column differently): reconcile manually inside the merge.
CI gate (single head)
- name: Check single head
run: |
if [ "$(alembic heads | wc -l)" -gt 1 ]; then
echo "Multiple heads — merge required"
exit 1
fi
Block PRs that introduce a branch without resolution.
Avoid branches in workflow
- Pull main before generating.
- Quick review + merge.
- Communicate when adding migrations.
For small teams: branches happen rarely if discipline holds.
Rebase before merge (alternative)
# Locally, before pushing
git pull --rebase main
alembic upgrade head # apply latest
alembic revision --autogenerate -m "..." # generates with correct parent
Cleaner history than merge migrations.
Branch labels (for monorepo)
# Per-app branch labels
revision = "api_001"
down_revision = None
branch_labels = ("api",)
revision = "admin_001"
down_revision = None
branch_labels = ("admin",)
Then:
alembic upgrade api@head # only api branch
alembic upgrade admin@head
Merge multiple branches
alembic merge -m "merge all" a1 b1 c1
Three parents. Rare.
Recovery from “what just happened”
alembic heads
alembic history --verbose
Map the graph. Identify the problem revision. Use alembic stamp <rev> to set state if needed.
Down revision tuple
For merge revisions, down_revision is a tuple:
revision = "j1k2l3"
down_revision = ("def456", "ghi789")
Manual graph fix (last resort)
Edit a revision’s down_revision and revision. Dangerous; only for unshared migrations.
For already-applied migrations: don’t rewrite history. Add a new migration.
When branches are intentional
Multi-app monorepo with independent migration trees:
api branch: api_001 → api_002 → ...
admin branch: admin_001 → admin_002 → ...
Set branch_labels per revision. alembic upgrade api@head per branch.
Common mistakes
- Generating a migration with stale local main → branch.
- Force-pushing migration history — breaks deployments that ran the old.
- Editing
down_revisionafter the migration is shared. - Merging without understanding what each branch did.
Read this next
If you want my single-head CI gate + branch-resolution playbook, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .