Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Git Provider Migrator

A CLI tool and Python library to migrate Git repositories between providers — preserving full commit history, branches, tags, and Git LFS objects.

Current support: any Git host as source · GitHub as destination

Features

  • Full history migration via git clone --mirror + git push
  • Batch migration from a JSON file or directly from the GitLab API
  • Incremental sync — if the destination is behind the source, missing commits are pushed automatically
  • Git LFS support — fetches and pushes LFS objects; auto-converts plain blobs > 100 MB to LFS
  • Large repo support — push in configurable commit slices to stay under GitHub's 2 GB per-push limit
  • Parallel workers — migrate multiple repositories concurrently
  • Organization support — migrate to a personal account or a GitHub organization
  • Real-time error reporting — failures written to a CSV file as they occur

Compatibility Matrix

As source (origin)

Provider Clone & push history Token auth LFS fetch Batch via API Branch listing via API Archive after sync
GitLab ✅ Full ✅ --gitlab-token ✅ ✅ --from-gitlab ✅ ✅ --archive-synced
GitHub ✅ Full ✅ --github-token ✅ ❌ ❌ ❌
Bitbucket ✅ Full ⚠️ Embed in URL¹ ✅ ❌ ❌ ❌

¹ Bitbucket does not support token-as-password in the same way; credentials must be embedded directly in the HTTPS URL (https://user:app_password@bitbucket.org/...).

As destination

Provider Repo creation Push history LFS push Org support Sync check
GitHub ✅ ✅ ✅ ✅ ✅
GitLab ❌ ❌ ❌ ❌ ❌
Bitbucket ❌ ❌ ❌ ❌ ❌

Legend

Symbol Meaning
✅ Fully supported
⚠️ Partially supported — works but requires manual steps or workarounds
❌ Not supported

Requirements

  • Python 3.8+
  • git on PATH
  • git-lfs on PATH (optional — required only for LFS object transfer)

Installation

pip install git-provider-migrator

Or install from source:

git clone https://github.com/emanuelflp/git-provider-migrator.git
cd git-provider-migrator
pip install -e .

Destination: GitHub

All migrations push to GitHub. Create a Personal Access Token with the following permissions:

Token type Required permissions
Classic repo (full control)
Fine-grained Contents (read/write) · Metadata (read) · Administration (read/write)
export GITHUB_TOKEN="ghp_..."

Token resolution order

Tokens are resolved in this priority order:

Priority Source Details
1 CLI flags --github-token, --gitlab-token
2 tokens.csv Looked up in the current working directory by default; override with --tokens-csv <path>
3 Environment variables GITHUB_TOKEN, GITLAB_TOKEN

If a token is not found via any of the three sources, the tool exits with an error.


Usage by Source Provider

GitLab

Authentication

Create a Personal Access Token with the read_repository scope.

export GITLAB_TOKEN="glpat-..."

For self-hosted instances, pass the base URL via --gitlab-base-url.

Migrate a single repository

git-provider-migrator \
  --source-provider gitlab \
  --source-url      https://gitlab.com/your-org/your-repo.git \
  --repo-name       your-repo \
  --private

Migrate to a GitHub organization

git-provider-migrator \
  --source-provider gitlab \
  --github-org      your-github-org \
  --source-url      https://gitlab.com/your-org/your-repo.git \
  --repo-name       your-repo

Batch migration via GitLab API

Automatically fetches the repository list via the GitLab API — no need to maintain a JSON file.

# All repos from a GitLab group
git-provider-migrator --source-provider gitlab --from-gitlab --gitlab-namespace your-group

# All repos from a GitLab user
git-provider-migrator --source-provider gitlab --from-gitlab --gitlab-namespace username

# All repos owned by the authenticated GitLab user
git-provider-migrator --source-provider gitlab --from-gitlab

# From a self-hosted GitLab instance
git-provider-migrator --source-provider gitlab \
  --from-gitlab \
  --gitlab-base-url  https://gitlab.mycompany.com \
  --gitlab-namespace your-group

Archive source projects after sync

Pass --archive-synced to archive each GitLab project once all its branches are in sync:

git-provider-migrator --source-provider gitlab --from-gitlab --archive-synced

GitHub (as source)

Authentication

Create a Personal Access Token with the repo scope (classic) or Contents: read (fine-grained).

git-provider-migrator \
  --source-provider github \
  --gitlab-token    ghp_sourcetoken... \
  --source-url      https://github.com/source-org/your-repo.git \
  --repo-name       your-repo \
  --private

Bitbucket

Authentication

Create an App Password with Repositories: Read permission and embed it in the clone URL.

git-provider-migrator \
  --source-provider bitbucket \
  --source-url      "https://user:app_password@bitbucket.org/workspace/your-repo.git" \
  --repo-name       your-repo \
  --private

Azure DevOps

Authentication

Create a Personal Access Token with Code: Read scope and embed it in the clone URL.

git-provider-migrator \
  --source-url "https://user:token@dev.azure.com/org/project/_git/your-repo" \
  --repo-name  your-repo \
  --private

Gitea / Forgejo

Authentication

Create an API token in Settings → Applications → Access Tokens with repository: read permission.

git-provider-migrator \
  --gitlab-token your-gitea-token \
  --source-url   https://gitea.mycompany.com/user/your-repo.git \
  --repo-name    your-repo \
  --private

Any other provider

Any HTTPS URL with embedded credentials works out of the box:

git-provider-migrator \
  --source-url "https://user:token@git.mycompany.com/repo.git" \
  --repo-name  your-repo \
  --private

Batch Migration from a JSON File

Works with any mix of source providers in the same file:

[
  {
    "source_url":  "https://gitlab.com/your-org/frontend.git",
    "repo_name":   "frontend",
    "private":     true,
    "description": "Frontend application"
  },
  {
    "source_url":  "https://github.com/source-org/backend.git",
    "repo_name":   "backend",
    "private":     true
  },
  {
    "source_url":  "https://user:pass@bitbucket.org/workspace/mobile.git",
    "repo_name":   "mobile",
    "private":     true
  }
]
git-provider-migrator --batch-file repositories.json

CLI Reference

Provider selection

Flag Default Description
--source-provider gitlab Source Git provider: gitlab, github, bitbucket
--dest-provider github Destination Git provider (only github supported)

Authentication

Flag Default Description
--github-token $GITHUB_TOKEN GitHub personal access token
--gitlab-token $GITLAB_TOKEN Source provider personal access token
--tokens-csv ./tokens.csv Path to a CSV file with github_token and gitlab_token columns (defaults to tokens.csv in the current directory)

Single-repo migration

Flag Default Description
--source-url — Source repository HTTPS URL
--repo-name — Target repository name on GitHub
--private true Create the repository as private
--public — Create the repository as public
--description — Description for the new repository

Batch migration

Flag Default Description
--batch-file — Path to a JSON file listing repositories to migrate
--from-gitlab — Fetch repository list from the GitLab API (requires --source-provider gitlab)
--gitlab-namespace (authenticated user) GitLab group path or username (only with --source-provider gitlab)
--gitlab-base-url https://gitlab.com GitLab instance URL (only with --source-provider gitlab)

Destination

Flag Default Description
--github-org (user account) Migrate into a GitHub organization instead of the user account

Behaviour

Flag Default Description
--archive-synced false Archive the source project after all branches sync (GitLab only)
--workers 1 Number of parallel migration threads
--commits-per-slice (disabled) Push branches in slices of N commits. Use only when the push fails due to GitHub's 2 GB limit (e.g. --commits-per-slice 200). A warning is shown automatically when the mirror exceeds 2 GB.
--skip-lfs false Skip Git LFS binary object transfer
--errors-output migration_errors_<ts>.csv Path for the error report CSV
--debug false Enable verbose DEBUG logging

Python API

from migrator.clients.github import GitHubMigrator

migrator = GitHubMigrator(github_token="ghp_...", github_org="my-org")

# Single repository (any source)
success, reason = migrator.migrate_repository(
    source_url="https://gitlab.com/your-org/your-repo.git",  # any git URL
    repo_name="your-repo",
    gitlab_token="glpat-...",   # source token, if needed
    private=True,
)

# Multiple repositories (mixed sources)
results = migrator.migrate_repositories(
    repositories=[
        {"source_url": "https://gitlab.com/org/repo1.git",           "repo_name": "repo1", "private": True},
        {"source_url": "https://github.com/source-org/repo2.git",    "repo_name": "repo2", "private": True},
        {"source_url": "https://user:pass@bitbucket.org/ws/repo3.git","repo_name": "repo3", "private": True},
    ],
    gitlab_token="glpat-...",
    workers=4,
)

for repo, info in results.items():
    print(repo, "✓" if info["success"] else f"✗ {info['reason']}")

How It Works

  1. Compare — if the repository already exists on GitHub, all branches are compared against the source. If GitHub is up to date, the migration is skipped.
  2. Clone — the source is cloned with git clone --mirror into a temporary directory.
  3. LFS — existing LFS objects are fetched from the source; plain blobs larger than 100 MB are automatically converted to LFS via git lfs migrate import.
  4. Push — all refs are pushed with git push --mirror by default. For repositories that exceed GitHub's 2 GB per-push limit, use --commits-per-slice N to push branches in commit slices.
  5. LFS push — LFS objects are pushed to GitHub with git lfs push --all.
  6. Cleanup — the temporary clone is removed.

Contributing

Contributions are welcome. Please open an issue to discuss significant changes before submitting a pull request.

git clone https://github.com/emanuelflp/git-provider-migrator.git
cd git-provider-migrator
pip install -e .

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages