Branch switching without broken test data
Switching between feature branches without bringing along the matching database means testing against wrong states or losing fixtures. An automated workflow with database snapshots per branch makes switching as effortless as a plain git checkout.
Table of Contents
- 1. Why feature branches fail without database snapshots
- 2. Snapshot strategies: dump vs. filesystem snapshot
- 3. Automated snapshots on every branch switch
- 4. n98-magerun2 in the snapshot workflow
- 5. Docker volumes and fast snapshots with LVM and Btrfs
- 6. Anonymizing customer data in snapshots
- 7. Snapshot rotation and disk space management
- 8. Branch naming convention for database mapping
- 9. Snapshot methods compared
- 10. Summary
- 11. FAQ
1. Why feature branches fail without database snapshots
Magento couples code and database schema more tightly than most frameworks, because declarative schema changes, EAV attributes and configuration values land directly in the database. Switching between feature branches without a matching database snapshot quickly leads to an instance that matches neither the old nor the new code: migrations from one branch remain in place while the other branch expects different tables. The result is cryptic errors that have nothing to do with the actual feature.
A clean database snapshot per feature branch solves this problem at the root: switching to a branch automatically restores the associated snapshot, so code and data always stay in sync. This matters especially for branches that bring their own db_schema.xml changes, data patches or test data for a new feature that simply do not exist on another branch.
This article shows how a team integrates database snapshots into the Git workflow so that switching between feature branches becomes as natural as a plain checkout, including automation, anonymization and disk space management.
2. Snapshot strategies: dump vs. filesystem snapshot
There are two fundamental approaches to database snapshots in Magento development: logical dumps with mysqldump and physical filesystem snapshots via LVM, Btrfs or ZFS. Logical dumps are portable, can be versioned and work independently of the storage backend, but are slow to create and restore for large catalogs with several gigabytes of product data.
Filesystem snapshots are nearly instant because they only create copy-on-write pointers to existing data blocks instead of physically copying data. The downside: they are tied to the specific filesystem and cannot easily be exchanged between developers using different storage backends. For a team with a unified Docker infrastructure on Linux hosts, filesystem database snapshots are usually the faster choice; for heterogeneous teams mixing macOS and Linux, mysqldump remains the more practical option.
#!/usr/bin/env bash
# db-snapshot.sh — logical snapshot per feature branch
set -euo pipefail
BRANCH="$(git rev-parse --abbrev-ref HEAD)"
SAFE_NAME="${BRANCH//\//_}"
SNAPSHOT_DIR=".db-snapshots"
SNAPSHOT_FILE="${SNAPSHOT_DIR}/${SAFE_NAME}.sql.gz"
mkdir -p "$SNAPSHOT_DIR"
echo "[INFO] Creating database snapshot for branch: $BRANCH"
bin/mysqldump --single-transaction --quick --no-tablespaces \
| gzip -9 > "$SNAPSHOT_FILE"
echo "[OK] Snapshot saved to $SNAPSHOT_FILE ($(du -h "$SNAPSHOT_FILE" | cut -f1))"
3. Automated snapshots on every branch switch
Manually run snapshot scripts get forgotten in practice, especially under time pressure. The more reliable approach is a Git hook that automatically checks on every checkout whether a database snapshot exists for the target branch and restores it if needed. The post-checkout hook receives the old and new commit reference plus a flag indicating whether this is a branch or file checkout, so it can react only to actual branch switches.
Important for automation: a snapshot of the current state should always be created before the new one is restored, so changes on the old branch are not lost. This order, save first, then switch, is the core principle of a robust database snapshot workflow and prevents a spontaneous branch switch from overwriting unsaved test data.
#!/usr/bin/env bash
# .git/hooks/post-checkout — auto-restore db snapshot on branch switch
set -euo pipefail
PREV_HEAD="$1"
NEW_HEAD="$2"
IS_BRANCH_CHECKOUT="$3"
[[ "$IS_BRANCH_CHECKOUT" -eq 1 ]] || exit 0
CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
SAFE_NAME="${CURRENT_BRANCH//\//_}"
SNAPSHOT_FILE=".db-snapshots/${SAFE_NAME}.sql.gz"
if [[ -f "$SNAPSHOT_FILE" ]]; then
echo "[INFO] Restoring database snapshot for branch: $CURRENT_BRANCH"
gunzip -c "$SNAPSHOT_FILE" | bin/mysql
bin/magento cache:flush
else
echo "[WARN] No snapshot found for $CURRENT_BRANCH, keeping current database"
fi
4. n98-magerun2 in the snapshot workflow
n98-magerun2 offers db:dump and db:import commands tailored specifically to Magento, going beyond plain mysqldump. The option --strip="@development" excludes predefined table groups such as logs, sessions and reports from the database snapshot, which often reduces dump size by more than half without losing data relevant to development.
Another advantage: n98-magerun2 db:dump understands Magento's table structure and can consistently back up all core and EAV tables, including correct foreign key order on import. For a branch-based database snapshot workflow, it is worth defining the strip group project-wide in an n98-magerun2.yaml, so every developer produces the same reduced snapshots.
# Create a stripped snapshot excluding logs, sessions and reports
n98-magerun2 db:dump \
--strip="@development" \
--compression="gzip" \
".db-snapshots/$(git rev-parse --abbrev-ref HEAD | tr '/' '_').sql.gz"
# Restore the snapshot for the current branch
n98-magerun2 db:import \
--compression="gzip" \
".db-snapshots/$(git rev-parse --abbrev-ref HEAD | tr '/' '_').sql.gz"
# List all available table-strip groups defined project-wide
n98-magerun2 db:dump --list-groups
5. Docker volumes and fast snapshots with LVM and Btrfs
For teams running MySQL data directories as Docker volumes on a Linux host with LVM or Btrfs, filesystem database snapshots are a considerably faster alternative to logical dumps. An LVM snapshot of the volume holding the MySQL data directory takes only fractions of a second regardless of database size, because only metadata is copied, not the actual data blocks.
The catch with this method: the MySQL server should briefly pause or at least be brought into a consistent state with FLUSH TABLES WITH READ LOCK before the filesystem snapshot is triggered. Without this step, the database snapshot can freeze InnoDB transactions in an inconsistent state. Btrfs subvolumes offer a convenient middle ground here: btrfs subvolume snapshot creates an instant, writable clone that can evolve independently while the original stays untouched.
6. Anonymizing customer data in snapshots
A database snapshot created from a production or staging database typically contains real customer data: names, email addresses, addresses and sometimes payment references. Distributing this data unversioned and unprotected to developer laptops is a GDPR risk that many teams underestimate. Before handing a snapshot to the entire team, an anonymization step should be firmly anchored in the workflow.
Anonymization does not mean deleting all data, but inserting realistic yet fictitious values so features like address validation or email sending can still be tested meaningfully. SQL updates that replace names and email addresses with generated placeholders should ideally run automatically after every import of a production-derived database snapshot, never manually and never optionally.
-- anonymize.sql — run automatically after importing a production-derived snapshot
-- Replaces real customer data with deterministic, fake-but-realistic values
UPDATE customer_entity
SET
email = CONCAT('customer', entity_id, '@example.test'),
firstname = CONCAT('Test', entity_id),
lastname = 'User';
UPDATE customer_address_entity
SET
firstname = 'Test',
lastname = 'User',
street = '123 Test Street',
telephone = '000000000';
UPDATE sales_order_address
SET
email = CONCAT('order', entity_id, '@example.test'),
firstname = 'Test',
lastname = 'User';
-- Never anonymize table structure or product/catalog data — only PII
7. Snapshot rotation and disk space management
Without rotation, the directory holding database snapshots grows uncontrollably, especially in teams with many short-lived feature branches. A simple rotation script that automatically removes snapshots for deleted branches and limits the number of retained snapshots per active branch keeps disk usage in check. Comparing existing snapshots against currently existing branches can be done directly with git branch --list.
A cron-based cleanup script running weekly is enough for most teams. It matters that deleted branches are not removed immediately but only after a grace period of a few days, in case a branch is accidentally deleted and then restored. An overly aggressive rotation script for database snapshots can otherwise delete exactly the data that is needed again shortly afterward.
#!/usr/bin/env bash
# rotate-snapshots.sh — remove snapshots for branches deleted more than 7 days ago
set -euo pipefail
SNAPSHOT_DIR=".db-snapshots"
GRACE_DAYS=7
git branch --list | sed 's/^[* ]*//' > /tmp/active_branches.txt
for snapshot in "$SNAPSHOT_DIR"/*.sql.gz; do
[[ -e "$snapshot" ]] || continue
branch_name="$(basename "$snapshot" .sql.gz | tr '_' '/')"
if ! grep -qx "$branch_name" /tmp/active_branches.txt; then
age_days=$(( ( $(date +%s) - $(stat -c %Y "$snapshot") ) / 86400 ))
if (( age_days > GRACE_DAYS )); then
echo "[INFO] Removing stale snapshot: $snapshot (branch deleted $age_days days ago)"
rm -f "$snapshot"
fi
fi
done
8. Branch naming convention for database mapping
The entire automation workflow for database snapshots stands or falls with a consistent branch naming convention. If branches are named arbitrarily, for example with special characters or without a clear prefix, the mapping between branch names and snapshot file names becomes error-prone. A convention like feature/JIRA-1234-short-description maps reliably to a filename by replacing slashes with underscores.
For teams with a strict separation between feature work and hotfixes, a dedicated prefix like hotfix/ is also recommended, so the snapshot script can distinguish whether a production-derived or a purely local database snapshot should be loaded. This distinction prevents a hotfix from being accidentally tested against stale feature test data, while a feature branch mistakenly loads a production-derived snapshot that does not match the planned schema changes.
9. Snapshot methods compared
The right snapshot method depends on team size, infrastructure and data volume. The following table compares the most important approaches to database snapshots in Magento projects.
| Method | Speed | Portability | Recommended for |
|---|---|---|---|
| mysqldump | Slow | Very high | Heterogeneous teams, small catalogs |
| n98-magerun2 db:dump | Medium | High | Standard workflow, large catalogs |
| LVM snapshot | Very fast | Low | Unified Linux Docker hosts |
| Btrfs subvolume | Very fast | Low | CI runners with Btrfs storage |
In practice, many teams combine both approaches: LVM or Btrfs snapshots for fast local switching between their own branches, and n98-magerun2 dumps for exchanging database snapshots with other team members or storing them on shared network storage.
Mironsoft
Magento 2 development, Git workflows and infrastructure automation
Branch switching without broken test data and without wasted time?
We build automated database snapshot workflows for your Magento projects, with Git hooks, anonymization and rotation, so every feature branch brings the right data along.
Workflow design
Snapshot strategy tailored to your infrastructure and team size
Automation
Setting up Git hooks, n98-magerun2 and rotation scripts
GDPR safeguarding
Anonymization routines for production-derived snapshots
10. Summary
Database snapshots per feature branch solve a fundamental problem in Magento development: code and database schema are tightly coupled, and switching branches without matching data leads to errors that have nothing to do with the actual feature. An automated Git hook that restores the right snapshot on checkout makes switching branches as simple as switching code branches normally.
The choice between logical dumps and filesystem snapshots depends on infrastructure and team size, but more important than the method itself are anonymizing production-derived data and a working rotation that keeps the snapshot directory from growing uncontrollably. Teams that consistently implement these three building blocks, automation, anonymization, rotation, reclaim noticeable development time.
Feature Branches and Database Snapshots — Key Takeaways
Automation
A post-checkout hook restores the matching snapshot automatically, save first, then switch.
n98-magerun2
db:dump --strip="@development" significantly reduces snapshot size without losing relevant data.
Anonymization
Always anonymize production-derived snapshots with SQL update scripts, never share them unprotected.
Rotation
Automatically remove snapshots of deleted branches after a grace period, keep disk usage in check.