Skip to content

Instantly share code, notes, and snippets.

@Konamiman
Last active October 28, 2025 10:09
Show Gist options
  • Save Konamiman/77f7858a0267b5bc3d506fad69961654 to your computer and use it in GitHub Desktop.
Save Konamiman/77f7858a0267b5bc3d506fad69961654 to your computer and use it in GitHub Desktop.
A script to easily handle multiple WooCommerce branches using git worktrees
#!/bin/bash
# WooCommerce Worktree Management Script
# By Konamiman (and Claude)
# This script is provided as-is without warranty of any kind.
# It's not officially supported by WooCommerce or Automattic.
# Hint: add it to your ~/.bashrc or equivalent:
# alias wwt='~/woo-worktrees.sh'
set -e
# Adjust these three to your setup.
# Note: the default value of WOO_WP_PLUGINS assumes a ~/plugins symlink
# is in place pointing to the WordPress' plugins directory.
WOO_REPO="$HOME/woocommerce"
WOO_WORKTREES="$HOME/worktrees"
WOO_WP_PLUGIN="$HOME/plugins/woocommerce"
WOO_CORE="$WOO_REPO/plugins/woocommerce"
show_help() {
cat << EOF
WooCommerce Worktree Management
Usage:
woo-worktrees a[dd] <branch> [base] Create a new worktree for the specified branch
woo-worktrees d[el] <branch> [opts] Delete a worktree (fails if uncommitted changes)
woo-worktrees s[et] <branch> Set plugin symlink to worktree
woo-worktrees p[rint] Show currently linked worktree
woo-worktrees r[eset] Reset plugin symlink to main repository
woo-worktrees Show this help
Options:
-f, -force Force delete worktree even with uncommitted changes
-db, --delete-branch Also delete the local branch after removing worktree
Examples:
woo-worktrees add feature-123 # Create worktree for branch feature-123 from current HEAD
woo-worktrees add feature-123 trunk # Create worktree for feature-123 based on trunk branch
woo-worktrees add . # Create worktree for current branch, switch ~/woocommerce to trunk
woo-worktrees add . develop # Create worktree for current branch, switch ~/woocommerce to develop
woo-worktrees set feature-123 # Switch to feature-123 worktree
woo-worktrees print # Show currently linked worktree
woo-worktrees reset # Switch back to main repository
woo-worktrees del feature-123 # Delete feature-123 worktree
woo-worktrees del feature-123 -f # Force delete even with uncommitted changes
woo-worktrees del feature-123 -db # Delete worktree and local branch
woo-worktrees del feature-123 -f -db # Force delete worktree and branch
Alias: wwt <command> <args>
EOF
}
cmd_add() {
local branch="$1"
local base_branch="$2"
# Strip trailing slash (for directory autocompletion)
branch="${branch%/}"
base_branch="${base_branch%/}"
if [ -z "$branch" ]; then
echo "Error: Branch name required"
echo "Usage: woo-worktrees add <branch> [base-branch]"
echo " woo-worktrees add . [base-branch] # Use current branch from ~/woocommerce"
exit 1
fi
if [ ! -d "$WOO_REPO" ]; then
echo "Error: Main WooCommerce repository not found: $WOO_REPO"
exit 1
fi
cd "$WOO_REPO"
# Special case: "." means use current branch, then switch to base
if [ "$branch" = "." ]; then
branch=$(git rev-parse --abbrev-ref HEAD)
echo "Current branch: $branch"
# Default to trunk if no base branch specified
if [ -z "$base_branch" ]; then
base_branch="trunk"
fi
echo "Switching ~/woocommerce to: $base_branch"
git checkout "$base_branch"
if [ $? -ne 0 ]; then
echo "Error: Failed to checkout $base_branch"
exit 1
fi
fi
local worktree_dir="$WOO_WORKTREES/$branch"
if [ -d "$worktree_dir" ]; then
echo "Error: Worktree directory already exists: $worktree_dir"
exit 1
fi
# Create parent directories if branch name contains slashes (e.g., feature/my-branch)
mkdir -p "$(dirname "$worktree_dir")"
# Check if branch exists locally
local branch_exists_locally=false
if git show-ref --verify --quiet "refs/heads/$branch"; then
branch_exists_locally=true
fi
# Check if branch exists on remote
local branch_exists_remotely=false
if git show-ref --verify --quiet "refs/remotes/origin/$branch"; then
branch_exists_remotely=true
fi
# If base branch is specified, verify it exists and create new branch from it
if [ -n "$base_branch" ]; then
if ! git show-ref --verify --quiet "refs/heads/$base_branch"; then
echo "Error: Base branch '$base_branch' does not exist"
exit 1
fi
if [ "$branch_exists_locally" = true ]; then
echo "Creating worktree for existing branch: $branch"
git worktree add "$worktree_dir" "$branch" 2>&1 | grep -v "fatal: bad object"
else
echo "Creating worktree with new branch '$branch' from '$base_branch'..."
git worktree add -b "$branch" "$worktree_dir" "$base_branch" 2>&1 | grep -v "fatal: bad object"
fi
else
# No base branch specified
if [ "$branch_exists_locally" = true ]; then
echo "Creating worktree for local branch: $branch"
git worktree add "$worktree_dir" "$branch" 2>&1 | grep -v "fatal: bad object"
elif [ "$branch_exists_remotely" = true ]; then
echo "Creating worktree for remote branch: $branch"
git worktree add --track -b "$branch" "$worktree_dir" "origin/$branch" 2>&1 | grep -v "fatal: bad object"
else
# Branch doesn't exist anywhere, create from current branch
echo "Creating worktree with new branch '$branch' from current branch..."
local current_branch=$(git rev-parse --abbrev-ref HEAD)
git worktree add -b "$branch" "$worktree_dir" "$current_branch" 2>&1 | grep -v "fatal: bad object"
fi
fi
echo "✓ Worktree created: $worktree_dir"
# Create utility symlink for shorter path (especially useful for shell prompt)
# Replace slashes with underscores in symlink name
local symlink_name="_${branch//\//_}"
local symlink_path="$WOO_WORKTREES/$symlink_name"
local symlink_target="$worktree_dir/plugins/woocommerce"
if [ -d "$symlink_target" ]; then
ln -s "$symlink_target" "$symlink_path"
echo "✓ Utility symlink created: $symlink_path"
else
echo "⚠️ Warning: Could not create utility symlink (plugins/woocommerce not found)"
fi
}
cmd_del() {
local branch="$1"
shift
local force=false
local delete_branch=false
# Strip trailing slash (for directory autocompletion)
branch="${branch%/}"
if [ -z "$branch" ]; then
echo "Error: Branch name required"
echo "Usage: woo-worktrees del <branch> [-f] [-db]"
exit 1
fi
# Parse all flags
while [ $# -gt 0 ]; do
case "$1" in
-f|-force|--force)
force=true
;;
-db|--delete-branch)
delete_branch=true
;;
*)
echo "Error: Unknown flag: $1"
echo "Usage: woo-worktrees del <branch> [-f] [-db]"
exit 1
;;
esac
shift
done
local worktree_dir="$WOO_WORKTREES/$branch"
local worktree_exists=false
if [ -d "$worktree_dir" ]; then
worktree_exists=true
fi
# If worktree doesn't exist but -db is specified, just delete the branch
if [ "$worktree_exists" = false ]; then
if [ "$delete_branch" = true ]; then
cd "$WOO_REPO"
if git show-ref --verify --quiet "refs/heads/$branch"; then
echo "Worktree does not exist, but branch does. Deleting branch: $branch"
if [ "$force" = true ]; then
git branch -D "$branch"
else
git branch -d "$branch"
fi
echo "✓ Branch deleted: $branch"
# Also remove utility symlink if it exists
local symlink_name="_${branch//\//_}"
local symlink_path="$WOO_WORKTREES/$symlink_name"
if [ -L "$symlink_path" ]; then
rm "$symlink_path"
echo "✓ Utility symlink removed: $symlink_path"
fi
return 0
else
echo "Error: Neither worktree nor branch exists: $branch"
exit 1
fi
else
echo "Error: Worktree directory does not exist: $worktree_dir"
exit 1
fi
fi
# Check for uncommitted changes unless force is enabled
if [ "$force" = false ]; then
echo "Checking for uncommitted changes..."
cd "$worktree_dir"
if [ -n "$(git status --porcelain)" ]; then
echo "Error: Worktree has uncommitted changes:"
git status --short
echo ""
echo "Use -f or -force to delete anyway"
exit 1
fi
else
echo "⚠️ Force deleting worktree (ignoring uncommitted changes)"
fi
echo "Removing worktree: $branch"
cd "$WOO_REPO"
if [ "$force" = true ]; then
git worktree remove --force "$worktree_dir"
else
git worktree remove "$worktree_dir"
fi
echo "✓ Worktree removed: $worktree_dir"
# Remove utility symlink if it exists
local symlink_name="_${branch//\//_}"
local symlink_path="$WOO_WORKTREES/$symlink_name"
if [ -L "$symlink_path" ]; then
rm "$symlink_path"
echo "✓ Utility symlink removed: $symlink_path"
fi
# Clean up empty parent directories (for branches with slashes like feature/my-branch)
local parent_dir=$(dirname "$worktree_dir")
while [ "$parent_dir" != "$WOO_WORKTREES" ] && [ -d "$parent_dir" ]; do
if [ -z "$(ls -A "$parent_dir")" ]; then
echo "Removing empty directory: $parent_dir"
rmdir "$parent_dir"
parent_dir=$(dirname "$parent_dir")
else
# Directory not empty, stop here
break
fi
done
# Delete the branch if requested
if [ "$delete_branch" = true ]; then
if git show-ref --verify --quiet "refs/heads/$branch"; then
echo "Deleting local branch: $branch"
if [ "$force" = true ]; then
git branch -D "$branch"
else
git branch -d "$branch"
fi
echo "✓ Branch deleted: $branch"
else
echo "⚠️ Branch does not exist: $branch (skipping branch deletion)"
fi
else
# -db not specified, check if branch exists and inform user
if git show-ref --verify --quiet "refs/heads/$branch"; then
echo "ℹ️ Branch '$branch' still exists. Run with -db to delete it."
fi
fi
}
cmd_set() {
local branch="$1"
# Strip trailing slash (for directory autocompletion)
branch="${branch%/}"
if [ -z "$branch" ]; then
echo "Error: Branch name required"
echo "Usage: woo-worktrees set <branch>"
exit 1
fi
local worktree_dir="$WOO_WORKTREES/$branch"
local target="$worktree_dir/plugins/woocommerce"
if [ ! -d "$target" ]; then
echo "Error: Target directory does not exist: $target"
echo "Make sure the worktree exists and contains plugins/woocommerce"
exit 1
fi
# Remove existing symlink if it exists
if [ -L "$WOO_WP_PLUGIN" ] || [ -e "$WOO_WP_PLUGIN" ]; then
echo "Removing existing symlink/directory..."
rm -f "$WOO_WP_PLUGIN"
fi
# Create parent directory if needed
mkdir -p "$(dirname "$WOO_WP_PLUGIN")"
ln -s "$target" "$WOO_WP_PLUGIN"
echo "✓ Symlink updated:"
echo " $WOO_WP_PLUGIN -> $target"
}
cmd_print() {
if [ ! -L "$WOO_WP_PLUGIN" ] && [ ! -e "$WOO_WP_PLUGIN" ]; then
echo "No symlink exists at: $WOO_WP_PLUGIN"
exit 1
fi
if [ ! -L "$WOO_WP_PLUGIN" ]; then
echo "Error: $WOO_WP_PLUGIN exists but is not a symlink"
exit 1
fi
local target=$(readlink "$WOO_WP_PLUGIN")
echo "Plugin symlink: $WOO_WP_PLUGIN"
echo "Points to: $target"
# Determine if it's main repo or a worktree
if [[ "$target" == "$WOO_CORE" ]]; then
echo "Type: Main repository"
elif [[ "$target" == $WOO_WORKTREES/* ]]; then
# Extract branch name from path
local branch_path="${target#$WOO_WORKTREES/}"
local branch="${branch_path%/plugins/woocommerce}"
echo "Type: Worktree"
echo "Branch: $branch"
else
echo "Type: Unknown (not in expected location)"
fi
}
cmd_reset() {
local target="$WOO_CORE"
if [ ! -d "$target" ]; then
echo "Error: Target directory does not exist: $target"
exit 1
fi
# Remove existing symlink if it exists
if [ -L "$WOO_WP_PLUGIN" ] || [ -e "$WOO_WP_PLUGIN" ]; then
echo "Removing existing symlink/directory..."
rm -f "$WOO_WP_PLUGIN"
fi
# Create parent directory if needed
mkdir -p "$(dirname "$WOO_WP_PLUGIN")"
ln -s "$target" "$WOO_WP_PLUGIN"
echo "✓ Symlink reset to main repository:"
echo " $WOO_WP_PLUGIN -> $target"
}
# Main script logic
main() {
local command="$1"
shift || true
case "$command" in
a|add)
cmd_add "$@"
;;
d|del)
cmd_del "$@"
;;
s|set)
cmd_set "$@"
;;
p|print)
cmd_print "$@"
;;
r|reset)
cmd_reset "$@"
;;
-h|--help|help|"")
show_help
;;
*)
echo "Error: Unknown command: $command"
echo ""
show_help
exit 1
;;
esac
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment