BitbucketのMercurialリポジトリをGitHubのGitリポジトリに移動させた話


概要

本内容は、BitbucketのMercurialで管理していたソースコードを、GitHubに移動させた時のやり方を残したメモ的な物です。内容のポイントは、履歴やタグなどの情報を保持したMercurialとGitのリポジトリ変換です。

5月ぐらいにやった内容のメモを元に、今頃になって書き起こした内容なので、現在と違う部分を含んでいる可能性がある点ついてはご了承ください。

経緯

自分は、個人開発のソースコードをBitbucket管理していました。これは、プライベートリポジトリを利用できるため、あまり表に晒したくない開発途中のソースを公開しなくていいという利点があったためでした。このバージョン管理には、Mercurialを利用していました(ちなみに、Mercurialを採用したのは、コマンドがSubversionと似ていて覚えやすかったからとか、そんな理由だったと思います)。

ただ、現在ソフトウェア開発においてはGitおよびGitHubを利用するシーンが多く、Bitbucketはサービス自体がGitHubに比べて弱いと感じていたこともあり、リポジトリを移行させることにしました。

そこで、以下のような形でリポジトリの移行を図ることにしました。

  • MercurialリポジトリをGitリポジトリに変換する
  • コミットの履歴を残す
  • タグを保持する
  • コミットしたユーザをGitHubのユーザに置換える (個人開発だったのでユーザは自分1名のみ)

コミット履歴やタグの保持は、過去の情報ごと全部取り込みたかったからです。
ユーザの置換えについては、過去のコミットについてもGitHubで草を生やしたかったからです。

作業環境

作業OS: Windows
(私が作業した環境がWindowsという話で、多分MacやLinuxでも可能です。)

使用ソフト:
- Git for Windows (プログラムフォルダにパスを設定済み)
- TortoiseHg (プログラムフォルダにパスを設定済み)
- hg-git (%USERPROFILE%\mercurial.iniにパスを設定済み)

最新版を使用するようにしましょう。古いまま作業していたところ、上手く動かないことがありました。

作業内容

作業内容はそれほど複雑ではありません。キモは、MercurialのリポジトリをGitのリポジトリにプッシュする点でしょうか。

1. メールアドレスを一致させる

BitbucketとGitHubで、メインで使用しているメールアドレスを一致させておきます。GitHubに取り込まれた際に、メールアドレスで自動的にユーザを照合してくれます。もしかしたら、ローカル側のソフトでもメールの設定が必要かもしれません。以下参考。

GitHubをMercurialで使う - Qiita
https://qiita.com/g_maeda/items/8be8a36edb58d77f78dd

2019-01-10 追記:

一部リポジトリにおいて、古いコミットのユーザメールアドレスが何故か"none@none"となっていたため、GitHubがユーザを認識しない事象がありました。そのため、上記サイトの設定は行った方が良いと思います。(ここで設定すれば、無理にメールアドレスを一致させる必要はありません。)

変換対象のMercurialリポジトリにある.hg/hgrc ファイルの末尾に以下の記述を追加します。

.hg/hgrc
[git]
authors = .hg/authors.txt

さらに authers.txt ファイルを作成し、ユーザの対応付けを記述します。以下は公式サンプル。

.hg/authers.txt
johnny = John Smith <[email protected]>
dougie = Doug Johnson <[email protected]>

2. ローカルにMercurialのクローンを作成

コマンドなりGUIなりを使用して、Bitbucketのリポジトリのクローンをローカルに作成します。

3. ローカルにGitのリポジトリを作成

コマンドなりGUIなりを使用して、Gitの空リポジトリをローカルに作成します。

4. MercurialリポジトリをGitリポジトリにプッシュ

以下のコマンドで、Mercurialリポジトリを作成したGitリポジトリにプッシュすることで、自動的にGitに変換されます。(恐らく、hg-gitがよろしくやってくれて勝手に変換してくれているのでしょう。)
プッシュの際、ブランチがそのまま移行されるのではなく、ブックマークを与える必要があるようです。

ローカルMercurialリポジトリのフォルダ内
# Gitキャッシュのクリア (念のため)
> hg gclear
clearing out the git cache data

# ブックマーク作成
> hg bookmark hg

# Gitリポジトリへプッシュ
> hg push ..\git-repository\
..\git-repository\ への反映中
変更点を探索中
adding objects
added 102 commits with 1225 trees and 647 blobs

ローカルGitリポジトリのフォルダ内
# hgブランチにチェックアウト
> git checkout -b master hg

この時点で履歴やブランチが正しく展開されていること、ユーザが1で設定したメールアドレスになっていることを確認します。

2019-01-10 追記 複数ブランチをプッシュ

GitHubが無料でプライベートリポジトリを使えるようになったので、本格的に移行しようと思い、複数ブランチに分かれているリポジトリの移行を行いました。

