2つの Git リポジトリ間で、コードと Wiki をミラーリングする方法を紹介します。GitLab を使って動作確認をしていますが、GitHub でも同様だろうと思います。
また、例えば GitLab には、プロジェクトを新規作成するときに別の Git リポジトリから インポートする機能 や、ずばり ミラーリングする機能 などがデフォルトで存在します。ここでは、ネットワークの問題や利用エディションの問題などで、これらの機能ではなく git コマンドだけで実現したいケースを想定しています。
確認環境
- Git 2.30.0
- GitLab 14.9.0-pre
リポジトリ構成
- ミラーリング元のリポジトリ
https://gitlab.com/oohira/awesome-source.git
- ミラーリング先のリポジトリ
- GitLab で空のプロジェクトを作成しておく
https://gitlab.com/oohira/awesome-mirror.git
push 前に確認する
作業の性質上、間違っていないか不安になるので、git remote -v や git push --dry-run などを使って、push しようとしているミラーリング先リポジトリに間違いがないか、事前によく確認するとよいでしょう。
コードをミラーリングする方法1
後述しますが、GitLab の Merge Request や GitHub の Pull Request がない場合は、この方法が簡単です。
## 1. ミラーリング元リポジトリをローカルに取得
$ git clone --mirror https://gitlab.com/oohira/awesome-source.git awesome-mirror
$ cd awesome-mirror
## 2. push先だけミラーリング先リポジトリに差し替えて確認
$ git remote set-url --push origin https://gitlab.com/oohira/awesome-mirror.git
$ git remote -v
origin https://gitlab.com/oohira/awesome-source.git (fetch)
origin https://gitlab.com/oohira/awesome-mirror.git (push)
## 3. ミラーリング元リポジトリの変更をローカルに取得(初回は不要)
$ git remote update
## 4. ローカルをミラーリング先リポジトリにpush
$ git push --mirror
...
To https://gitlab.com/oohira/awesome-mirror.git
* [new branch] dev -> dev
* [new branch] main -> main
* [new tag] v1.0 -> v1.0
* [new tag] v2.0 -> v2.0
GitLab CI の注意
.gitlab-ci.yml が含まれていると、ミラーリング先でも意図せず GitLab CI がキックされてしまう可能性があります。ミラーリング先では CI/CD など使用予定のない機能は無効にしておくとよいでしょう。
保護ブランチ(タグ)の注意
GitLab や GitHub には、特定のブランチやタグを保護する機能があります(protected branches/tags)。ミラーリング先のリポジトリでこの設定が有効になっている場合、git push で上書きしようとしたタイミングで失敗する可能性があるので、その場合は保護設定を見直してください。
bare リポジトリの注意
--mirror オプションの指定によりワークツリーをもたない bare リポジトリが作成されるため、このディレクトリ内では git status など一部の Git コマンドが使えません。そのため、シェルのプロンプトに Git の情報を表示するようにしている場合は、何かコマンドを実行するたびに裏で git コマンドが実行され、fatal: this operation must be run in a work tree
のメッセージが表示されることがあります。私の環境でも起きましたが、いったん無視してコマンド例からも除外しています。
Merge Request や Pull Request がある場合、GitLab や GitHub が refs/merge-requests/1
のような ref を追加で作っているようで、これを push しようとすると失敗します。無視して放っておいても実害はないのですが、気になる場合は「コードをミラーリングする方法2」をお試しください。
! [remote rejected] refs/merge-requests/1/head -> refs/merge-requests/1/head (deny updating a hidden ref)
! [remote rejected] refs/merge-requests/1/merge -> refs/merge-requests/1/merge (deny updating a hidden ref)
! [remote rejected] refs/merge-requests/14/head -> refs/merge-requests/14/head (deny updating a hidden ref)
! [remote rejected] refs/merge-requests/14/merge -> refs/merge-requests/14/merge (deny updating a hidden ref)
...
error: failed to push some refs to 'https://gitlab.com/oohira/awesome-mirror.git'
コードをミラーリングする方法2
GitLab の Merge Request や GitHub の Pull Request がある環境下でも、不要な警告を出さずにミラーリングする方法です。初回 fetch する前に fetch 対象を調整する必要があるので、手動でリポジトリをセットアップする必要があり、若干面倒です。
## 1. 空のリポジトリを作成(git clone --mirror は使わない)
$ git init --bare awesome-mirror
$ cd awesome-mirror/
## 2. fetch元にミラーリング元リポジトリを登録し、fetch対象をブランチ&タグに限定
$ git remote add --mirror=fetch origin https://gitlab.com/oohira/awesome-source.git
$ vi config
...
[remote "origin"]
url = https://gitlab.com/oohira/awesome-source.git
- fetch = +refs/*:refs/*
+ fetch = +refs/heads/*:refs/heads/*
+ fetch = +refs/tags/*:refs/tags/*
## 3. push先にミラーリング先リポジトリを登録して確認
$ git remote set-url --push origin https://gitlab.com/oohira/awesome-mirror.git
$ git remote -v
origin https://gitlab.com/oohira/awesome-source.git (fetch)
origin https://gitlab.com/oohira/awesome-mirror.git (push)
## 4. ミラーリング元リポジトリの変更をローカルに取得
$ git remote update
## 5. ローカルをミラーリング先リポジトリにpush
$ git push --mirror
...
To https://gitlab.com/oohira/awesome-mirror.git
* [new branch] dev -> dev
* [new branch] main -> main
* [new tag] v1.0 -> v1.0
* [new tag] v2.0 -> v2.0
Wiki をミラーリングする方法
GitLab や GitHub では、Wiki も専用の Git リポジトリ(〜.wiki.git
)で管理されているので、「コードをミラーリングする方法1」と同じ手法で Wiki のミラーリングを実現できます。リポジトリの URL を Wiki 用に変更する点だけ注意してください。
## 1. ミラーリング元リポジトリをローカルに取得
$ git clone --mirror https://gitlab.com/oohira/awesome-source.wiki.git awesome-mirror.wiki
$ cd awesome-mirror.wiki/
## 2. push先だけミラーリング先リポジトリに差し替えて確認
$ git remote set-url --push origin https://gitlab.com/oohira/awesome-mirror.wiki.git
$ git remote -v
origin https://gitlab.com/oohira/awesome-source.wiki.git (fetch)
origin https://gitlab.com/oohira/awesome-mirror.wiki.git (push)
## 3. ミラーリング元リポジトリの変更をローカルに取得(初回は不要)
$ git remote update
## 4. ローカルをミラーリング先リポジトリにpush
$ git push --mirror
Wiki リンクの注意
Wiki ページ間のリンクに Wiki 記法を使わず、絶対 URL で記載していると、ミラーリング先リポジトリ内でのリンクにならず、ミラーリング元と同じ URL へのリンクになってしまいます。ミラーリング先リポジトリ内で完結してリンクを機能させるためには、Wiki 記法を使った相対リンクでの記述を徹底しましょう。
補足
- ミラーリング先リポジトリでの変更の扱い
- やらないと思いますが、ミラーリング先のリポジトリで独自のコミットをしても、ミラーリング元の変更を再度 push するタイミングで上書きされます。ただし、force push されるので、保護ブランチが無効になっている必要があります
- 間違ってミラーリング元に push した場合の扱い
- 万が一、ミラーリング元のコミット履歴の方が進んでいる状態で、ローカルを誤って(ミラーリング先ではなく)ミラーリング元に push すると、ミラーリング元のコミット履歴が上書きされて先祖返りします。恐ろしいので、ここで紹介した方法は push 先が常にミラーリング先を向くようにしています(=ミラーリング元とミラーリング先で remote を分けるのではなく、1つの remote の fetch/push で分ける)
- 細かいことを気にせずもっと簡単にミラーリングする方法
- git clone した後で origin とは別の remote を1つ登録して push すれば、ほぼ近しいことはできます。main ブランチだけミラーリングできればよい場合などは、これで十分かもしれません