Last active
December 5, 2025 09:29
-
-
Save mrtysn/04e0c8e1f29668203d7d23fbb05ab630 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Pull of Wonders - Unity project sync script | |
| # Cleans local changes, pulls latest code, and updates the foundation submodule | |
| # | |
| # Usage: pull_of_wonders [OPTIONS] | |
| # | |
| # Interactive Mode (default): | |
| # Run without arguments for a wizard-based interface: | |
| # 1) Sync to development | |
| # 2) Sync to a release branch (with branch picker) | |
| # 3) Just clean local changes (no pull) | |
| # 4) Cancel | |
| # | |
| # Options: | |
| # --auto-discard Automatically discard all local changes without prompting | |
| # --yes, -y Automatically answer yes to all prompts (uses defaults) | |
| # --force-fetch Force fetch even if recent fetch detected | |
| # --branch=NAME Target branch for main repository (default: development) | |
| # --submodule-branch=NAME Target branch for submodule (default: main) | |
| # --auto-release Skip wizard, go straight to release branch picker | |
| # --release-variants Include variant branches (hotfix, debug, etc.) in release picker | |
| # --help, -h Show help message | |
| # | |
| # Examples: | |
| # pull_of_wonders # Interactive wizard | |
| # pull_of_wonders --yes # Auto-sync to development with defaults | |
| # pull_of_wonders --auto-release # Pick a release branch | |
| # pull_of_wonders --auto-release -y # Auto-pick latest release branch | |
| # Set project directory | |
| PROJECT_DIR="/Users/mert/dev/merge-of-wonders" # Default path | |
| # Set defaults for automatic operation | |
| AUTO_DISCARD=false | |
| AUTO_YES=false | |
| FAST_MODE=true # Use cached data when possible (5 min threshold) | |
| TARGET_BRANCH="development" # Default main repository branch | |
| SUBMODULE_BRANCH="main" # Default submodule branch | |
| AUTO_RELEASE=false # Automatically detect latest release branch | |
| INCLUDE_RELEASE_VARIANTS=false # Include variant branches (hotfix, debug, etc.) | |
| # Function to detect the latest release branch | |
| detect_latest_release() { | |
| local include_variants=$1 | |
| echo "π Detecting release branches..." | |
| # Only fetch release branches that were updated recently (last 30 days) | |
| # This is much faster than fetching all branches | |
| # Note: With a 2-week release cycle, 30 days covers ~2 releases with buffer | |
| echo "β‘ Fetching recently updated release branches (last 30 days)..." | |
| # Fetch only release branches from remote (much faster than fetching everything) | |
| git fetch origin 'refs/heads/release-*:refs/remotes/origin/release-*' --prune 2>/dev/null || { | |
| echo "Warning: Could not fetch branches, using local branch information" | |
| } | |
| # Get release branches with their last commit date, sorted by commit date | |
| local branches_with_dates=$(git for-each-ref \ | |
| --sort=-committerdate \ | |
| --format='%(refname:short)|%(committerdate:iso8601)' \ | |
| 'refs/remotes/origin/release-*' 2>/dev/null) | |
| if [ -z "$branches_with_dates" ]; then | |
| echo "β No release branches found" | |
| return 1 | |
| fi | |
| # Month name to number mapping (bash 3.x compatible - no associative arrays) | |
| month_to_num() { | |
| case "$1" in | |
| jan) echo 1 ;; feb) echo 2 ;; mar) echo 3 ;; apr) echo 4 ;; | |
| may) echo 5 ;; jun) echo 6 ;; jul) echo 7 ;; aug) echo 8 ;; | |
| sep|sept) echo 9 ;; oct) echo 10 ;; nov) echo 11 ;; dec) echo 12 ;; | |
| *) echo "" ;; | |
| esac | |
| } | |
| # Arrays to store branch info | |
| local branch_names=() | |
| local branch_times=() | |
| local branch_dates=() | |
| local current_year=$(date +%Y) | |
| local current_month=$(date +%-m) | |
| while IFS='|' read -r branch commit_date; do | |
| # Remove 'origin/' prefix | |
| branch=$(echo "$branch" | sed 's/^origin\///') | |
| # Skip empty lines | |
| [ -z "$branch" ] && continue | |
| # Extract the commit timestamp for filtering | |
| local commit_timestamp=$(date -j -f "%Y-%m-%d %H:%M:%S %z" "$commit_date" "+%s" 2>/dev/null || \ | |
| date -d "$commit_date" "+%s" 2>/dev/null) | |
| local now_timestamp=$(date +%s) | |
| local days_ago=$(( (now_timestamp - commit_timestamp) / 86400 )) | |
| # Only show branches updated in last 30 days | |
| if [ $days_ago -gt 30 ]; then | |
| continue | |
| fi | |
| # Check if this is a variant branch | |
| local is_variant=false | |
| if echo "$branch" | grep -qE -- "-(hotfix|debug|levels|dev-test|VFX|soil-anim|tutorial|addressable)"; then | |
| is_variant=true | |
| fi | |
| # Skip variants if not including them | |
| if [ "$is_variant" = true ] && [ "$include_variants" = false ]; then | |
| continue | |
| fi | |
| # Format the relative time | |
| local time_str | |
| if [ $days_ago -eq 0 ]; then | |
| time_str="today" | |
| elif [ $days_ago -eq 1 ]; then | |
| time_str="yesterday" | |
| elif [ $days_ago -lt 7 ]; then | |
| time_str="${days_ago} days ago" | |
| elif [ $days_ago -lt 30 ]; then | |
| local weeks=$((days_ago / 7)) | |
| time_str="${weeks} week(s) ago" | |
| else | |
| local months=$((days_ago / 30)) | |
| time_str="${months} month(s) ago" | |
| fi | |
| # Extract base branch name for date parsing | |
| local base_branch=$(echo "$branch" | grep -oE "release-[a-z]+[0-9]+" | head -1) | |
| local branch_date=0 | |
| if [ -n "$base_branch" ]; then | |
| if [[ "$base_branch" =~ release-([a-z]+)([0-9]+) ]]; then | |
| local month_name="${BASH_REMATCH[1]}" | |
| local day="${BASH_REMATCH[2]}" | |
| local month_num=$(month_to_num "$month_name") | |
| if [ -n "$month_num" ]; then | |
| day=$(printf "%02d" $day) | |
| local branch_year=$current_year | |
| if [ $month_num -gt $current_month ] && [ $((month_num - current_month)) -gt 6 ]; then | |
| branch_year=$((current_year - 1)) | |
| fi | |
| branch_date=$(printf "%04d%02d%02d" $branch_year $month_num $day) | |
| fi | |
| fi | |
| fi | |
| branch_names+=("$branch") | |
| branch_times+=("$time_str") | |
| branch_dates+=("$branch_date") | |
| done <<< "$branches_with_dates" | |
| local count=${#branch_names[@]} | |
| if [ $count -eq 0 ]; then | |
| echo "β No recently active release branches found (last 30 days)" | |
| return 1 | |
| fi | |
| # Find the latest branch by date (for default selection) | |
| local latest_idx=0 | |
| local latest_date=0 | |
| for i in "${!branch_dates[@]}"; do | |
| if [ "${branch_dates[$i]}" -gt "$latest_date" ]; then | |
| latest_date="${branch_dates[$i]}" | |
| latest_idx=$i | |
| fi | |
| done | |
| # Display menu | |
| echo "" | |
| echo "π Select a release branch:" | |
| echo "" | |
| for i in "${!branch_names[@]}"; do | |
| local num=$((i + 1)) | |
| local marker="" | |
| if [ $i -eq $latest_idx ]; then | |
| marker=" [latest]" | |
| fi | |
| # Format the date as ISO (YYYY-MM-DD) from YYYYMMDD | |
| local raw_date="${branch_dates[$i]}" | |
| local iso_date="" | |
| if [ -n "$raw_date" ] && [ "$raw_date" != "0" ]; then | |
| iso_date="${raw_date:0:4}-${raw_date:4:2}-${raw_date:6:2}" | |
| fi | |
| echo " $num) ${branch_names[$i]} ($iso_date, ${branch_times[$i]})$marker" | |
| done | |
| echo "" | |
| local default_num=$((latest_idx + 1)) | |
| if [ "$AUTO_YES" = true ]; then | |
| RELEASE_CHOICE="$default_num" | |
| echo "Auto-selecting: ${branch_names[$latest_idx]} (--yes flag is set)" | |
| else | |
| read -p "Choice [$default_num]: " RELEASE_CHOICE | |
| RELEASE_CHOICE=${RELEASE_CHOICE:-$default_num} | |
| fi | |
| # Validate choice | |
| if ! [[ "$RELEASE_CHOICE" =~ ^[0-9]+$ ]] || [ "$RELEASE_CHOICE" -lt 1 ] || [ "$RELEASE_CHOICE" -gt $count ]; then | |
| echo "β Invalid choice" | |
| return 1 | |
| fi | |
| local selected_idx=$((RELEASE_CHOICE - 1)) | |
| TARGET_BRANCH="${branch_names[$selected_idx]}" | |
| echo "" | |
| echo "β Selected: $TARGET_BRANCH" | |
| return 0 | |
| } | |
| # Parse arguments | |
| while [[ $# -gt 0 ]]; do | |
| case $1 in | |
| --auto-discard) | |
| AUTO_DISCARD=true | |
| shift | |
| ;; | |
| --yes|-y) | |
| AUTO_YES=true | |
| shift | |
| ;; | |
| --force-fetch) | |
| FAST_MODE=false | |
| shift | |
| ;; | |
| --branch=*) | |
| TARGET_BRANCH="${1#*=}" | |
| shift | |
| ;; | |
| --submodule-branch=*) | |
| SUBMODULE_BRANCH="${1#*=}" | |
| shift | |
| ;; | |
| --auto-release) | |
| AUTO_RELEASE=true | |
| shift | |
| ;; | |
| --release-variants) | |
| INCLUDE_RELEASE_VARIANTS=true | |
| shift | |
| ;; | |
| --help|-h) | |
| echo "Pull of Wonders - Unity project sync script" | |
| echo "" | |
| echo "Usage: pull_of_wonders [OPTIONS]" | |
| echo "" | |
| echo "INTERACTIVE MODE (default):" | |
| echo " Run without arguments for a wizard-based interface:" | |
| echo " 1) Sync to development" | |
| echo " 2) Sync to a release branch (with branch picker)" | |
| echo " 3) Just clean local changes (no pull)" | |
| echo " 4) Cancel" | |
| echo "" | |
| echo "OPTIONS:" | |
| echo " --yes, -y Auto-confirm all prompts (uses defaults)" | |
| echo " --auto-discard Auto-discard local changes without prompting" | |
| echo " --auto-release Skip wizard, go straight to release branch picker" | |
| echo " --release-variants Include variant branches (hotfix, debug, etc.)" | |
| echo " --force-fetch Force fetch even if recent fetch detected" | |
| echo " --branch=NAME Target branch for main repo (default: development)" | |
| echo " --submodule-branch=NAME Target branch for submodule (default: main)" | |
| echo " --help, -h Show this help message" | |
| echo "" | |
| echo "EXAMPLES:" | |
| echo " pull_of_wonders # Interactive wizard" | |
| echo " pull_of_wonders --yes # Auto-sync to development" | |
| echo " pull_of_wonders --auto-release # Pick a release branch" | |
| echo " pull_of_wonders --auto-release -y # Auto-pick latest release" | |
| echo " pull_of_wonders --branch=feature/x # Sync to specific branch" | |
| exit 0 | |
| ;; | |
| --fast) | |
| echo "Warning: --fast is deprecated. Caching is enabled by default, use --force-fetch to disable." | |
| shift | |
| ;; | |
| -*) | |
| echo "Unknown option $1" | |
| echo "Available options: --auto-discard, --yes/-y, --force-fetch, --branch=NAME, --submodule-branch=NAME, --auto-release, --release-variants, --help/-h" | |
| echo "Use --help for detailed usage information." | |
| exit 1 | |
| ;; | |
| *) | |
| # Positional argument - treat as project directory | |
| PROJECT_DIR="$1" | |
| shift | |
| ;; | |
| esac | |
| done | |
| # Function to check if fetch is recent (less than 5 minutes ago) | |
| # Works for both regular repos and submodules | |
| is_fetch_recent() { | |
| local fetch_head_file | |
| # Check if we're in a submodule (has .git file instead of .git directory) | |
| if [ -f ".git" ]; then | |
| # Submodule: .git is a file pointing to the real git directory | |
| local git_dir=$(cat .git | sed 's/gitdir: //') | |
| fetch_head_file="$git_dir/FETCH_HEAD" | |
| elif [ -d ".git" ]; then | |
| # Regular repo: .git is a directory | |
| fetch_head_file=".git/FETCH_HEAD" | |
| else | |
| # Not in a git repo | |
| return 1 | |
| fi | |
| if [ -f "$fetch_head_file" ]; then | |
| # Use a more reliable method to get file modification time | |
| # Try different approaches for macOS | |
| local fetch_time | |
| # Method 1: Try stat with -t flag (should work on most macOS versions) | |
| fetch_time=$(stat -t "%s" -f "%m" "$fetch_head_file" 2>/dev/null) | |
| # Method 2: If that fails, try using ls and date | |
| if [ -z "$fetch_time" ] || ! [[ "$fetch_time" =~ ^[0-9]+$ ]]; then | |
| # Get the modification time using ls and convert to epoch | |
| local mod_time_str=$(ls -l "$fetch_head_file" | awk '{print $6" "$7" "$8}') | |
| fetch_time=$(date -j -f "%b %d %H:%M" "$mod_time_str" "+%s" 2>/dev/null) | |
| fi | |
| # Method 3: If still failing, use Python as fallback | |
| if [ -z "$fetch_time" ] || ! [[ "$fetch_time" =~ ^[0-9]+$ ]]; then | |
| fetch_time=$(python3 -c "import os; print(int(os.path.getmtime('$fetch_head_file')))" 2>/dev/null) | |
| fi | |
| if [ -n "$fetch_time" ] && [[ "$fetch_time" =~ ^[0-9]+$ ]]; then | |
| local current_time=$(date +%s) | |
| local diff=$((current_time - fetch_time)) | |
| if [ $diff -lt 300 ]; then # 5 minutes = 300 seconds | |
| return 0 # Recent | |
| fi | |
| fi | |
| fi | |
| return 1 # Not recent or doesn't exist | |
| } | |
| echo "===== Pull of Wonders =====" | |
| echo "Project: $PROJECT_DIR" | |
| echo "" | |
| cd "$PROJECT_DIR" || { echo "Error: Could not change to directory $PROJECT_DIR"; exit 1; } | |
| # Verify we're in a git repository | |
| if ! git rev-parse --is-inside-work-tree > /dev/null 2>&1; then | |
| echo "Error: Not in a git repository" | |
| exit 1 | |
| fi | |
| # Function to check for any local changes (staged, unstaged, or untracked that matter) | |
| has_local_changes() { | |
| # Check for staged changes | |
| if ! git diff --cached --quiet; then | |
| return 0 # Has changes | |
| fi | |
| # Check for unstaged changes | |
| if ! git diff --quiet; then | |
| return 0 # Has changes | |
| fi | |
| # Check for untracked files that aren't gitignored | |
| if [ -n "$(git ls-files --others --exclude-standard)" ]; then | |
| return 0 # Has changes | |
| fi | |
| return 1 # No changes | |
| } | |
| # Function to handle local changes | |
| handle_local_changes() { | |
| local context="$1" # "main" or "submodule" | |
| local return_to_dir="$2" # Directory to return to on error (for submodules) | |
| echo "You have local changes in $context that need to be handled" | |
| if [ "$AUTO_DISCARD" = true ]; then | |
| CHANGE_OPTION=1 | |
| echo "Auto-discarding all local changes (--auto-discard flag is set)" | |
| else | |
| echo " 1) Discard all changes [default]" | |
| echo " 2) Stash changes (save for later)" | |
| echo " 3) Keep changes (may cause conflicts)" | |
| echo "" | |
| read -p "Choice [1]: " CHANGE_OPTION | |
| CHANGE_OPTION=${CHANGE_OPTION:-1} | |
| fi | |
| case $CHANGE_OPTION in | |
| 1) | |
| echo "Discarding all local changes in $context..." | |
| git reset --hard HEAD || { | |
| echo "Error: Could not reset changes in $context" | |
| [ -n "$return_to_dir" ] && cd "$return_to_dir" | |
| exit 1 | |
| } | |
| git clean -fd || { | |
| echo "Error: Could not clean untracked files in $context" | |
| [ -n "$return_to_dir" ] && cd "$return_to_dir" | |
| exit 1 | |
| } | |
| echo "β All local changes discarded" | |
| return 0 | |
| ;; | |
| 2) | |
| git stash push -m "Auto-stash from pull_of_wonders - $context" || { | |
| echo "Error: Could not stash changes in $context" | |
| [ -n "$return_to_dir" ] && cd "$return_to_dir" | |
| exit 1 | |
| } | |
| echo "β Changes stashed" | |
| return 1 # Return 1 to indicate stash was created | |
| ;; | |
| 3) | |
| echo "β Proceeding with local changes (may cause conflicts)" | |
| return 2 # Return 2 to indicate changes were kept | |
| ;; | |
| *) | |
| echo "Invalid option, discarding changes..." | |
| git reset --hard HEAD || { | |
| echo "Error: Could not reset changes in $context" | |
| [ -n "$return_to_dir" ] && cd "$return_to_dir" | |
| exit 1 | |
| } | |
| git clean -fd || { | |
| echo "Error: Could not clean untracked files in $context" | |
| [ -n "$return_to_dir" ] && cd "$return_to_dir" | |
| exit 1 | |
| } | |
| echo "β All local changes discarded" | |
| return 0 | |
| ;; | |
| esac | |
| } | |
| CURRENT_BRANCH=$(git branch --show-current) | |
| echo "Current branch: $CURRENT_BRANCH" | |
| echo "" | |
| # Main mode selection (unless flags override) | |
| if [ "$AUTO_RELEASE" = false ] && [ "$TARGET_BRANCH" = "development" ]; then | |
| echo "What would you like to do?" | |
| echo "" | |
| echo " 1) Sync to development [default]" | |
| echo " 2) Sync to a release branch" | |
| echo " 3) Just clean local changes (no pull)" | |
| echo " 4) Cancel" | |
| echo "" | |
| read -p "Choice [1]: " MODE_CHOICE | |
| MODE_CHOICE=${MODE_CHOICE:-1} | |
| case $MODE_CHOICE in | |
| 1) | |
| TARGET_BRANCH="development" | |
| ;; | |
| 2) | |
| detect_latest_release "$INCLUDE_RELEASE_VARIANTS" || { | |
| echo "Failed to detect release branch, exiting" | |
| exit 1 | |
| } | |
| ;; | |
| 3) | |
| # Just clean mode - discard changes and exit | |
| echo "" | |
| echo "π§Ή Cleaning local changes only (no pull)..." | |
| if has_local_changes; then | |
| handle_local_changes "main repository" | |
| else | |
| echo "β Main repository is already clean" | |
| fi | |
| # Also clean submodule | |
| local sub_path="" | |
| if [ -d "Assets/Foundation" ] && [ -e "Assets/Foundation/.git" -o -d "$PROJECT_DIR/.git/modules/Assets/Foundation" ]; then | |
| sub_path="Assets/Foundation" | |
| elif [ -d "foundation" ] && [ -e "foundation/.git" ]; then | |
| sub_path="foundation" | |
| elif [ -d "Foundation" ] && [ -e "Foundation/.git" ]; then | |
| sub_path="Foundation" | |
| fi | |
| if [ -n "$sub_path" ]; then | |
| echo "" | |
| echo "π§Ή Cleaning submodule at $sub_path..." | |
| cd "$sub_path" || { echo "Error: Could not change to submodule directory"; exit 1; } | |
| if has_local_changes; then | |
| handle_local_changes "foundation submodule" "$PROJECT_DIR" | |
| else | |
| echo "β Submodule is already clean" | |
| fi | |
| cd "$PROJECT_DIR" | |
| fi | |
| echo "" | |
| echo "===== Done =====" | |
| exit 0 | |
| ;; | |
| 4|*) | |
| if [ "$MODE_CHOICE" != "4" ] && [ -n "$MODE_CHOICE" ]; then | |
| echo "Invalid choice." | |
| fi | |
| echo "Operation canceled" | |
| exit 0 | |
| ;; | |
| esac | |
| elif [ "$AUTO_RELEASE" = true ]; then | |
| detect_latest_release "$INCLUDE_RELEASE_VARIANTS" || { | |
| echo "Failed to detect release branch, falling back to default branch" | |
| } | |
| fi | |
| # Display git status before cleaning | |
| echo "Current git status:" | |
| STATUS_OUTPUT=$(git status --short) | |
| if [ -z "$STATUS_OUTPUT" ]; then | |
| echo "β Working directory is clean" | |
| else | |
| echo "$STATUS_OUTPUT" | |
| fi | |
| echo "------------------------" | |
| # Check if branch switch is needed | |
| NEEDS_BRANCH_SWITCH=false | |
| if [ "$CURRENT_BRANCH" != "$TARGET_BRANCH" ]; then | |
| NEEDS_BRANCH_SWITCH=true | |
| else | |
| echo "β Already on target branch ($TARGET_BRANCH)" | |
| fi | |
| # Handle local changes first if they exist | |
| MAIN_STASH_CREATED=false | |
| if has_local_changes; then | |
| handle_local_changes "main repository" | |
| case $? in | |
| 1) MAIN_STASH_CREATED=true ;; | |
| 2) NEEDS_BRANCH_SWITCH=false ;; # Can't switch branches with conflicting changes | |
| esac | |
| fi | |
| # Handle branch switching if needed and safe | |
| if [ "$NEEDS_BRANCH_SWITCH" = true ]; then | |
| echo "" | |
| echo "π Branch: $CURRENT_BRANCH β $TARGET_BRANCH" | |
| echo "" | |
| if [ "$AUTO_YES" = true ]; then | |
| BRANCH_CHOICE="1" | |
| echo "Auto-switching to $TARGET_BRANCH branch (--yes flag is set)" | |
| else | |
| echo "What would you like to do?" | |
| echo " 1) Switch to $TARGET_BRANCH and continue [default]" | |
| echo " 2) Stay on $CURRENT_BRANCH and continue" | |
| echo " 3) Cancel" | |
| echo "" | |
| read -p "Choice [1]: " BRANCH_CHOICE | |
| BRANCH_CHOICE=${BRANCH_CHOICE:-1} | |
| fi | |
| case $BRANCH_CHOICE in | |
| 1) | |
| git checkout "$TARGET_BRANCH" || { echo "Error: Could not switch to $TARGET_BRANCH branch"; exit 1; } | |
| ;; | |
| 2) | |
| echo "Staying on $CURRENT_BRANCH (will pull from $CURRENT_BRANCH instead)..." | |
| TARGET_BRANCH="$CURRENT_BRANCH" | |
| ;; | |
| 3|*) | |
| if [ "$BRANCH_CHOICE" != "3" ] && [ -n "$BRANCH_CHOICE" ]; then | |
| echo "Invalid choice. Canceling." | |
| else | |
| echo "Operation canceled" | |
| fi | |
| exit 0 | |
| ;; | |
| esac | |
| fi | |
| echo "Cleaning untracked meta files..." | |
| # List of patterns to clean (add more as needed) | |
| META_PATTERNS=( | |
| "Assets/Packages/Microsoft.*.meta" | |
| "Assets/Packages/System.*.meta" | |
| ) | |
| # Create a temporary file for git clean command | |
| TEMP_FILE=$(mktemp) | |
| for pattern in "${META_PATTERNS[@]}"; do | |
| git ls-files --others --exclude-standard | grep "$pattern" >> "$TEMP_FILE" | |
| done | |
| # If there are files to clean | |
| if [ -s "$TEMP_FILE" ]; then | |
| echo "The following files will be removed:" | |
| cat "$TEMP_FILE" | |
| # Confirm before deletion | |
| if [ "$AUTO_YES" = true ]; then | |
| CONFIRM="y" | |
| echo "Auto-confirming removal (--yes flag is set)" | |
| else | |
| read -p "Continue with removal? (y/n): " CONFIRM | |
| fi | |
| if [ "$CONFIRM" = "y" ]; then | |
| while IFS= read -r file; do | |
| rm -f "$file" | |
| echo "Removed: $file" | |
| done < "$TEMP_FILE" | |
| echo "Cleanup completed" | |
| else | |
| echo "Cleanup skipped" | |
| fi | |
| else | |
| echo "No matching untracked files found" | |
| fi | |
| rm -f "$TEMP_FILE" | |
| # Fetch optimization based on cache age | |
| SHOULD_FETCH=true | |
| if [ "$FAST_MODE" = true ]; then | |
| if is_fetch_recent; then | |
| echo "β‘ Using cached fetch data (< 5 min old, use --force-fetch for fresh data)" | |
| SHOULD_FETCH=false | |
| else | |
| echo "π Cache expired, fetching latest changes..." | |
| fi | |
| else | |
| echo "π Force fetch mode - always getting latest data" | |
| fi | |
| if [ "$SHOULD_FETCH" = true ]; then | |
| echo "Fetching latest changes..." | |
| git fetch origin || { echo "Error: Could not fetch from origin"; exit 1; } | |
| else | |
| echo "β Using cached fetch data" | |
| fi | |
| # Check if local and remote commits differ before pulling | |
| LOCAL_COMMIT=$(git rev-parse HEAD) | |
| REMOTE_COMMIT=$(git rev-parse "origin/$TARGET_BRANCH" 2>/dev/null || echo "unknown") | |
| if [ "$LOCAL_COMMIT" = "$REMOTE_COMMIT" ] && [ "$SHOULD_FETCH" = false ]; then | |
| echo "β Already up to date (skipping pull)" | |
| else | |
| echo "Pulling latest changes from origin/$TARGET_BRANCH..." | |
| git pull origin "$TARGET_BRANCH" || { echo "Error: Could not pull from origin/$TARGET_BRANCH"; exit 1; } | |
| fi | |
| # Pop the stash if we stashed changes | |
| if [ "$MAIN_STASH_CREATED" = true ]; then | |
| echo "Reapplying stashed changes..." | |
| git stash pop || { echo "Warning: Could not pop stash - you may need to resolve conflicts manually"; } | |
| echo "Stashed changes reapplied" | |
| fi | |
| # Update the foundation submodule | |
| echo "Checking for 'foundation' submodule..." | |
| # Check for different possible locations of the Foundation submodule | |
| if [ -d "foundation" ] && [ -e "foundation/.git" ]; then | |
| SUBMODULE_PATH="foundation" | |
| elif [ -d "Foundation" ] && [ -e "Foundation/.git" ]; then | |
| SUBMODULE_PATH="Foundation" | |
| elif [ -d "Assets/Foundation" ] && [ -e "Assets/Foundation/.git" -o -d "$PROJECT_DIR/.git/modules/Assets/Foundation" ]; then | |
| SUBMODULE_PATH="Assets/Foundation" | |
| else | |
| # Try to find the submodule using git | |
| SUBMODULE_PATH=$(git config --file .gitmodules --get-regexp path | grep -i foundation | sed 's/.*path = //') | |
| fi | |
| if [ -n "$SUBMODULE_PATH" ]; then | |
| echo "===== Updating 'foundation' submodule at path: $SUBMODULE_PATH =====" | |
| cd "$SUBMODULE_PATH" || { echo "Error: Could not change to foundation directory at $SUBMODULE_PATH"; exit 1; } | |
| # Verify we're in a git repository | |
| if ! git rev-parse --is-inside-work-tree > /dev/null 2>&1; then | |
| echo "Error: foundation is not a valid git repository" | |
| cd "$PROJECT_DIR" | |
| exit 1 | |
| fi | |
| # Get current branch of the submodule | |
| SUB_CURRENT_BRANCH=$(git branch --show-current) | |
| echo "Current foundation branch: $SUB_CURRENT_BRANCH" | |
| # Submodule fetch optimization | |
| SUB_SHOULD_FETCH=true | |
| if [ "$FAST_MODE" = true ]; then | |
| if is_fetch_recent; then | |
| echo "β‘ Using cached submodule data (< 5 min old)" | |
| SUB_SHOULD_FETCH=false | |
| else | |
| echo "π Submodule cache expired, fetching..." | |
| fi | |
| else | |
| echo "π Force fetching submodule data" | |
| fi | |
| if [ "$SUB_SHOULD_FETCH" = true ]; then | |
| echo "Fetching latest changes for the foundation submodule..." | |
| git fetch origin || { echo "Error: Could not fetch from origin in submodule"; cd "$PROJECT_DIR"; exit 1; } | |
| else | |
| echo "β Using cached submodule fetch data" | |
| fi | |
| # Handle submodule changes | |
| SUB_STASH_CREATED=false | |
| if has_local_changes; then | |
| handle_local_changes "foundation submodule" "$PROJECT_DIR" | |
| case $? in | |
| 1) SUB_STASH_CREATED=true ;; | |
| esac | |
| fi | |
| # Switch to target branch if not already on it | |
| if [ "$SUB_CURRENT_BRANCH" != "$SUBMODULE_BRANCH" ]; then | |
| echo "Switching to $SUBMODULE_BRANCH branch in foundation submodule..." | |
| git checkout "$SUBMODULE_BRANCH" || { echo "Error: Could not switch to $SUBMODULE_BRANCH branch in submodule"; cd "$PROJECT_DIR"; exit 1; } | |
| fi | |
| # Optimize submodule pull based on commit comparison | |
| SUB_LOCAL_COMMIT=$(git rev-parse HEAD) | |
| SUB_REMOTE_COMMIT=$(git rev-parse "origin/$SUBMODULE_BRANCH" 2>/dev/null || echo "unknown") | |
| if [ "$SUB_LOCAL_COMMIT" = "$SUB_REMOTE_COMMIT" ] && [ "$SUB_SHOULD_FETCH" = false ]; then | |
| echo "β Submodule already up to date (skipping pull)" | |
| else | |
| echo "Pulling latest changes from origin/$SUBMODULE_BRANCH for foundation submodule..." | |
| git pull origin "$SUBMODULE_BRANCH" || { echo "Error: Could not pull from origin/$SUBMODULE_BRANCH in submodule"; cd "$PROJECT_DIR"; exit 1; } | |
| fi | |
| # Pop the stash if we stashed changes | |
| if [ "$SUB_STASH_CREATED" = true ]; then | |
| echo "Reapplying stashed changes in submodule..." | |
| git stash pop || { echo "Warning: Could not pop stash in submodule - you may need to resolve conflicts manually"; } | |
| echo "Stashed changes reapplied in submodule" | |
| fi | |
| # Return to the main project directory | |
| cd "$PROJECT_DIR" || { echo "Error: Could not return to main project directory"; exit 1; } | |
| echo "Foundation submodule updated successfully" | |
| elif [ -f ".gitmodules" ] && grep -qi "foundation" ".gitmodules"; then | |
| echo "Foundation submodule exists but isn't initialized" | |
| if [ "$AUTO_YES" = true ]; then | |
| INIT_SUB="y" | |
| echo "Auto-initializing submodule (--yes flag is set)" | |
| else | |
| read -p "Do you want to initialize and update the submodule? (y/n): " INIT_SUB | |
| fi | |
| if [ "$INIT_SUB" = "y" ]; then | |
| # Extract the submodule path from .gitmodules | |
| SUBMODULE_PATH=$(git config --file .gitmodules --get-regexp path | grep -i foundation | sed 's/.*path = //') | |
| if [ -n "$SUBMODULE_PATH" ]; then | |
| echo "Initializing submodule at path: $SUBMODULE_PATH" | |
| git submodule update --init --recursive "$SUBMODULE_PATH" || { echo "Error: Could not initialize foundation submodule"; exit 1; } | |
| echo "Foundation submodule initialized and updated" | |
| else | |
| echo "Could not determine submodule path from .gitmodules" | |
| git submodule update --init --recursive || { echo "Error: Could not initialize all submodules"; exit 1; } | |
| echo "All submodules initialized and updated" | |
| fi | |
| fi | |
| else | |
| echo "No 'foundation' submodule found in this project (.gitmodules check)" | |
| # One final check - use git directly to list submodules | |
| if git submodule status | grep -qi "foundation"; then | |
| echo "Submodule detected through git submodule status" | |
| if [ "$AUTO_YES" = true ]; then | |
| INIT_SUB="y" | |
| echo "Auto-initializing all submodules (--yes flag is set)" | |
| else | |
| read -p "Do you want to initialize and update all submodules? (y/n): " INIT_SUB | |
| fi | |
| if [ "$INIT_SUB" = "y" ]; then | |
| git submodule update --init --recursive || { echo "Error: Could not initialize all submodules"; exit 1; } | |
| echo "All submodules initialized and updated" | |
| fi | |
| else | |
| echo "No 'foundation' submodule found in this project" | |
| fi | |
| fi | |
| echo "===== Operation completed successfully =====" | |
| echo "Your local branch is now up-to-date with origin/$TARGET_BRANCH" | |
| if [ -n "$SUBMODULE_PATH" ]; then | |
| echo "And foundation submodule at path '$SUBMODULE_PATH' is up-to-date with origin/$SUBMODULE_BRANCH" | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment