hg qqueueや--mqを使ってパッチキューを活用する

このエントリはhttp://partake.in/events/902cd6d9-0215-4ea3-b51f-b8ff32e56426:title=の24日目です。

MQ(Mercurial Queue)を使い始めると感じるのがパッチキューを一つしか持てないという窮屈感とパッチキューはバージョン管理を出来ないの?という疑問です。
この二つの不安はhg qqueue--mqオプションで取り除く事が出来ます。

hg qqueue

MQを使っている方は'hg qqueue'というコマンドを知っていますか?'hg qqueue'はMercurial 1.6から追加されたMQのパッチキューを複数扱うためのサブコマンドです。

パッチキューを一覧する

適当なhgのリポジトリに移動して、パッチキューが何個有るか確認してみます。確認には'hg qqueue --list'を利用します。

$ hg qqueue --list
patches (active)

patchesという名前のパッチキューが見えます。patchesはデフォルトのパッチキューです。(active)は現在選択されているパッチキューを指します。
[追記]'hg qqueue'だけでも一覧してくれます。

パッチキューを作成する

では新しいパッチキューを作成してみましょう。作成には'hg qqueue --create NAME'を利用します

$ hg qqueue --create some-feature
% hg qqueue --list
patches
some-feature (active)

新しいパッチキューが作成され、そちらが選択されました。

ちなみに、パッチキューは次の様に管理されています。

.hg/
   |-- patches/ # patches用パッチキュー
   |     |-- series
   |     |-- status
   |     |-- some-patches # パッチ群
   |     |-- ...
   |
   |-- patches-some-feature/ # some-feature用パッチキュー
   |     |-- series
   |     |-- status
   |     |-- some-patches # パッチ群
   |     |-- ...
   |
   |-- patches.queues # パッチキューの名前一覧が管理されている。

[追記]'hg qqueue -c NAME'直後(パッチキューにパッチが一つも無い状態)の場合、ディレクトリが作成されないので注意が必要です。'hg qnew'でパッチを作成するとディレクトリが作成されます。

パッチキューを切り替える

別のパッチキューに移る場合は'hg qqueue NAME'を利用します。patchesに切り替えてみましょう。

$ hg qqueue patches
% hg qqueue --list
patches (active)
some-feature

patchesに切り替えられました。

切り替えの注意点としては、リポジトリに未コミットの変更がある場合、パッチが適応されている場合はパッチキューの切り替えは行えません。'hg qpop --all'を利用してパッチを取り外してから切り替えましょう。

$ hg qqueue --list # 初期状態はpatches
patches (active)
some-feature
$ touch a
$ hg add a
$ hg qqueue some-feature # 未コミットの変更がある場合は切り替え出来ない
abort: patches applied - cannot set new queue active
$ hg qnew some-patch
$ hg qqueue some-feature # パッチが適応されている場合も切り替え出来ない
abort: patches applied - cannot set new queue active
$ hg qpop --all # パッチを全て取り外す
popping some-patch
patch queue now empty
$ hg qqueue some-feature
$ hg qqueue --list # 切り替え出来た!
patches
some-feature (active)

ちなみに、Mercurialは親切なので、存在しないパッチキューに切り替えようとすると「作れば?」とアドバイスをくれます。

$ hg qqueue not-exists-queue
abort: use --create to create a new queue

--mqオプション

Mercurialの様々なサブコマンドに存在する--mqオプション、このオプションの存在意義について疑問を抱く方は多いと思います。実はこのオプションはパッチキューをバージョン管理する場合に役に立つオプションなのです。

パッチキューをバージョン管理する。

現在選択しているパッチキューをバージョン管理するには'hg init --mq'を利用します。

$ hg qqueue --list
patches
some-feature (active)
$ hg qnew first.diff
$ hg init --mq
adding .hg/patches-some-feature/first.diff

パッチキューのリポジトリがどのような状態か'hg status --mq'で調べてみましょう。

$ hg status --mq
A .hgignore
A first.diff
A series

ファイルが追加されていますね。'hg commit --mq'でコミットして履歴を確認してみましょう。

$ hg commit --mq -m "initial import"
$ hg log -p --mq
changeset:   0:4d48afe4ddf4
tag:         tip
user:        Takumi IINO <trot.thunder@gmail.com>
date:        Fri Dec 23 23:34:29 2011 +0900
summary:     initial import

diff --git a/.hgignore b/.hgignore
new file mode 100644
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,5 @@
+^\.hg
+^\.mq
+syntax: glob
+status
+guards
diff --git a/first.diff b/first.diff
new file mode 100644
--- /dev/null
+++ b/first.diff
@@ -0,0 +1,2 @@
+# HG changeset patch
+# Parent f124f111a4682e52c30215634fd58468ecbecdd9
diff --git a/series b/series
new file mode 100644
--- /dev/null
+++ b/series
@@ -0,0 +1,1 @@
+first.diff
新しいパッチを追加してみる

'hg qnew'で新しいパッチを追加してみましょう。

$ hg qnew second.diff
$ hg status --mq
M series
A second.diff

'hg diff --mq'で差分も確認できます。

$ hg diff --mq
diff --git a/second.diff b/second.diff
new file mode 100644
--- /dev/null
+++ b/second.diff
@@ -0,0 +1,2 @@
+# HG changeset patch
+# Parent 8bd778fced5ba3769c1ed0d60da011fae9157e89
diff --git a/series b/series
--- a/series
+++ b/series
@@ -1,1 +1,2 @@
 first.diff
+second.diff

コミットします。ついでに'hg glog --mq'でリビジョングラフも確認してみましょう。

$ hg ci --mq -m "add second.diff"
$ hg glog --mq
@  changeset:   1:224df7eb738f
|  tag:         tip
|  user:        Takumi IINO <trot.thunder@gmail.com>
|  date:        Fri Dec 23 23:40:08 2011 +0900
|  summary:     add second.diff
|
o  changeset:   0:4d48afe4ddf4
   user:        Takumi IINO <trot.thunder@gmail.com>
   date:        Fri Dec 23 23:34:29 2011 +0900
   summary:     initial import

ここまで来れば--mqオプションは何をしているのか想像が付いていると思います。--mqは次のオプションの指定と等価です。

--repository .hg/patches-some-feature

パッチキューを共有する

パッチキューを複数持ったり、パッチキューを手元でバージョン管理する方法はわかりました。ではパッチキューを共有する為にはどうすれば良いのでしょうか?

次の二つのリポジトリが存在するケースを考えます。

  • 中央リポジトリA
  • 中央リポジトリAのパッチキューのリポジトリPA
同時にクローン

この二つを同時にcloneする為には'hg qclone'を利用します。

$ hg qclone 中央リポジトリA --patches リポジトリPA
後から追加

中央リポジトリAがクローン済みで、あとからパッチキューのリポジトリPAを追加する場合はどうすれば良いでしょうか?

残念ながら、専用の手段は用意されていないようです(知っている人が居たら教えてください)。次の手順を取る必要があります。

$ cd .hg
$ hg clone リポジトリPA patches-{NAME}
$ cd ..
$ hg qqueue -c NAME

[追記]もう一つの方法。hgrcの修正が必要です。うーん、専用のサブコマンドが欲しいですね。

$ hg qqueue --create {QUEUENAME}
$ hg init --mq
$ hg pull --mq リポジトリPA
$ echo [paths] > .hg/patches-{QUEUENAME}/.hg/hgrc
$ echo 'defualt = リポジトリPA' >> .hg/patches-{QUEUENAME}/.hg/hgrc

まとめ

'hg qqueue'を利用して複数のパッチキューを扱う方法と、'--mqオプション'を利用してパッチキューをバージョン管理、共有する方法を紹介しました。

Bitbucketユーザであればforkボタンの隣にある「patch queueボタン」の意味、使い方が、なぜ「Forks/queues」というタブが存在するのか、想像が付いたのでは無いでしょうか?

使いたくなってきませんか?