mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	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:
		| @@ -1,6 +1,6 @@ | ||||
| from itertools import takewhile | ||||
| import sys | ||||
| import os | ||||
| import operator | ||||
|  | ||||
| from django.apps import apps | ||||
| 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.writer import MigrationWriter | ||||
| from django.utils.six import iteritems | ||||
| from django.utils.six.moves import reduce | ||||
| from django.utils.six.moves import zip | ||||
|  | ||||
|  | ||||
| class Command(BaseCommand): | ||||
| @@ -187,24 +187,18 @@ class Command(BaseCommand): | ||||
|                 migration = loader.get_migration(app_label, migration_name) | ||||
|                 migration.ancestry = loader.graph.forwards_plan((app_label, migration_name)) | ||||
|                 merge_migrations.append(migration) | ||||
|             common_ancestor = None | ||||
|             for level in zip(*[m.ancestry for m in merge_migrations]): | ||||
|                 if reduce(operator.eq, level): | ||||
|                     common_ancestor = level[0] | ||||
|                 else: | ||||
|                     break | ||||
|             if common_ancestor is None: | ||||
|             all_items_equal = lambda seq: all(item == seq[0] for item in seq[1:]) | ||||
|             merge_migrations_generations = zip(*[m.ancestry for m in merge_migrations]) | ||||
|             common_ancestor_count = sum(1 for common_ancestor_generation | ||||
|                                         in takewhile(all_items_equal, merge_migrations_generations)) | ||||
|             if not common_ancestor_count: | ||||
|                 raise ValueError("Could not find common ancestor of %s" % migration_names) | ||||
|             # Now work out the operations along each divergent branch | ||||
|             for migration in merge_migrations: | ||||
|                 migration.branch = migration.ancestry[ | ||||
|                     (migration.ancestry.index(common_ancestor) + 1): | ||||
|                 ] | ||||
|                 migration.merged_operations = [] | ||||
|                 for node_app, node_name in migration.branch: | ||||
|                     migration.merged_operations.extend( | ||||
|                         loader.get_migration(node_app, node_name).operations | ||||
|                     ) | ||||
|                 migration.branch = migration.ancestry[common_ancestor_count:] | ||||
|                 migrations_ops = (loader.get_migration(node_app, node_name).operations | ||||
|                                   for node_app, node_name in migration.branch) | ||||
|                 migration.merged_operations = sum(migrations_ops, []) | ||||
|             # In future, this could use some of the Optimizer code | ||||
|             # (can_optimize_through) to automatically see if they're | ||||
|             # mergeable. For now, we always just prompt the user. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user