#!/bin/bash # Backup main branch with smart duplicate detection # Also pushes any unpushed main commits before backing up set -e # Exit on any error # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' BOLD='\033[1m' DIM='\033[2m' NC='\033[0m' # No Color # Summary table data declare -a SUMMARY_REPO declare -a SUMMARY_STATUS declare -a SUMMARY_COMMITS declare -a SUMMARY_LAST_MSG declare -a SUMMARY_BRANCH declare -a SUMMARY_PUSHED SUMMARY_IDX=0 echo -e "${BOLD}🔄 Starting main branch backup...${NC}" echo -e "${DIM}$(date '+%Y-%m-%d %H:%M:%S')${NC}" # Function to backup a single repository backup_repo() { local repo_path=$1 local repo_name=$(basename "$repo_path") echo -e "\n${YELLOW}━━━ $repo_name ━━━${NC}" cd "$repo_path" # Check if this is a git repository if ! git rev-parse --git-dir > /dev/null 2>&1; then echo -e "${RED}❌ Not a git repository!${NC}" SUMMARY_REPO[$SUMMARY_IDX]="$repo_name" SUMMARY_STATUS[$SUMMARY_IDX]="❌ Not a git repo" SUMMARY_COMMITS[$SUMMARY_IDX]="-" SUMMARY_LAST_MSG[$SUMMARY_IDX]="-" SUMMARY_BRANCH[$SUMMARY_IDX]="-" SUMMARY_PUSHED[$SUMMARY_IDX]="-" SUMMARY_IDX=$((SUMMARY_IDX + 1)) return 1 fi # Ensure we're on main CURRENT_BRANCH=$(git branch --show-current 2>/dev/null || echo "") if [ "$CURRENT_BRANCH" != "main" ]; then echo " Switching to main branch..." git switch main 2>/dev/null || git checkout main fi # Check if working directory is clean if [ -n "$(git status --porcelain)" ]; then echo -e " ${RED}⚠️ Working directory not clean — skipping${NC}" SUMMARY_REPO[$SUMMARY_IDX]="$repo_name" SUMMARY_STATUS[$SUMMARY_IDX]="⚠️ Dirty worktree" SUMMARY_COMMITS[$SUMMARY_IDX]="-" SUMMARY_LAST_MSG[$SUMMARY_IDX]="-" SUMMARY_BRANCH[$SUMMARY_IDX]="-" SUMMARY_PUSHED[$SUMMARY_IDX]="-" SUMMARY_IDX=$((SUMMARY_IDX + 1)) return 1 fi # Gather repo stats local last_msg=$(git log -1 --pretty=format:'%s' 2>/dev/null | head -c 50) # Push any unpushed main commits first local ahead_count=0 ahead_count=$(git rev-list --count origin/main..HEAD 2>/dev/null || echo "0") local pushed_main="—" if [ "$ahead_count" -gt 0 ]; then echo -e " ${CYAN}Pushing $ahead_count unpushed commit(s) on main...${NC}" if git push origin main 2>/dev/null; then pushed_main="✅ $ahead_count pushed" echo -e " ${GREEN}✅ Main pushed${NC}" else pushed_main="❌ push failed" echo -e " ${RED}❌ Push failed${NC}" fi else pushed_main="up to date" fi # Fetch to ensure we have latest remote backup refs git fetch origin 'refs/heads/backup/*:refs/remotes/origin/backup/*' 2>/dev/null || true # Check if backup is needed (compare HEAD with latest backup) LATEST_BACKUP=$(git branch -r --sort=-committerdate 2>/dev/null | grep 'origin/backup/main-' | head -1 | sed 's|^[[:space:]]*origin/||' || true) # Count commits since last backup local commits_since=0 if [ -n "$LATEST_BACKUP" ]; then commits_since=$(git rev-list --count "origin/$LATEST_BACKUP"..HEAD 2>/dev/null || echo "0") else commits_since=$(git rev-list --count HEAD 2>/dev/null || echo "0") fi if [ -n "$LATEST_BACKUP" ]; then MAIN_COMMIT=$(git rev-parse HEAD 2>/dev/null || echo "") BACKUP_COMMIT=$(git rev-parse "origin/$LATEST_BACKUP" 2>/dev/null || echo "") if [ -n "$MAIN_COMMIT" ] && [ "$MAIN_COMMIT" = "$BACKUP_COMMIT" ]; then echo -e " ${GREEN}✅ Already backed up → $LATEST_BACKUP${NC}" SUMMARY_REPO[$SUMMARY_IDX]="$repo_name" SUMMARY_STATUS[$SUMMARY_IDX]="✅ Already backed up" SUMMARY_COMMITS[$SUMMARY_IDX]="0" SUMMARY_LAST_MSG[$SUMMARY_IDX]="$last_msg" SUMMARY_BRANCH[$SUMMARY_IDX]="$LATEST_BACKUP" SUMMARY_PUSHED[$SUMMARY_IDX]="$pushed_main" SUMMARY_IDX=$((SUMMARY_IDX + 1)) return 0 fi fi # Create new backup BACKUP_BRANCH="backup/main-$(date +%Y-%m-%d-%H%M%S)" echo -e " Creating: $BACKUP_BRANCH" git checkout -b "$BACKUP_BRANCH" local backup_status="" if git push -u origin "$BACKUP_BRANCH" 2>/dev/null; then backup_status="✅ New backup" echo -e " ${GREEN}✅ Pushed to remote${NC}" else backup_status="⚠️ Local only" echo -e " ${YELLOW}⚠️ Local only (push failed)${NC}" fi git checkout main 2>/dev/null # Cleanup old backups (keep last 7) local old_branches=$(git branch -r --sort=-committerdate 2>/dev/null | grep 'origin/backup/main-' | tail -n +8 || true) if [ -n "$old_branches" ]; then echo -e " ${DIM}Cleaning up old backups...${NC}" while IFS= read -r branch; do branch=$(echo "$branch" | sed 's|^[[:space:]]*origin/||') if [ -n "$branch" ]; then echo -e " ${DIM} Deleting: $branch${NC}" git push origin --delete "$branch" 2>/dev/null || true fi done <<< "$old_branches" fi SUMMARY_REPO[$SUMMARY_IDX]="$repo_name" SUMMARY_STATUS[$SUMMARY_IDX]="$backup_status" SUMMARY_COMMITS[$SUMMARY_IDX]="$commits_since" SUMMARY_LAST_MSG[$SUMMARY_IDX]="$last_msg" SUMMARY_BRANCH[$SUMMARY_IDX]="$BACKUP_BRANCH" SUMMARY_PUSHED[$SUMMARY_IDX]="$pushed_main" SUMMARY_IDX=$((SUMMARY_IDX + 1)) } # Backup all repositories (resolve from script location) WORKSPACE_DIR="${WORKSPACE_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}" REPOS=( "$WORKSPACE_DIR/learning_ai_common_plat" "$WORKSPACE_DIR/learning_voice_ai_agent" "$WORKSPACE_DIR/learning_multimodal_memory_agents" ) for repo in "${REPOS[@]}"; do if [ -d "$repo" ]; then backup_repo "$repo" || true else echo -e "${YELLOW}Repository not found: $repo${NC}" SUMMARY_REPO[$SUMMARY_IDX]="$(basename "$repo")" SUMMARY_STATUS[$SUMMARY_IDX]="❌ Not found" SUMMARY_COMMITS[$SUMMARY_IDX]="-" SUMMARY_LAST_MSG[$SUMMARY_IDX]="-" SUMMARY_BRANCH[$SUMMARY_IDX]="-" SUMMARY_PUSHED[$SUMMARY_IDX]="-" SUMMARY_IDX=$((SUMMARY_IDX + 1)) fi done # Print summary table echo "" echo -e "${BOLD}┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐${NC}" echo -e "${BOLD}│ BACKUP SUMMARY $(date '+%Y-%m-%d %H:%M') │${NC}" echo -e "${BOLD}├──────────────────────────────────┬──────────────────────┬─────────┬────────────────┬──────────────────────────────────┤${NC}" printf "${BOLD}│ %-32s │ %-20s │ %7s │ %-14s │ %-32s │${NC}\n" "Repository" "Backup Status" "New" "Main Push" "Last Commit" echo -e "${BOLD}├──────────────────────────────────┼──────────────────────┼─────────┼────────────────┼──────────────────────────────────┤${NC}" for ((i=0; i