複数ブランチの場合、Mercurialのブランチを切り替えて、それぞれにブックマークを追加してプッシュを行います。ブックマークはブランチと同じ名前はつけられないので、ここでは末尾に"-hg"とつけることにします。
例えば、default、develop、stagingのブランチがあるMercurialリポジトリについてのパターンを示します。この時、Mercurialのdefaultブランチを、Gitのmasterにします。

ローカルMercurialリポジトリのフォルダ内
# defaultブランチ
> hg update default
> hg bookmark default-hg
> hg push ..\git-repository\

# developブランチ
> hg update develop
> hg bookmark develop-hg
> hg push ..\git-repository\

# stagingブランチ
> hg update staging
> hg bookmark staging-hg
> hg push ..\git-repository\

ブックマークがGitのブランチとして見えることを確認します。

ローカルGitリポジトリのフォルダ内
> git branch
default-hg
develop-hg
staging-hg

これを、正しいブランチ名にします。

ローカルGitリポジトリのフォルダ内
# default-hg -> master となる点に注意
> git checkout -b master default-hg
> git checkout -b develop develop-hg
> git checkout -b staging staging-hg

元のブックマーク名のブランチは不要なら削除します。

ローカルGitリポジトリのフォルダ内
> git branch --delete default-hg
> git branch --delete develop-hg
> git branch --delete staging-hg

とりあえず自分の場合はそんなに複雑なブランチ構成ではなかったのでこれで上手く行きましたが、これで必ずうまいくいくのかどうかは分かりません。

5. GitHubにプッシュ

GitHubにリポジトリを作成してリモートのリポジトリとし、ローカルのリポジトリをプッシュします。

ローカルGitリポジトリのフォルダ内
# GitHubをリモートブランチに設定
> git remote add origin [email protected]:[リポジトリのパス].git
# 全てのブランチをプッシュ
> git push -u origin --all
# 全てのタグをプッシュ
> git push -u origin --tags

問題点(補足事項)

これは、記事の本筋とは直接関係ありません。
上記の手順自体に問題はなく、自分の環境では大体のMercurialリポジトリをGitリポジトリに変換し、GitHubに登録することができました。ただ1件、GitHubへのプッシュに失敗するリポジトリが1件ありました。色々調べてみたところ、以下の結果が吐き出されていました。これは、履歴上で巨大なファイルがコミットされていたためです。

remote: error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com.
remote: error: Trace: 32e6263792d3b4426988844101c9ee19
remote: error: See http://git.io/iEPt8g for more information.
remote: error: File java_pid1188.hprof is 939.60 MB; this exceeds GitHub's file size limit of 100.00 MB

ある一時期、CPUプロファイリングデータ(.hprof)がコミットに紛れてしまい、履歴の途中に1GB近く(939MB)のサイズのファイルが存在するという状態になっていました。これは後から気付いて削除したのですが、当然履歴上には残っています。
過去の履歴データ上にあるので、単純に消すという手段もとれません。色々調べた結果、以下のgitコマンドで履歴上の該当ファイルを削除することで、無事GitHubにプッシュすることができました。
(削除対象のファイル名は、"java_pid1188.hprof"です。)

> git filter-branch -f --index-filter 'git rm --cached --ignore-unmatch java_pid1188.hprof' --prune-empty  -- --all

[参考]
git filter-branchで過去の全てのcommitから画像ファイルの追加/変更をなかったことにしてリポジトリを軽量化する - dskd
https://dskd.jp/archives/46.html

結果

上記の作業によって、

  • コミット履歴
  • タグ
  • ユーザ情報

を保持した状態で、BitbucketのMercurialリポジトリを、GitHubのGitリポジトリに移行させることができました。見えてない細かい問題点などが潜在してる可能性はありますが、とりあえず現状使用する分に問題はなさそうです。

その他の変換

今回の結果に至るまでに、いくつか試した方法がありましたので、書き残しておきます。他にも何かやった気がしますが、忘れました。

GitHubの標準インポート

Import a Repository
https://github.com/new/import

GitHub標準で用意されているインポート機能はMercurialにも対応しているようなので、これを試してみたところ、コミットログのコメントの日本語が全部化けしていました。さらに、BitbucketとGitHubのユーザを同一のユーザと認識しませんでした。そのため、不採用としました。
ちなみに、文字化けの原因は調べましたがよく分かりませんでした。

fast-export

これはWindowsではなく別環境で試してみたのですが、なんだかんだで変換自体が上手く行かなかった記憶があります。詳細は忘れました。本当にダメだったのか、自分の勘違いだったのかも分かりません。

[参考]
【Proton.jp】 MercurialのリポジトリをGitリポジトリに変換するには
http://www.proton.jp/main/programming/git/hg2git.html
BitBucketのhgリポジトリを、githubのgitリポジトリに移行する - Qiita
https://qiita.com/misogi@github/items/034b0aca6d6f5ae64cd9