Fix "Conflicting migrations detected" in Django
Fix Django's 'Conflicting migrations detected; multiple leaf nodes' error: merge or re-parent migrations in development, handle already-applied branches, and block conflicts in CI before deploy.
Problem statement
A deploy stops at the migration step with:
CommandError: Conflicting migrations detected; multiple leaf nodes in the migration graph:
(0012_invoice_due_date, 0012_order_notes in orders).
To fix them run 'python manage.py makemigrations --merge'
Two branches each added a migration to the orders app starting from the same parent (0011). Both merged into main, and now the app's migration graph has two ends ("leaf nodes"). Django refuses to guess the order and stops. Nothing has been applied, so production is unchanged, but the release is blocked.
The fix is simple, but where you apply it matters: in the repository, reviewed, not on the server. This guide covers both ways to resolve it, the special case where both branches already ran somewhere, and a CI check that stops it from reaching a deploy again.
- Do not fix it on the server. Pull
mainlocally. - If neither new migration has been applied to any shared database: re-parent one migration to depend on the other (and renumber it), so the history is linear.
- If either has been applied somewhere (staging, production, a colleague's shared database): run
python manage.py makemigrations --mergeto create a merge migration. - Review that the two migrations do not touch the same fields; if they do, resolve the conflict by hand.
- Commit, run tests, and deploy.
- Add
makemigrations --check --dry-runto CI so conflicts fail the pull request, not the deploy.
Step-by-step solution
1. Confirm the conflict locally
git checkout main && git pull
python manage.py showmigrations orders
python manage.py makemigrations --check --dry-run
makemigrations reports the same conflict. showmigrations lists both 0012_* migrations.
2. Option A: linearise (preferred when nothing is applied)
If neither 0012 migration has run on any shared database, make one depend on the other. Pick the one merged second, rename it to 0013_order_notes.py, and change its dependency:
# orders/migrations/0013_order_notes.py
class Migration(migrations.Migration):
dependencies = [
("orders", "0012_invoice_due_date"), # was ("orders", "0011_...")
]
operations = [...]
Check that nothing else refers to the old name (other migrations' dependencies or run_before):
grep -rn "0012_order_notes" */migrations/
python manage.py makemigrations --check --dry-run
python manage.py migrate --plan
The result is a straight history with no merge migration, which is easier to read later.
Do not rename a migration that has already been applied anywhere: the database records applied migrations by name, and the renamed file would be applied a second time.
3. Option B: create a merge migration
If one or both migrations have already been applied to a shared database (for example branch A was deployed to staging), keep both names and join them:
python manage.py makemigrations --merge
Django creates something like 0013_merge_20260925_1030.py:
class Migration(migrations.Migration):
dependencies = [
("orders", "0012_invoice_due_date"),
("orders", "0012_order_notes"),
]
operations = []
A database that already applied one of the 0012 migrations applies the other and then the empty merge; a fresh database applies both in dependency order. The operations in the two branches must not depend on each other, because their relative order is not guaranteed.
4. Check whether the branches really are independent
Look at what each migration does:
python manage.py sqlmigrate orders 0012_invoice_due_date
python manage.py sqlmigrate orders 0012_order_notes
Independent changes (different fields, different models) merge safely. Real conflicts include:
- both add a field with the same name,
- one renames or removes a field the other alters,
- both alter the same field in different ways.
For those, fix the models to the intended final state, delete the unapplied migration, and regenerate it on top of the other with makemigrations. If both were applied in different environments, write a corrective migration and test it against copies of each environment's schema.
5. Commit and deploy
git add orders/migrations/
git commit -m "orders: resolve migration conflict between invoice due date and order notes"
python manage.py test
Deploy as usual. The migration step now sees one leaf per app.
6. Stop it happening again
Add to CI, on every pull request against the current main:
python manage.py makemigrations --check --dry-run
It fails on missing migrations and on conflicts. Because two pull requests can each pass individually and conflict only after both merge, also run it on main after every merge, or require branches to be up to date before merging.
The django-linear-migrations package takes a different approach: it records the latest migration per app in a text file, so two branches adding migrations produce a Git merge conflict in that file, which is noticed before merging.
Verification check:
python manage.py makemigrations --check --dry-run && echo "no conflicts, no missing migrations"
python manage.py migrate --plan | tail -5
Explanation
Each migration lists its dependencies, and Django builds a directed graph from them. Within one app, the graph normally forms a chain ending in one leaf, the latest migration. When two migrations share a parent and nothing depends on either, the app has two leaves and Django cannot determine the final state safely, so migrate and makemigrations both refuse to continue. Linearising or merging restores a single leaf; which one to choose depends on whether any database has already recorded the migrations by name.
Edge cases / notes
Never merge on the production server
Running makemigrations --merge on the server creates a file that exists only there. The next deploy removes it (or conflicts with a different merge file from the repository), and the database history no longer matches the code.
Squashed migrations
Conflicts can also appear around squashed migrations when old and new branches meet. Resolve them in the same way, and keep the replaced migrations until every environment has moved past them.
When to automate this
The CI check above is the automation. Pair it with a rule that migrations are reviewed like code: a reviewer who sees a new migration numbered the same as one already on main can ask for it to be re-parented before merge.
Next steps
- Plan migrations across releases with Django Migration Strategy for Safe Deployments.
- If recorded history no longer matches the files, see Fix InconsistentMigrationHistory in Django.
- If a migration fails partway, use Django Migration Failed During Deploy: Recovery Playbook.
- Add the checks to your pipeline with Deploy Django with GitHub Actions CI/CD.
FAQ
What does "multiple leaf nodes in the migration graph" mean?
Two migrations in the same app share a parent and nothing depends on either, so the app's history has two ends. Django needs one.
Is makemigrations --merge safe?
Yes, when the two branches change unrelated things. It creates an empty migration that depends on both. Review the branches for overlapping changes first.
Should I merge or renumber conflicting migrations?
Renumber (re-parent) when neither migration has been applied to a shared database; it keeps history linear. Merge when one has already been applied somewhere.
How do I catch migration conflicts before deploying?
Run python manage.py makemigrations --check --dry-run in CI on pull requests and after merges to main.