GitLabのプロジェクトを別のグループ配下に移動する

## はじめに

GitLabで管理しているリポジトリについて、１つのグループ配下のプロジェクトの数が多くなってしまった際に、後からサブグループを追加して整理したいことがありました。
その場合の既に使用中の既存プロジェクトへの影響が気になったので、確認用のグループ・プロジェクトを用意して動作確認しました。

※ ここで言う「グループ」「プロジェクト」は、GitLabのリポジトリ管理単位を指す用語としてのGroupとProjectです。

## 動作確認に使用した環境

動作確認にはすぐ試せるgitlab.comをとりあえず使用しました。
確認時のバージョンは`14.8.0-pre`でした。

![version.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123840/c79a99ab-59b6-abcc-4544-0ee6b053bfc6.png)

バージョンやエディションによって動作が変わる可能性もありそうなので、実際に整理を行いたいGitLab環境での確認も別途必要となります。

今回の動作確認には関係なさそうですが、一応クライアントのgitバージョンも載せておきます。

```bash
$ git --version
git version 2.32.0 (Apple Git-132)
```


## 変更前の状態

動作確認用の最小限の状態として以下を用意しました。

- グループ（`transfer-test-group1`）
    - グループ配下のプロジェクト（`project1`）

![before-group.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123840/d36c394e-577e-648e-4692-0bf50d0ece6b.png)

既に使用中の想定で、クライアント側でcloneやcommitをしておきます。（ブランクプロジェクト作成後に表示されるコマンド例ほぼそのままです。）

```bash
$ git clone git@gitlab.com:transfer-test-group1/project1.git
$ cd project1

$ git config user.name "testuser1"
$ git config user.email "testuser1@example.com"

$ git switch -c main
Switched to a new branch 'main'

$ touch README.md
$ git add README.md
$ git commit -m "add README"
[main (root-commit) d96e364] add README
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 README.md

$ git push -u origin main
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 214 bytes | 214.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To gitlab.com:transfer-test-group1/project1.git
 * [new branch]      main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.
```

![before-project.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123840/32ebbdd1-d273-736d-fd95-b682c8619da7.png)


IssueとMergeRequestも作成しておきます。

![before-issue.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123840/739bede3-8af2-6cc2-4d3d-5d1021151abf.png)

```bash
$ git checkout -b test
Switched to a new branch 'test'

$ echo 'test1' > README.md

$ git commit -am "change README 1"
[test b76d7d4] change README 1
 1 file changed, 1 insertion(+)

$ git push -u origin test
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 253 bytes | 253.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
remote: 
remote: To create a merge request for test, visit:
remote:   https://gitlab.com/transfer-test-group1/project1/-/merge_requests/new?merge_request%5Bsource_branch%5D=test
remote: 
To gitlab.com:transfer-test-group1/project1.git
 * [new branch]      test -> test
Branch 'test' set up to track remote branch 'test' from 'origin'.
```

![before-mr.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123840/ff804287-4bfa-0dca-dbe6-6f6b943a3f30.png)



## プロジェクトの移動

以下のように、グループの下にサブグループを作成し、グループ直下にあるプロジェクトをサブグループの下に移動します。

- グループ（`transfer-test-group1`）
    - サブグループ（`subgroup1`）
        - サブグループ配下のプロジェクト（`project1`）

公式ドキュメントに記載されているとおり操作します。

https://docs.gitlab.com/ee/user/project/settings/index.html#transferring-an-existing-project-into-another-namespace

まずグループ（`transfer-test-group1`）配下にサブグループ（`subgroup1`）を作成します。

![created-subgroup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123840/6ef6cc4e-ec1f-7f14-d5ba-d824ab13972c.png)

次にプロジェクト（`project1`）をサブグループ（`subgroup1`）の下に移動します。

- `project1`の画面内で左サイドバー「Settings」をクリック
- 「General Settings」画面一番下の「Advanced」セクションの「Expand」ボタンをクリック
- 「Advanced」内の下の方の「Transfer project」セクションでnamespaceとして「`transfer-test-group1/subgroup1`」を選択して「Transfer project」ボタンをクリック
- ダイアログでプロジェクト名（`project1`）を入力して「Confirm」ボタンをクリック
    
![transfer1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123840/6cade26a-d1a8-fdc1-4a22-c288f3edff8a.png)
![transfer2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123840/ab470a2e-630f-1152-72f8-c929433557e6.png)
![transfer3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123840/271de5e7-9d88-89ce-a2ef-de473d63de03.png)

これでプロジェクトがサブグループ配下に移動されました。

![after-project.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123840/8b2f173b-0c7e-4fae-e9ef-f26ef241275f.png)



## 変更後の確認

変更後に、クライアント側で何も変更等せずにそのままcommit、pushを行ってみると、プロジェクトが移動された旨のメッセージが出力されましたが、GitLab側でリダイレクトされて問題なく元のプロジェクトにpushできました。

```bash
# 先ほどpushしてMergeRequestを出したtestブランチで引き続き作業
$ git branch
  main
* test

# リモートリポジトリは変更前のURLに紐づいたまま
$ git remote -v
origin  git@gitlab.com:transfer-test-group1/project1.git (fetch)
origin  git@gitlab.com:transfer-test-group1/project1.git (push)

# pullは問題なく可能
$ git pull
Already up to date.

# ファイルを更新してcommit,push
$ echo 'test2' >> README.md
$ git commit -am "change README 2"
[test 8b235dc] change README 2
 1 file changed, 1 insertion(+)
$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 259 bytes | 259.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
remote: 
remote: View merge request for test:
remote:   https://gitlab.com/transfer-test-group1/subgrup1/project1/-/merge_requests/1
remote: 
remote: 
remote: Project 'transfer-test-group1/project1' was moved to 'transfer-test-group1/subgrup1/project1'.
remote: 
remote: Please update your Git remote:
remote: 
remote:   git remote set-url origin git@gitlab.com:transfer-test-group1/subgrup1/project1.git
remote: 
remote: 
To gitlab.com:transfer-test-group1/project1.git
   b76d7d4..8b235dc  test -> test
```

IssueやMergeRequestはURLが変更されています。

- 変更前
    - Issue
        - `https://gitlab.com/transfer-test-group1/project1/-/issues/1`
    - MergeRequest
        - `https://gitlab.com/transfer-test-group1/project1/-/merge_requests/1`
- 変更後
    - Issue
        - `https://gitlab.com/transfer-test-group1/subgrup1/project1/-/issues/1`
    - MergeRequest
        - `https://gitlab.com/transfer-test-group1/subgrup1/project1/-/merge_requests/1`

IssueとMergeRequestのいずれも、変更前のURLにアクセスすると、変更後のURLにリダイレクトされ、プロジェクトが移動された旨のメッセージが表示されました。

![after-issue-message.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123840/14bdb57c-1f15-5ffe-a496-624bd7ca6dee.png)

リダイレクトされる旨は公式ドキュメントの以下にも記載されています。

https://docs.gitlab.com/ee/user/project/repository/index.html#what-happens-when-a-repository-path-changes


なお、MergeRequest内のコメントでメンションしたIssueのリンクは、変更前のURLとなっていました。さすがにコメント内のリンクまでは自動修正されないようです。

> • The redirects are available as long as the original path is not claimed by another group, user, or project.

ドキュメント内の上記の部分が気になったので、試しに変更前と同じパスで新たにプロジェクト作成してみたところ、特に警告等もなく普通に作成できてしまいました。

![after-new-project.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123840/86d1c917-3a89-b64a-1bda-79a2d37ab593.png)

この状態で変更前のURLでIssueやMergeRequestにアクセスすると、リダイレクトされずにNot Foundが返されるようになりました。

![notfound.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123840/321f4513-812d-a309-cc66-4d9f9b8aff37.png)

