A small tool to migrate repositories from Bitbucket to GitHub. It uses the Bitbucket API to list repos, creates matching private repos on GitHub, and mirrors each repository (all branches, tags, and history) via git push --mirror.
migrate.py— Full automation: lists repos from your Bitbucket workspace, creates each repo on GitHub withgh repo create, then clones (bare) from Bitbucket and pushes a mirror to GitHub. Credentials and workspace names come from a.envfile.upload.sh— For when you already have bare clones (e.g.repo.gitfolders): creates the GitHub repo and runsgit push --mirrorfor each. The GitHub org in the script is hardcoded (see below).
- Python 3 (3.7+)
- Git installed and available in your PATH
- GitHub CLI (
gh) — used to create repos and authenticate pushes- Install: https://cli.github.com/
- Log in:
gh auth login
- Bitbucket App Password (not your normal password)
- Bitbucket → Personal settings → App passwords → Create. Give it Repository: Read (and any other permissions you need).
cd /path/to/bb2ghpython3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activatepip install -r requirements.txtCopy the example env file and edit it with your values:
cp .env.example .envEdit .env and set:
| Variable | Required | Description |
|---|---|---|
BITBUCKET_USERNAME |
Yes | Your Bitbucket username (e.g. nuclearsam) |
BITBUCKET_APP_PASSWORD |
Yes | Bitbucket App Password (from step 4 in Prerequisites) |
BITBUCKET_WORKSPACE |
Yes | Bitbucket workspace slug (from the workspace URL) |
GITHUB_WORKSPACE |
Yes | GitHub org or username where repos will be created (e.g. ssntpl) |
GITHUB_TOKEN |
No | Only needed if you use GitHub API code; gh CLI uses its own auth |
Important: Never commit .env — it is listed in .gitignore. Only .env.example (with placeholders) should be in the repo.
From the project root (with .env configured and gh logged in):
python migrate.pyThis will:
- Check that required env vars are set (exits with a clear message if not).
- Call the Bitbucket API to list repositories in your workspace (with pagination).
- For each repo:
- Create a private GitHub repo
{GITHUB_WORKSPACE}/{repo_slug}with the same description. - Clone the Bitbucket repo as a bare clone.
- Run
git push --mirrorto the new GitHub repo (all refs and history). - Remove the local bare clone.
- Create a private GitHub repo
Pagination note: The script starts from a specific page of the Bitbucket API (see “Configuration” below). If you have many repos, it will follow the next page URL and process all pages until there are no more.
If you already have bare clones (e.g. myrepo.git directories) in the current folder:
- Ensure GitHub CLI is installed and logged in (
gh auth login). - The script uses a hardcoded GitHub org (
ssntpl). Editupload.shand replacessntplwith your GitHub org/username if different. - Run:
./upload.shFor each *.git directory it will:
- Create a private repo
ssntpl/<repo_name>on GitHub. - Run
git push --mirrorto that repo.
Make sure the script is executable: chmod +x upload.sh if needed.
Defined in .env (see .env.example):
- BITBUCKET_USERNAME — Bitbucket username.
- BITBUCKET_APP_PASSWORD — Bitbucket App Password.
- BITBUCKET_WORKSPACE — Bitbucket workspace slug.
- GITHUB_WORKSPACE — GitHub org or user for new repos.
- GITHUB_TOKEN — Optional; for GitHub API usage only.
-
Start page for Bitbucket listing
Inmigrate.py, the initial URL is:url = f"https://api.bitbucket.org/2.0/repositories/{bitbucket_workspace}?page=8"
To process from the first page, change
page=8topage=1or remove the?page=8part (Bitbucket defaults to page 1). -
Repo visibility
Repos are created with--private. To create public repos, change thegh repo createline inmigrate.py(remove or replace--private). -
upload.sh
The GitHub org is hardcoded asssntpl. Edit the script to use your org, or source a variable from a file if you prefer.
-
“Missing required env vars”
Copy.env.exampleto.env, fill in all required variables, and run from the same directory (or ensureload_dotenv()can find.env). -
“Failed to fetch Bitbucket repositories”
CheckBITBUCKET_USERNAME,BITBUCKET_APP_PASSWORD, andBITBUCKET_WORKSPACE. Ensure the App Password has at least Repository: Read. -
“Failed to create … on GitHub”
Rungh auth loginand ensure you have permission to create repos in the org/user set inGITHUB_WORKSPACE. If the repo already exists, the script will skip creation; you may need to delete it on GitHub or change the script to handle existing repos. -
Clone or push errors
Ensuregitandghare on your PATH and that your Bitbucket clone URL (SSH or HTTPS) is accessible from your machine (SSH keys or credentials configured as needed).
Use and modify as you like. No warranty.