#!/usr/bin/env bash # sync-repos — Pull latest for all git repos under ~/dev/claude # Usage: # sync-repos # pull all repos # sync-repos --dryrun # show what would be pulled # sync-repos -n # same as --dryrun set -euo pipefail BASE_DIR="${HOME}/dev/claude" DRYRUN=false if [[ "${1:-}" == "--dryrun" || "${1:-}" == "-n" ]]; then DRYRUN=true fi GREEN='\033[0;32m' YELLOW='\033[0;33m' RED='\033[0;31m' DIM='\033[2m' RESET='\033[0m' pulled=0 skipped=0 failed=0 dirty=0 # Find all git repos (look for .git dirs, max depth 3 to cover projects/ and octopus/) while IFS= read -r gitdir; do repo_dir="$(dirname "$gitdir")" repo_name="${repo_dir#"${BASE_DIR}/"}" # Skip if no remote configured if ! git -C "$repo_dir" remote get-url origin &>/dev/null; then printf "${DIM} skip %-40s no remote${RESET}\n" "$repo_name" skipped=$((skipped + 1)) continue fi # Check for uncommitted changes if ! git -C "$repo_dir" diff --quiet 2>/dev/null || ! git -C "$repo_dir" diff --cached --quiet 2>/dev/null; then has_changes=true else has_changes=false fi if $DRYRUN; then branch="$(git -C "$repo_dir" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")" if $has_changes; then printf "${YELLOW} dirty %-40s %s (has uncommitted changes)${RESET}\n" "$repo_name" "$branch" dirty=$((dirty + 1)) else printf "${GREEN} pull %-40s %s${RESET}\n" "$repo_name" "$branch" pulled=$((pulled + 1)) fi continue fi # Pull with --ff-only to avoid creating merge commits branch="$(git -C "$repo_dir" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")" output="$(git -C "$repo_dir" pull --ff-only 2>&1)" && rc=0 || rc=$? if [[ $rc -eq 0 ]]; then if echo "$output" | grep -q "Already up to date"; then printf "${DIM} ok %-40s %s (up to date)${RESET}\n" "$repo_name" "$branch" else printf "${GREEN} pull %-40s %s${RESET}\n" "$repo_name" "$branch" fi pulled=$((pulled + 1)) else printf "${RED} FAIL %-40s %s${RESET}\n" "$repo_name" "$branch" printf "${DIM} %s${RESET}\n" "$output" failed=$((failed + 1)) fi done < <(find "$BASE_DIR" -maxdepth 4 -name .git -type d 2>/dev/null | sort) echo "" if $DRYRUN; then echo "Dryrun: ${pulled} would pull, ${dirty} dirty, ${skipped} skipped" else echo "Done: ${pulled} pulled, ${failed} failed, ${skipped} skipped" fi [[ $failed -eq 0 ]]