また、クライアント側でcommit、pushしてみると、新しく作ったプロジェクトの方にpushされてしまいました。

```bash
$ echo 'test3' >> README.md
$ git commit -am "change README 3"
[test c6a4bbc] change README 3
 1 file changed, 1 insertion(+)
$ git push
Enumerating objects: 12, done.
Counting objects: 100% (12/12), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (12/12), 890 bytes | 296.00 KiB/s, done.
Total 12 (delta 0), reused 0 (delta 0), pack-reused 0
To gitlab.com:transfer-test-group1/project1.git
 * [new branch]      test -> test
```

![after-new-project-updated.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123840/7632eb31-0d85-c9c3-36bf-0249ceda5838.png)


こうなってしまうと問題があるので、クライアント側ではメッセージの指示のとおり`set-url`を実施します。

```bash
$ git remote -v
origin  git@gitlab.com:transfer-test-group1/project1.git (fetch)
origin  git@gitlab.com:transfer-test-group1/project1.git (push)

$ git remote set-url origin git@gitlab.com:transfer-test-group1/subgrup1/project1.git

$ git remote -v
origin  git@gitlab.com:transfer-test-group1/subgrup1/project1.git (fetch)
origin  git@gitlab.com:transfer-test-group1/subgrup1/project1.git (push)
```

リモートリポジトリを修正したうえでpull,pushすると、元のプロジェクトにpushでき、その後WebUI上で無事マージもできました。

```bash
# ログ上は新しいプロジェクトにpush済となっている
$ git log --oneline
c6a4bbc (HEAD -> test, origin/test) change README 3
8b235dc change README 2
b76d7d4 change README 1
d96e364 (origin/main, main) add README

# set-url後にpullすると元のプロジェクトと不整合なため警告が出る
$ git pull
From gitlab.com:transfer-test-group1/subgrup1/project1
 + c6a4bbc...8b235dc test       -> origin/test  (forced update)
hint: Pulling without specifying how to reconcile divergent branches is
hint: discouraged. You can squelch this message by running one of the following
hint: commands sometime before your next pull:
hint: 
hint:   git config pull.rebase false  # merge (the default strategy)
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
hint: 
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
Already up to date.

# デフォルト動作で、元のプロジェクトのリモートリポジトリからmergeされたようだ
$ git log --oneline
c6a4bbc (HEAD -> test) change README 3
8b235dc (origin/test) change README 2
b76d7d4 change README 1
d96e364 (origin/main, main) add README

# 改めてpushすると、元のプロジェクトにpushできた
$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 260 bytes | 260.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
remote: 
remote: View merge request for test:
remote:   https://gitlab.com/transfer-test-group1/subgrup1/project1/-/merge_requests/1
remote: 
To gitlab.com:transfer-test-group1/subgrup1/project1.git
   8b235dc..c6a4bbc  test -> test
```

![after-mr-updated.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123840/d064bbf4-210b-3ce2-a125-30bb7d655220.png)


今回はかなり単純な状態なので、さほど手間なく無事復旧できましたが、実際の開発プロジェクトでこんな状態になってしまうと、復旧が大変なことになってしまう危険があります。


## まとめ

Transfer Projectで比較的容易にプロジェクトの移動ができ、リダイレクトのおかげで影響も小さくなることが確認できました。

ただし、実際にやる場合は、例えば以下のように注意しながら実施する必要があると思います。

- プロジェクト整理前にローカルリポジトリ状態は全てpushしておく
- プロジェクト整理中はリモートリポジトリにアクセスしない
- プロジェクト整理後はすぐにset-url、pullをしてから作業再開する
- プロジェクト整理後は整理前とパスが重複するグループ・プロジェクトを作成しない

なお、プロジェクトでContaner Registry等の他機能を使用している場合は、それらも影響確認が必要です。Container Registryについては、ドキュメントのTransfer Project部分でも以下の記載があります。
> The project must not contain any container images.