Forráskód Böngészése

Add --history option to svn_git_sync for multi-rev import

cere 1 hónapja
szülő
commit
c67734b1cc
1 módosított fájl, 151 hozzáadás és 60 törlés
  1. 151 60
      svn_interact/svn_git_sync

+ 151 - 60
svn_interact/svn_git_sync

@@ -1,12 +1,12 @@
 #!/usr/bin/env bash
 # svn_git_sync — sync current SVN working copy into a Git mirror
 # Usage:
-#   svn_git_sync init [mirror_name]
+#   svn_git_sync init [--history=N] [mirror_name]
 
 set -euo pipefail
 
 show_usage() {
-  echo "Usage: $0 init [mirror_name]" >&2
+  echo "Usage: $0 init [--history=N] [mirror_name]" >&2
   exit 1
 }
 
@@ -14,8 +14,39 @@ subcommand="${1:-}"
 [[ -n "$subcommand" ]] || show_usage
 shift
 
+history_count=0
+mirror_arg=""
+
+parse_opts() {
+  while [[ $# -gt 0 ]]; do
+    case "$1" in
+      --history=*)
+        history_count="${1#*=}"
+        shift
+        ;;
+      --history)
+        history_count="${2:-}"
+        shift 2
+        ;;
+      -h|--help)
+        show_usage
+        ;;
+      *)
+        mirror_arg="$1"
+        shift
+        break
+        ;;
+    esac
+  done
+  if [[ -n "$mirror_arg" && $# -gt 0 ]]; then
+    echo "Unexpected extra arguments: $*" >&2
+    show_usage
+  fi
+}
+
 case "$subcommand" in
   init)
+    parse_opts "$@"
     ;;
   -h|--help|help)
     show_usage
@@ -30,7 +61,7 @@ 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)}"
+raw_name="${mirror_arg:-$(pwd -P)}"
 BASE_NAME="$(printf "%s" "$raw_name" | sed 's#^[\\/]*##; s#[\\/]#_#g')"
 DEST="${MIRROR_ROOT}/${BASE_NAME}"
 
@@ -38,12 +69,6 @@ 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
@@ -51,68 +76,134 @@ elapsed() {
   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
+simple_export() {
+  # Optionally compute total items upfront for a rough percentage
+  local total_items=""
+  if total=$(svn list -R . 2>/dev/null | wc -l || true); then
+    total_items="$total"
   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)}')
+  svn export --quiet . "$DEST" &
+  export_pid=$!
+
+  local 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
-    printf "[%ss] Exported %d items%s\n" "$elapsed_val" "$current" "$pct"
-    last_reported="$current"
+
+    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"
+}
+
+init_git_repo() {
+  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
+}
 
-  sleep 1
-done
+link_git_to_source() {
+  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"
 
-wait "$export_pid"
+  echo "[$(elapsed) s] Syncing working tree to repo content..."
+  GIT_QUIET=1 git checkout -q -- .
+}
 
-final_count=$(find "$DEST" -mindepth 1 -print | wc -l 2>/dev/null || echo 0)
-printf "[%ss] Exported %d items (done)\n" "$(elapsed)" "$final_count"
+stage_and_commit() {
+  local msg="$1"
+  echo "[$(elapsed) s] Staging files..."
+  git add -A
 
-cd "$DEST"
+  echo "[$(elapsed) s] Creating commit: $msg"
+  GIT_QUIET=1 git commit -q -m "$msg" || true
+}
 
-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
+# Validate history_count is numeric (default 0)
+if [[ -z "${history_count:-}" ]]; then
+  history_count=0
+elif ! [[ "$history_count" =~ ^[0-9]+$ ]]; then
+  echo "--history must be a non-negative integer" >&2
+  exit 1
 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
+if (( history_count == 0 )); then
+  simple_export
+  cd "$DEST"
+  init_git_repo
+  stage_and_commit "Import SVN snapshot"
+  link_git_to_source
+  exit 0
+fi
 
-# 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
+# History mode: pull last N revisions into Git.
+head_rev="$(svn info --show-item revision 2>/dev/null || svn info | awk -F': ' '/^Revision:/ {print $2; exit}')"
+if [[ -z "$head_rev" ]]; then
+  echo "Unable to determine HEAD revision" >&2
+  exit 1
 fi
-printf "gitdir: %s/.git\n" "$DEST_ABS" > .git
-echo "[$(elapsed)s] Linked .git to $DEST_ABS/.git"
+start_rev=$(( head_rev - history_count + 1 ))
+if (( start_rev < 1 )); then start_rev=1; fi
+
+echo "Including last $history_count revisions (r${start_rev}..r${head_rev})..."
+
+STAGE_DIR="$DEST/.stage"
+mkdir -p "$DEST" "$STAGE_DIR"
+cd "$DEST"
+init_git_repo
+
+for rev in $(seq "$start_rev" "$head_rev"); do
+  echo "[$(elapsed) s] Exporting r${rev}..."
+  rm -rf -- "$STAGE_DIR"
+  mkdir -p "$STAGE_DIR"
+  svn export --quiet -r "$rev" "$SRC_DIR" "$STAGE_DIR"
+
+  # Sync exported snapshot into repo working tree, preserving .git and .gitignore
+  rsync -a --delete --exclude '.git' --exclude '.gitignore' "$STAGE_DIR"/ "$DEST"/
+
+  # Ensure ignores persist
+  if [[ ! -e .gitignore ]]; then
+    printf "*.pyc\n__pycache__/\n" > .gitignore
+  fi
+
+  if [[ "$rev" -eq "$start_rev" ]]; then
+    stage_and_commit "Import SVN r${rev}"
+  else
+    stage_and_commit "Update to SVN r${rev}"
+  fi
+done
 
-# 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 -- .
+link_git_to_source