# 使用 GraphQL 将存储库从 GitLab 迁移到 GitHub Enterprise Cloud

可以生成自己的工具，将存储库从 GitLab GitHub Enterprise Cloud 迁移到使用 GraphQL API。

> \[!NOTE] 您也可以使用 GL2GH extension of the GitHub CLI 执行迁移操作。 请参阅“[了解从 GitLab 迁移到 GitHub](/zh/migrations/using-github-enterprise-importer/migrate-from-gitlab/understand-migrations)”。

## 步骤 0：准备好使用 GitHub GraphQL API

要进行 GraphQL 查询，需要编写自己的脚本或使用 HTTP 客户端（如 [Insomnia](https://insomnia.rest/)）。

若要详细了解 GitHub GraphQL API 入门（包括如何进行身份验证），请参阅“[使用 GraphQL 建立调用](/zh/graphql/guides/forming-calls-with-graphql)”。

将所有 GraphQL 查询发送到迁移目标。\*\*\*\* 如果要迁移到 带有数据驻留权的 GitHub Enterprise Cloud，请确保将查询发送到 GHE.com 的企业子域的终结点。

## 步骤 1：获取迁移目标的 `ownerId`

作为 GitHub Enterprise Cloud 中的组织所有者，请使用 `GetOrgInfo` 查询为要拥有已迁移存储库的组织返回 `ownerId`（也称为组织 ID）。 需要使用 `ownerId` 来标识迁移目标。

#### `GetOrgInfo` 查询

```graphql
query(
  $login: String!
){
  organization (login: $login)
  {
    login
    id
    name
    databaseId
  }
}
```

| 查询变量    | 说明    |
| ------- | ----- |
| `login` | 组织名称。 |

#### `GetOrgInfo` 响应

```json
{
  "data": {
    "organization": {
      "login": "Octo",
      "id": "MDEyOk9yZ2FuaXphdGlvbjU2MTA=",
      "name": "Octo-org",
      "databaseId": 5610
    }
  }
}
```

在此示例中，`MDEyOk9yZ2FuaXphdGlvbjU2MTA=` 是组织 ID 或 `ownerId`，在下一步中会用到它。

## 步骤 2：确定要从何处迁移

可以使用 `createMigrationSource` 查询设置迁移源。 需要提供从 `GetOrgInfo` 查询收集的 `ownerId` 或组织 ID。

迁移源是 GitLab 实例。

### `createMigrationSource` 突变

```graphql
mutation createMigrationSource($name: String!, $url: String!, $ownerId: ID!) {
  createMigrationSource(input: {name: $name, url: $url, ownerId: $ownerId, type: GITLAB}) {
    migrationSource {
      id
      name
      url
      type
    }
  }
}
```

设置为 `url` GitLab 实例的完整 URL，例如 `https://gitlab.com` 或 `https://gitlab.example.com`。 请确保对 `GITLAB` 使用 `type`。

| 查询变量      | 说明                                 |
| --------- | ---------------------------------- |
| `name`    | 迁移源的名称。 此名称仅供你自己参考，因此可以使用任何字符串。    |
| `ownerId` | GitHub Enterprise Cloud 上组织的组织 ID。 |

### `createMigrationSource` 响应

```json
{
  "data": {
    "createMigrationSource": {
      "migrationSource": {
        "id": "MS_kgDaACQxYmYxOWU4Yi0wNzZmLTQ3NTMtOTdkZC1hNGUzZmYxN2U2YzA",
        "name": "GitLab Source",
        "url": "https://gitlab.com",
        "type": "GITLAB"
      }
    }
  }
}
```

在此示例中，`MS_kgDaACQxYmYxOWU4Yi0wNzZmLTQ3NTMtOTdkZC1hNGUzZmYxN2U2YzA` 是迁移源 ID，我们将在后面的步骤中使用。

## 步骤 3：生成和托管迁移存档

从 GitLab 迁移基于存档。 导入从 GitLab 项目生成的迁移存档， GitHub Enterprise Importer 而不是在迁移过程中连接到 GitLab 实例。 GitLab 存档是包含 Git 源和存储库元数据的单个文件。

在开始迁移之前，必须：

1. 为要迁移的 GitLab 项目生成迁移存档。
2. 在可访问的 GitHub Enterprise Cloud URL 上托管存档。

在下一步中，你将提供此 URL 作为 `gitArchiveUrl` 值。

### 生成迁移存档

使用 GitLab [项目导出 API](https://docs.gitlab.com/api/project_import_export/) 导出要迁移的项目。 使用的令牌必须具有 `api` 用于导出项目的作用域和角色。 有关详细信息，请参阅“[管理从 GitLab 迁移到 GitHub的访问](/zh/migrations/using-github-enterprise-importer/migrate-from-gitlab/manage-access)”。

在以下请求中，将 `GITLAB_PAT` 环境变量设置为在 [管理从 GitLab 迁移到 GitHub的访问](/zh/migrations/using-github-enterprise-importer/migrate-from-gitlab/manage-access) 中创建的令牌。 替换为 `GITLAB-SERVER` GitLab 实例的主机，例如 `gitlab.com`，替换为 `GROUP%2FPROJECT` 项目的 URL 编码路径。 例如，项目 `acme-group/my-project` 编码为 `acme-group%2Fmy-project`. 对于嵌套子组，包括完整路径，例如 `parent-group%2Fsubgroup%2Fmy-project`。

1. 计划导出。

   ```shell
   curl --request POST \
     --header "PRIVATE-TOKEN: $GITLAB_PAT" \
     "https://GITLAB-SERVER/api/v4/projects/GROUP%2FPROJECT/export"
   ```

2. 检查导出的状态。 重复此请求，直到 `export_status` 是 `finished`。

   ```shell
   curl --header "PRIVATE-TOKEN: $GITLAB_PAT" \
     "https://GITLAB-SERVER/api/v4/projects/GROUP%2FPROJECT/export"
   ```

3. 下载存档。

   ```shell
   curl --location \
     --header "PRIVATE-TOKEN: $GITLAB_PAT" \
     --output archive.tar.gz \
     "https://GITLAB-SERVER/api/v4/projects/GROUP%2FPROJECT/export/download"
   ```

### 托管存档

必须在可访问的 GitHub Enterprise Cloud URL 上托管存档。 可以将存档 GitHub-owned blob storage 上传到外部 Blob 存储提供程序或使用外部 Blob 存储提供程序。 有关外部提供程序的信息，请参阅 [配置 Blob 存储](/zh/migrations/using-github-enterprise-importer/migrate-from-gitlab/configure-storage)。

若要将存档 GitHub-owned blob storage上传到，需要组织 GitHub Enterprise Cloud的数据库 ID。 替换为 `ORGANIZATION` 组织的名称，以便从 `id` 响应中的字段获取此 ID。

```shell
curl --header "Authorization: Bearer YOUR-TOKEN" \
  "https://api.github.com/orgs/ORGANIZATION"
```

> \[!NOTE] 如果要迁移到 GHE.com，请替换为 `https://api.github.com` 企业子域的基本 API URL，例如 `https://api.octocorp.ghe.com`。

使用 `POST` 请求上传存档，并将其 `ORGANIZATION-ID` 替换为组织的数据库 ID。 此请求适用于高达 100 MiB 的存档。 对于较大的存档，请使用外部 Blob 存储提供程序。

```shell
curl --request POST \
  --header "Authorization: Bearer YOUR-TOKEN" \
  --header "Content-Type: application/octet-stream" \
  --data-binary @archive.tar.gz \
  "https://uploads.github.com/organizations/ORGANIZATION-ID/gei/archive?name=archive.tar.gz"
```

> \[!NOTE] 如果要迁移到 GHE.com，请替换为 `uploads.github.com` 企业子域的上传主机，例如 `uploads.octocorp.ghe.com`。

响应采用 `uri` 格式 `gei://archive/GUID`。 在下一步中使用此值作为 `gitArchiveUrl` 。

```json
{
  "guid": "ff7b1a25-aa10-41a9-8e42-f170304b1c0d",
  "node_id": "MA_kgDaACRmZjdiMWEyNS1hYTEwLTQxYTktOGU0Mi1mMTcwMzA0YjFjMGQ",
  "name": "archive.tar.gz",
  "size": 7103,
  "uri": "gei://archive/ff7b1a25-aa10-41a9-8e42-f170304b1c0d",
  "created_at": "2024-11-13T12:35:45.761-08:00"
}
```

## 步骤 4：开始存储库迁移

开始迁移时，单个存储库及其随附的数据将迁移到你标识的全新 GitHub 存储库。

如果要从同一源组织一次移动多个存储库，则可以将多个迁移排入队列。 最多可以同时运行 5 个存储库迁移。

### `startRepositoryMigration` 突变

```graphql
mutation startRepositoryMigration (
  $sourceId: ID!,
  $ownerId: ID!,
  $sourceRepositoryUrl: URI!,
  $repositoryName: String!,
  $continueOnError: Boolean!,
  $accessToken: String!,
  $githubPat: String!,
  $gitArchiveUrl: String!,
  $targetRepoVisibility: String!
){
  startRepositoryMigration( input: {
    sourceId: $sourceId,
    ownerId: $ownerId,
    repositoryName: $repositoryName,
    continueOnError: $continueOnError,
    accessToken: $accessToken,
    githubPat: $githubPat,
    targetRepoVisibility: $targetRepoVisibility,
    gitArchiveUrl: $gitArchiveUrl,
    sourceRepositoryUrl: $sourceRepositoryUrl,
  }) {
    repositoryMigration {
      id
      migrationSource {
        id
        name
        type
      }
      sourceUrl
    }
  }
}
```

| 查询变量                   | 说明                                                                                                                                               |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `sourceId`             | 从 `createMigrationSource` 突变返回的迁移源 `id`。                                                                                                         |
| `ownerId`              | 组织在 GitHub Enterprise Cloud 上的组织 ID。                                                                                                             |
| `repositoryName`       | 组织在 GitHub Enterprise Cloud 上拥有的任何存储库当前未使用的自定义唯一存储库名称。 迁移完成或停止时，将在此存储库中创建一个错误记录问题。                                                               |
| `continueOnError`      | 迁移设置，允许在遇到不会导致迁移失败的错误时继续迁移。 必须为 `true` 或 `false`。 强烈建议将 `continueOnError` 设置为 `true`，以便继续迁移，除非 Importer 无法移动 Git 源，或 Importer 已断开连接且无法重新连接以完成迁移。 |
| `githubPat`            | 目标组织在 personal access token 上的 GitHub Enterprise Cloud。                                                                                          |
| `accessToken`          | 源的 personal access token。                                                                                                                        |
| `targetRepoVisibility` | 新存储库的可见性。 必须为 `private`、`public` 或 `internal`。 如果未设置，则存储库将作为专用存储库迁移。                                                                             |

|
`gitArchiveUrl` |在上一 GitHub Enterprise Cloud步中生成的迁移存档的可访问 URL。 GitLab 迁移使用包含 Git 源和元数据的单个存档，因此无需提供单独的 `metadataArchiveUrl`存档。

\| `sourceRepositoryUrl` |GitLab 上的源存储库的 URL，采用格式 `https://GITLAB-SERVER/{group}/{project}`。 对于嵌套子组，包括完整路径，例如 `https://GITLAB-SERVER/{parent-group}/{subgroup}/{project}`。
GitHub Enterprise Cloud 在迁移期间不连接到此 URL;记录以供参考。

由于 GitLab 迁移是基于存档的， GitHub Enterprise Cloud 因此在迁移过程中不会连接到 GitLab。 该 `accessToken` 变量是突变所必需的，但未使用，因此你可以将其设置为任何占位符值，例如 `not-used`。

有关 personal access token 要求，请参阅 [管理从 GitLab 迁移到 GitHub的访问](/zh/migrations/using-github-enterprise-importer/migrate-from-gitlab/manage-access)。

在下一步中，将使用从 `startRepositoryMigration` 突变返回的迁移 ID 来检查迁移状态。

## 步骤 5：检查迁移的状态

要检测任何迁移失败并确保迁移正常工作，可以使用 `getMigration` 查询检查迁移状态。 还可以使用 `getMigrations` 检查多个迁移的状态。

`getMigration` 查询将返回状态，让你知道迁移状态是 `queued`、`in progress`、`failed` 还是 `completed`。 如果迁移失败，Importer 将提供失败原因。

#### `getMigration` 查询

```graphql
query (
  $id: ID!
){
  node( id: $id ) {
    ... on Migration {
      id
      sourceUrl
      migrationSource {
        name
      }
      state
      failureReason
    }
  }
}
```

| 查询变量 | 说明                                                                              |
| ---- | ------------------------------------------------------------------------------- |
| `id` | [`startRepositoryMigration` 突变](#startrepositorymigration-mutation)返回的迁移的 `id`。 |

## 步骤 6：验证迁移并检查错误日志

要完成迁移，建议检查“迁移日志”问题。 此问题在目标存储库中的 GitHub 上创建。

![标题为“迁移日志”的问题的屏幕截图。 问题中的第二个注释包括迁移的日志。](/assets/images/help/github-enterprise-importer/migration-log-issue.png)

最后，建议查看已迁移的存储库以进行完整性检查。

## 延伸阅读

* [后续任务](/zh/migrations/using-github-enterprise-importer/migrate-from-gitlab/follow-up-tasks)