Mercurial: "Managing change with Mercurial Queues" を読む(3)
Tagged:  •    •  

前回に引き続き "More about patches" から読み始めます。

この節では、"Understanding patches" 節が unified diff 形式について触れる程度で軽く流した 「パッチ」の詳細について説明しています。

12.6.1 The strip count

patch コマンドの "-p" オプションによる、 いわゆる "strip count" に関して説明しています。 patch コマンドを使用した経験があれば、 おそらく誰もが "-p" オプションを使ったことがあると思うのですが、 最近はそうでもないのでしょうか?

ちなみに、MQ では hg qimport コマンドが、 "-p" オプションを提供していない(いずれサポートされる予定?)ので、 patch コマンドで直接パッチを宛てて欲しい、 とのことです。

12.6.2 Strategies for applying a patch

厳密一致が失敗した場合の patch コマンドの振る舞いについては、 私自身、この節を読んで初めて知りました。

文脈情報を徐々に削りながら適用することで、 適用対象ファイルの多少の変更にも追従できるようになっている、 というのは確かに「なるほど」なのですが、 「行番号情報は完全無視」というのは、 内容反復性の高いファイルだとちょっと怖いところです。

12.6.3 Some quirks of patch representation

パッチファイルを見る度に「空のファイルとファイルの削除は、 どうやって区別するのだろう?」と思いつつ、 切羽詰っていなかったのでとりあえず無視していたのですが、 パッチファイルは「空ファイル」を扱えなかったのですね。

12.6.4 Beware the fuzz

1つのパッチで複数の版の適用対象ファイルに対応しようとすると、 行番号/コンテキストの厳密一致では難しいため、 あえて曖昧な(文脈情報を削りながらの)適用が行われるパッチにしておく、 という発想はちょっと新鮮でした。

12.6.5 Handling rejection

「却下されたパッチの取り扱いに王道無し」ということだそうです。

普段の仕事では、所謂「パッチ」を作ることは殆ど無いため、 wiggle とか、 mpatch といったツールははじめて名前を知りました。

12.7 Getting the best performance out of MQ
旧式のノートPCで、 Linux 2.6.12-rc2 から Linux 2.6.17 にかけての、 27,472 のリビジョン全てを持つ Linux カーネルリポジトリに対して、 1,738 個のパッチを持つ Linux 2.6.17-mm1 パッチ系列を適用 (hg qpush -a)するのに 3.5 分

というのが早いのか遅いのか、私にはよくわかりませんが、 どうやらかなり早いらしいです。

12.8 Updating your patches when the underlying code changes

最初に原文を読んだ際には、 「hg qpush -a で適用しておいて、 hg update -C で上書き」 する理由が今ひとつよくわからなかったのですが、 要するに「3-way マージのためにはチェンジセットが必要」 ということなんですね。

簡単な図で説明すると、 単純なリベース(rebase: 上流リポジトリから更新を取り込み、 パッチの再適用を行うこと)では、パッチが当たっている状態から:

hg qpop -a でパッチ適用を取り消し:

hg pull -u で上流から更新を取り込み:

hg qpush -a で再度パッチを適用することで、 リベースは完了します (最初のパッチ適用とリベースでのパッチ適用は、 対応するチェンジセットの ID が異なることから、 以下のように図示しています)。

しかし、 上流リポジトリでも頻繁に手が入っているような箇所にパッチを適用したい場合、 所謂「衝突(conflict)」の心配がありますから、 そのような状況でのリベースの際には、初期状態こそ同じものの:

hg qpop -a をすることなく hg pull で上流の更新を取り込み (内部情報保存用の qsave のことは、 ここではとりあえず置いておきます)、 hg update -C で作業領域の(親)リビジョンを、 上流リポジトリの tip (ここでは rev:6)に設定します。

以後、順次パッチを適用するわけですが:

衝突が検出される度に、 Mercurial の通常のマージ機構を用いて 3-way マージが実施されます。

この 3-way マージのためには、 パッチに対応するチェンジセットが存在している必要が有ります。 ですから、 単純なリベースの場合と違い、 hg qpop -a によるパッチ適用の取り消しを行わないわけです。

全てのパッチが適用されるまでこれを繰り返し:

パッチ適用が完了したなら、 事前のパッチ適用で生成された(余分な)チェンジセットを破棄します。

「パッチ適用対象のソースが更新されたのに追従するのは大なり小なり面倒」 なのは変わりないのでしょうが、 MQ のお陰で随分「小なり」になりそうな気はします。

12.9 Identifying patches

MQ はパッチの名前付けに Mercurial の internal tag を利用しているので、 通常のコマンドでも、リビジョン代わりにパッチ名を指定できる、 というのはなかなか嬉しい話です。

12.10 Useful things to know about

「詳細は後ほど」ということになってますが、 同じパッチでも hg qpush の度にチェンジセット識別子が異なる、 というのは、識別子生成の際に日付情報を使っているからですよね?


以下、次回に続く。


"Distributed revision control with Mercurial" 関連エントリの一覧は、BOSBook(Bryan O’Sullivan Book)タグで参照できます。