Skip to content

Instantly share code, notes, and snippets.

@CJHarmath
Created November 16, 2024 04:44
Show Gist options
  • Select an option

  • Save CJHarmath/ff1af5a66b4582541b4bb997166d998b to your computer and use it in GitHub Desktop.

Select an option

Save CJHarmath/ff1af5a66b4582541b4bb997166d998b to your computer and use it in GitHub Desktop.

Revisions

  1. CJHarmath created this gist Nov 16, 2024.
    232 changes: 232 additions & 0 deletions svn_to_git.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,232 @@
    #!/bin/bash

    # SVN to Git Migration Script
    # ==========================
    #
    # This script performs a complete migration from SVN to Git while preserving
    # history, usernames, branches, and tags.
    #
    # Usage:
    # ./svn-to-git-migrate.sh <SVN_REPO_URL> <GIT_REPO_NAME>
    #
    # Examples:
    # ./svn-to-git-migrate.sh http://svn.example.com/repo my-project
    # ./svn-to-git-migrate.sh svn://svn.example.com/repo/trunk project-name
    # ./svn-to-git-migrate.sh file:///path/to/local/svn/repo local-project
    #
    # Prerequisites:
    # - git
    # - git-svn
    # - svn
    #
    # The script will:
    # 1. Create an authors mapping file (authors.txt)
    # 2. Convert SVN history to Git format
    # 3. Convert SVN branches and tags to Git format
    # 4. Create a server-ready Git repository
    # 5. Provide instructions for pushing to GitHub/GitLab
    #
    # Important Notes:
    # - Review authors.txt before proceeding with migration
    # - Ensure sufficient disk space (at least 2x the SVN repository size)
    # - Large repositories may take several hours to migrate
    # - The script assumes standard SVN layout (trunk/branches/tags)
    # - Have your GitHub/GitLab repository URL ready for the final push
    #
    # Output:
    # - authors.txt: SVN to Git username mapping
    # - migration.log: Detailed migration log
    # - temp_migration/: Temporary working directory
    # - <GIT_REPO_NAME>.git/: Final server-ready Git repository with complete history
    #
    # Post-Migration:
    # The script will provide instructions for pushing to GitHub/GitLab:
    # 1. Create an empty repository on GitHub/GitLab (DO NOT initialize with README)
    # 2. Follow the provided commands to push all branches and tags
    #
    # Example authors.txt format:
    # svnuser = Git User <[email protected]>
    # john = John Doe <[email protected]>
    #

    set -e

    SVN_REPO_URL=""
    GIT_REPO_NAME=""
    AUTHORS_FILE="authors.txt"
    TEMP_DIR="temp_migration"
    LOG_FILE="migration.log"

    # Function to show usage
    show_usage() {
    grep '^#' "$0" | tail -n +2 | cut -c 3-
    exit 1
    }

    # Function to log messages
    log_message() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
    }

    # Function to verify prerequisites
    check_prerequisites() {
    log_message "Checking prerequisites..."

    # Check for required commands
    commands=("git" "git-svn" "svn")
    for cmd in "${commands[@]}"; do
    if ! command -v "$cmd" >/dev/null 2>&1; then
    log_message "ERROR: $cmd is not installed"
    exit 1
    fi
    done
    }

    # Function to create authors file
    create_authors_file() {
    log_message "Creating authors mapping file..."

    if [ -f "$AUTHORS_FILE" ]; then
    log_message "Authors file already exists, skipping..."
    return
    }

    svn log -q "$SVN_REPO_URL" | \
    awk -F '|' '/^r/ {sub("^ ", "", $2); sub(" $", "", $2); print $2}' | \
    sort -u | \
    while read -r author; do
    echo "$author = $author <$author@example.com>"
    done > "$AUTHORS_FILE"

    log_message "AUTHORS_FILE created. Please review and update email addresses!"
    exit 1
    }

    # Function to display post-migration instructions
    show_post_migration_instructions() {
    local repo_name="$1"

    echo
    echo "=== Post-Migration Instructions ==="
    echo
    echo "Your repository is ready! Follow these steps to push to GitHub or GitLab:"
    echo
    echo "1. Create a new empty repository on GitHub or GitLab"
    echo " - DO NOT initialize it with a README, license, or .gitignore"
    echo " - GitHub: Click 'New' at github.com/yourname?tab=repositories"
    echo " - GitLab: Click 'New project' at gitlab.com/projects/new"
    echo
    echo "2. Push the repository (replace REMOTE_URL with your GitHub/GitLab URL):"
    echo
    echo " cd ${repo_name}.git"
    echo " # For GitHub:"
    echo " git remote add origin https://github.com/YOUR_USERNAME/${repo_name}.git"
    echo " # OR for GitLab:"
    echo " git remote add origin https://gitlab.com/YOUR_USERNAME/${repo_name}.git"
    echo
    echo " # Push all branches and tags:"
    echo " git push origin --all"
    echo " git push origin --tags"
    echo
    echo "3. Verify the migration:"
    echo " - Check the repository on GitHub/GitLab"
    echo " - Verify branches and tags are present"
    echo " - Review commit history"
    echo " - Clone a fresh copy and check the contents:"
    echo " git clone https://github.com/YOUR_USERNAME/${repo_name}.git"
    echo
    echo "4. Clean up local migration files (optional):"
    echo " cd .."
    echo " rm -rf temp_migration"
    echo " # Keep ${repo_name}.git if you want a local backup"
    echo
    echo "Migration log is available in: migration.log"
    echo
    }

    # Function to perform the migration
    perform_migration() {
    log_message "Starting migration process..."

    # Create and enter temporary directory
    mkdir -p "$TEMP_DIR"
    cd "$TEMP_DIR"

    # Initialize git-svn
    log_message "Initializing git-svn..."
    git svn init "$SVN_REPO_URL" --no-metadata \
    --authors-file="../$AUTHORS_FILE" \
    --trunk=trunk \
    --tags=tags \
    --branches=branches \
    --prefix=origin/

    # Fetch SVN repository
    log_message "Fetching SVN repository (this may take a while)..."
    git svn fetch

    # Fix tags
    log_message "Converting SVN tags to Git tags..."
    git for-each-ref refs/remotes/origin/tags | cut -d / -f 5- | \
    while read -r tag; do
    git tag "$tag" "origin/tags/$tag^" || true
    git branch -r -d "origin/tags/$tag" || true
    done

    # Fix branches
    log_message "Converting SVN branches to Git branches..."
    git for-each-ref refs/remotes/origin | cut -d / -f 4- | \
    while read -r branch; do
    if [ "$branch" != "trunk" ]; then
    git branch "$branch" "refs/remotes/origin/$branch" || true
    git branch -r -d "origin/$branch" || true
    fi
    done

    # Create master from trunk
    log_message "Creating master branch from trunk..."
    git branch -m trunk master

    # Clean up
    log_message "Cleaning up..."
    git gc --aggressive
    git prune

    # Create server-ready repository
    log_message "Creating server-ready repository with complete history..."
    cd ..
    git clone --bare "$TEMP_DIR" "$GIT_REPO_NAME.git"

    # Verify repository integrity
    cd "$GIT_REPO_NAME.git"
    log_message "Verifying repository integrity..."
    git fsck --full

    log_message "Migration completed successfully! Repository is ready for GitHub/GitLab."

    # Return to original directory
    cd ..

    # Show instructions for pushing to GitHub/GitLab
    show_post_migration_instructions "$GIT_REPO_NAME"
    }

    # Main execution
    main() {
    # Show usage if --help flag is passed or no arguments provided
    if [ "$1" = "--help" ] || [ "$1" = "-h" ] || [ -z "$1" ] || [ -z "$2" ]; then
    show_usage
    fi

    SVN_REPO_URL="$1"
    GIT_REPO_NAME="$2"

    # Initialize log file
    echo "=== Migration Log Started $(date) ===" > "$LOG_FILE"

    check_prerequisites
    create_authors_file
    perform_migration
    }

    main "$@"