Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GitLab → Forgejo Migration Tool

A PHP command-line tool that migrates a self-hosted GitLab instance to Forgejo using both REST APIs. It can migrate:

  • Users — creates Forgejo users for every GitLab user
  • Organisations — creates Forgejo orgs for every GitLab group (subgroups are flattened)
  • Repositories — imports every GitLab project into Forgejo, including branches, tags, and optionally wiki and Git LFS

Requires PHP >= 8.1 with the curl and json extensions.

Installation

composer install

Prerequisites

What Why
GitLab personal access token with the api scope, belonging to an administrator user Listing all users, groups and projects requires admin access
Forgejo access token with the admin scope Creating users/orgs and migrating repos into other owners requires site admin
Network access from the Forgejo server to the GitLab instance Repositories are cloned by Forgejo itself (POST /api/v1/repos/migrate), not by this tool

Quick start

# 1. Preview everything without changing anything
php bin/migrate \
  --gitlab-url https://gitlab.example.com \
  --gitlab-token <gitlab-pat> \
  --forgejo-url https://forgejo.example.com \
  --forgejo-token <forgejo-admin-token> \
  --all --dry-run

# 2. Run the migration
php bin/migrate \
  --gitlab-url https://gitlab.example.com \
  --gitlab-token <gitlab-pat> \
  --forgejo-url https://forgejo.example.com \
  --forgejo-token <forgejo-admin-token> \
  --all

Values can also be provided via environment variables: GITLAB_URL, GITLAB_TOKEN, FORGEJO_URL, FORGEJO_TOKEN.

Options

Scope

Option Description
--users Migrate users
--orgs Migrate organisations (GitLab groups)
--repos Migrate repositories (git data, branches, tags, wiki, LFS)
--all Shortcut for --users --orgs --repos
--dry-run Show what would be done without making any changes

The recommended order is users → orgs → repos (which is what --all does), because repositories need their owners to exist to be assigned correctly.

Filters

Option Description
--include-bots Migrate GitLab bot users (skipped by default)
--include-inactive Migrate blocked/deactivated GitLab users (skipped by default)
--include-forks Migrate forked repositories (skipped by default)
--include-archived Migrate archived repositories (skipped by default)
--visibility=public|internal|private Only migrate repositories with this visibility
--filter='/regex/' Only migrate repositories whose full path matches this PCRE regex (delimiters required), e.g. --filter='/^engineering\//'
--limit=N Migrate at most N items per category (useful for testing)

Ownership

Option Description
--owner=NAME Forgejo user that owns created orgs and receives repositories whose GitLab owner was not migrated (default: the user owning the Forgejo token)

Owner resolution per repository:

  1. User-owned project → the Forgejo user created in this run, or an existing Forgejo user with the same name
  2. Group-owned project → the Forgejo org created in this run, or an existing Forgejo org with the computed name
  3. Otherwise → --owner, with a visible warning

Users

Option Description
--password=PASS Use this fixed password for all newly created users (default: a random password per user, printed at the end)
--passwords-file=PATH Write generated user passwords to this CSV file (username,password) instead of printing them
--must-change-password / --no-must-change-password Require created users to change their password on first login (default: on)
--migrate-admins Make users that are GitLab admins into Forgejo site administrators

Repositories

Option Description
--wiki / --no-wiki Migrate wikis (default: on)
--lfs / --no-lfs Migrate Git LFS objects (default: on)
--pull-requests / --no-pull-requests Migrate Pull Requests (default: on)
--issues / --no-issues Migrate Git LFS objects (default: on)
--migrate-timeout=SECONDS Timeout for each repository migration (default: 600). Increase for very large repositories

Behaviour and mapping

  • Idempotent: users, orgs and repos that already exist on Forgejo are skipped (not duplicated) and still used for owner mapping, so the command can be safely re-run after a partial migration.
  • Visibility mapping: GitLab public → Forgejo public, internallimited, privateprivate.
  • Subgroups: Forgejo has no subgroups, so a GitLab group path is flattened into the org name: engineering/frontendengineering-frontend (a single hyphen, because consecutive dashes are invalid Forgejo names). Names are sanitized to [a-z0-9._-] (max 30 chars); collisions get a _2, _3, … suffix.
  • Repo cloning: repositories are imported via Forgejo's migrate endpoint (service=gitlab) using your GitLab token as the clone credential. The import runs server-side on Forgejo and blocks until the git data is cloned; if it fails, Forgejo deletes the partially created repository.
  • GitLab token scope for repos: the GitLab token must have read_repository (covered by api) and must be able to read every project being migrated.
  • Exit codes: 0 when no item failed, 1 when any item failed or a phase was aborted (e.g. bad credentials, non-admin token). A final summary table and a failure list are always printed.

Example: staged migration

# Users only
php bin/migrate ... --users

# Orgs only (owned by the token user)
php bin/migrate ... --orgs

# Only public repos of the engineering groups
php bin/migrate ... --repos --visibility public --filter='/^engineering\//'

Development / mocks

Two mock API servers are included for local testing (no real GitLab/Forgejo needed):

# Terminal 1 — mock GitLab
php -S 127.0.0.1:8081 test/mock/gitlab.php

# Terminal 2 — mock Forgejo (persists to test/mock/state/forgejo.json)
php -S 127.0.0.1:8082 test/mock/forgejo.php

# Terminal 3 — run the migration
php bin/migrate --gitlab-url http://127.0.0.1:8081 --gitlab-token test \
  --forgejo-url http://127.0.0.1:8082 --forgejo-token test --all

# Re-run to verify idempotency (everything should be skipped)
php bin/migrate ... --all

Remove test/mock/state/ between experiments to start from a clean Forgejo.

About

CLI tool to migrate a self-hosted GitLab instance to Forgejo - users, orgs and repositories (git, wiki, LFS) via both REST APIs. Dry-run and idempotent.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages