Merge Handling¶
dbwarden supports branch merge reconciliation for teams using git workflows. When two branches generate migrations that diverge from a common model state, dbwarden can detect the merge, generate a reconciliation migration, and mark the branch migrations as superseded.
Overview¶
dbwarden is declarative: SQLAlchemy models are the source of truth, and migrations are derived output. When two branches diverge and each generates migrations, the merged schema is defined entirely by the merged models. The correct migration after a merge is diff(merge_base_state, merged_models).
This means: - Branch migrations carry no schema meaning going forward (only provenance) - The reconciliation migration is the only new runnable migration - Branch migrations are marked as superseded (audit-only, never run again)
Commands¶
dbwarden merge¶
Reconciles divergent migration histories after a branch merge.
Flags:
- --database, -d: Target database name
- --rename-column: Column rename to confirm (format: table.old=new)
- --rename-table: Table rename to confirm (format: old=new)
- --force, -f: Force marking hand-edited migrations
- --commit: Create a git commit with the changes
- --verbose, -v: Enable verbose logging
Algorithm: 1. Check preconditions (clean working tree, no conflict markers, merge-base resolvable) 2. Resolve merge-base state from git 3. Rebuild current model state from merged models 4. Compute reconciliation diff 5. Probe persistent environments 6. Generate reconciliation migration 7. Mark branch migrations as superseded 8. Report
dbwarden rebase¶
Recovers a disposable environment after a merge.
Flags:
- --database, -d: Target database name
- --yes, -y: Skip confirmation prompts
- --force, -f: Force operation even against persistent environments
- --check: Only check what would happen, don't make changes
- --verbose, -v: Enable verbose logging
Algorithm: 1. Snapshot live environment 2. Diff against merged models 3. Generate environment-specific reconciliation 4. Apply with lock discipline 5. Update merge record
dbwarden reconcile¶
Recovers a persistent environment after a dirty merge.
Flags:
- environment: Environment name to reconcile (required)
- --database, -d: Target database name
- --rename-column: Column rename to confirm
- --dry-run: Only show what would happen
- --verbose, -v: Enable verbose logging
File Formats¶
Superseded Marker¶
Prepended to migration files that have been superseded by a merge:
-- dbwarden:superseded
-- merged-into: 0006
-- merged-at: 2026-08-22T14:03:11Z
-- merge-base: 0004
-- branch: feature/profile-fields
-- applied-persistent: none
-- file-checksum: sha256:7ab1...
-- upgrade
ALTER TABLE users ADD COLUMN bio TEXT;
-- rollback
ALTER TABLE users DROP COLUMN bio;
Rules:
- Marked files are excluded from the runnable chain
- Marked files are never deleted (audit trail)
- applied-persistent tracks which environments applied the migration
Reconciliation Migration Header¶
Added to migration files generated at merge time:
-- dbwarden:merge-reconciliation
-- merge-base: 0004 (state checksum 9f2c...)
-- supersedes: 0005_add_profile.sql, 0005_extend_billing.sql
-- probe: staging=clean, production=clean, qa=unknown
-- generated-by: dbwarden merge (0.18.0)
-- upgrade
...
-- rollback
...
Environment Registry¶
Configure persistent vs disposable environments in your database config:
from dbwarden import DbwardenDatabase, EnvironmentConfig
class Primary(DbwardenDatabase):
database_name = "primary"
database_type = "postgresql"
database_url_sync = "postgresql://..."
default = True
environments = [
EnvironmentConfig(name="staging", url_env="STAGING_DATABASE_URL", persistent=True),
EnvironmentConfig(name="production", url_env="PROD_DATABASE_URL", persistent=True),
]
Rules:
- Unregistered environments are disposable by default
- Persistent environments require reconcile after a dirty merge
- Disposable environments can be reset with rebase
Merge Detection¶
dbwarden detects merges by checking for: 1. Divergent generation base: Newest migration's base checksum doesn't match current model state 2. Version collisions: Multiple migration files share the same version prefix 3. Snapshot discontinuity: Latest snapshot doesn't match model state
When detected, make-migrations refuses generation and status shows MERGE_PENDING.
Workflow¶
After pulling a merge¶
-
Check status:
-
Run merge:
-
Recover local database:
-
Recover persistent environments (if dirty):
-
Commit and push:
Known Limitations¶
- MariaDB: Snapshot support is incomplete; merge-base resolution uses
model_state.jsononly. Rename detection features degrade as they do in the legacy live-fallback path. The mandatory confirmation rule (§9.1.1) becomes the primary safeguard for MariaDB merges. - Git required: All merge operations require git to be available
- No Python data migrations: Manual migrations are SQL-only
- No revision branching/merging: Linear versioned sequence per database
MariaDB Specifics¶
MariaDB has limited snapshot support compared to PostgreSQL. When merging on a MariaDB database:
- Snapshot-based rename detection is degraded because MariaDB snapshots don't capture all column metadata
- Merge-base resolution uses only
model_state.json, not the full snapshot - Mandatory confirmation is required for all rename candidates during merge (§9.1.1)
- Column type changes may not be fully detected without a complete snapshot
For MariaDB projects, consider:
- Using --rename-column and --rename-table flags explicitly during merge
- Verifying the reconciliation migration carefully before applying
- Running dbwarden diff after merge to confirm no unexpected changes
See Also¶
- Migration Locking: How migrations are locked
- Offline Integrity: Model state file management
- CLI Reference: All commands