Skip to content

Instantly share code, notes, and snippets.

@GabrielVidal1
Created October 16, 2025 23:18
Show Gist options
  • Select an option

  • Save GabrielVidal1/f6457ec2d19620ebe5ed0fc67a5653dc to your computer and use it in GitHub Desktop.

Select an option

Save GabrielVidal1/f6457ec2d19620ebe5ed0fc67a5653dc to your computer and use it in GitHub Desktop.
Automatic django migration handling for git workflow
#!/bin/bash
# ==============================================================================
# Git Branch Switch with Safe Django Migrations
# ==============================================================================
# This script automates switching between Git branches in a Django project
# while handling database migrations safely.
#
# It:
# - Compares Django migrations between the current and target branches.
# - Reverts apps to the last common migration if migrations diverged.
# - Switches branches.
# - Applies the latest migrations for the new branch.
#
# USAGE:
# git pswitch <branch-name>
#
# EXAMPLE:
# git pswitch feature/new-ui
#
# SETUP AS A GIT ALIAS:
# 1. Save this script as `.git-pswitch` in your project root or somewhere in your PATH.
# 2. Make it executable:
# chmod +x .git-pswitch
# 3. Add the alias to your Git configuration:
# git config --global alias.pswitch '!bash .git-pswitch'
#
# After setup, you can simply run:
# git pswitch main
#
# ==============================================================================
set -e
MIGRATE_COMMAND="python manage.py migrate"
current_branch=$(git rev-parse --abbrev-ref HEAD);
target_branch=$1
if [ -z \"$target_branch\" ]; then \
echo 'Usage: git pswitch <branch>'; \
return 1; \
fi;
if [ "$current_branch" == "$target_branch" ]; then
echo "Already on branch '$target_branch'. No switch needed."
exit 0
fi
if ! git show-ref --verify --quiet "refs/heads/$target_branch"; then
echo "Branch '$target_branch' does not exist."
exit 1
fi
# The root directory where Django apps are located
APPS_ROOT="apps"
apps_to_revert=()
# Loop over all apps
while read -r migrations_dir; do
app=$(basename "$(dirname "$migrations_dir")")
# --- Migrations from old branch ---
mapfile -t old_migrations < <(
git ls-tree -r --name-only "$current_branch" -- "$migrations_dir" 2>/dev/null |
grep '/[0-9]\+_.*\.py$' |
xargs -n1 basename |
sed 's/\.py$//' |
sort
)
# --- Migrations from new branch ---
mapfile -t new_migrations < <(
git ls-tree -r --name-only "$target_branch" -- "$migrations_dir" 2>/dev/null |
grep '/[0-9]\+_.*\.py$' |
xargs -n1 basename |
sed 's/\.py$//' |
sort
)
# If the migrations are identical, skip
diff_output=$(diff <(printf '%s\n' "${old_migrations[@]}") <(printf '%s\n' "${new_migrations[@]}") || true)
if [ -z "$diff_output" ]; then
continue
fi
# --- Find last common migration ---
last_common=""
for mig in "${old_migrations[@]}"; do
if printf '%s\n' "${new_migrations[@]}" | grep -qx "$mig"; then
last_common="$mig"
else
break
fi
done
# If migrations are missing or diverged, record the app
if [ -n "$last_common" ]; then
apps_to_revert+=("$app $last_common")
fi
done < <(find "$APPS_ROOT" -type d -name "migrations")
if [ ${#apps_to_revert[@]} -eq 0 ]; then exit 0; fi
echo "The following apps will be reverted to previous migrations:"
for entry in "${apps_to_revert[@]}"; do
read -r app migration <<< "$entry"
echo " - $app $migration"
done
for entry in "${apps_to_revert[@]}"; do
read -r app migration <<< "$entry"
$MIGRATE_COMMAND "$app" "$migration"
done
git switch "$target_branch"
$MIGRATE_COMMAND
echo "Switched to branch '$target_branch' and applied necessary migrations."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment