foozy@708: \chapter{Managing change with Mercurial Queues} foozy@708: \label{chap:mq} foozy@708: foozy@708: \section{パッチ管理問題} foozy@708: \label{sec:mq:patch-mgmt} foozy@708: foozy@708: ソフトウェアパッケージをソースからインストールする必要があるのに、 foozy@708: パッケージ使用前に修正しておかなければならないバグをソース中に発見してしまう、 foozy@708: というような事態はよくあることです。 foozy@708: 変更の後、暫くパッケージのことを忘れていると、 foozy@708: 数ヵ月後にパッケージを新しい版で更新する必要が出てきたとします。 foozy@708: パッケージの新しい版が未だにバグを残していたなら、 foozy@708: 古い版のソースツリーから修正内容を抽出して、 foozy@708: 新しい版に適用しなければなりません。 foozy@708: このような作業は退屈で間違いを起こしやすいものです。 foozy@708: foozy@708: これは``パッチ管理''問題の単純なケースです。 foozy@708: 自分では変更することができない``上流''のソースツリーがあるとします。 foozy@708: 上流のソースツリーの上でローカルな修正を行う必要があるなら、 foozy@708: 上流ソースの新しい版に対してローカルな修正を適用できるように、 foozy@708: そういった修正を別途管理したいと思うでしょう。 foozy@708: foozy@708: パッチ管理問題はさまざまな状況で発生します。 foozy@708: オープンソースソフトウェアプロジェクトのユーザが、 foozy@708: プロジェクトのメンテナンス担当へ、 foozy@708: バグ修正や新規機能をパッチ形式で送付する状況が、 foozy@708: おそらく最もわかりやすい状況でしょう。 foozy@708: foozy@708: オープンソースソフトウェアを含むオペレーティングシステムの配布者は、 foozy@708: 配布するパッケージに対する変更を頻繁に行うので、 foozy@708: 自分たちの環境においてビルドを行うのは当然のことです。 foozy@708: foozy@708: 整備の上で幾つか変更を行いたい場合、 foozy@708: 標準的な foozy@708: \command{diff} および \command{patch} プログラム foozy@708: (これらのツールに関する議論は \ref{sec:mq:patch} 節を参照のこと) foozy@708: を使用して、単一のパッチを管理することは簡単です。 foozy@708: しかし、一旦変更の数が増え始めると、 foozy@708: 単一のパッチの管理は関連性の無い``成果の塊''に感じ始めるため、 foozy@708: 例えば、単一のパッチは単一のバグ修正のみを含む foozy@708: (パッチは複数のファイルを修正するかもしれませんが、 foozy@708: ``単一の事''しか行わない)ようになるでしょうから、 foozy@708: 異なるバグやローカルな修正に必要とされるパッチを、 foozy@708: いくつも抱えることになるかもしれません。 foozy@708: このような状況で、 foozy@708: 上流のパッケージ保守担当者にバグ修正のパッチを送ったとすると、 foozy@708: 彼らはその後のリリースにおいてその修正を取り込むでしょうから、 foozy@708: 新しい版への更新の際には、 foozy@708: そのパッチの適用を取りやめることができます。 foozy@708: foozy@708: 上流のソースツリーに対して単一のパッチを保守することは、 foozy@708: 退屈で間違いやすいですが難しくはありません。 foozy@708: しかし、保守しなければならないパッチの数が増えるにしたがい、 foozy@708: 問題の複雑さはすみやかに増加します。 foozy@708: すくなからぬパッチを抱え込むことで、 foozy@708: 適用の有無を把握したり、それらを保守することが、 foozy@708: 「面倒なこと」から「圧倒されること」へと変化するでしょう。 foozy@708: foozy@708: 幸いなことに、Mercurial は foozy@708: Mercurial Queues foozy@708: (あるいは単に ``MQ'')と呼ばれる、 foozy@708: パッチ管理問題を簡素化する強力な拡張機能を持っています。 foozy@708: foozy@708: \section{Mercurial Queues 以前} foozy@708: \label{sec:mq:history} foozy@708: foozy@708: 1990 年代後半、何人かの Linux カーネル開発者達は、 foozy@708: Linux カーネルの挙動を変える``パッチ系列''の保守を始めていました。 foozy@708: 幾つかの系列は安定性に、幾つかは網羅性に、 foozy@708: その他の系列はより実験的な部分に焦点を当てていました。 foozy@708: foozy@708: これらのパッチのサイズは速やかに巨大化しました。 foozy@708: 2002 年、Andrew Morton が、 foozy@708: 自分のパッチキュー管理作業を自動化するのに用いていた、 foozy@708: 幾つかのシェルスクリプトを発表しました。 foozy@708: Andrew は、 foozy@708: Linux カーネルソース上での数百(時には数千)のパッチの管理に、 foozy@708: これらのスクリプトを上手に利用していました。 foozy@708: foozy@708: \subsection{A patchwork quilt(訳注:継ぎはぎの上掛け)} foozy@708: \label{sec:mq:quilt} foozy@708: foozy@708: 2003 年の初頭、 foozy@708: Andreas Gruenbacher と Martin Quinson は、 foozy@708: Andrew によるスクリプトの手法を取り入れて、 foozy@708: foozy@708: ``patchwork quilt''~\cite{web:quilt} あるいは単に ``quilt'' foozy@708: (これについて述べた論文は~\cite{gruenbacher:2005}を参照のこと) foozy@708: と呼ばれるツールを発表しました。 foozy@708: パッチ管理が大幅に自動化されることから、 foozy@708: quilt はオープンソース開発者の間で瞬く間に大きな支持を得ました。 foozy@708: foozy@708: quilt は、 foozy@708: 最上位のディレクトリにおいて\emph{パッチのスタック}を管理します。 foozy@708: 管理開始の際には、 foozy@708: quilt に対してディレクトリツリーを管理する旨と、 foozy@708: どのファイルを管理したいのかを伝えます。 foozy@708: quilt はこれらのファイルの名前と内容を別な場所に保存します。 foozy@708: バグの修正の際には、 foozy@708: 新しいパッチを(単一のコマンドを使用して)作成し、 foozy@708: 修正する必要の有るファイルの編集を行い、 foozy@708: パッチを``refresh''します。 foozy@708: foozy@708: refresh の段階で quilt はディレクトリツリーを走査します。 foozy@708: quilt は実施された全ての変更でパッチを更新します。 foozy@708: 最上位のディレクトリにおいて作成した別なパッチを用いることで、 foozy@708: ``1つのパッチが適用されたツリー''から foozy@708: ``2つのパッチが適用されたツリー''へと変化させるために必要な変更を、 foozy@708: 追跡することができます。 foozy@708: foozy@708: ツリーに対するパッチの適用状況を\emph{変更}することもできます。 foozy@708: パッチを``pop''すると、 foozy@708: そのパッチによる変更はディレクトリツリーから取り除かれます。 foozy@708: しかし、 foozy@708: quilt はどのパッチが取り除かれたのかを覚えているので、 foozy@708: 取り除かれたパッチを再び``push''することができ、 foozy@708: ディレクトリツリーには当該パッチによる変更が復元されます。 foozy@708: 最も重要な点は、 foozy@708: ``refresh''コマンドの実行と、 foozy@708: それによる最上位のパッチの内容更新が任意の時点にできることです。 foozy@708: これは、 foozy@708: パッチの適用状況と、そのパッチによる変更内容の両方を、 foozy@708: 任意の時点で変更できることを意味します。 foozy@708: foozy@708: quilt は変更制御ツールを意識しないため、 foozy@708: 展開された tarball の最上位ディレクトリにおいても、 foozy@708: Subversion リポジトリにおいても同等に機能します。 foozy@708: foozy@708: \subsection{patchwork quilt から Mercurial Queues へ} foozy@708: \label{sec:mq:quilt-mq} foozy@708: foozy@708: 2005 年中旬、 foozy@708: quilt 的な振る舞いを Mercurial に追加するための、 foozy@708: Mercurial Queues と呼ばれる拡張機能が、 foozy@708: Chris Mason により実装されました。 foozy@708: foozy@708: quilt と MQ の大きな違いは、 foozy@708: quilt が変更制御システムを意識しないのに対して、 foozy@708: MQ が Mercurial に\emph{統合}されていることです。 foozy@708: push される個々のパッチは、 foozy@708: Mercurial のチェンジセットとして表現されます。 foozy@708: パッチを pop することで、チェンジセットは取り除かれます。 foozy@708: foozy@708: 変更制御システムを意識しないことから、 foozy@708: Mercurial と MQ を利用できない状況について知る上で、 foozy@708: 依然として quilt は非常に有用なソフトウェアです。 foozy@708: foozy@708: \section{MQ の大きな利点} foozy@708: foozy@708: パッチと変更管理の統一を通して MQ が提供するものの価値を、 foozy@708: 誇張し過ぎることはありません。 foozy@708: foozy@708: フリーソフトウェアおよびオープンソースの世界でパッチが利用され続けるのは、 foozy@708: 変更管理ツールが年々その機能を向上させているにも関わらず、 foozy@708: パッチが\emph{軽快さ}を持っていることが大きな理由の一つです。 foozy@708: foozy@708: 伝統的な変更制御ツールは、 foozy@708: 実施したことに関する全てを、 foozy@708: 永続的で取り消しの出来ないものとして記録します。 foozy@708: この振る舞いに大きな価値がある一方で、 foozy@708: 幾分堅苦しくもあります。 foozy@708: 過激な実験を行おうとする場合、 foozy@708: 自分が行おうとすることに慎重になるか、 foozy@708: 必要とされない〜なお悪いことには、誤解や不安定の元となる〜 foozy@708: 失敗と間違いの記録を、 foozy@708: 永続的な履歴記録中に残す危険を冒す必要があります。 foozy@708: foozy@708: 対照的に、 foozy@708: MQ における分散履歴管理とパッチの結合により、 foozy@708: あなたの作業を容易に隔離することができます。 foozy@708: あなたのパッチは通常の変更履歴の上で存続し続け、 foozy@708: 望む時にそれらの実施/取り消しを行うことが出来ます。 foozy@708: そのパッチが気に入らない場合、それを取りやめることができます。 foozy@708: そのパッチが完全には望むものでない場合、 foozy@708: 望む姿に洗練させるまで、必要なだけ何度でも修正することが出来ます。 foozy@708: foozy@708: 例えば、 foozy@708: パッチと変更管理の統合により、 foozy@708: パッチの理解とその効果〜および元になったコードとの連携〜のデバッグが、 foozy@708: \emph{非常に}簡単になります。 foozy@708: 全ての適用済みパッチが関連したチェンジセットを持っているので、 foozy@708: どのチェンジセットとパッチがそのファイルに影響を及ぼしているのかを、 foozy@708: \hgcmdargs{log}{\emph{filename}} によって見ることが出来ます。 foozy@708: \hgext{bisect} 拡張を用いることで、 foozy@708: バグが持ち込まれたり修正された時点を見るために、 foozy@708: 全てのチェンジセットと適用済みパッチを通しての二分探索を行うことができます。 foozy@708: \hgcmd{annotate} コマンドを用いることで、 foozy@708: ソースファイルの特定の行を変更したのが、 foozy@708: どのチェンジセットやパッチであるかを見ることが出来ます。 foozy@708: foozy@708: \section{パッチの理解} foozy@708: \label{sec:mq:patch} foozy@708: foozy@708: MQ は、それがパッチ指向の特性を持つことを表に出しているため、 foozy@708: パッチがどういったものであるかや、 foozy@708: パッチとともに機能するツールに関することがらを理解する手助けになります。 foozy@708: foozy@708: 伝統的な Unix の \command{diff} コマンドは、 foozy@708: 2つのファイルを比較し両者の違いを表示します。 foozy@708: \command{patch} コマンドは、 foozy@708: この違いをファイルに対する\emph{変更}とみなします。 foozy@708: これらのコマンドの簡単な動作例として、 foozy@708: 図~\ref{ex:mq:diff}を見てください。 foozy@708: foozy@708: \begin{figure}[ht] foozy@708: \interaction{mq.dodiff.diff} foozy@708: \label{ex:mq:diff} foozy@708: \caption{\command{diff} および \command{patch} コマンドの利用例} foozy@708: \end{figure} foozy@708: foozy@708: \command{diff} が生成する(そして、\command{patch} が入力する) foozy@708: ファイルの形式は``パッチ(patch)''ないし``差分(diff)''と呼ばれます。 foozy@708: パッチと差分の間に違いはありません foozy@708: (以後は、より一般的に使用される``パッチ''という呼称を使用します)。 foozy@708: foozy@708: パッチファイルは、任意のテキストから始めることができます。 foozy@708: \command{patch} コマンドはこのテキストを無視しますが、 foozy@708: MQ はチェンジセットを生成する際のコミットメッセージとみなします。 foozy@708: パッチ内容を開始を見つけるために、 foozy@708: \command{patch} は ``\texttt{diff~-}'' で始まる最初の行を探します。 foozy@708: foozy@708: MQ は \emph{unified} 差分と共に機能します foozy@708: (\command{patch} はそれ以外の何種類かの差分形式でも機能しますが、 foozy@708: MQ は \emph{unified} 差分でないと機能しません)。 foozy@708: unified 差分は2種類のヘッダを持っています。 foozy@708: \emph{ファイルヘッダ header}には、 foozy@708: 変更対象となるファイルのファイル名が記述され、 foozy@708: \command{patch} コマンドが新規のファイルヘッダを見つけた際には、 foozy@708: 変更を行うために当該する名前のファイルを探します。 foozy@708: foozy@708: ファイルヘッダに続いて、\emph{hunk} 列が記述されます。 foozy@708: それぞれの hunk はヘッダで開始され、 foozy@708: その hunk により変更される対象の、 foozy@708: ファイルにおける行番号の範囲を識別します。 foozy@708: ヘッダに続く hunk は、 foozy@708: ファイルの改変されない部分からなる数行のテキストが前後に付加されます。 foozy@708: これらの改変されない部分のことを、hunk に対する\emph{コンテキスト}と呼びます。 foozy@708: 後続の hunk との間に少量のコンテキストしかない場合、 foozy@708: \command{diff} は新たな hunk ヘッダを表示しません。 foozy@708: 変更内容の間に数行のコンテキスト行を置いて、 foozy@708: hunk をそのまま続けます。 foozy@708: foozy@708: コンテキストの個々の行は空白文字で始まります。 foozy@708: hunk 内部では、 foozy@708: ``\texttt{-}'' で始まる行は``削除される行''を、 foozy@708: ``\texttt{+}'' で始まる行は``挿入される行''を意味します。 foozy@708: 例えば、変更される行は、1行の削除と1行の挿入で表現されます。 foozy@708: foozy@708: パッチのより微妙な側面に関しては後ほど(~\ref{sec:mq:adv-patch}節にて) foozy@708: 説明しますが、MQ を利用するに当たってはここまでの知識で十分です。 foozy@708: foozy@708: \section{Mercurial Queues の利用} foozy@708: \label{sec:mq:start} foozy@708: foozy@708: MQ は Mercurial の拡張として実装されているので、 foozy@708: 利用の前に明示的に有効化する必要があります foozy@708: (ダウンロードの必要はありません。 foozy@708: MQ は通常の Mercurial の配布物に含まれています)。 foozy@708: MQ を有効にするには、 foozy@708: \tildefile{.hgrc} ファイルを編集し、 foozy@708: ~\ref{ex:mq:config} に示す行を追加してください。 foozy@708: foozy@708: \begin{figure}[ht] foozy@708: \begin{codesample4} foozy@708: [extensions] foozy@708: hgext.mq = foozy@708: \end{codesample4} foozy@708: \label{ex:mq:config} foozy@708: \caption{MQ 拡張有効化のために \tildefile{.hgrc} に追加する内容} foozy@708: \end{figure} foozy@708: foozy@708: 拡張が有効化されると、 foozy@708: いくつかの新しいコマンドが有効化されます。 foozy@708: \hgcmd{help} を使って \hgxcmd{mq}{qinit} コマンドの利用可否を見ることで、 foozy@708: 拡張が機能することを確認できます。 foozy@708: ~\ref{ex:mq:enabled} の例を参照してください。 foozy@708: foozy@708: \begin{figure}[ht] foozy@708: \interaction{mq.qinit-help.help} foozy@708: \caption{MQ 利用可否の確認} foozy@708: \label{ex:mq:enabled} foozy@708: \end{figure} foozy@708: foozy@708: MQ は\emph{全ての} Mercurial リポジトリで利用でき、 foozy@708: コマンドはそのリポジトリにしか作用しません。 foozy@708: 利用開始の際には、 foozy@708: \hgxcmd{mq}{qinit} コマンドによりリポジトリの準備を行います foozy@708: (~\ref{ex:mq:qinit} 参照)。 foozy@708: このコマンドは、\sdirname{.hg/patches} と呼ばれる空のディレクトリを作成し、 foozy@708: MQ はこのディレクトリにメタデータを格納します。 foozy@708: 多くの Mercurial コマンドと同様、 foozy@708: \hgxcmd{mq}{qinit} コマンドは実行が正常に終了した場合には、 foozy@708: 特に何も表示しません。 foozy@708: foozy@708: \begin{figure}[ht] foozy@708: \interaction{mq.tutorial.qinit} foozy@708: \caption{MQ 利用に向けたリポジトリの準備} foozy@708: \label{ex:mq:qinit} foozy@708: \end{figure} foozy@708: foozy@708: \begin{figure}[ht] foozy@708: \interaction{mq.tutorial.qnew} foozy@708: \caption{新しいパッチの作成} foozy@708: \label{ex:mq:qnew} foozy@708: \end{figure} foozy@708: foozy@708: \subsection{新しいパッチの作成} foozy@708: foozy@708: 新しいパッチで作業を開始するには、 foozy@708: \hgxcmd{mq}{qnew} コマンドを使います。 foozy@708: このコマンドは作成するパッチの名前を引数に取ります。 foozy@708: 例~\ref{ex:mq:qnew}に示すように、 foozy@708: MQ はこれを \sdirname{.hg/patches} foozy@708: ディレクトリ中の実ファイルの名前とみなします。 foozy@708: foozy@708: \sdirname{.hg/patches} ディレクトリ配下にはそれ以外にも、 foozy@708: \sfilename{series} と \sfilename{status} foozy@708: という2つの新しいファイルが作成されます。 foozy@708: \sfilename{series} は、 foozy@708: そのリポジトリにおいて MQ が管理する全てのパッチの一覧を、 foozy@708: 1行1パッチで保持しています。 foozy@708: \sfilename{status} は foozy@708: そのリポジトリにおいて MQ が\emph{適用}した全てのパッチを追跡するための、 foozy@708: 内部帳簿的な用途に使用されます。 foozy@708: foozy@708: \begin{note} foozy@708: 例えば、パッチの適用順序を変更したいような場合、 foozy@708: \sfilename{series} を手動で変更したい場合があるかもしれません。 foozy@708: しかし、MQ の認識状況を容易に損なうことから、 foozy@708: 手動での \sfilename{status} 編集は殆ど全ての場合において不適切です。 foozy@708: \end{note} foozy@708: foozy@708: 新しいパッチを作成したならば、 foozy@708: 普段と同じように作業領域ディレクトリのファイルを編集できます。 foozy@708: \hgcmd{diff} や \hgcmd{annotate} といった、 foozy@708: Mercurial の全ての通常コマンドはそれ以前と全く同様に機能します。 foozy@708: foozy@708: \subsection{パッチの refresh} foozy@708: foozy@708: 作業内容を保存する段階になったなら、 foozy@708: 作業中のパッチを更新するために \hgxcmd{mq}{qrefresh} を使用します foozy@708: (図~\ref{ex:mq:qnew}参照)。 foozy@708: このコマンドは、 foozy@708: 作業領域ディレクトリでの変更内容をパッチへと格納し、 foozy@708: 対応するチェンジセットを、それらの変更内容を保持するように更新します。 foozy@708: foozy@708: \begin{figure}[ht] foozy@708: \interaction{mq.tutorial.qrefresh} foozy@708: \caption{パッチの refresh} foozy@708: \label{ex:mq:qrefresh} foozy@708: \end{figure} foozy@708: foozy@708: \hgxcmd{mq}{qrefresh} コマンドはいつでも何度でも実行できるので、 foozy@708: 作業の``チェックポイント''として利用するのも良いでしょう。 foozy@708: 都合の良い時にパッチの refresh を実施することで、 foozy@708: 実験的な作業を行ってみて、それがうまく機能しない場合には、 foozy@708: 直近の refresh 時点までの変更を、 foozy@708: \hgcmd{revert} コマンドにより取り消すことができます。 foozy@708: foozy@708: \begin{figure}[ht] foozy@708: \interaction{mq.tutorial.qrefresh2} foozy@708: \caption{複数回のパッチ refresh による変更の蓄積} foozy@708: \label{ex:mq:qrefresh2} foozy@708: \end{figure} foozy@708: foozy@708: \subsection{パッチの積み重ねと追跡} foozy@708: foozy@708: パッチに対する作業を終えるか、 foozy@708: 他のパッチに対する作業が必要になったなら、 foozy@708: 再度 \hgxcmd{mq}{qnew} コマンドを実行することで、 foozy@708: 新しいパッチを作成します。 foozy@708: Mercurial は、新規に作成したこのパッチを、 foozy@708: 既存のパッチの最上位に適用します。 foozy@708: 図~\ref{ex:mq:qnew2}を参照してください。 foozy@708: 先に作業していたパッチに含まれる変更は、 foozy@708: この新しいパッチの文脈の一部として含まれます foozy@708: (\hgcmd{annotate} 出力を見れば、このことは明らかです)。 foozy@708: foozy@708: \begin{figure}[ht] foozy@708: \interaction{mq.tutorial.qnew2} foozy@708: \caption{1つ目の上に積み重ねられる2つ目のパッチ} foozy@708: \label{ex:mq:qnew2} foozy@708: \end{figure} foozy@708: foozy@708: これまでは、 foozy@708: \hgxcmd{mq}{qnew} と \hgxcmd{mq}{qrefresh} を除いて、 foozy@708: Mercurial の通常コマンドのみを使用するように注意してきました。 foozy@708: しかし、 foozy@708: 図~\ref{ex:mq:qseries} に示すように、 foozy@708: パッチに関する作業を行う際により便利な多くのコマンドを、 foozy@708: MQ は提供しています。 foozy@708: foozy@708: \begin{itemize} foozy@708: \item \hgxcmd{mq}{qseries} コマンドは foozy@708: MQ が当該リポジトリ中で管理している全てのパッチの一覧を、 foozy@708: 古いものから新しいもの(最も最近\emph{作成されたもの}) foozy@708: の順序で一覧表示します。 foozy@708: foozy@708: \item \hgxcmd{mq}{qapplied} コマンドは、 foozy@708: MQ が当該リポジトリで\emph{適用した}全てのパッチの一覧を、 foozy@708: 古いものから新しいもの(最も最近適用されたもの) foozy@708: の順序で一覧表示します。 foozy@708: \end{itemize} foozy@708: foozy@708: \begin{figure}[ht] foozy@708: \interaction{mq.tutorial.qseries} foozy@708: \label{ex:mq:qseries} foozy@708: \caption{\hgxcmd{mq}{qseries} および foozy@708: \hgxcmd{mq}{qapplied} によるパッチの積み重ねの習得} foozy@708: \end{figure} foozy@708: foozy@708: \subsection{パッチの積み重ねの操作} foozy@708: foozy@708: ``管理されている''パッチと``適用されている''それの間に違いがあることを、 foozy@708: 先の記述では暗に示していますが、 foozy@708: 実際に両者の間には違いがあります。 foozy@708: MQ は適用すること無しに、パッチをリポジトリ中で管理することができます。 foozy@708: foozy@708: \emph{適用された}パッチは、 foozy@708: リポジトリ中に対応するチェンジセットを持ち、 foozy@708: パッチとチェンジセットの効果は作業領域ディレクトリにおいて見ることができます。 foozy@708: \hgxcmd{mq}{qpop} コマンドを使用して、 foozy@708: パッチの適用を取り消すこともできます。 foozy@708: foozy@708: MQ は取り除かれたパッチを\emph{管理}し続けますが、 foozy@708: そのパッチはもはやリポジトリ中に対応するチェンジセットを持たず、 foozy@708: 作業領域ディレクトリにはパッチによる変更の痕跡は残されていません。 foozy@708: 図~\ref{fig:mq:stack}に、 foozy@708: 適用されたパッチと追跡されているそれの違いを示します。 foozy@708: foozy@708: \begin{figure}[ht] foozy@708: \centering foozy@708: \grafix{mq-stack} foozy@708: \caption{MQ のパッチの積み重ねにおける適用済みパッチと未適用パッチ} foozy@708: \label{fig:mq:stack} foozy@708: \end{figure} foozy@708: foozy@708: \hgxcmd{mq}{qpush} コマンドを使用することで、 foozy@708: 未適用パッチの再適用、ないし取り除きを行うことができます。 foozy@708: この操作によりパッチに対応する新しいチェンジセットが作成され、 foozy@708: パッチによる変更は再び作業領域ディレクトリに現れます。 foozy@708: 図~\ref{ex:mq:qpop}に、 foozy@708: \hgxcmd{mq}{qpop} および \hgxcmd{mq}{qpush} の実施例を示します。 foozy@708: 図のように1つないし2つのパッチを一度取り除いても、 foozy@708: \hgxcmd{mq}{qseries} の出力は変化しませんが、 foozy@708: その一方で \hgxcmd{mq}{qapplied} の出力は変化します。 foozy@708: foozy@708: \begin{figure}[ht] foozy@708: \interaction{mq.tutorial.qpop} foozy@708: \caption{適用パッチの積み重ねの変更} foozy@708: \label{ex:mq:qpop} foozy@708: \end{figure} foozy@708: foozy@708: \subsection{複数パッチの適用(push)および取り消し(pop)} foozy@708: foozy@708: \hgxcmd{mq}{qpush} および \hgxcmd{mq}{qpop} のそれぞれが、 foozy@708: デフォルトでは一度に一つのパッチに対して処理を行う一方で、 foozy@708: 一度に複数のパッチの適用や取り消しを行うこともできます。 foozy@708: \hgxcmd{mq}{qpush} に foozy@708: \hgxopt{mq}{qpush}{-a} オプションを指定することにより、 foozy@708: 全ての未適用パッチの適用が、 foozy@708: \hgxcmd{mq}{qpop} に foozy@708: \hgxopt{mq}{qpop}{-a} オプションを指定することにより、 foozy@708: 全ての適用済みパッチの取り消しを行うことができます。 foozy@708: (それ以外の複数パッチの適用/取り消しの方法に関しては、 foozy@708: ~\ref{sec:mq:perf} 節を参照してください。) foozy@708: foozy@708: \begin{figure}[ht] foozy@708: \interaction{mq.tutorial.qpush-a} foozy@708: \caption{全ての未適用パッチの適用} foozy@708: \label{ex:mq:qpush-a} foozy@708: \end{figure} foozy@708: foozy@708: \subsection{安全確認とその無効化} foozy@708: foozy@708: いくつかの MQ コマンドは、 foozy@708: 処理の前に作業領域ディレクトリの確認を行い、 foozy@708: 何らかの改変が検出された場合には処理を中断します。 foozy@708: この確認は、 foozy@708: パッチに取り込まれていない変更内容を失わないために行われます。 foozy@708: 図~\ref{ex:mq:add} に例を示します。 foozy@708: \hgxcmd{mq}{qnew} コマンドは未取り込みの変更 foozy@708: (このケースでは \filename{file3} の \hgcmd{add} に起因するもの)がある場合、 foozy@708: 新しいパッチを生成しません。 foozy@708: foozy@708: \begin{figure}[ht] foozy@708: \interaction{mq.tutorial.add} foozy@708: \caption{強制的なパッチの生成} foozy@708: \label{ex:mq:add} foozy@708: \end{figure} foozy@708: foozy@708: 作業領域ディレクトリを確認するコマンドは、 foozy@708: すべて``了解済み''オプションを取ることができ、 foozy@708: そのオプションは常に \option{-f} と名づけられています。 foozy@708: \option{-f} オプションの厳密な意味はコマンドごとに異なります。 foozy@708: 例えば、 foozy@708: \hgcmdargs{qnew}{\hgxopt{mq}{qnew}{-f}} foozy@708: は新たに生成されるパッチに未取り込みの変更を全て取り込みますが、 foozy@708: \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-f}} foozy@708: は取り消されるパッチが影響を及ぼすファイルに対する変更を元に戻します foozy@708: \footnote{訳注: foozy@708: 「パッチの影響を元に戻す」のではなく、 foozy@708: 「パッチが影響を及ぼすファイル」を全て元に戻す、の意}。 foozy@708: 利用する前に各コマンドの \option{-f} オプションのドキュメントを確認しましょう! foozy@708: foozy@708: \subsection{複数パッチの一括処理} foozy@708: foozy@708: \hgxcmd{mq}{qrefresh} コマンドは、 foozy@708: 常に\emph{最上位の}適用済みパッチを更新します。 foozy@708: これは、あるパッチに対する操作を(refresh することで)中断し、 foozy@708: 取り消し(pop)ないし適用(push)により別のパッチを最上位に持ってくることで、 foozy@708: そのパッチに対して作業することができることを意味します。 foozy@708: foozy@708: この機能によって可能になることを例によって示します。 foozy@708: 2つのパッチによって新しい機能を開発しているものとしましょう。 foozy@708: 1つ目のパッチはソフトウェアの中核機能の変更を、 foozy@708: そして2つ目のパッチは --- 1つ目のパッチの上で --- foozy@708: 中核機能の変更を使用するためのユーザーインタフェース(UI)の変更を行います。 foozy@708: UI へのパッチの作業中に、 foozy@708: 中核機能へのパッチにバグを見つけたとしても、 foozy@708: それを修正するのは簡単なことです。 foozy@708: UI へのパッチに対する \hgxcmd{mq}{qrefresh} により作業中の変更を保存した後に、 foozy@708: \hgxcmd{mq}{qpop} により操作対象パッチを中核機能へのそれに変更します foozy@708: (パッチスタックを下へと移動します)。 foozy@708: 中核機能へのパッチのバグを修正し、 foozy@708: \hgxcmd{mq}{qrefresh} によってパッチへの反映を行った後に、 foozy@708: \hgxcmd{mq}{qpush} により操作対象パッチを UI へのパッチに戻すことで、 foozy@708: やりかけの作業を継続することができます。 foozy@708: foozy@708: \section{パッチに関して更に詳しく} foozy@708: \label{sec:mq:adv-patch} foozy@708: foozy@708: MQ はパッチの適用に GNU \command{patch} コマンドを使用しますので、 foozy@708: \command{patch} コマンドの動作とパッチそのものに関して、 foozy@708: より詳細な情報を知ることは有用です。 foozy@708: foozy@708: \subsection{除去数} foozy@708: foozy@708: パッチのファイルヘッダを見ると、 foozy@708: 実際のパス名には現れない余分な要素を先頭に持っていることに気が付くでしょう。 foozy@708: これは以前にパッチが生成されていた方法の名残です foozy@708: (今でもこの方法を用いていますが、 foozy@708: 近年の構成管理ツールでは稀です)。 foozy@708: foozy@708: Alice が tarball を展開してファイルを編集した後で、 foozy@708: パッチを作成しようと考えたとします。 foozy@708: 作業領域ディレクトリを改名し、 foozy@708: 再度 tarball を展開(この展開のために改名することが必要になります)し、 foozy@708: \command{diff} コマンドに foozy@708: \cmdopt{diff}{-r} および \cmdopt{diff}{-N} オプションを指定することで、 foozy@708: 改変前のディレクトリと改変後のディレクトリの間で再帰的にパッチを生成します。 foozy@708: 一方には改変前のディレクトリ名が全てのファイルのパス冒頭に付加され、 foozy@708: 他方には改変後のディレクトリ名が同様に付加されます。 foozy@708: foozy@708: Alices からパッチを受け取った人物の環境に、 foozy@708: 改変前と改変後ディレクトリの両方と厳密に一致する名前のディレクトリがある、 foozy@708: というのはありそうもない事ですから、 foozy@708: \command{patch} コマンドは、 foozy@708: パッチ適用時にパス名要素の何番目までを取り除くかを指す foozy@708: \cmdopt{patch}{-p} オプションを持っています。 foozy@708: このオプションに指定される数を\emph{除去数}(strip count)と呼びます。 foozy@708: foozy@708: ``\texttt{-p1}'' オプションは、 foozy@708: ``除去数を1とみなす''ことを意味します。 foozy@708: \command{patch} コマンドが、 foozy@708: ファイルヘッダにおいてファイル名 \filename{foo/bar/baz} を検知した場合、 foozy@708: \filename{foo} 部分を除去した foozy@708: \filename{bar/baz} というファイルに対してパッチをあてます foozy@708: (厳密なことを言えば、 foozy@708: 除去数は除去される\emph{パス区切り}(およびそれに付随する要素)の数を指します。 foozy@708: 除去数1は、\filename{foo/bar} を \filename{bar} にしますが、 foozy@708: \filename{/foo/bar}(先頭のスラッシュに注意)は foozy@708: \filename{foo/bar} になります)。 foozy@708: foozy@708: パッチにおける``標準の''除去数は1ですので、 foozy@708: ほとんど全てのパッチは取り除かれる先頭要素を1つ含んでいます。 foozy@708: Mercurial の \hgcmd{diff} コマンドはこの形式でパス名を生成しますので、 foozy@708: \hgcmd{import} コマンドや MQ は除去数1のパッチを期待しています。 foozy@708: foozy@708: 除去数が1ではないパッチをパッチキューに追加しようとした場合、 foozy@708: 現時点で \texttt{-p} オプションを持っていない foozy@708: \hgxcmd{mq}{qimport} (~\bug{311} 参照のこと)では取り込むことができません。 foozy@708: その場合、 foozy@708: \hgxcmd{mq}{qnew} で新規パッチを MQ 上に作成し、 foozy@708: \cmdargs{patch}{-p\emph{N}} によりパッチを適用、 foozy@708: \hgcmd{addremove} でパッチにより追加/削除されたファイルを特定し、 foozy@708: \hgxcmd{mq}{qrefresh} を行うのが最善の方法です。 foozy@708: このような面倒な手順はいずれ不要になるかもしれません。 foozy@708: 詳細は ~\bug{311} を参照してください。 foozy@708: foozy@708: \subsection{パッチ適用手順} foozy@708: foozy@708: \command{patch} が hunk を適用する際には、 foozy@708: it tries a handful of foozy@708: (successively はどこに掛かる?) foozy@708: successively less accurate strategies to try to make the hunk apply XXXXX foozy@708: 用心深いこの方法により、古い版のファイルで生成されたパッチであっても、 foozy@708: 新しい版のファイルに適用することが、多くの場合で可能となります。 foozy@708: foozy@708: \command{patch} コマンドは、 foozy@708: 最初は hunk における行番号、 foozy@708: コンテキストおよび変更対象テキストの厳密一致を試みます。 foozy@708: 厳密一致ができない場合、 foozy@708: 行番号に関する情報を無視し、 foozy@708: コンテキストのみの厳密一致を試みます。 foozy@708: これが成功した場合、 foozy@708: \command{patch} コマンドは、 foozy@708: hunk が適用されたことと、 foozy@708: 元の行番号から\emph{オフセット分}ずれていることを表示します。 foozy@708: foozy@708: コンテキストのみによる一致が失敗した場合、 foozy@708: \command{patch} は冒頭および末尾行を取り除いたコンテキストを用いて、 foozy@708: \emph{縮小}コンテキストのみによる一致を試みます。 foozy@708: 縮小コンテキストによる hunk 適用が成功した場合、 foozy@708: \emph{あいまいな要因}を元に hunk が適用されたことを表示します foozy@708: (この時示される数値は、 foozy@708: \command{patch} コマンドがパッチ適用前にコンテキストから取り除いた行数です)。 foozy@708: foozy@708: これらのどの技法でも適用できない場合、 foozy@708: \command{patch} コマンドは争点となっている hunk が却下された旨を表示します。 foozy@708: \command{patch} コマンドは却下された hunk (単に ``reject'' とも呼ばれます) foozy@708: を同名で \sfilename{.rej} 拡張子を持つファイルに保存します。 foozy@708: 更にその上で、 foozy@708: パッチ適用前のファイルのコピーを \sfilename{.orig} 拡張子付きで保存します。 foozy@708: 拡張子無しのファイルは、 foozy@708: 適切にの適用\emph{された} hunk による変更を含んでいます。 foozy@708: ファイル \filename{foo} を変更する6つの hunk を持つパッチがあり、 foozy@708: そのうちの1つが適用できなかった場合、 foozy@708: 変更前の内容を持つ \filename{foo.orig}、 foozy@708: 適用できなかった hunk を1つ持つ \filename{foo.rej} および foozy@708: 適用できた5つの hynk による変更を含む \filename{foo} foozy@708: の3つのファイルができます。 foozy@708: foozy@708: \subsection{パッチの実現上の癖} foozy@708: foozy@708: \command{patch} コマンドのファイルへの作用を知る上で、 foozy@708: 有用な事がいくつかあります。 foozy@708: foozy@708: \begin{itemize} foozy@708: \item わかりきった事ですが、\command{patch} はバイナリファイルを扱えません。 foozy@708: foozy@708: \item 実行ビットも扱えませんので、新しいファイルを作成する際には、 foozy@708: 読み取り可能にはしますが、実行可能にはしません。 foozy@708: foozy@708: \item \command{patch} は、削除対象ファイルと空ファイルの差分をもって、 foozy@708: ファイルの削除を表します。 foozy@708: そのため、``ファイルを削除する''ことは、 foozy@708: パッチにおいては``全ての行が削除される''ように見えます。 foozy@708: foozy@708: \item 空のファイルと追加対象ファイルの差分をもって、 foozy@708: ファイルの追加を表します。 foozy@708: そのため、``ファイルを追加する''ことは、 foozy@708: パッチにおいては``全ての行が追加される''ように見えます。 foozy@708: foozy@708: \item 古い名前のファイルの削除と新しい名前のファイルの追加をもって、 foozy@708: ファイルの改名を表します。 foozy@708: これは、ファイルの改名を行うパッチのサイズ foozy@708: (footprint)が大きくなることを意味します foozy@708: (パッチにおけるファイルの改名やコピーを Mercurial が推測することは、 foozy@708: 現状では行われないことにも留意してください) 。 foozy@708: foozy@708: \item \command{patch} は空のファイルを表現できませんので、 foozy@708: ``空のファイルをツリーに追加する''ことをパッチで表現することは出来ません。 foozy@708: foozy@708: \end{itemize} foozy@708: foozy@708: \subsection{あいまいさに注意} foozy@708: foozy@708: オフセット付きや、あいまいな要因を元にしている場合であっても、 foozy@708: パッチの適用は完全に成功することが多いのですが、 foozy@708: 一方でこのような厳密性を欠いた適用手法は、 foozy@708: おのずとファイルへのパッチ適用が不完全である可能性を残してしまいます。 foozy@708: 最も典型的な事例は、 foozy@708: パッチを2度適用してしまうことや、 foozy@708: 不適切な位置に適用してしまうことです。 foozy@708: \command{patch} や \hgxcmd{mq}{qpush} foozy@708: がオフセットやあいまい要因に関して言及した際には、 foozy@708: ファイルが適切に変更されていることを後から確認してください。 foozy@708: foozy@708: オフセット付きや、あいあまいな要因を元に適用されたパッチを refresh するのが、 foozy@708: 多くの場合においておすすめなのは、 foozy@708: パッチの refresh が、 foozy@708: パッチを綺麗に適用するための新しいコンテキスト情報を生成するからです。 foozy@708: ただし、パッチを refresh することで、 foozy@708: 元ファイルの異なる版に対してパッチの適用が失敗するようになる場合があるため、 foozy@708: ``多くの場合''おすすめですが、``常に''ではありません。 foozy@708: ソースツリーの複数の版に対して適用可能なパッチを保守するような場合、 foozy@708: パッチ適用処理の結果を検証する機会を得ることが出来るので、 foozy@708: パッチにあいまい要因を持たせておくのは許容範囲です。 foozy@708: foozy@708: \subsection{却下された hunk の取り扱い} foozy@708: foozy@708: パッチの適用に失敗すると、 foozy@708: \hgxcmd{mq}{qpush} はエラーメッセージを表示して終了します。 foozy@708: \sfilename{.rej} ファイルが残されている場合、 foozy@708: それ以上のパッチを push したり他の作業をする前に、 foozy@708: 却下された hunk の修正を行うことが一般的には最善です。 foozy@708: foozy@708: パッチの適用対象であるソースの更新により、 foozy@708: \emph{それまでは}きちんと適用できていたパッチが適用できなくなった場合の foozy@708: Mercurial Queues の使い方の詳細に関しては、 foozy@708: ~\ref{sec:mq:merge} 節を参照してください。 foozy@708: foozy@708: 残念なことに、却下された hunk を扱うための決定的な技法は存在しません。 foozy@708: 多くの場合、\sfilename{.rej} ファイルを参照しながら、 foozy@708: 対象ファイルを編集し、 foozy@708: 却下された hunk を手動で適用しなければなりません。 foozy@708: foozy@708: 思い切った事も辞さないのであれば、 foozy@708: パッチの適用に関しては \command{patch} よりも強力な、 foozy@708: \command{wiggle}~\cite{web:wiggle} と呼ばれるツールが、 foozy@708: Linux カーネルハッカーの Neil Brown により書かれています。 foozy@708: foozy@708: \command{patch} により却下された hunk の適用を自動化するために、 foozy@708: 簡便な手法を用いる \command{mpatch}~\cite{web:mpatch} と呼ばれるツールも、 foozy@708: 別の Linux カーネルハッカーの Chris Mason foozy@708: (Mercurial Queues の作者です)により書かれています。 foozy@708: \command{mpatch} は、 foozy@708: 4つのよくある理由で却下された hunk の適用を助けることができます。 foozy@708: foozy@708: \begin{itemize} foozy@708: \item hunk 中程のコンテキストが変更された。 foozy@708: \item hunk のコンテキストの、先頭あるいは末尾の一方が見当たらない。 foozy@708: \item 大きな hunk よりも---全部なり一部なりが--- foozy@708: 小さな hunk に分割された方が適用しやすい。 foozy@708: \item 現時点でのファイルとわずかに内容の異なる行を foozy@708: hunk が削除しようとしている。 foozy@708: \end{itemize} foozy@708: foozy@708: \command{wiggle} ないし \command{mpatch} を使用する際には、 foozy@708: 実施結果に対して二重に注意を払う必要があります。 foozy@708: 実のところ \command{mpatch} は、 foozy@708: 処理の完了時に自動的にマージプログラムへと誘導することで、 foozy@708: ツール出力の二重確認の手法を強要していますので、 foozy@708: \command{mpatch} の実行結果を確認し、 foozy@708: 残されたマージ処理を完了させることが出来ます。 foozy@708: foozy@708: \section{MQ で最高性能を出すために} foozy@708: \label{sec:mq:perf} foozy@708: foozy@708: MQ は大量のパッチの取り扱いを効率よく実施します。 foozy@708: 2006 EuroPython conference~\cite{web:europython} での講演のために、 foozy@708: 2006 年中旬に性能実験を実施しました。 foozy@708: 適用パッチとして、 foozy@708: 1,738 個のパッチを持つ Linux 2.6.17-mm1 パッチ系列を使用しています。 foozy@708: Linux 2.6.12-rc2 から Linux 2.6.17 にかけての、 foozy@708: 27,472 のリビジョン全てを持つ Linux カーネルリポジトリに対して、 foozy@708: これらのパッチを適用したのです。 foozy@708: foozy@708: 旧式の遅いラップトップ PC 上で、 foozy@708: 1,738 個のパッチ全てを foozy@708: \hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-a}} するのに 3.5 分、 foozy@708: それらを \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} するのに 30 秒かかりました foozy@708: (新しいラップトップなら、 foozy@708: 全てのパッチを push する時間は2分まで下がりました)。 foozy@708: 最も大きなパッチの1つ(22,779 行の変更を 287 のファイルに対して行います) foozy@708: を 6.6 秒で \hgxcmd{mq}{qrefresh} できています。 foozy@708: foozy@708: MQ が巨大なソースツリーで作業するのに適しているのは明らかですが、 foozy@708: 最高の性能を出すために知っておいたほうが良い幾つかのコツがあります。 foozy@708: foozy@708: 最初のコツは、``一括''操作を行うことです。 foozy@708: \hgxcmd{mq}{qpush} および \hgxcmd{mq}{qpop} の実行の際には、 foozy@708: 何ら変更がされていないことと、 foozy@708: \hgxcmd{mq}{qrefresh} し忘れがないことを確認するために、 foozy@708: 常に作業領域ディレクトリを走査しています。 foozy@708: 小さなソースツリーの場合は、 foozy@708: この走査に要する時間は気になりません。 foozy@708: しかし、中程度(10,000 ファイル程度)のソースツリーでは、 foozy@708: 1秒からそれ以上の時間が必要です。 foozy@708: foozy@708: \hgxcmd{mq}{qpush} および \hgxcmd{mq}{qpop} コマンドでは、 foozy@708: 複数パッチを一括して push および pop する際に、 foozy@708: 作業を切り上げる``到達パッチ''を指定することができます。 foozy@708: 到達パッチ指定付きで実行することで、 foozy@708: \hgxcmd{mq}{qpush} foozy@708: は指定したパッチが適用スタックの最上位になるまでパッチの適用を行います。 foozy@708: \hgxcmd{mq}{qpop} の場合は、 foozy@708: 到達パッチが適用スタックの最上位になるまでパッチの取り消しを行います。 foozy@708: foozy@708: 到達パッチの指定には、パッチの名前か数値が使用できます。 foozy@708: 数値指定の場合、パッチは0から数え始めるため、 foozy@708: 最初のパッチは0、次のパッチの1となります。 foozy@708: foozy@708: \section{元ソース変更時のパッチの更新} foozy@708: \label{sec:mq:merge} foozy@708: foozy@708: 直接変更することのできないリポジトリに対して、 foozy@708: パッチスタックを持つことはよくある事です。 foozy@708: 第三者のソースに対する変更や、 foozy@708: 元ソースの更新頻度よりも開発に時間の掛かる機能を実装している場合、 foozy@708: 元ソースの更新との同期や、 foozy@708: 適用できなくなったパッチの hunk を修正する必要があります。 foozy@708: このような作業は、パッチ系列の\emph{リベース}と呼ばれます。 foozy@708: foozy@708: リベースの一番単純な方法は、 foozy@708: パッチに対して \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} を行い、 foozy@708: \hgcmd{pull} で元ソースの変更をリポジトリに取り込み、 foozy@708: 最後に \hgcmdargs{qpush}{\hgxopt{mq}{qpop}{-a}} でパッチを再適用します。 foozy@708: MQ によるパッチ適用では、 foozy@708: 衝突が検出されている間は適用できないパッチの適用を止めることで、 foozy@708: 衝突の解消とパッチの \hgxcmd{mq}{qrefresh} を行う機会を設けつつ、 foozy@708: パッチスタック中の全てのパッチを更新し終わるまでパッチの適用を継続します。 foozy@708: foozy@708: 元ソースの変更がパッチの適用具合に悪影響を及ぼす心配が無いのであれば、 foozy@708: この手法は手軽で且つ上手く機能するでしょう。 foozy@708: しかしながら、 foozy@708: 元ソースで頻繁に更新される部分に触れるようなパッチスタックの場合、 foozy@708: 却下された hunk の手動での修正は、 foozy@708: すぐにでも面倒な作業と化すでしょう。 foozy@708: foozy@708: リベース処理を部分的に自動化する事は可能です。 foozy@708: 元ソースの幾つかのリビジョンに対してきちんと適用できるパッチであれば、 foozy@708: 異なるリビジョンとパッチとの間での衝突に対して、 foozy@708: 事前の適用情報を用いた解消を MQ により行うことができます。 foozy@708: foozy@708: 手順は少々込み入っています。 foozy@708: foozy@708: \begin{enumerate} foozy@708: \item 開始に当たって、 foozy@708: パッチがきちんと適用できている最上位リビジョンに対して foozy@708: \hgcmdargs{qpush}{-a} により全てのパッチを適用します。 foozy@708: foozy@708: \item \hgcmdargs{qsave}{\hgxopt{mq}{qsave}{-e} \hgxopt{mq}{qsave}{-c}} foozy@708: を用いてパッチディレクトリのバックアップを保存します。 foozy@708: このコマンドの実行の際には、パッチを保存したディレクトリの名前を表示します。 foozy@708: \texttt{\emph{N}} を小さい整数とした場合、 foozy@708: \sdirname{.hg/patches.\emph{N}} foozy@708: という形式の名前のディレクトリにパッチが保存されます。 foozy@708: 適用されたパッチ以外に、 foozy@708: ``保存されたチェンジセット''もコミットしますが、 foozy@708: これは内部的な情報と、 foozy@708: \sfilename{series} および \sfilename{status} の状態を記録するためです。 foozy@708: foozy@708: \item hgcmd{pull} により、更新をリポジトリに取り込みます foozy@708: (\hgcmdargs{pull}{-u} を用いない理由は、以降の記述を参照してください)。 foozy@708: foozy@708: \item \hgcmdargs{update}{\hgopt{update}{-C}} を用いて最新の foozy@708: tip リビジョンに更新することで、適用したパッチを無効にしてください。 foozy@708: foozy@708: \item \hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-m} foozy@708: \hgxopt{mq}{qpush}{-a}} を用いて全てのパッチをマージします。 foozy@708: \hgxcmd{mq}{qpush} への \hgxopt{mq}{qpush}{-m} オプション指定により、 foozy@708: パッチ適用に失敗した際に、MQ は 3-way マージを実施します。 foozy@708: foozy@708: \end{enumerate} foozy@708: foozy@708: \hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-m}} 実施の際には、 foozy@708: \sfilename{series} foozy@708: ファイルに列挙されたそれぞれのパッチは通常通り適用されます。 foozy@708: あいまい要因を元にパッチが適用されたり、パッチの適用が却下された場合、 foozy@708: MQ は \hgxcmd{mq}{qsave} により保存されたパッチキューを参照し、 foozy@708: パッチに対応するチェンジセットを用いた 3-way マージを行います。 foozy@708: このマージ処理には Mercurial の通常のマージ機構が利用されますので、 foozy@708: 衝突の解消の際には GUI マージツールが起動されるかもしれません。 foozy@708: foozy@708: パッチの影響を解消し終えると、 foozy@708: マージ結果を元に MQ によるパッチの refresh が行われます。 foozy@708: foozy@708: この手順を終えたリポジトリには、 foozy@708: 古いパッチキューに相当するチェンジセットを元にした余分な head と、 foozy@708: \sdirname{.hg/patches.\emph{N}} に保存された古いパッチキューが残ります。 foozy@708: 余分な head の削除は、 foozy@708: \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a} \hgxopt{mq}{qpop}{-n} patches.\emph{N}} foozy@708: ないし \hgcmd{strip} で行うことができます。 foozy@708: バックアップとしての必要性がなくなったなら、 foozy@708: \sdirname{.hg/patches.\emph{N}} も削除してしまって構いません。 foozy@708: foozy@708: \section{パッチの指定} foozy@708: foozy@708: パッチを操作する MQ コマンドにおけるパッチの指定は、 foozy@708: パッチの名前か数値で行います。 foozy@708: 名前による指定は非常にわかりやすいでしょう。 foozy@708: 例えば、\hgxcmd{mq}{qpush} コマンドへの foozy@708: \filename{foo.patch} の指定により、 foozy@708: \filename{foo.patch} が適用されるまでパッチの適用が繰り返されます。 foozy@708: foozy@708: 短縮形式として、名前と数値オフセットの両方を指定することもできます。 foozy@708: \texttt{foo.patch-2} は foozy@708: ``\texttt{foo.patch} パッチの2つ前''を、 foozy@708: \texttt{bar.patch+4} は foozy@708: ``\texttt{bar.patch} パッチの4つ後ろ''を意味します。 foozy@708: foozy@708: 数値によるパッチの指定はそれほど難しくありません。 foozy@708: \hgxcmd{mq}{qseries} により最初に表示されるパッチは0、 foozy@708: 2番目は1、となっています foozy@708: (そう、0から数え始める仕組みです)。 foozy@708: foozy@708: MQ は、通常の Mercurial コマンドの利用時におけるパッチ操作も簡便にします。 foozy@708: チェンジセット識別子を受け付ける全てのコマンドは、 foozy@708: 適用済みのパッチ名も受け付けます。 foozy@708: リポジトリ中に元々あった通常のタグに加えて、 foozy@708: パッチ適用の際の起点となるリビジョンにタグ\footnote{ foozy@708: \index{tags!特殊タグ名!\texttt{qbase}}\texttt{qparent} foozy@708: }が付与されます。 foozy@708: それに加えて、 foozy@708: \index{tags!特殊タグ名!\texttt{qbase}}\texttt{qbase} および foozy@708: \index{tags!特殊タグ名!\texttt{qtip}}\texttt{qtip} タグにより、 foozy@708: 最下位および最上位の適用ずみパッチをそれぞれ指定できます。 foozy@708: foozy@708: Mercurial の通常タグに対するこれらの拡張は、 foozy@708: パッチの取り扱いをより簡便にします。 foozy@708: foozy@708: \begin{itemize} foozy@708: foozy@708: \item 最新の一連の変更を元に、メーリングリストへパッチ爆弾(patchbomb) foozy@708: を投稿したい場合には? foozy@708: \begin{codesample4} foozy@708: hg email qbase:qtip foozy@708: \end{codesample4} foozy@708: (``パッチ爆弾''については \ref{sec:hgext:patchbomb} 節を参照してください) foozy@708: foozy@708: \item \texttt{foo.patch} 以降のパッチで、 foozy@708: 特定のディレクトリ配下のファイルに関与しているものを、 foozy@708: 全て知りたい場合には? foozy@708: \begin{codesample4} foozy@708: hg log -r foo.patch:qtip \emph{subdir} foozy@708: \end{codesample4} foozy@708: foozy@708: \end{itemize} foozy@708: foozy@708: パッチの名前を利用可能にするために、 foozy@708: MQ は Mercurial の持つ内部タグ機能を使用しているので、 foozy@708: パッチを名前で指定する場合には、 foozy@708: その名前を全て入力する必要はありません。 foozy@708: foozy@708: \begin{figure}[ht] foozy@708: \interaction{mq.id.output} foozy@708: \caption{MQ のタグ機能を使用したパッチの操作} foozy@708: \label{ex:mq:id} foozy@708: \end{figure} foozy@708: foozy@708: パッチの名前をタグで実現することで、 foozy@708: \hgcmd{log} コマンドの実行時に、 foozy@708: その出力の一部としてタグとしてのパッチ名が表示される、 foozy@708: という副作用も得られます。 foozy@708: このことにより、 foozy@708: 適用済みのパッチと``通常の''リビジョンを、 foozy@708: 視覚的に識別することを容易にします。 foozy@708: 適用済みパッチと連携する Mercurial の通常コマンドの実行例を foozy@708: 図 ~\ref{ex:mq:id} に示します。 foozy@708: foozy@708: \section{知っておくと便利な事柄} foozy@708: foozy@708: MQ の利用に関して、独立した節を設ける程ではないものの、 foozy@708: 知っておいたほうが良い事柄が幾つかあります。 foozy@708: ここでは、そういった事柄を集めてみました。 foozy@708: foozy@708: \begin{itemize} foozy@708: \item \hgxcmd{mq}{qpop} でパッチを取り消した後に、 foozy@708: \hgxcmd{mq}{qpush} で再度適用した場合、 foozy@708: その時点での適用済みパッチに相当するチェンジセットは、 foozy@708: pop/push する前のチェンジセットとは\emph{異なる識別子}を持ちます。 foozy@708: 識別子が異なる理由は ~\ref{sec:mqref:cmd:qpush} 節を参照してください。 foozy@708: foozy@708: \item 少なくとも、 foozy@708: パッチスタック上のパッチによるチェンジセット群の foozy@708: ``パッチ性''を保ちたいのであれば、 foozy@708: 他のブランチとそれらを\hgcmd{マージ}すべきではありません。 foozy@708: \hgcmd{マージ}した場合、それ自体は成功するでしょうが、 foozy@708: 結果として MQ が混乱してしまうでしょう。 foozy@708: \end{itemize} foozy@708: foozy@708: \section{リポジトリにおけるパッチの管理} foozy@708: \label{sec:mq:repo} foozy@708: foozy@708: MQ が利用する \sdirname{.hg/patches} ディレクトリが foozy@708: Mercurial の作業領域ディレクトリの外にあるため、 foozy@708: MQ の``下にある''Mercurial のリポジトリは、 foozy@708: パッチの管理や存在に関して何も認識していません。 foozy@708: foozy@708: このことは、 foozy@708: パッチディレクトリの内容をそれ自身の Mercurial リポジトリを用いて管理できる、 foozy@708: という興味深い可能性をもたらします。 foozy@708: 例えば、 foozy@708: パッチに関する作業を行い、\hgxcmd{mq}{qrefresh} をした後で、 foozy@708: パッチの現状を \hgcmd{commit} することで、 foozy@708: 後からその状態へとパッチを``巻き戻す''(roll back)することができるなど、 foozy@708: 有用な機能を提供します。 foozy@708: foozy@708: 複数のリポジトリの間で、 foozy@708: 同一パッチスタックの異なる版を共有することも出来ます。 foozy@708: 筆者は Linux カーネル機能の開発の際にこの手法を使用しています。 foozy@708: 複数の CPU アーキテクチャごとにそれぞれ真新しいカーネルソースのコピーを用意し、 foozy@708: それぞれに作業中のパッチを含むリポジトリを複製します。 foozy@708: 別なアーキテクチャで変更内容の試験を行う際には、 foozy@708: 対応するカーネルソースのパッチリポジトリへ現時点のパッチを push し、 foozy@708: 全てのパッチを最適用(pop 後に push)した後に、 foozy@708: そのカーネルのビルドおよび試験を行います。 foozy@708: foozy@708: リポジトリ形式の上でパッチを管理することで、 foozy@708: 適用対象のソースに対する制御の可否に関わり無く、 foozy@708: 開発者同士でお互いに衝突すること無しに、 foozy@708: 同じパッチ系列に対する作業を実施できます foozy@708: foozy@708: \subsection{MQ のパッチリポジトリサポート} foozy@708: foozy@708: MQ は \sdirname{.hg/patches} ディレクトリを自身のリポジトリとして、 foozy@708: パッチ操作を補助しますが、 foozy@708: \hgxcmd{mq}{qinit} での初期化の際に foozy@708: \hgxopt{mq}{qinit}{-c} オプションを指定することで、 foozy@708: \sdirname{.hg/patches} ディレクトリを foozy@708: Mercurial リポジトリとして作成することが出来ます。 foozy@708: foozy@708: \begin{note} foozy@708: \hgxopt{mq}{qinit}{-c} オプションの指定を忘れた場合、 foozy@708: 任意の時点で \sdirname{.hg/patches} ディレクトリで foozy@708: \hgcmd{init} を実行してください。 foozy@708: \sfilename{status} を履歴管理しようと思うことは\emph{本当に}ありませんから、 foozy@708: \sfilename{.hgignore} ファイルに foozy@708: \sfilename{status} を追加するのを忘れないでください foozy@708: (\hgcmdargs{qinit}{\hgxopt{mq}{qinit}{-c}} は、 foozy@708: この作業を自動的に行います)。 foozy@708: \end{note} foozy@708: foozy@708: 利便性上、 foozy@708: \dirname{.hg/patches} ディレクトリが foozy@708: Mercurial リポジトリである場合、 foozy@708: MQ は作成・取り込みを行ったパッチの全てを自動的に foozy@708: \hgcmd{add} します。 foozy@708: foozy@708: 最後になりますが、 foozy@708: MQ は \sdirname{.hg/patches} において foozy@708: \hgcmd{commit} を実行する短縮コマンド foozy@708: \hgxcmd{mq}{qcommit} を提供していますので、 foozy@708: (ディレクトリ移動等の)煩わしいキー入力が省略できます。 foozy@708: foozy@708: \subsection{幾つかの注意点} foozy@708: foozy@708: MQ によるパッチのリポジトリ管理のサポートは、限定的なものです。 foozy@708: foozy@708: MQ は、パッチディレクトリに対して行われた変更を、 foozy@708: 自動的に検出することはできません。 foozy@708: \hgcmd{pull} の実行や、手動での編集、 foozy@708: あるいは \hgcmd{update} の実行によるパッチや foozy@708: \sfilename{series} の変更を行った場合、 foozy@708: パッチ適用対象のリポジトリにおいて foozy@708: \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} の後に foozy@708: \hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-a}} を行って、 foozy@708: それらの変更を有効にする必要があります。 foozy@708: この作業を忘れた場合、 foozy@708: MQ は適用されているパッチがどれなのか混乱してしまうでしょう。 foozy@708: foozy@708: \section{パッチ操作のためのサードパーティー製ツール} foozy@708: \label{sec:mq:tools} foozy@708: foozy@708: 暫くの間、パッチを使った作業をしていると、 foozy@708: 扱っているパッチの解釈や操作を補助するツールが、 foozy@708: 欲しくてたまらなくなっているに違いありません。 foozy@708: foozy@708: \command{diffstat} コマンド ~\cite{web:diffstat} は、 foozy@708: パッチによって各ファイルがどれだけ変更されるかを表すヒストグラムを生成します。 foozy@708: どのファイルが、どの程度の影響を受けるのか、 foozy@708: といった全体的な``感覚を掴む''には良い方法です foozy@708: (\command{diffstat} の foozy@708: \cmdopt{diffstat}{-p} オプション利用は勿論良いのですが、 foozy@708: ファイル名の前置詞に対して行う \cmdopt{diffstat}{-p} オプションの巧妙な処理は、 foozy@708: 少なくとも筆者にとってはわかりにくいです)。 foozy@708: foozy@708: \begin{figure}[ht] foozy@708: \interaction{mq.tools.tools} foozy@708: \label{ex:mq:tools} foozy@708: \caption{\command{diffstat}、\command{filterdiff} および \command{lsdiff} コマンド} foozy@708: \end{figure} foozy@708: foozy@708: \package{patchutils} パッケージ ~\cite{web:patchutils} は貴重な存在です。 foozy@708: このパッケージは、 foozy@708: ``Unix の理念''に従って、 foozy@708: それぞれがパッチに対して単一の処理を行う小さなツールの集まりです。 foozy@708: \package{patchutils} の中で筆者が最も利用しているのは、 foozy@708: パッチファイルから一部を展開する \command{filterdiff} です。 foozy@708: 例えば、 foozy@708: あるパッチが数ダースのディレクトリに渡って数百のファイルを変更する場合、 foozy@708: \command{filterdiff} を起動することで、 foozy@708: 指定したパターンに名前が合致するファイルにだけ変更を行う、 foozy@708: 小さなパッチを生成することが出来ます。 foozy@708: それ以外の例については、 foozy@708: ~\ref{mq-collab:tips:interdiff} 節を参照してください。 foozy@708: foozy@708: \section{パッチを扱う良い方法} foozy@708: foozy@708: 一連のパッチが、 foozy@708: フリーソフトウェアやオープンソースプロジェクトへ送付するものであろうと、 foozy@708: あなたの作業における定期的な変更手続きとみなされるものであろうとも、 foozy@708: より良く作業するための、 foozy@708: 簡単に利用できる手法があります。 foozy@708: foozy@708: まずは、パッチに説明的な名前をつけましょう。 foozy@708: 例えば \filename{rework-device-alloc.patch} といった名前は、 foozy@708: そのパッチが何を行うものかというヒントをすばやく与えてくれるので、 foozy@708: 良い名前と言えるでしょう。 foozy@708: 名前は長くても問題にはなりません。 foozy@708: 名前を入力することはそれほど多くはないでしょうが、 foozy@708: \hgxcmd{mq}{qapplied} や \hgxcmd{mq}{qtop} といったコマンドは、 foozy@708: 何度も何度も実行するものですから。 foozy@708: 多くのパッチを扱う場合や、 foozy@708: 多くの異なるタスクに手一杯でパッチに多くの注意を割けないような場合、 foozy@708: 名前の適切さはとりわけ重要です。 foozy@708: foozy@708: 次に、どのパッチに対して作業しているのかに注意しましょう。 foozy@708: \hgxcmd{mq}{qtop} コマンドを foozy@708: ---例えば、\hgcmdargs{tip}{\hgopt{tip}{-p}} を指定しつつ--- foozy@708: 使用して頻繁にパッチの名前を見ることで、 foozy@708: どんな作業をしているのかを確認しましょう。 foozy@708: 筆者は作業中に何度も意図しないパッチに対して foozy@708: \hgxcmd{mq}{qrefresh} を実行してしまったことがありますが、 foozy@708: 間違ったパッチに取り込んでしまった変更を正しいパッチに移動させるのは、 foozy@708: 往々にして手のかかるものです。 foozy@708: foozy@708: 上記の理由から、 foozy@708: ~\ref{sec:mq:tools} 節で紹介している foozy@708: \command{diffstat} や \command{filterdiff} foozy@708: のようなサードパーティー製ツールの学習に、 foozy@708: 少しでも良いので時間を費やすべきです。 foozy@708: 前者はパッチの及ぼす変更に関してすばやい見解を得ることが、 foozy@708: 後者はパッチ中の hunk foozy@708: を選択的に継ぎ合わせて異なるパッチに組み上げることができます。 foozy@708: foozy@708: \section{MQ クックブック} foozy@708: foozy@708: \subsection{``些細な''パッチの管理} foozy@708: foozy@708: 真新しい Mercurial リポジトリにファイルを投入するのは、 foozy@708: 非常にオーバーヘッドが低いので、 foozy@708: 単にダウンロードしたソース tarball に対して変更を加えるのだとしても、 foozy@708: MQ によりパッチ管理を行うことは非常に理にかなっています。 foozy@708: foozy@708: まずはソース tarball のダウンロードと展開を行い、 foozy@708: Mercurial リポジトリに投入します。 foozy@708: foozy@708: \interaction{mq.tarball.download} foozy@708: foozy@708: 次にパッチスタックを作成し、変更を行います。 foozy@708: foozy@708: \interaction{mq.tarball.qinit} foozy@708: foozy@708: 数週間から数ヵ月経ってから、 foozy@708: そのパッケージの著者が新しい版をリリースしたとします。 foozy@708: まずはリポジトリに変更を取り込みます。 foozy@708: foozy@708: \interaction{mq.tarball.newsource} foozy@708: foozy@708: 上記手順で \hgcmd{locate} により始まるパイプラインは、 foozy@708: 作業領域ディレクトリ中の全てのファイルを削除しますので、 foozy@708: \hgcmd{commit} の \hgopt{commit}{--addremove} オプションは、 foozy@708: 新しい版においてどのファイルが本当に追加/削除されたのかを判定できます。 foozy@708: foozy@708: 最後に、新しくなったソースツリーの最上位でパッチを適用します。 foozy@708: foozy@708: \interaction{mq.tarball.repush} foozy@708: foozy@708: \subsection{パッチ全体の結合} foozy@708: \label{sec:mq:combine} foozy@708: foozy@708: MQ はパッチ全体を結合する foozy@708: \hgxcmd{mq}{qfold} コマンドを提供しています。 foozy@708: このコマンドは、 foozy@708: 名前を指定したパッチを指定した順序で、 foozy@708: 最上位の適用済みパッチへと``結合''し、 foozy@708: それらの説明文を最上位パッチの説明文末尾へ追加します。 foozy@708: 結合対象のパッチは、結合の時点で未適用でなければなりません。 foozy@708: foozy@708: パッチの結合順序は重要です。 foozy@708: 最上位の適用済みパッチが \texttt{foo} で、 foozy@708: そこに \hgxcmd{mq}{qfold} と \texttt{quux} を foozy@708: \hgxcmd{mq}{qfold} する場合、 foozy@708: 順に \texttt{foo}、\texttt{bar} そして \texttt{quux} foozy@708: と適用するのと同じ効果を持つパッチができあがります。 foozy@708: foozy@708: \subsection{パッチの一部の他のパッチへの併合} foozy@708: foozy@708: パッチの\emph{一部}を他のパッへ併合するのは、 foozy@708: パッチ全体を結合するよりも面倒です。 foozy@708: foozy@708: あるファイル(群)に対する変更全体を移動したい場合、 foozy@708: \command{filterdiff} の \cmdopt{filterdiff}{-i} および foozy@708: \cmdopt{filterdiff}{-x} オプションを用いることで、 foozy@708: パッチから切り出す変更点を選択して、 foozy@708: その結果を併合先パッチへと取り込むことでができます。 foozy@708: 通常は取り込み元となったパッチそのものは変更したくないものです。 foozy@708: そこで、 foozy@708: MQ は取り込み元パッチを \hgxcmd{mq}{qpush} する際に、 foozy@708: 取り込まれた分の hunk が拒否されたことが報告されますから、 foozy@708: \hgxcmd{mq}{qrefresh} でパッチを更新することで、 foozy@708: 重複した hunk を取り除くことができます。 foozy@708: foozy@708: 1つのファイルに対する複数の hunk を持つパッチの一部だけが欲しい場合、 foozy@708: 事態はもう少し厄介ですが、 foozy@708: それでも部分的に自動化することができます。 foozy@708: \cmdargs{lsdiff}{-nvv} を使うことで、 foozy@708: パッチに関するメタデータを表示させます。 foozy@708: foozy@708: \interaction{mq.tools.lsdiff} foozy@708: foozy@708: このコマンドは、3つの異なる数値の類を表示します。 foozy@708: foozy@708: \begin{itemize} foozy@708: \item (最初のカラムは)改変対象の個々のファイルをパッチ中で識別するための foozy@708: \emph{ファイル番号}で、 foozy@708: foozy@708: \item (字下げされた次の行には)変更されるファイルでの hunk の開始行番号と、 foozy@708: foozy@708: \item (同じ行に)hunk を識別するための \emph{hunk 番号} foozy@708: foozy@708: \end{itemize} foozy@708: foozy@708: 必要なファイル番号や hunk 番号を特定するためには、 foozy@708: 視覚的な精査やパッチの読解が必要とされますが、 foozy@708: それらの数値を \command{filterdiff} の foozy@708: \cmdopt{filterdiff}{--files} や foozy@708: \cmdopt{filterdiff}{--hunks} といったオプションに指定することで、 foozy@708: ファイルや hunk を正確に選択することができます。 foozy@708: foozy@708: 一度 hunk を取り出してしまえば、 foozy@708: 結合先パッチの末尾に結合して foozy@708: ~\ref{sec:mq:combine} 節の残りの作業を再開することができます。 foozy@708: foozy@708: \section{quilt と MQ の違い} foozy@708: foozy@708: 既に quilt を熟知しているのであれば、 foozy@708: MQ は同様のコマンド群を持っていますが、 foozy@708: その働きにはいくらかの違いがあります。 foozy@708: foozy@708: 殆どの quilt コマンドに対して、 foozy@708: ``\texttt{q}'' で始まる対応する foozy@708: MQ のコマンドがあることに気付くことでしょう。 foozy@708: 但し、 foozy@708: quilt の \texttt{add} および \texttt{remove} コマンドに対応するのが、 foozy@708: Mercurial の通常の \hgcmd{add} および \hgcmd{remove} であるのが例外です。 foozy@708: また、MQ には quilt の \texttt{edit} に対応するコマンドはありません。 foozy@708: foozy@708: %%% Local Variables: foozy@708: %%% mode: latex foozy@708: %%% TeX-master: "00book" foozy@708: %%% End: