Introduction
Django migration conflicts occur when two developers create migrations on different branches that modify the same model, and both migrations end up with the same numeric prefix or conflicting dependencies. After merging branches, Django detects multiple leaf migrations for an app and refuses to run migrations until the conflict is resolved. This is one of the most common friction points in team Django development, especially in projects with multiple concurrent feature branches touching the same models.
Symptoms
Running python manage.py migrate after a merge:
CommandError: Conflicting migrations detected; multiple leaf nodes in the migration graph: (0015_add_email_field, 0015_add_phone_number in users on default).
To fix them run 'python manage.py makemigrations --merge'Or during CI deployment:
django.db.migrations.exceptions.InconsistentMigrationHistory:
Migration admin.0001_initial is applied before its dependency users.0015_add_email_fieldOr when running makemigrations:
You are trying to add a new field to 'userprofile' but the migration
history has diverged. Multiple leaf nodes detected.Common Causes
- Parallel development on same model: Two branches both add a field to
UserProfile, creating0015_add_email.pyand0015_add_phone.py - Migration squash on one branch: One branch squashes migrations while another branch adds a new migration depending on the pre-squash state
- Reordering migrations manually: Editing migration files to change the
dependencieslist creates graph inconsistency - Deleted migration files: Removing migration files from version control but they were already applied in production
- Third-party app migration changes: Upgrading a package that changes its migration files, conflicting with local state
Step-by-Step Fix
Step 1: Detect the conflict
```bash # Check for conflicting migrations python manage.py makemigrations --check --dry-run
# See the current migration state python manage.py showmigrations ```
Look for apps with multiple migrations marked with [X] at the same position:
users
[X] 0013_auto_20240101_1200
[X] 0014_userprofile_bio
[X] 0015_add_email_field
[X] 0015_add_phone_number <-- conflict: two 0015 migrationsStep 2: Create a merge migration
python manage.py makemigrations --mergeThis generates a migration that depends on both conflicting migrations:
```python # users/migrations/0016_merge.py from django.db import migrations
class Migration(migrations.Migration): dependencies = [ ("users", "0015_add_email_field"), ("users", "0015_add_phone_number"), ]
operations = [] ```
Step 3: Handle complex dependency conflicts
If the two migrations modify the same field, a simple merge is not enough. You need to decide which version wins:
```bash # Roll back to the common ancestor python manage.py migrate users 0014
# Delete the conflicting migration files rm users/migrations/0015_add_*.py
# Create a single combined migration python manage.py makemigrations users
# Verify the result python manage.py migrate --plan ```
The plan output shows the exact execution order:
Planned operations:
users.0015_add_combined_fields
Alter field email on userprofile
Alter field phone on userprofile
Add field avatar to userprofileStep 4: Fix inconsistent migration history in production
If production has applied migrations that differ from the merged codebase:
```bash # Check what migrations are actually applied python manage.py showmigrations
# Fake the problematic migration if the schema is already correct python manage.py migrate users 0015_add_email_field --fake
# Then apply the merge python manage.py migrate users 0016_merge
# Finally apply remaining migrations python manage.py migrate ```
Prevention
- Run
python manage.py makemigrations --checkin CI to catch migration conflicts before merge - Rebase feature branches onto main before merging to surface migration conflicts early
- Use descriptive migration names like
0015_userprofile_add_email_field.pyinstead of relying on auto-generated numbers - Document migration dependencies when working on shared models in team standups
- Consider using
--squashperiodically to reduce migration count and simplify the graph - Add a pre-commit hook that runs
makemigrations --check --dry-runto detect unstated model changes