7
7
import subprocess
8
8
from pathlib import Path
9
9
from typing import Any
10
+ from typing import List
11
+ from typing import Tuple
10
12
11
13
from django .apps import apps
12
14
from django .core .management import BaseCommand
@@ -46,27 +48,43 @@ def handle(self, *args: Any, app_label: str, **options: Any) -> None:
46
48
if not max_migration_txt .exists ():
47
49
raise CommandError (f"{ app_label } does not have a max_migration.txt." )
48
50
49
- migration_names = find_migration_names (
50
- max_migration_txt .read_text ().splitlines ()
51
- )
51
+ migration_names = find_migration_names (max_migration_txt .read_text ())
52
52
if migration_names is None :
53
53
raise CommandError (
54
54
f"{ app_label } 's max_migration.txt does not seem to contain a"
55
55
+ " merge conflict."
56
56
)
57
- merged_migration_name , rebased_migration_name = migration_names
58
- if merged_migration_name not in migration_details .names :
59
- raise CommandError (
60
- f"Parsed { merged_migration_name !r} as the already-merged"
61
- + f" migration name from { app_label } 's max_migration.txt, but"
62
- + " this migration does not exist."
63
- )
64
- if rebased_migration_name not in migration_details .names :
65
- raise CommandError (
66
- f"Parsed { rebased_migration_name !r} as the rebased migration"
67
- + f" name from { app_label } 's max_migration.txt, but this"
68
- + " migration does not exist."
69
- )
57
+
58
+ merged_migration_names , rebased_migration_names = migration_names
59
+
60
+ for merged_migration_name in merged_migration_names :
61
+ if merged_migration_name not in migration_details .names :
62
+ raise CommandError (
63
+ f"Parsed { merged_migration_name !r} as the already-merged"
64
+ + f" migration name from { app_label } 's max_migration.txt, but"
65
+ + " this migration does not exist."
66
+ )
67
+
68
+ for rebased_migration_name in rebased_migration_names :
69
+ if rebased_migration_name not in migration_details .names :
70
+ raise CommandError (
71
+ f"Parsed { rebased_migration_name !r} as the rebased migration"
72
+ + f" name from { app_label } 's max_migration.txt, but this"
73
+ + " migration does not exist."
74
+ )
75
+
76
+ self .last_migration_name = merged_migration_names [- 1 ]
77
+
78
+ first_migration = True
79
+ for rebased_migration_name in rebased_migration_names :
80
+ self .rebase_migration (app_label , rebased_migration_name , first_migration )
81
+ first_migration = False
82
+
83
+ def rebase_migration (
84
+ self , app_label : str , rebased_migration_name : str , first_migration : bool
85
+ ) -> None :
86
+ migration_details = MigrationDetails (app_label )
87
+ max_migration_txt = migration_details .dir / "max_migration.txt"
70
88
71
89
rebased_migration_filename = f"{ rebased_migration_name } .py"
72
90
rebased_migration_path = migration_details .dir / rebased_migration_filename
@@ -136,7 +154,7 @@ def handle(self, *args: Any, app_label: str, **options: Any) -> None:
136
154
ast .Tuple (
137
155
elts = [
138
156
ast .Constant (app_label ),
139
- ast .Constant (merged_migration_name ),
157
+ ast .Constant (self . last_migration_name ),
140
158
]
141
159
)
142
160
)
@@ -152,16 +170,23 @@ def handle(self, *args: Any, app_label: str, **options: Any) -> None:
152
170
153
171
new_content = before_deps + ast_unparse (new_dependencies ) + after_deps
154
172
155
- merged_number , _merged_rest = merged_migration_name .split ("_" , 1 )
173
+ last_merged_number , _merged_rest = self . last_migration_name .split ("_" , 1 )
156
174
_rebased_number , rebased_rest = rebased_migration_name .split ("_" , 1 )
157
- new_number = int (merged_number ) + 1
175
+ new_number = int (last_merged_number ) + 1
158
176
new_name = str (new_number ).zfill (4 ) + "_" + rebased_rest
159
177
new_path_parts = rebased_migration_path .parts [:- 1 ] + (f"{ new_name } .py" ,)
160
178
new_path = Path (* new_path_parts )
161
179
162
180
rebased_migration_path .rename (new_path )
163
181
new_path .write_text (new_content )
164
- max_migration_txt .write_text (f"{ new_name } \n " )
182
+
183
+ if first_migration :
184
+ max_migration_txt .write_text (f"{ new_name } \n " )
185
+ else :
186
+ current_version_migrations = max_migration_txt .read_text ()
187
+ max_migration_txt .write_text (current_version_migrations + f"{ new_name } \n " )
188
+
189
+ self .last_migration_name = new_name
165
190
166
191
black_path = shutil .which ("black" )
167
192
if black_path : # pragma: no cover
@@ -176,19 +201,45 @@ def handle(self, *args: Any, app_label: str, **options: Any) -> None:
176
201
)
177
202
178
203
179
- def find_migration_names (max_migration_lines : list [str ]) -> tuple [str , str ] | None :
180
- lines = max_migration_lines
181
- if len (lines ) <= 1 :
204
+ def find_migration_names (
205
+ current_version_migrations : str ,
206
+ ) -> Tuple [List [str ], List [str ]] | None :
207
+ migrations_lines = current_version_migrations .strip ().splitlines ()
208
+
209
+ if len (migrations_lines ) <= 1 :
182
210
return None
183
- if not lines [0 ].startswith ("<<<<<<<" ):
211
+ if not migrations_lines [0 ].startswith ("<<<<<<<" ):
184
212
return None
185
- if not lines [- 1 ].startswith (">>>>>>>" ):
213
+ if not migrations_lines [- 1 ].startswith (">>>>>>>" ):
186
214
return None
187
- migration_names = (lines [1 ].strip (), lines [- 2 ].strip ())
215
+
216
+ merged_migration_names = []
217
+ rebased_migration_names = []
218
+
219
+ index = 0
220
+ while index < len (migrations_lines ):
221
+ if migrations_lines [index ].startswith ("<<<<<<<" ):
222
+ index += 1
223
+ while not migrations_lines [index ].startswith ("=======" ):
224
+ if migrations_lines [index ] == "|||||||" :
225
+ while not migrations_lines [index ].startswith ("=======" ):
226
+ index += 1
227
+ else :
228
+ merged_migration_names .append (migrations_lines [index ])
229
+ index += 1
230
+
231
+ index += 1
232
+
233
+ else :
234
+ while not migrations_lines [index ].startswith (">>>>>>>" ):
235
+ rebased_migration_names .append (migrations_lines [index ])
236
+ index += 1
237
+ break
238
+
188
239
if is_merge_in_progress ():
189
240
# During the merge 'ours' and 'theirs' are swapped in comparison with rebase
190
- migration_names = ( migration_names [ 1 ], migration_names [ 0 ] )
191
- return migration_names
241
+ return ( rebased_migration_names , merged_migration_names )
242
+ return ( merged_migration_names , rebased_migration_names )
192
243
193
244
194
245
def is_merge_in_progress () -> bool :
0 commit comments