#!/bin/bash # Sync (copy) this project to another location, excluding git-ignored files, # the .git directory, and local-only cruft. # # Respects every .gitignore in the tree (root and nested, e.g. ui/.gitignore) # via rsync's per-directory merge filter, so node_modules/, build output, # output/ runs, samples/, local-run.sh, etc. are never copied. Tracked files # (including the .gitignore files themselves) are copied so the destination is # a clean, working copy of the project. # # Usage: # ctrl/sync.sh [-n] [-d] # # local path or rsync target # (e.g. ../meetus-copy, /mnt/backup/meetus, host:~/meetus) # -n dry run — list what would change, copy nothing # -d mirror mode — also delete files in that are # not in the source (destructive; off by default) # # Examples: # ctrl/sync.sh ../meetus-copy # ctrl/sync.sh -n /mnt/backup/meetus # preview # ctrl/sync.sh -d server:~/projects/meetus # exact mirror set -euo pipefail PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)" usage() { # Print the header comment block (skip shebang, stop at first code line). awk 'NR==1{next} /^#/{sub(/^# ?/,""); print; next} {exit}' "$0" } DRY_RUN="" DELETE="" DEST="" while [ $# -gt 0 ]; do case "$1" in -n) DRY_RUN="--dry-run" ;; -d) DELETE="--delete" ;; -h|--help) usage; exit 0 ;; --) shift; break ;; -*) echo "Unknown option: $1" >&2; usage >&2; exit 1 ;; *) if [ -z "$DEST" ]; then DEST="$1"; else echo "Unexpected argument: $1" >&2; exit 1; fi ;; esac shift done # Any trailing args after -- are the destination too (support one). if [ -z "$DEST" ] && [ $# -gt 0 ]; then DEST="$1"; fi if [ -z "$DEST" ]; then echo "Error: destination required." >&2 echo "Usage: ctrl/sync.sh [-n] [-d] " >&2 exit 1 fi echo "Syncing project -> $DEST" [ -n "$DRY_RUN" ] && echo " (dry run: no changes will be made)" [ -n "$DELETE" ] && echo " (mirror mode: extraneous files in destination will be DELETED)" # --filter ':- .gitignore' reads a .gitignore in every directory rsync visits # and applies its ignore patterns to that subtree. rsync -ah --info=stats1 $DRY_RUN $DELETE \ --exclude='.git/' \ --exclude='.DS_Store' \ --filter=':- .gitignore' \ "$PROJECT_DIR"/ "$DEST"/ echo "Sync complete."