migrate: pre-validate --stop-before targets across all sets before applying

When sqlite-utils migrate is invoked with multiple migration sets, an already-applied --stop-before target in a subsequent set would cause an error inside that set's apply() call only after earlier sets had already executed and committed their pending migrations.

Pre-validate across all loaded migration sets upfront that no requested --stop-before targets have already been applied before entering the application loop, ensuring no pending migrations are applied when an error is raised.

Fixes #870

Signed-off-by: Samran Asif <samranwebdev2000@gmail.com>
This commit is contained in:
Samran Asif 2026-09-21 19:59:52 +05:00
commit 8665adb695
2 changed files with 49 additions and 0 deletions

View file

@ -3598,6 +3598,20 @@ def migrate(db_path, migrations, stop_before, list_, verbose):
", ".join(unknown)
)
)
for migration_set in migration_sets:
matches = _stop_before_for_migration_set(stop_before, migration_set.name)
if hasattr(migration_set, "applied"):
applied_names = {m.name for m in migration_set.applied(db)}
already_applied = set(matches).intersection(applied_names)
if already_applied:
raise click.ClickException(
"Cannot stop before migration{} {} in set '{}' - already "
"been applied".format(
"s" if len(already_applied) > 1 else "",
", ".join(sorted(already_applied)),
migration_set.name,
)
)
for migration_set in migration_sets:
matches = _stop_before_for_migration_set(stop_before, migration_set.name)
if isinstance(migration_set, sqlite_utils.Migrations):