Skip to content

Commit d537bc3

Browse files
committed
rebase_migration: Un apply and rebase migrations in case migrations are applied.
Previously, while rebasing migrations, if the migration to be rebased is already applied, it would throw out an error asking to un apply the migration before rebasing. This commit un applies the migrations and rebases them when the migrations to be rebased are already applied. This is done by first creating a --merge migration, un applying the previous migrations, removing the --merge migration, and then rebasing the existing migrations. Fixes #184
1 parent e0b27b9 commit d537bc3

File tree

1 file changed

+61
-5
lines changed

1 file changed

+61
-5
lines changed

src/django_linear_migrations/management/commands/rebase_migration.py

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,16 @@ def rebase_migration(
9393
)
9494

9595
if migration_applied(app_label, rebased_migration_name):
96-
raise CommandError(
97-
f"Detected {rebased_migration_name} as the rebased migration,"
98-
+ " but it is applied to the local database. Undo the rebase,"
99-
+ " reverse the migration, and try again."
100-
)
96+
try:
97+
unapply_migrations(
98+
app_label, rebased_migration_name, self.last_migration_name
99+
)
100+
except (FileNotFoundError, subprocess.SubprocessError):
101+
raise CommandError(
102+
f"Detected {rebased_migration_name} as the rebased migration,"
103+
+ " but it is applied to the local database. Undo the rebase,"
104+
+ " reverse the migration, and try again."
105+
)
101106

102107
content = rebased_migration_path.read_text()
103108
split_result = re.split(
@@ -272,3 +277,54 @@ def migration_applied(app_label: str, migration_name: str) -> bool:
272277
# django_migrations table does not exist -> no migrations applied
273278
pass
274279
return False
280+
281+
282+
def unapply_migrations(
283+
app_label: str, first_rebase_migration: str, last_merged_migration: str
284+
) -> None: # pragma: no cover
285+
first_rebase_migration_number, _merged_rest = first_rebase_migration.split("_", 1)
286+
last_merged_number, _merged_rest = last_merged_migration.split("_", 1)
287+
migration_details = MigrationDetails(app_label)
288+
289+
subprocess.run(
290+
[
291+
"./manage.py",
292+
"makemigrations",
293+
"--merge",
294+
"--noinput",
295+
"--skip-checks",
296+
f"{app_label}",
297+
],
298+
check=True,
299+
)
300+
301+
last_migration_to_be_applied = None
302+
merge_migration_name = None
303+
304+
for migration_name in migration_details.names:
305+
if migration_name.startswith(
306+
f"{int(first_rebase_migration_number) - 1}".zfill(4)
307+
):
308+
last_migration_to_be_applied = migration_name
309+
310+
elif migration_name.startswith(f"{int(last_merged_number) + 1}".zfill(4)):
311+
merge_migration_name = migration_name
312+
313+
assert last_migration_to_be_applied is not None and merge_migration_name is not None
314+
merge_migration_path = migration_details.dir / f"{merge_migration_name}.py"
315+
316+
subprocess.run(
317+
[
318+
"./manage.py",
319+
"migrate",
320+
"--skip-checks",
321+
f"{app_label}",
322+
last_migration_to_be_applied,
323+
],
324+
check=True,
325+
)
326+
327+
subprocess.run(
328+
["rm", f"{merge_migration_path}"],
329+
check=True,
330+
)

0 commit comments

Comments
 (0)