I recently fell victim to the conveniences of big tech (at least temporarily), and was faced with the task of migrating hundreds of my personal git repositories from my self-hosted Gitea instance to Github. With this many repos to migrate, completing any part of this migration manually wasn’t really an option. Instead, I leveraged the Gitea and Github APIs to automate the whole process.
First, I created an auth token on Gitea for use with the Gitea API and setup the Github CLI. Next, I used the Gitea API to retrieve repository info. You’ll need to call this multiple times and paginate manually if you have more than 50 repos under a single entity.
For repositories under organizations:
1 | curl -k -u "<ORG_NAME>:<GITEA_TOKEN>" "https://<YOUR_GITEA_URL>/api/v1/orgs/<ORG_NAME>/repos?limit=50&page=1" >> <ORG_NAME>.json |
For repositories under my profile:
1 | curl -k -u "<USERNAME>:<GITEA_TOKEN>" "https:/<YOUR_GITEA_URL>/api/v1/users/<USERNAME>/repos?limit=50&page=1" >> <USERNAME>.json |
If you don’t care about organization and just want everything your user account has access to on Gitea:
1 | curl -k -u "<USERNAME>:<GITEA_TOKEN>" "https://<YOUR_GITEA_URL>/api/v1/user/repos?limit=50&page=1" >> <USERNAME>.json |
Next, for each entity to migrate, I cloned each repo. If you have multiple branches you want to preserve, you’ll need to do a full clone instead of this.
1 2 | mkdir <USERNAME> && cd <USERNAME> jq -r '.[].name' ../<USERNAME>.json | while read i; do git clone git@<YOUR_GITEA_URL>:<USERNAME>/$i.git; done |
Finally, I used gh, the Github CLI, to create new private repositories preserving the old names and descriptions:
1 2 | jq -r '.[] | "gh repo create \(.name) --private --description=\"\(.description)\""' ../<USERNAME>.json | while read i; do eval $i; done jq -r '.[] | "cd \(.name) && git remote set-url origin git@github.com:<GITHUB_USERNAME>/\(.name).git && git push && cd .."' ../<USERNAME>.json | while read i; do eval $i; done |
As part of this migration, I also marked all repositories I wasn’t actively working on anymore (ie. everything in Github pre-migration) as archived, also using gh:
1 2 | gh repo list -L 100 --json name >> github_repos_to_archive.json jq -r '.[].name' github_repos_to_archive.json | while read i; do gh repo archive $i -y; done |