Skip to main content
The Repository Migration API lets you move a project’s Git repository to a different repository or Git provider without recreating the project. Use these endpoints to send a single request that starts a migration, then poll a status endpoint until the migration is no longer in progress. migrates the project’s working tree to the target repository you specify and updates your project to use the target repository.
  • Migration is an asynchronous, long-running operation. Only one migration can run for a project at any time.
  • While a migration is in progress, all other Git operations on the project’s repository are blocked until the migration is no longer in progress.

Prerequisites

Before you begin, you need:
  • An existing project that has an associated Git repository. To associate a repository with a project, see Initialize Repository.
  • A target repository that already exists on the target Git provider. does not create the target repository for you. The supported target providers are GitHub, GitLab, Azure DevOps, and Bitbucket.
If a repository involved in the migration is hosted on Azure DevOps, GitLab, or Bitbucket, complete the additional setup described below for that provider before starting the migration.

Azure DevOps

If either the source or target repository is hosted on Azure DevOps, you must:
  1. Install the Maia for Azure DevOps application in the Azure tenant that hosts the repository. If the application is already installed in that tenant—for example, because another user has previously connected an Azure DevOps repository to —you can skip this step. Otherwise, follow Installing the Maia app in Azure.
  2. Add the service principal to the Azure DevOps repository, and grant it explicit Read and Force push (rewrite history, delete branches and tags) permissions.
Migration requires the service principal to have explicit Force push permission on the repository, in addition to Read. Read and Contribute permissions alone are not sufficient—without Force push, can’t complete a MIRROR_PUSH migration.
For details on Azure DevOps access controls, refer to the Microsoft documentation on permissions and access.

GitLab

If the target repository is hosted on GitLab, you must:
  1. Provide the personal access token for a GitLab service account using the Git-Provider-Access-Token request header.
  2. Add the service account to the group, or to the project that the group belongs to—depending on whether it’s a group-level or project-level service account—with the Maintainer role or higher.
  3. Allow force pushes on the target repository. Force pushes aren’t allowed by default. You can allow them at the group level, or configure the setting per-repository in the target repository’s protected branch settings.
Migration requires the service account to have Maintainer access or higher on the target repository, and force pushes must be allowed on it. Without both, can’t complete a MIRROR_PUSH migration.
The Git-Provider-Access-Token header is optional, and is only required when the target repository is hosted on GitLab or Bitbucket.
For information on how to acquire a service account personal access token, see GitLab in the Project Provisioning API.
rotates the personal access token immediately after you provide it, and revokes the original. This means a personal access token can only be used once—if you start a migration more than once, for example after a failed attempt, provide a fresh personal access token each time.
If the source repository (the project’s currently associated repository) is hosted on GitLab, you don’t need to provide a token for it— already holds a valid, automatically rotated personal access token from when the repository was first associated with the project. If that token has expired and was unable to rotate it automatically, manually provide a new one using Manually rotate an expired personal access token before starting the migration.

Bitbucket

If the target repository is hosted on Bitbucket Cloud, you must:
  1. Create a Bitbucket access token with write access to the target repository, then provide it using the Git-Provider-Access-Token request header. A repository access token with the repository:write scope is recommended, because it grants the narrowest access needed. For how to create one, see Create a Bitbucket access token in the Project Provisioning API.
  2. Make sure the target repository allows force pushes. Branch restrictions must not prevent force pushes or history rewrites.
Migration requires a Bitbucket access token with write access to the target repository, and the target repository must allow force pushes. Without both, can’t complete a MIRROR_PUSH migration.
Unlike GitLab, doesn’t rotate the Bitbucket access token—it stores the token exactly as you supply it. If the token has expired or been revoked, can’t authenticate with Bitbucket, and the migration fails. Create a new token and supply it again when you start the migration.

Migration modes

Use the mode field to choose how the migration runs. The two modes, MIRROR_PUSH and RECONFIGURE, are explained in the table below.

Start a repository migration

Use this endpoint to start an asynchronous migration of the project’s Git repository to the target repository. This request returns a migrationId that you can use to track the progress of this migration.

Request headers

Request fields

Example request

Base URL: POST /v1/projects/{projectId}/repository-migration Replace {projectId} with your project ID. Example request header:
Example request body:
To migrate to an Azure DevOps repository, include the providerParameters object with the organization, repository ID, and tenant ID for the target repository:
If the target repository is hosted on GitLab, include the Git-Provider-Access-Token header in your request, in addition to Authorization:
Also include the providerParameters object with the repository ID for the target repository:
If the target repository is hosted on Bitbucket, include the Git-Provider-Access-Token header in your request, in addition to Authorization:
Also include the providerParameters object with the workspace slug and repository slug for the target repository:
Example success response:

Check migration status

Use this endpoint and the migrationId returned by the previous request to poll the status of a migration. Repeat this request until the status field is no longer IN_PROGRESS.

Example request

Base URL: GET /v1/projects/{projectId}/repository-migration/{migrationId} Replace {projectId} with your project ID and {migrationId} with the migration ID returned when you started the migration. Example request header:
Example success response:
The response contains the following fields:
  • migrationId: The unique identifier of the repository migration.
  • status: The current status of the migration. The table below explains what each status means.
  • detail: An optional, human-readable description of the migration’s current state, such as a failure reason. This field is only included if this information is available.

Error responses

If an error occurs, the repository migration endpoints return a ProblemDetail response body. The table below explains what the included status code means.