#!/usr/bin/env bash # svn_git_sync — sync current SVN working copy into a Git mirror # Usage: # svn_git_sync init [mirror_name] set -euo pipefail show_usage() { echo "Usage: $0 init [mirror_name]" >&2 exit 1 } subcommand="${1:-}" [[ -n "$subcommand" ]] || show_usage shift case "$subcommand" in init) ;; -h|--help|help) show_usage ;; *) echo "Unknown command: $subcommand" >&2 show_usage ;; esac SRC_DIR="$(pwd)" MIRROR_ROOT="${GIT_SVN_MIRROR_ROOT:-$HOME/git_svn_mirror}" # Derive a collision-resistant mirror name from the full path (unless overridden). raw_name="${1:-$(pwd -P)}" BASE_NAME="$(printf "%s" "$raw_name" | sed 's#^[\\/]*##; s#[\\/]#_#g')" DEST="${MIRROR_ROOT}/${BASE_NAME}" echo "Exporting SVN working copy to $DEST (versioned files only)..." rm -rf -- "$DEST" mkdir -p "$MIRROR_ROOT" # Optionally compute total items upfront for a rough percentage total_items="" if total=$(svn list -R . 2>/dev/null | wc -l || true); then total_items="$total" fi start_ts="$(date +%s.%N)" elapsed() { local now now="${1:-$(date +%s.%N)}" awk -v s="$start_ts" -v n="$now" 'BEGIN{split(s,sa,".");split(n,na,".");printf "%.1f", (na[1]-sa[1]) + (na[2]-sa[2])/1e9}' } # Run export in background so we can emit our own progress based on files created. svn export --quiet . "$DEST" & export_pid=$! last_reported=-1 while kill -0 "$export_pid" 2>/dev/null; do if [[ -d "$DEST" ]]; then current=$(find "$DEST" -mindepth 1 -print | wc -l) else current=0 fi if [[ "$current" -ne "$last_reported" ]]; then elapsed_val="$(elapsed)" pct="" if [[ -n "$total_items" && "$total_items" -gt 0 ]]; then pct=$(awk -v c="$current" -v t="$total_items" 'BEGIN{printf(" (%.1f%%)", (c*100)/t)}') fi printf "[%ss] Exported %d items%s\n" "$elapsed_val" "$current" "$pct" last_reported="$current" fi sleep 1 done wait "$export_pid" final_count=$(find "$DEST" -mindepth 1 -print | wc -l 2>/dev/null || echo 0) printf "[%ss] Exported %d items (done)\n" "$(elapsed)" "$final_count" cd "$DEST" echo "[$(elapsed) s] Initializing git repository..." GIT_QUIET=1 git init >/dev/null git config init.defaultBranch trunk || true git config core.fileMode false || true git config core.autocrlf true || true git config core.safecrlf false || true # Seed ignores for common noise (pyc/cache) if [[ ! -e .gitignore ]]; then echo "[$(elapsed) s] Adding default ignores (.pyc/__pycache__)..." printf "*.pyc\n__pycache__/\n" > .gitignore fi echo "[$(elapsed) s] Staging files..." git add -A echo "[$(elapsed) s] Creating initial commit..." GIT_QUIET=1 git commit -q -m "Import SVN snapshot" || true # Reuse the repo for the source working tree by linking .git back to the new repo. echo "[$(elapsed) s] Linking git metadata to source working tree..." DEST_ABS="$(pwd -P)" cd "$SRC_DIR" if [[ -e .git || -L .git ]]; then echo "[$(elapsed)s] Replacing existing .git in $SRC_DIR..." rm -rf -- .git fi printf "gitdir: %s/.git\n" "$DEST_ABS" > .git echo "[$(elapsed)s] Linked .git to $DEST_ABS/.git" # Normalize working tree to match repo filters (e.g., CRLF handling). echo "[$(elapsed) s] Syncing working tree to repo content..." GIT_QUIET=1 git checkout -q -- .