BitBucket な Mercurial to Git なマイグレーションな。

急げ。

2019年12月31日期限かと思ってたら、まだ少し猶予あるみたいね。
公式アナウンスによると、

  1. 2020年2月1日より Mercurial レポジトリを作れなくなる。
  2. 2020年6月1日より BitBucket とその API で Mercurial 機能を使えなくなり、また、全ての Mercurial レポジトリが削除される

んで、「ばかちょんまいぐれーしょんなつーる」が…。ない。ない。ご自分でマイグれ、つーことらしい。

ワタシの場合、持ってるレポジトリは大きく二種類ある。無論:

  1. 履歴に価値がないやーつ
  2. 履歴が大事なやーつ

であり、もっと細かく言えば、「さらしもの」(Public)とそうでないもの(Private)がある。

先に「履歴に価値がないやーつ」からやっつけとく。こーいうのは、価値がないてだけでなくて、「いっそまっさらにしちまいてー」やつでもある。ゆえに、考えることは単純で、

  1. ローカルにクローンとかダウンロードとか
  2. BitBucket にホストしてもらってる方を一度「心置きなく」Delete (WEB インターフェイスで)
  3. ローカルに「クローン」した場合は、その .hg を「心置きなく」削除。
  4. BitBucket WEB インターフェイスから、新しいレポジトリを「Git」で作る(PrivateにしたけりゃPrivateに)。無論削除前と同じ名前にしたけりゃそうする。
  5. 作ったばかりのそれを git clone
  6. その git clone してきた .git を 1. のフォルダにぶちこむ。(4. で名前変えたならこちらも名前変えるべし。)
  7. こっからは通常運転と同じ。亀git使うなり CUI でやるなりご随意に。add、commit、push ね。

幸いなのはやはり Git が高速なことね。これの移行は10分くらいで済んだ。つまり、2. で一時的にユーザから見えなくなったわけだが、その時間は10分程度だった、てこと。歴史がさほどでもないのでそんなにデカいレポジトリにはなってなかったけれど、ファイル数はかなりの量。たぶん「歴史の浅い小規模プロジェクト」の時間の目安くらいにはなるんじゃないかな。

さて問題は「履歴が大事なやーつ」特に「さらしもの」。公式アナウンス内でサードパーティのマイグレーションツールが紹介されてるのだが、なんだか読んでも肝心なことがわからなくてな…。仕方がないので、持ってる private レポジトリのうち、「失敗しても屁でもないやつ」を選んで、そいつでやってみた。

出来てみた手順を列挙するならば結構簡単:

  1. マイグレーション前の hg レポジトリをクローン
  2. hg-fast-export.sh (後述) の実行
  3. git checkout HEAD
  4. マイグレーション前の hg レポジトリを削除し、同じ名前で Git のレポジトリ作る。
  5. git remote add origin https://you@bitbucket.org/you/<今作り直したレポジトリ>.git
  6. git push -u origin master

簡単…にみえて、その過程において少々迷走した。

まず「hg-fast-export.sh」だが、これは fast-export のこと。ただこれ、python のくせに標準インストーラはないわ、構造が標準的ではないわで。何がよろしくないって、「plugin」というフォルダを前提とするので「この構造のまま」使うしかないが、命名が非常によろしくないので、どこに置くにしてもトラブルメーカになっちゃうてこと。ので、「任意の場所に置いて、フルパス指定で呼び出す」のが良いんじゃないだろうか。それを踏まえて、上の手順の詳細は実際はこんな具合:

 1 [me@host: ~]$ mkdir _old
 2 [me@host: ~]$ cd _old
 3 [me@host: ~]$ # hhsprings 部分はむろん「おれさま」だからだぞ。
 4 [me@host: _old]$ hg clone https://hhsprings@bitbucket.org/hhsprings/myrepo
 5 [me@host: _old]$ cd ..
 6 [me@host: ~]$ mkdir -p _new/myrepo
 7 [me@host: ~]$ cd _new/myrepo
 8 [me@host: myrepo]$ git init
 9 [me@host: myrepo]$ # --force をつけないと(Windowsだから)ignore case設定で叱られる。
10 [me@host: myrepo]$ sh ../../fast-export/hg-fast-export.sh -r ../../_old/myrepo --force
11 [me@host: myrepo]$ git checkout HEAD
12 [me@host: myrepo]$ # この間に 4.、つまり BitBucket WEB UI で「新規 Git レポジトリ」
13 [me@host: myrepo]$ # 作っておく。そうしないと以下(の2つ目)は失敗する。
14 [me@host: myrepo]$ git remote add origin https://hhsprings@bitbucket.org/hhsprings/myrepo.git
15 [me@host: myrepo]$ git push -u origin master

「迷走」の種類は2つで、一つは無論上の手順内で書いた「先にリモートを作っとかなけりゃいかん」の件だが、もう一つは「fast-exportの説明不足」に関すること。説明読んでもぜんっぜんわからんわけよ。「うーん、履歴は維持されんの、されへんの。されないならする方法はあるの、ないの。」が。名前も良くない。「export」という名前からは、VCS のコンテキストだと「リビジョン管理から切り離して内容だけオクレヨ」のことを指すのが普通だからね。(Git 本体でさえそうであろう。)これの答えは「維持される」。これの確信は git checkout HEAD 直後にすぐわかる。この時点でもう git レポジトリとして機能していて、「黒歴史」をしみじみ鑑賞することが出来る。


にしてもあれな。こんな期間限定かつ期限切れ間近の情報をあげる必要があるのか、てことだ。

が、fast-export そのものは BitBucket と無関係に使えて、場合によっては便利なんではないか、てのはある。

あとね。BB の公式アナウンスで「なげにまーきゅりあるを捨てる決断をするに至ったのか」の「苦渋苦渋しさ」について結構「真剣に」書いてあるんだけれど、なんかちょっと違うよなぁ、と思った部分もあった。

まず Mercurial vs Git の勝敗なんぞは、誰の目にも「とっくに決着している」のは確か。性能の開きが大きすぎる。それはそうなんだけれどもね。「StackOverflow における質問数が Git 97%、Mercurial 3% である」のを根拠にするのって、個人的には違うと思うんだよな。Git って、なんだかんだ、やっぱ「難しい」んだよ。考え方も操作も。というか Mercurial が「わかりやすすぎる」のだ。cvs → svn と渡り歩いてきたユーザにとっては、ほとんど何も考え方も操作も変えなくていいんだから。唯一「push/pull」という DVCS ならではの「新しい考え方」を「増やす」だけで良かった。ので、「質問数が少ない」のって、そのおかげもあるはずなんだよね。

てわけで、BB チームが考えるよりはもっと Mercurial ユーザはまだ多いんじゃないかと想像する。けど個人的にはやっぱ Git に少しずつでも乗り換えてった方がいいとは思うけどね。特にサイズの大きなファイルをたくさん扱う場合の Mercurial の遅さってね、もう「筆舌に尽くしがたい」よ。Git ではこんなんもあっちゅーまに処理しちゃうんで、かなりびっくり出来る。


さてと。あといくつレポジトリあるんだ…? 20くらいかな。うーん、めんどい。


翌日追記:
うーん、適当に読んだ結果、間違ったこと言ってる。

「StackOverflow における質問数が Git 97%、Mercurial 3% である」じゃなくて、「StackOverflow Developper Survey によると Git 利用者90%、Mercurial 3%」てことで、質問数なのかどうかは書いてない。まぁ StuckOverflow 関連のチームが質問数を根拠にしないとは思えないけれど。あと、もっと根本的に、「BitBucket ユーザでの Mercurial 利用者が日に日に減り続け、今では 1% しかいない」ことも書かれてる。つーか一番の根拠ってこれだよね。

あと timeline についてだけど、

  1. 2020年2月1日より Mercurial レポジトリを作れなくなる。
  2. 2020年3月31日までは、作成以外の全ての Mercurial 機能が使える。
  3. 2020年6月1日より BitBucket とその API で Mercurial 機能を使えなくなり、また、全ての Mercurial レポジトリが削除される

つまり、マイグレたい人にとっての実質的な期限はたぶん 3月31日。最低でもローカルにクローンしとくとこまではこの日前にやっとかなきゃいけない。(さすがに「削除も出来なくなる」なんてバカなスケジュール設計はしてないとは思うけれど、そこいらの詳細は書かれてないので、3月31日までに全てを済ませるのがやはり安全だろう。)

あと、蛇足。

fast-exportは BB 関係なく使える」について。結局これはローカルのレポジトリをごにょるツールでしかないので、当然「おれさまPC内にしかないおれさまレポジトリ」の変換に使える。

あと、これは未検証だけれど、BB ではなく GitHub に引っ越したい場合でも使える可能性がある。私の使い方ではそういうことはほとんどしないので、待っててもこれについての追加情報は書かない可能性の方が高いと思うけどね。


2020-01-03追記1:
複数追記したいことがあるのだが、作業しながらなので、書けることから先に。

ひとつめ。

およそ30くらい対象があったのだが、ひとつだけモロに作業ミスで、レポジトリを永遠に失ってもうた。2012年に作ってその後全然使ってなかったプライベートレポジトリなので、まぁ傷は深くないとは思うんだけれど、なんせその作業ミス、「中身の確認」つまりスタートの際に混入したミスだったので、本当に傷が浅かったのかがよくわからない。ワタシのプライベートレポジトリのほとんどが、何かテクニカルな「検証メモ」であることが多く、そいつもこれだったのだが、今でも役に立つものが含まれていたのかどうかさえわからぬまま消えちゃった。

何をしちゃったかというと、名前の似ている二つのレポジトリ A と B があって、そのうちの A を WEB UI で開き、B を hg clone → fast-import → … してしまっていて、そしてめでたく上の手順 4. を実行してしまった。作業の性質上、このミスはやらかしやすいんではないかと思う。気をつけようね。

ふたつめ。

上の手順(履歴維持のほうね)、WEB UI からの操作を経る必要があるので全自動には出来ないんだけれど、半自動は出来るわけで、以下スクリプトで作業した:

 1 #! /bin/sh
 2 user="$1"
 3 if test -z "${user}" ; then
 4     echo "specify your mercurial user account!"
 5     exit 1
 6 fi
 7 repos="$2"
 8 if test -z "${repos}" ; then
 9     echo "specify your mercurial repository!"
10     exit 1
11 fi
12 tmphgclonedir=_hg
13 mkdir -p ${tmphgclonedir}
14 (
15     set -x
16     cd ${tmphgclonedir}
17     hg clone https://${user}@bitbucket.org/${user}/${repos}
18 )
19 mkdir ${repos} || exit 1
20 (
21     set -x
22     cd ${repos}/
23     git init
24     ../fast-export/hg-fast-export.sh -r ../${tmphgclonedir}/${repos} --force 2>&1 || exit 1
25     git checkout HEAD || exit 1
26     set +x
27     #
28     yn=n
29     while test ${yn} != "y" ; do
30         echo -n "### ready to 'git remote add'? " ; read yn
31     done
32     #
33     set -x
34     git remote add origin https://${user}@bitbucket.org/${user}/${repos}.git || exit 1
35     git push -u origin master || exit 1
36 ) || exit 1
37 yn=n
38 while test ${yn} != "y" ; do
39     echo -n "### ready to remove temporaries? " ; read yn
40 done
41 #
42 set -x
43 rm -fr ${tmphgclonedir}/${repos}
44 rm -fr ${repos}

いきなり実行すんなよ。最低でも、どこに何が配置されていることを前提にしているのかの理解だけはすること。あと、わたしは最後の「### ready to remove temporaries?」で y と答える前に、ワーキングコピーが別にある場合は、そちらに .git を移動している。ここらへんは色々考え方で、少なくとも「合理的」ではないので、基本的には自分の作業スタイルに合わせて書き換えて使うのがよいと思う。(そもそも既にワーキングコピーを持ってるなら、最初のクローンがそもそも無駄だ、てのもひとつの考え方。)

もうひとつ(厳密にはふたつ)書くべきことがあるが、それはのちほど。


2020-01-03追記2:
残りの追記。

まずは issue tracker、pull requests についてなのだが、すまん、ワタシのケースには該当がなかったので、何も書けない。というか、こんな期限切れ間近にこんなワタシの情報にすがろうとしてる時点で、確実にその読者は「小さな個人プロジェクト」とかプライベートレポジトリだけが該当のユーザだろうと思うので、あんまし害はないんだと思う。

ワタシのケースの場合、事情により wiki だけのレポジトリがあった。いまいまでアクティブには動かしてはいないけれど、もともとは他のプロジェクトにちょっかいを出したかったときに、手軽に画像等で説明出来る場所が欲しくて作ったレポジトリだった。こいつに関して。

issue tracker、pull request とも性質が似てるとは思うんだけれど、wiki って要するに Mercurial/Git そのものとは無関係なわけね。あくまでも BitBucket や GitHub が提供している機能であって…、そしてここからが問題なのだけれど、「なのにも関わらず各プロジェクトの木の枝」になるわけね。つまり「https://youracc@bitbucket.org/youracc/yourrepo/wiki」というふうに、「レポジトリ」の子であるかのように管理されることになる。けれども、中身としては yourrepo と yourrepo/wiki は「ともに DVCS なレポジトリであり、なおかつ別のレポジトリ」なわけ。なのでつまり、上の手順で 4. で「元の Mercurial レポジトリを削除」とともに /wiki ももろとも消えるし、「新しく Git レポジトリを作る」で自動で子の /wiki が作られるわけではない(無関係の二つのレポジトリなんだから当たり前)。

言いたいことはわかってくれた、かしら。wiki は wiki で「別途同じことをする」必要がある、てことね。追記1であげたスクリプトはそのままでは使えないのはもちろんなんだけれど、「履歴を維持する」ほうはワタシは諦めた。もとの Mercurial 版のほうの wiki を hg clone し、Git 化した方のプロジェクト設定で wiki を有効にしてからその wiki を git clone し、hg clone してきた中身を git clone のほうにつっこみ…。一番最初に説明したのと同じノリだよ。(なお、wikiを使える設定をした時点で Home.md が問答無用で作られることに注意。これが移行に問題を起こす可能性がある。)