GitLabリポジトリをサーバー間で移行する

GitLabリポジトリ（プロジェクト）で管理しているソースコードをブランチ、コミット履歴を含めて別サーバーのGitLabに移行する作業を行ったのでその手順をまとめました。
移行方法はいくつかありますが、今回はgit cloneでmirrorオプションを使って移行する方法を採用したのでその手順を説明します。
また今回は200件弱のリポジトリを移行したため、作業の効率を上げるために一部の作業をシェルスクリプトにして実行したのでその手順も最後に説明します。

# 移行の流れ
移行の大まかな流れは以下の図のようになります。

①旧GitLabサーバーから作業端末のローカルディレクトリにclone
②remoteリポジトリを新GitLabのリポジトリに変更する
③新GitLabのリポジトリにpushする

![移行イメージ図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124648/63a7ef07-75da-26cb-6b48-1b714684c5ee.png)

# 環境情報
旧GitLabサーバー：v15.11.12
新GitLabサーバー：v16.7.0
作業PC：Windows10
作業はWindowsのPowershell上で行いました

# 前提
移行作業には以下の前提を満たしている必要があります。
* 新旧GitLabサーバーのURLを控えていること
* 作業PCのPowershellにgitが導入済みであること


# 手順
ここから実際の移行手順を説明していきます。

### 1. 新GitLabサーバーにProjectを作成する
あらかじめ新GitLabに移行先となるGitLabプロジェクトを作成しておきます。今回は多数のプロジェクトを移行する必要があった＋移行先のプロジェクトに共通の設定が必要であったため、GitLabのProject Templateという機能を使ってプロジェクトを作成しました。具体的なプロジェクトの作成方法についてはここでは省略します。

[Project Templateに関するGitLab公式ドキュメント（英語）](https://docs.gitlab.com/ee/user/group/custom_project_templates.html "GitLab Custom group-level project templates")

### 2. 旧GitLabのProjectをローカルにcloneする
作業端末で以下のコマンドを実行して旧GitLabからプロジェクトをCloneする。
```
git clone --mirror <移行元プロジェクトのurl.git>
```

:::note info
cloneする際に`--mirror`オプションを付けることで、リポジトリのすべてのブランチやタグを含めてcloneすることが出来ます。
:::

### 3. cloneしたリポジトリのリモートリポジトリに移行先を追加する
cloneして作成された.gitディレクトリに入ってリモートリポジトリを追加します

```
cd <cloneしたリポジトリ>
git remote add <任意のリモート名> <移行先プロジェクトURL.git>
```
``` markdown:例
cd sample-project.git
git remote add repo_to https://sample-gitlab.com/group/new-sample-project.git
```

### 4. 追加したリモートリポジトリを指定してpushする
上記で追加したリモートリポジトリを指定して以下のコマンドの通りpushします。
```
git push --force --mirror <追加したリモート名>
```
``` markdown:例
git push --force --mirror repo_to
```

以上の手順で移行完了です。

# 注意点
移行時の注意点をまとめました。
### 1. Marge Requestは移行されない
git clone、git pushで--mirrorオプションを指定するとブランチや履歴は移行される一方で、marge requestは移行されません。なので移行元にmarge requestがある場合は先にマージしておくか、コミットは移行されるので移行後にもう一度marge requestを作成する必要があります。
また、移行元リポジトリにmarge requestが存在する場合、ローカルから移行先へpushする際にmarge requestが移行できない旨の警告が出てしまいますが、単純にmarge requestが移行されないだけで、push自体に問題はありません。

### 2. ファイルサイズが大きいとpushの処理が止まる場合がある
リポジトリに含まれるファイルのサイズが大きい場合、ローカルからpushする際に以下の様に途中で処理が止まってしまう場合があります。調べてみるとどうやらgit Configで設定できる”http postBuffer”が関係しているようでした。この”http postBuffer”のデフォルト値が1MBなのですがこれを超えるファイルサイズの場合処理が止まってしまうため、移行するファイルのサイズに応じて設定値を変更しておく必要があります。
["http postBuffer"設定の変更方法はこちらのQiita記事を参照してください](https://qiita.com/akiko-pusu/items/2d65a54e9d2a6c7f9d13 "Qiita:gitで大きいサイズのファイルを扱う時")

なおgit Configコマンドで設定した場合、適用されるのはコマンドを打ったリポジトリのみに適用されるため、ローカル内の他のgitリポジトリにも設定を適用するためには-globalオプションを指定するか必要があります。

# 補足
今回の移行では対象リポジトリが多数あったため、作業効率を上げるためにコマンドをPowershellスクリプトに書き出して実行しました。新旧のGitLabでリポジトリのパスが異なる（=所属するグループの名前が異なる）ため、リポジトリごとの新旧グループ名を配列（$arr）に格納して参照しています。また、移行が１件完了する度に正しく移行出来ているかGitLabのGUIで確認したかったため、”git push”後に”Pause”を入れることで、１件完了する度に処理が一時停止するようにしてみました。

``` markdown:example.ps1
#$arrに移行対象のリポジトリ情報を格納する
$arr = @(

  ('Group 1', 'New Group 1','Project 1'),

  ('Group 2', 'New Group 2', 'Project 2')

)

for ($i = 0; $i -lt $arr.Count; $i++) {

  $oldUrl = ("https://gitlab.com/" + $arr[$i][0] + "/" + $arr[$i][1] + ".git")

  $newUrl = ("https://10.184.2.207/sp1/git/" + $arr[$i][2] + "/" + $arr[$i][3] + ".git")

  $reponame = ($arr[$i][1] + ".git")

  git clone --mirror $oldUrl

  cd $reponame

  git remote add repo_to $newUrl

  git push --mirror --force repo_to

  git branch

  Pause

  cd ../

}
```
以下のコマンドで実行
```
powershell -ExecutionPolicy .\example.ps1
```