﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
22862	makemigrations --merge broken because --noinput isn't registered	art@…	Huu Nguyen	"* The `makemigrations` management command doesn't register a `--noinput` option
  with the argument parser.
* The logic for the `--merge` option expects `--noinput` to populate an instance variable on Command,
  `self.interactive`, with False if present, or True by default.
* As the option isn't registered, `self.interactive` is None, leading to an incorrect default value,
  and the command behaves as if `--noinput` is set.
* The non-interactive decision-maker [MigrationQuestioner] is safe/conservative,
  and appears to silently deny all merge operations.
* Consequently, the command `makemigrations --merge` is effectively broken.

== Steps to reproduce ==

An example Django project is attached, with the default SQlite database configuration.
To reproduce, unpack the archive, create and activate a virtualenv containing django/stable@1.7.x (not included),
and cd to the directory containing manage.py.

Assuming you're using virtualenvwrapper,
{{{
$ mkvirtualenv django-makemigrations-noinput
$ tar -xvzf django-makemigrations-noinput.tar.gz
$ cd djangobugs
$ pip install -r requirements.txt
}}}
The following procedure assumes the same working directory and active virtualenv.

The project's one model, `testapp.MergeExample`, has had independent modifications requiring migrating.
Makemigrations was run independently, and consequently there are two 0002-series migrations in testapp/migrations.

{{{
$ ls -1 testapp/migrations
0001_initial.py
0002_auto_20140618_1441.py
0002_mergeexample_unrelated_int.py
__init__.py
__pycache__/
}}}
{{{
$ ./manage.py migrate
CommandError: Conflicting migrations detected (0002_auto_20140618_1441, 0002_mergeexample_unrelated_int in testapp).
To fix them run 'python manage.py makemigrations --merge'
}}}

Clear enough.

{{{
$ ./manage.py makemigrations --merge
Merging testapp
  Branch 0002_mergeexample_unrelated_int
    - Add field unrelated_int to mergeexample
  Branch 0002_auto_20140618_1441
    - Add field long_title to mergeexample
    - Remove field short_title from mergeexample
}}}
{{{
$ ls -1 testapp/migrations
0001_initial.py
0002_auto_20140618_1441.py
0002_mergeexample_unrelated_int.py
__init__.py
__pycache__/
}}}

Nothing has been changed.

{{{
$ ./manage.py migrate
CommandError: Conflicting migrations detected (0002_mergeexample_unrelated_int, 0002_auto_20140618_1441 in testapp).
To fix them run 'python manage.py makemigrations --merge'
}}}

== Relevant Django code ==

The following excerpt from `django/core/management/commands/makemigrations.py` shows the lines where `--noinput`
has been omitted, and the code interpreting its absence.

{{{#!python
    ...
17 class Command(BaseCommand):
18     option_list = BaseCommand.option_list + (
19         make_option('--dry-run', action='store_true', dest='dry_run', default=False,
20             help=""Just show what migrations would be made; don't actually write them.""),
21         make_option('--merge', action='store_true', dest='merge', default=False,
22             help=""Enable fixing of migration conflicts.""),
23         make_option('--empty', action='store_true', dest='empty', default=False,
24             help=""Create an empty migration.""),
25     )
    ...
31     def handle(self, *app_labels, **options):
32
33         self.verbosity = int(options.get('verbosity'))
34         self.interactive = options.get('interactive')
35         self.dry_run = options.get('dry_run', False)
36         self.merge = options.get('merge', False)
37         self.empty = options.get('empty', False)
    ...
151     def handle_merge(self, loader, conflicts):
152         """"""
153         Handles merging together conflicted migrations interactively,
154         if it's safe; otherwise, advises on how to fix it.
155         """"""
156         if self.interactive:
157             questioner = InteractiveMigrationQuestioner()
158         else:
159             questioner = MigrationQuestioner()
    ...
195             if questioner.ask_merge(app_label):
196                 # If they still want to merge it, then write out an empty
197                 # file depending on the migrations needing merging.
    ...
}}}

== Temporary workaround ==

Interestingly, it is possible to work around the lack of a registered `--noinput` option by passing the option
straight to command programmatically, executing the command as it ought to function.

{{{
$ DJANGO_SETTINGS_MODULE=djangobugs.settings python -c 'import django; django.setup(); django.core.management.call_command(""makemigrations"", merge=True, interactive=True)'
Merging testapp
  Branch 0002_auto_20140618_1441
    - Add field long_title to mergeexample
    - Remove field short_title from mergeexample
  Branch 0002_mergeexample_unrelated_int
    - Add field unrelated_int to mergeexample

Merging will only work if the operations printed above do not conflict
with each other (working on different fields or models)
Do you want to merge these migration branches? [y/N] y

Created new merge migration /Users/artortenburger/Sites/djangobugs/djangobugs/testapp/migrations/0003_merge.py
}}}
{{{
$ ./manage.py migrate
Operations to perform:
  Synchronize unmigrated apps: (none)
  Apply all migrations: admin, sessions, contenttypes, auth, testapp
Synchronizing apps without migrations:
  Creating tables...
  Installing custom SQL...
  Installing indexes...
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying sessions.0001_initial... OK
  Applying testapp.0001_initial... OK
  Applying testapp.0002_mergeexample_unrelated_int... OK
  Applying testapp.0002_auto_20140618_1441... OK
  Applying testapp.0003_merge... FAKED
}}}

//(Edit: as tarballed, the example contains another change to testapp.MergeExample, which has no migrations yet.
If you successfully run the merge and migrate, re-running migrate will produce a warning about unmigrated changes.
In my test-run, makemigrations and migrate applied it successfully, producing testapp/migrations/0004_remove_mergeexample_unrelated_int.py.)//

== Proposed resolution ==

The minimal fix appears to be registering the `--noinput` option for the makemigrations command.
The two lines have been sourced from django/core/management/commands/migrate.py.

{{{#!diff
--- makemigrations.py	2014-06-18 12:22:57.000000000 -0300
+++ /Users/artortenburger/.virtualenvs/djangobugs/lib/python3.4/site-packages/django/core/management/commands/makemigrations.py	2014-06-18 12:23:28.000000000 -0300
@@ -16,6 +16,8 @@
 
 class Command(BaseCommand):
     option_list = BaseCommand.option_list + (
+        make_option('--noinput', action='store_false', dest='interactive', default=True,
+            help='Tells Django to NOT prompt the user for input of any kind.'),
         make_option('--dry-run', action='store_true', dest='dry_run', default=False,
             help=""Just show what migrations would be made; don't actually write them.""),
         make_option('--merge', action='store_true', dest='merge', default=False,
}}}

A patchfile for `django/core/management/commands/makemigrations.py` is attached, reproduced above.
"	Bug	closed	Migrations	1.7-beta-2	Release blocker	fixed			Accepted	1	0	0	0	0	0
