Removed reduce() usage in makemigrations; refs #23796.
A lambda all_items_equal() replaced a reduce() that was broken for potential 3+-way merges. A reduce(operator.eq, ...) accumulates bools and can't generically check equality of all items in a sequence: >>> bool(reduce(operator.eq, [('migrations', '0001_initial')] * 3)) False The code now counts the number of common ancestors to calculate slice offsets for the branches. Each branch shares the same number of common ancestors. The common_ancestor for loop statement had incomplete branch coverage.
This commit is contained in:
parent
cfa26f29bd
commit
54d3dcbc51
|
@ -1,6 +1,6 @@
|
||||||
|
from itertools import takewhile
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import operator
|
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
@ -11,7 +11,7 @@ from django.db.migrations.questioner import MigrationQuestioner, InteractiveMigr
|
||||||
from django.db.migrations.state import ProjectState
|
from django.db.migrations.state import ProjectState
|
||||||
from django.db.migrations.writer import MigrationWriter
|
from django.db.migrations.writer import MigrationWriter
|
||||||
from django.utils.six import iteritems
|
from django.utils.six import iteritems
|
||||||
from django.utils.six.moves import reduce
|
from django.utils.six.moves import zip
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
|
@ -187,24 +187,18 @@ class Command(BaseCommand):
|
||||||
migration = loader.get_migration(app_label, migration_name)
|
migration = loader.get_migration(app_label, migration_name)
|
||||||
migration.ancestry = loader.graph.forwards_plan((app_label, migration_name))
|
migration.ancestry = loader.graph.forwards_plan((app_label, migration_name))
|
||||||
merge_migrations.append(migration)
|
merge_migrations.append(migration)
|
||||||
common_ancestor = None
|
all_items_equal = lambda seq: all(item == seq[0] for item in seq[1:])
|
||||||
for level in zip(*[m.ancestry for m in merge_migrations]):
|
merge_migrations_generations = zip(*[m.ancestry for m in merge_migrations])
|
||||||
if reduce(operator.eq, level):
|
common_ancestor_count = sum(1 for common_ancestor_generation
|
||||||
common_ancestor = level[0]
|
in takewhile(all_items_equal, merge_migrations_generations))
|
||||||
else:
|
if not common_ancestor_count:
|
||||||
break
|
|
||||||
if common_ancestor is None:
|
|
||||||
raise ValueError("Could not find common ancestor of %s" % migration_names)
|
raise ValueError("Could not find common ancestor of %s" % migration_names)
|
||||||
# Now work out the operations along each divergent branch
|
# Now work out the operations along each divergent branch
|
||||||
for migration in merge_migrations:
|
for migration in merge_migrations:
|
||||||
migration.branch = migration.ancestry[
|
migration.branch = migration.ancestry[common_ancestor_count:]
|
||||||
(migration.ancestry.index(common_ancestor) + 1):
|
migrations_ops = (loader.get_migration(node_app, node_name).operations
|
||||||
]
|
for node_app, node_name in migration.branch)
|
||||||
migration.merged_operations = []
|
migration.merged_operations = sum(migrations_ops, [])
|
||||||
for node_app, node_name in migration.branch:
|
|
||||||
migration.merged_operations.extend(
|
|
||||||
loader.get_migration(node_app, node_name).operations
|
|
||||||
)
|
|
||||||
# In future, this could use some of the Optimizer code
|
# In future, this could use some of the Optimizer code
|
||||||
# (can_optimize_through) to automatically see if they're
|
# (can_optimize_through) to automatically see if they're
|
||||||
# mergeable. For now, we always just prompt the user.
|
# mergeable. For now, we always just prompt the user.
|
||||||
|
|
Loading…
Reference in New Issue