svn_git_sync 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. #!/usr/bin/env bash
  2. # svn_git_sync — sync current SVN working copy into a Git mirror
  3. # Usage:
  4. # svn_git_sync init [mirror_name]
  5. set -euo pipefail
  6. show_usage() {
  7. echo "Usage: $0 init [mirror_name]" >&2
  8. exit 1
  9. }
  10. subcommand="${1:-}"
  11. [[ -n "$subcommand" ]] || show_usage
  12. shift
  13. case "$subcommand" in
  14. init)
  15. ;;
  16. -h|--help|help)
  17. show_usage
  18. ;;
  19. *)
  20. echo "Unknown command: $subcommand" >&2
  21. show_usage
  22. ;;
  23. esac
  24. SRC_DIR="$(pwd)"
  25. MIRROR_ROOT="${GIT_SVN_MIRROR_ROOT:-$HOME/git_svn_mirror}"
  26. # Derive a collision-resistant mirror name from the full path (unless overridden).
  27. raw_name="${1:-$(pwd -P)}"
  28. BASE_NAME="$(printf "%s" "$raw_name" | sed 's#^[\\/]*##; s#[\\/]#_#g')"
  29. DEST="${MIRROR_ROOT}/${BASE_NAME}"
  30. echo "Exporting SVN working copy to $DEST (versioned files only)..."
  31. rm -rf -- "$DEST"
  32. mkdir -p "$MIRROR_ROOT"
  33. # Optionally compute total items upfront for a rough percentage
  34. total_items=""
  35. if total=$(svn list -R . 2>/dev/null | wc -l || true); then
  36. total_items="$total"
  37. fi
  38. start_ts="$(date +%s.%N)"
  39. elapsed() {
  40. local now
  41. now="${1:-$(date +%s.%N)}"
  42. 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}'
  43. }
  44. # Run export in background so we can emit our own progress based on files created.
  45. svn export --quiet . "$DEST" &
  46. export_pid=$!
  47. last_reported=-1
  48. while kill -0 "$export_pid" 2>/dev/null; do
  49. if [[ -d "$DEST" ]]; then
  50. current=$(find "$DEST" -mindepth 1 -print | wc -l)
  51. else
  52. current=0
  53. fi
  54. if [[ "$current" -ne "$last_reported" ]]; then
  55. elapsed_val="$(elapsed)"
  56. pct=""
  57. if [[ -n "$total_items" && "$total_items" -gt 0 ]]; then
  58. pct=$(awk -v c="$current" -v t="$total_items" 'BEGIN{printf(" (%.1f%%)", (c*100)/t)}')
  59. fi
  60. printf "[%ss] Exported %d items%s\n" "$elapsed_val" "$current" "$pct"
  61. last_reported="$current"
  62. fi
  63. sleep 1
  64. done
  65. wait "$export_pid"
  66. final_count=$(find "$DEST" -mindepth 1 -print | wc -l 2>/dev/null || echo 0)
  67. printf "[%ss] Exported %d items (done)\n" "$(elapsed)" "$final_count"
  68. cd "$DEST"
  69. echo "[$(elapsed) s] Initializing git repository..."
  70. GIT_QUIET=1 git init >/dev/null
  71. git config init.defaultBranch trunk || true
  72. git config core.fileMode false || true
  73. git config core.autocrlf true || true
  74. git config core.safecrlf false || true
  75. echo "[$(elapsed) s] Staging files..."
  76. git add -A
  77. echo "[$(elapsed) s] Creating initial commit..."
  78. GIT_QUIET=1 git commit -q -m "Import SVN snapshot" || true
  79. # Reuse the repo for the source working tree by linking .git back to the new repo.
  80. echo "[$(elapsed) s] Linking git metadata to source working tree..."
  81. DEST_ABS="$(pwd -P)"
  82. cd "$SRC_DIR"
  83. if [[ -e .git || -L .git ]]; then
  84. echo "[$(elapsed)s] Replacing existing .git in $SRC_DIR..."
  85. rm -rf -- .git
  86. fi
  87. printf "gitdir: %s/.git\n" "$DEST_ABS" > .git
  88. echo "[$(elapsed)s] Linked .git to $DEST_ABS/.git"
  89. # Normalize working tree to match repo filters (e.g., CRLF handling).
  90. echo "[$(elapsed) s] Syncing working tree to repo content..."
  91. GIT_QUIET=1 git checkout -q -- .