Feature Branches and Database Snapshots in Magento 2 Development
AI generated
M2
di.xml
Magento 2 · Developer Workflow · Git · Database
Feature Branches and Database Snapshots
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.

17 min read n98-magerun2 · Git Hooks · mysqldump · Anonymization Magento 2.4.8 · MySQL/MariaDB

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.

11. FAQ: Feature Branches and Database Snapshots

1Isn't one shared DB state enough?
No, feature branches often bring their own schema changes that don't exist on other branches.
2Simplest method for small teams?
n98-magerun2 db:dump with @development stripping, portable and without special storage requirements.
3Automate switch on git checkout?
Via a post-checkout hook that saves first and then restores the matching snapshot.
4LVM/Btrfs vs. mysqldump speed?
Filesystem snapshots take fractions of a second, mysqldump can take several minutes for large catalogs.
5Pause MySQL before filesystem snapshot?
Yes, at least FLUSH TABLES WITH READ LOCK, otherwise InnoDB transactions risk an inconsistent freeze.
6Protect customer data in snapshots?
With automated anonymization after every import, mandatory and not optional.
7When to delete old snapshots?
Only after a grace period of a few days after branch deletion, never immediately.
8Why does branch naming convention matter?
The mapping between branch and snapshot file is automated and needs a fixed convention.
9Combinable with Docker volumes?
Yes, with the MySQL data directory as a volume on LVM or Btrfs, without stopping the container.
10How much smaller after stripping?
Often only a third to half of the original size, since logs, sessions and reports are excluded.