- Python 100%
| .idea | ||
| .gitignore | ||
| migrate_docker_skopeo.py | ||
| migrate_gitea_to_forgejo.py | ||
| README.md | ||
Gitea to Forgejo Migrator
This repository contains two migration scripts:
- migrate_gitea_to_forgejo.py — migrate users/organizations and repositories from a Gitea instance to a Forgejo instance via REST API.
- migrate_docker_skopeo.py — migrate container images between registries (e.g., Gitea/Forgejo registries) using skopeo and the OCI API.
Both scripts support pagination, dry-run mode, and delete-before-migrate for re-running migrations.
Attribution: These scripts and this documentation were generated with the assistance of AI Grok.
Quick setup (dependencies)
- Create and activate a virtual environment (recommended):
- Windows (PowerShell):
python -m venv .venv.venv\Scripts\Activate.ps1
- Linux/macOS:
python3 -m venv .venvsource .venv/bin/activate
- Windows (PowerShell):
- Install Python dependencies:
pip install --upgrade pippip install requests
- Install external tools required by
migrate_docker_skopeo.py:skopeo- Linux (Debian/Ubuntu):
sudo apt-get update && sudo apt-get install -y skopeo - Linux (RHEL/CentOS/Fedora):
sudo dnf install -y skopeo - macOS (Homebrew):
brew install skopeo - Windows: https://github.com/passcod/winskopeo/releases
- Linux (Debian/Ubuntu):
- Verify installations:
skopeo --version
1) migrate_gitea_to_forgejo.py
Migrate repositories from a source Gitea to a target Forgejo. It can:
- Migrate all organizations and their repositories.
- Migrate repositories for a specific user (owner).
- Ensure the target user/organization exists (creating organizations if missing, and falling back to the current authenticated user if the specified target user is absent).
- Migrate issues, pull requests, releases, labels, milestones, wiki, and LFS data (requires
service: gitea— the default).
Prerequisites
- Python 3.8+
requestslibrary (pip install requests)- API tokens for both source (Gitea) and target (Forgejo) instances.
- Network access between the machine running the script and both instances.
Usage
python migrate_gitea_to_forgejo.py <source_url> <source_token> <target_url> <target_token> <owner_name> <target_owner> [--include-sql] [--dry-run] [--delete-before-migrate]
Positional arguments
| Argument | Description |
|---|---|
source_url |
Base URL of the Gitea instance (e.g., https://gitea.example.com) |
source_token |
Gitea API token with permissions to list orgs/users and read repositories |
target_url |
Base URL of the Forgejo instance (e.g., https://forgejo.example.com) |
target_token |
Forgejo API token with permissions to create orgs/repos and perform migrations |
owner_name |
Source owner (username) to migrate user repos from. If empty, this step is skipped. |
target_owner |
Target username in Forgejo. Falls back to the token's current user if not found. |
Optional flags
| Flag | Description |
|---|---|
--include-sql |
Generate update_timestamps.sql to restore original updated_at timestamps in the Forgejo database. Run the SQL against Forgejo's database after migration (stop Forgejo first). |
--dry-run |
Verify connections and list all repos with details (branch, size, issues, PRs, etc.) without migrating anything. |
--delete-before-migrate |
Delete existing repos on Forgejo before re-migrating them. Useful when re-running with corrected settings (e.g., after switching from service: git to service: gitea). |
Help output
usage: migrate_gitea_to_forgejo.py [-h] [--include-sql] [--dry-run]
[--delete-before-migrate]
source_url source_token target_url
target_token owner_name target_owner
Migrate repositories from Gitea to Forgejo.
positional arguments:
source_url Base URL of the Gitea instance
source_token Gitea API token
target_url Base URL of the Forgejo instance
target_token Forgejo API token
owner_name Source owner (username) to migrate user repos from.
Empty to skip.
target_owner Target username in Forgejo.
options:
-h, --help show this help message and exit
--include-sql Generate SQL file to restore original updated_at
timestamps in Forgejo database
--dry-run Verify connections and list repos without migrating
anything
--delete-before-migrate
Delete existing repos on Forgejo before re-migrating
them
Example
python migrate_gitea_to_forgejo.py https://gitea.example.com <GITEA_TOKEN> https://forgejo.example.com <FORGEJO_TOKEN> alice alice
Behavior:
- If user "alice" exists in Forgejo, her ID is used as the migration target.
- If user "alice" does not exist, the script falls back to the current user associated with
<FORGEJO_TOKEN>. - After user repos, the script migrates all organizations and their repositories. Organizations are created if missing.
Dry-run example
python migrate_gitea_to_forgejo.py https://gitea.example.com <GITEA_TOKEN> https://forgejo.example.com <FORGEJO_TOKEN> alice alice --dry-run
Lists all repos with details like:
Processing repository: my-repo from alice
Default branch: main | Size: 1.2 MB | Language: Python
Issues: 3 open
Pull requests: 5 open
Stars: 5 | Forks: 2 | Watchers: 1
Created: 2024-01-15T10:30:00Z | Last pushed: 2024-03-20T14:22:00Z
Re-migrating with corrected settings
If you already ran the migration and repos exist on Forgejo but are missing issues/PRs (because the initial run used service: git), re-run with --delete-before-migrate:
python migrate_gitea_to_forgejo.py https://gitea.example.com <GITEA_TOKEN> https://forgejo.example.com <FORGEJO_TOKEN> alice alice --delete-before-migrate
This deletes each existing repo on Forgejo and re-migrates it with the correct service: gitea setting, which fetches issues, PRs, releases, labels, and milestones via the Gitea API.
Restoring timestamps
With --include-sql, the script generates update_timestamps.sql. After migration:
systemctl stop forgejo
psql -d forgejo -f update_timestamps.sql
systemctl start forgejo
This restores the original updated_at timestamps from Gitea, which otherwise get set to the migration time.
2) migrate_docker_skopeo.py
Migrate container images (all repos and their tags) from one registry to another using skopeo. The script uses the OCI API to list repositories and tags, then performs skopeo copy for each tag.
Prerequisites
- Python 3.8+
requestslibrary (pip install requests)skopeoinstalled and available in PATH- Valid credentials for both source and target registries (username + token)
- Network access between the machine running the script and both registries
If your registries use self-signed certificates, the script passes --src-tls-verify=false and --dest-tls-verify=false. Remove those flags for production-grade TLS verification.
Usage
python migrate_docker_skopeo.py <source_url> <source_user> <source_token> <target_url> <target_user> <target_token> [--dry-run] [--delete-before-migrate]
Positional arguments
| Argument | Description |
|---|---|
source_url |
Base URL of the source registry (e.g., https://git.example.com) |
source_user |
Username for source registry auth |
source_token |
Token/password for source registry |
target_url |
Base URL of the target registry (e.g., https://forgejo.example.com) |
target_user |
Username for target registry auth |
target_token |
Token/password for target registry |
Optional flags
| Flag | Description |
|---|---|
--dry-run |
List all images and tags without copying anything. |
--delete-before-migrate |
Delete existing images on target before re-copying them. |
Help output
usage: migrate_docker_skopeo.py [-h] [--dry-run] [--delete-before-migrate]
source_url source_user source_token target_url
target_user target_token
Migrate container images from Gitea to Forgejo using skopeo.
positional arguments:
source_url Base URL of the source Gitea instance (e.g.,
https://git.example.com)
source_user Username for source registry authentication
source_token Token/password for source registry authentication
target_url Base URL of the target Forgejo instance (e.g.,
https://forgejo.example.com)
target_user Username for target registry authentication
target_token Token/password for target registry authentication
options:
-h, --help show this help message and exit
--dry-run List all images without copying anything
--delete-before-migrate
Delete existing images on target before re-copying
them
Example
python migrate_docker_skopeo.py https://git.example.com alice SRC_TOKEN https://forgejo.example.com alice DST_TOKEN
The script will:
- Fetch the repositories catalog via
/v2/_catalog(paginated) - For each repository, fetch tags via
/v2/<repo>/tags/list(paginated) - For each tag, run
skopeo copyfrom source to target - Print progress and a final summary with copied/skipped/failed counts
Troubleshooting
- Ensure tokens are valid and have required permissions (read on source, write on target).
- If you get TLS/SSL errors, review whether to keep or remove the
--*-tls-verifyflags. For self-signed setups, they may be necessary. - Make sure
skopeois installed and discoverable (skopeo --versionworks in your shell). - If using Windows, run from a shell that can execute
skopeo(e.g., PowerShell with skopeo in PATH).
Development notes
- All console output and comments are in English.
- Both scripts paginate API responses (50 items per page for Gitea/Forgejo REST API, 100 for OCI registry catalog/tags).
- The repo migration uses
service: giteato ensure issues, PRs, releases, labels, and milestones are fetched via the Gitea API (not just git data).