GitHub上でGoプロジェクトを正しくTransferする
tl;dr
- リポジトリを新オーナーにTransferする
- 返す刀でTransferしたRepositoryを元オーナー側にForkしもどす
- Forkしたものをアーカイブする
- Transfer先のモジュール名を変更し、新しいタグを打って開発を継続する
GitHubのリポジトリのオーナー変更
この記事はGo Advent Calendar 2021カレンダー2の10日目の記事です。
さて、オーナー変更や個人プロジェクトをオーガニゼーションに移行したい等の理由で、GitHub上のリポジトリのオーナーを変更することがあるでしょう。
GitHubにはTransferring a repositoryの機能があり、それを使えば簡単です。issueやpull request等の履歴も含めて引き継いでくれますし、旧リポジトリから新リポジトリへのリダイレクトも自動的におこなわれます。なのでこれで解決なのですが、Goの場合他にやることがあります。
Goにおける固有状況
Goは御存知の通り、URLとインポートパスが強く結びつく設計であるため、オーナーを変更してURLが変更されると不都合が生じます。Go Modules以降、go.modにモジュール名が記述されるため、そちらとも整合を取る必要があります。例えば、以下の例で考えてみましょう。
- 旧: github.com/motemen/testmod
- 新: github.com/x-motemen/testmod
実は、Trasnferしたばかりの状況では何も問題はありません。旧パッケージ名でimportする分には、GitHubが適切にリダイレクトし、go.mod上のモジュール名も旧名のままであるため、問題なくimportできるからです。しかし、この時、新しいリポジトリ名に沿ったimportはおこなうことができません。go.mod上のパッケージ名と食い違うからです。
import "github.com/motemen/testmod" // OK
import "github.com/x-motemen/testmod" // NG
せっかくrepositoryを移したのにこれでは困ってしまいます。ちなみに、このまま古いインポートパスを使い続けると言う手も実はあるのですが、新規ユーザーの混乱を招きますし、嬉しくはないでしょう。
では、go.modを編集すれば良い、となるのですが、そうすると今度は旧インポートパスが使えなくなってしまうのです。
import "github.com/motemen/testmod" // NG
import "github.com/x-motemen/testmod" // OK
正確には、パブリックリポジトリであればModule Mirrorに旧名でのキャッシュが残っていますし、バージョンやハッシュを指定すれば go get
は旧名でもおそらく可能です。ただ単純な go get
はできなくなります。それは旧名を使っているユーザーにとっては不親切な状態でしょう。
ここで、旧名を使うユーザーを一律切り捨ててしまうという手もあるかもしれませんが、旧名で参照されているものをなるべく維持しようとするのがGo使いらしい態度といえるでしょう。何もしていないのに使っているツールやライブラリが壊れてしまっては嬉しくありません。
解決方法
そこで私が考えたのが以下の方法です。
- リポジトリを新オーナーにTransferする
- 返す刀でTransferしたRepositoryを元オーナー側にForkしもどす
- Forkしたものをアーカイブする
- Transfer先のモジュール名を変更し、新しいタグを打って開発を継続する
具体的には以下のようになります。
- https://github.com/motemen/testmod -Transfer-> https://github.com/x-motemen/testmod
- https://github.com/x-motemen/testmod -Fork-> https://github.com/motemen/testmod
- https://github.com/motemen/testmod -> Archive
- Continue Development https://github.com/x-motemen/testmod
2の時点ですかさずForkするのがポイントで、Transferした状態のスナップショットをArchiveできます。これにより、旧名でのimportはArchiveされたリポジトリの方を向いてくれるのです。そして、移行先のリポジトリは移行元のことは何も気にせず、モジュール名を変えて、タグを打って、心機一転開発を進められます。
少しまどろっこしいですが、GitHub上のGoプロジェクトでリポジトリを変えてメンテナンスを継続したい場合、この方法が使えるのであればこれでリポジトリを譲渡するのが良いと思います。もっと簡単な方法があるかもしれないので、何か他に良い方法をご存じの方がいれば教えてもらえると嬉しいです。
Forkとメンテナンス継続(余談)
ちなみに、GitHubのFork機能でForkしたリポジトリでメンテナンスを継続しているケースもたまに見かけますが、個人的にはあまりおすすめしません。誰かがForkしたリポジトリにpull requestを送ろうとしたときに、pull requestのデフォルトの向き先が、Fork元のリポジトリになってしまうのが大きな理由です。これは、新規のContributorからすると混乱を招く挙動です。
なので、Forkした側を中央リポジトリとしてメンテナンスを継続し、upstreamとの同期も必要ない、ということなのであれば、GitHubのFork機能を使うのではなく、新しい同名のrepositoryを作り、cloneした元のGit repositoryをpushしてしまうのが良いでしょう。その場合、ちゃんとfork元情報をREADMEやDescriptionに明記するのは忘れずに。