Automated tools for migrating repositories from a personal GitHub account to an organization, then swapping account names.
✅ Successfully migrated 594/600 repositories (99% success rate) on 2026-01-26
This toolkit automates GitHub repository migration between accounts and organizations.
Example use case (completed 2026-01-26):
- Transfer 600 repositories from personal account → organization
- Rename personal account to free up original username
- Rename organization to take original username
- Delete forks if needed to clear naming conflicts
All while preserving stars, forks, issues, and repository settings with 99% success rate.
- ✅ Automated transfers using GitHub API
- ✅ Parallel processing - 3-5x faster with concurrent operations
- ✅ Caching system - 30-minute cache for instant reruns
- ✅ Dry-run mode for safe testing
- ✅ Pilot mode for testing with small batches
- ✅ Fork deletion - bulk delete forks that block org renames
- ✅ Rate limiting to avoid API bans
- ✅ Error handling with automatic retry logic
- ✅ Progress tracking with detailed reports
- ✅ Pre-migration audit for backup and reconciliation
- ✅ Post-migration validation to verify success
- ✅ PEP 723 scripts - self-contained, no venv needed
- uv - For running PEP 723 scripts (already installed)
- GitHub CLI (gh) v2.76.2+ - For GitHub API access (already installed)
- Python 3.13+ - For script execution (already installed)
- Authenticated GitHub session - Run
gh auth loginif needed
Verify prerequisites:
uv --version # Should show uv version
gh --version # Should show gh version 2.76.2+
python --version # Should show Python 3.13+
gh auth status # Should show: Logged in to github.com as pythoninthegrass# Navigate to the project directory
cd github-migration
# Copy environment configuration
cp .env.example .env
# Edit configuration (set your values)
nano .env # or use your preferred editorRequired .env values:
SOURCE_OWNER=pythoninthegrass
TARGET_ORG=pythoninthegrass2
DRY_RUN=true # Start with dry-run mode!Generate inventory of all repositories:
uv run scripts/00_pre_migration_audit.pyThis creates:
backup/repos_inventory_YYYYMMDD_HHMMSS.csv- CSV reportbackup/repos_full_YYYYMMDD_HHMMSS.json- Complete backup
Review the CSV file to identify any repos you want to exclude.
Test with 5-10 low-risk repositories first:
# Edit .env and set:
# PILOT_MODE=true
# PILOT_REPOS=repo1,repo2,repo3,repo4,repo5
# DRY_RUN=true
# Run dry-run first
uv run scripts/01_transfer_repos.py
# Review output, then run for real
# Edit .env: DRY_RUN=false
uv run scripts/01_transfer_repos.pyVerify pilot repos in organization:
gh repo list pythoninthegrass2 --limit 10Wait 24 hours and monitor for any issues before proceeding.
Transfer all remaining repositories:
# Edit .env:
# PILOT_MODE=false
# DRY_RUN=false
# BATCH_SIZE=5
# DELAY_BETWEEN_BATCHES=10
# Run the transfer (will take 4-6 hours for 619 repos)
uv run scripts/01_transfer_repos.pyThe script will:
- Fetch all repositories
- Filter based on your settings
- Ask for confirmation before transferring
- Transfer in batches with rate limiting
- Generate a detailed report
Monitor the process - you can stop and restart if needed.
If any transfers fail:
uv run scripts/03_retry_failed_transfers.pyFollow the detailed guide:
cat docs/MANUAL_RENAME_PROCEDURE.mdSteps summary:
-
Rename personal account:
pythoninthegrass→pythoninthegrass_og- Go to: https://github.com/settings/admin
- Enter new name, confirm, done
-
Rename organization:
pythoninthegrass2→pythoninthegrass- Go to: https://github.com/organizations/pythoninthegrass2/settings/profile
- Enter new name, confirm, done
-
Verify:
gh api /user --jq '.login' # Should show: pythoninthegrass_og gh api /orgs/pythoninthegrass --jq '.login' # Should show: pythoninthegrass
Verify migration success:
# Run validation
uv run scripts/02_post_migration_validation.pyReview the validation report to ensure all repos transferred correctly.
Note: GitHub redirects handle URL changes for 90 days, so local git clones automatically work without updates.
github-migration/
├── README.md # This file
├── .env.example # Configuration template
├── .gitignore # Git ignore rules
│
├── scripts/ # PEP 723 self-contained scripts
│ ├── 00_pre_migration_audit.py # Generate pre-migration inventory
│ ├── 01_transfer_repos.py # Main transfer automation
│ ├── 02_post_migration_validation.py # Verify migration success
│ ├── 03_retry_failed_transfers.py # Retry failed transfers
│ └── 04_delete_forks.py # Delete forks from organization
│
├── docs/
│ └── MANUAL_RENAME_PROCEDURE.md # Detailed rename guide
│
├── backup/ # Pre-migration backups (gitignored)
├── results/ # Transfer reports (gitignored)
└── logs/ # Execution logs (gitignored)
Generates comprehensive inventory of all repositories.
# Configuration: .env
# - SOURCE_OWNER (default: pythoninthegrass)
uv run scripts/00_pre_migration_audit.pyOutput:
- CSV report with repo metadata
- JSON backup with complete data
- Summary statistics
Main automation script for repository transfers.
# Configuration: .env
# - SOURCE_OWNER, TARGET_ORG
# - DRY_RUN (true/false)
# - BATCH_SIZE, DELAY_BETWEEN_TRANSFERS, DELAY_BETWEEN_BATCHES
# - EXCLUDE_FORKS, EXCLUDE_ARCHIVED
# - PILOT_MODE, PILOT_REPOS
# Dry run (recommended first)
DRY_RUN=true uv run scripts/01_transfer_repos.py
# Live transfer
DRY_RUN=false uv run scripts/01_transfer_repos.pyFeatures:
- Batch processing with configurable delays
- Rate limiting (avoids API bans)
- Error handling and logging
- Progress tracking
- Confirmation prompts for safety
Validates migration success and generates reconciliation report.
# Configuration: .env
# - SOURCE_OWNER (for loading pre-migration inventory)
# - TARGET_ORG (org after rename, default: pythoninthegrass)
uv run scripts/02_post_migration_validation.pyChecks:
- Repository count matches pre-migration
- Sample metadata preserved (stars, forks, issues)
- Access control intact
- Missing repositories reported
Retries failed transfers with exponential backoff.
# Configuration: .env
# - SOURCE_OWNER, TARGET_ORG
# - MAX_RETRIES (default: 3)
# - INITIAL_DELAY (default: 5 seconds)
uv run scripts/03_retry_failed_transfers.pyHandles:
- Temporary rate limit errors
- Timeout errors
- Network issues
- Identifies permanent failures (pending transfers, not found)
Deletes all forked repositories from the target organization.
# Configuration: .env
# - TARGET_ORG
# - DRY_RUN (true/false)
# - MAX_CONCURRENT_DELETIONS (default: 5)
# Dry run first (recommended)
uv run scripts/04_delete_forks.py
# Live deletion (requires typing "DELETE" to confirm)
DRY_RUN=false uv run scripts/04_delete_forks.pyFeatures:
- Parallel deletion (5 concurrent by default)
- Batch processing with progress tracking
- Confirmation prompt for safety
- GraphQL API for efficient fork discovery
Use case: Bulk delete forks that may block organization renames or cause naming conflicts.
All scripts use environment variables from .env file:
# Required
SOURCE_OWNER=pythoninthegrass # Your personal account
TARGET_ORG=pythoninthegrass2 # Destination organization
# Transfer settings
DRY_RUN=true # Always test with dry-run first!
BATCH_SIZE=9 # Repos per batch
DELAY_BETWEEN_TRANSFERS=1 # Seconds between transfers
DELAY_BETWEEN_BATCHES=3 # Seconds between batches
MAX_CONCURRENT_TRANSFERS=3 # Parallel transfers
MAX_CONCURRENT_DELETIONS=5 # Parallel deletions
# Filters
EXCLUDE_FORKS=false # Transfer forks too
EXCLUDE_ARCHIVED=false # Transfer archived repos
# Pilot testing
PILOT_MODE=false # Enable for testing
PILOT_REPOS=repo1,repo2,repo3 # Comma-separated list- Dry-run mode: Test without making changes
- Pilot mode: Test with small batch first
- Confirmation prompts: Prevents accidental transfers
- Rate limiting: Avoids GitHub API bans
- Error handling: Graceful failure with detailed logs
- Retry logic: Exponential backoff for transient failures
- Pre-migration backup: Complete inventory for rollback
- Validation: Verify migration success
- Day 1: Audit (1-2 hours automated)
- Day 2: Pilot test (30 minutes + 24h wait)
- Day 3-4: Full migration (4-6 hours automated)
- Day 5: Manual renames (15 minutes manual)
- Day 6: Validation (1-2 hours automated)
Total active time: ~8-12 hours (mostly automated) Total calendar time: ~6 days
Install GitHub CLI:
brew install gh # macOS
# or visit: https://cli.github.com/Install uv:
curl -LsSf https://astral.sh/uv/install.sh | shEnsure you're authenticated:
gh auth login
gh auth statusIncrease delays in .env:
DELAY_BETWEEN_TRANSFERS=5
DELAY_BETWEEN_BATCHES=30Run retry script:
uv run scripts/03_retry_failed_transfers.pyCheck detailed error messages in:
- Console output
results/transfer_results_*.csv
Wait 24 hours and retry - GitHub imposes this limit.
- Rate limits: 5,000 requests/hour (authenticated)
- Transfer cooldown: 24 hours between transfer attempts
- Rename restrictions: 90-day redirect period
- Size limits: Repos >100GB may fail (rare)
✅ Repository metadata (stars, watchers, forks) ✅ Issues and pull requests ✅ Commits and history ✅ Branch protection rules ✅ Webhooks (may need URL updates) ✅ Deploy keys ✅ Repository settings
❌ GitHub Actions secrets (must be re-added) ❌ Some third-party integrations (may need reconfiguration)
- GitHub maintains redirects for 90 days after rename
- Old URLs automatically redirect to new ones
- Update URLs within 90 days to avoid breakage
- Never commit
.env- contains sensitive configuration - Review audit output before sharing - may contain private repo names
- Limit access to generated reports - contain repo metadata
- Use .gitignore provided to prevent accidental commits
- Transferring repos: https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository
- Renaming accounts: https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/changing-your-github-username
- Rate limits: https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting
Check for ongoing issues:
If you encounter persistent issues:
- GitHub Support: https://support.github.com/
This toolkit is provided as-is for personal use.
- Automated repository transfer script
- Pre-migration audit and backup
- Post-migration validation
- Local clone cleanup automation
- Retry logic for failed transfers
- Manual rename procedures
- PEP 723 self-contained scripts (no venv needed)