PATCH(1) — NEWS-OS Programmer’s Manual
名称
patch − 元のファイルに差分ファイルを適用するプログラム
形式
patch [options] orig patchfile [+ [options] orig] しかし通常は単に patch <patchfile
解説
patch は、 diff プログラムが作成する 3 つの形式のうちの任意の形式の差分リストを含んだ パッチファイルを受け取り、これらの差分を元のファイルに適用し、 パッチされたバージョンを作成します。 デフォルトでは、バッチされたバージョンは元のファイルに取って代わり、 元のファイルは元の名前に拡張子 “.orig” または “~”、 あるいは −b スイッチで指定した拡張子を付けてバックアップされます。また、 -o スイッチを指定すると、出力ファイル名を指定することもできます。 patchfile が省略された場合、またはハイフンであった場合、 patch は、標準入力から読みとられます。
起動時に patch は、 −c または、 −e、 −n スイッチで無効にされない限り、差分リストのタイプを判断しようとします。 コンテクストに関する差分と通常の差分は patch プログラム自身で処理されますが、 ed 差分は単にパイプを通して ed エディタに出力されるだけです。
patch は先頭の無意味なデータ(ガベージ)をスキップしてから差分を適用し、 それから最後のガベージをスキップしようとします。 したがって、差分リストを含んだ記事やメッセージを patch に与えてもよいわけで、それでも正常に動作するようになっています。 差分全体が一定の量インデントされている場合には、これも考慮されます。
コンテクストの差分の場合には (および、コンテクストの差分ほどではないが通常の差分の場合にも)、 パッチに記述されている行番号が不正だと、 patch はそれを検知できます。 そして、パッチ中の記述を適用する正しい場所を見つけようとします。 patch は最初の推測として、パッチのその部分で書かれている行番号を取り、 直前の部分を適用するのに使ったオフセットをプラスまたはマイナスします。 もしそれが正しい場所でないと、 patch は、その部分のコンテクストに一致する行のセットを 前方および後方に検索します。最初に、 patch はコンテクストのすべての行が一致する場所を検索します。 もしそのような場所が見つからず、 そしてそれがコンテクスト差分であり、 fuzz 因子の最大値が 1 以上にセットされていると、 その場合にはコンテクストの最初と最後の行を無視してもう一度検索を行います。 それが失敗し、fuzz 因子の最大値が 2 以上にセットされていると、 コンテクストの最初の 2 行と最後の 2 行が無視され、 もう一度検索が行われます(デフォルトの fuzz 因子の最大値は 2)。 patch がパッチのその部分を適用する場所を見つけられないと、 patch はその部分をリジェクトファイルに書き出します。 リジェクトファイルは通常、 出力ファイルに “.rej” または “#” を 付け加えた名前になります(リジェクトされた部分は、 入力のパッチがコンテクスト差分でも通常の差分でも、 コンテクスト差分形式で出力されます。 入力が通常の差分だった場合には、コンテクストの多くは単に失われます)。 リジェクトファイル中の部分の行番号は、 パッチファイルのものとは違う場合があります。 これは、 patch は失敗した部分を古いファイルではなく新しいファイルに準拠して考えており、 そのときのおおよその位置がリジェクトファイルに反映されているからです。
各部分の処理が終わると、ユーザにその部分が成功したか失敗したかが報告され、 その部分が何行目(新しいファイルで)に当たると patch が考えたのかが報告されます。 もしこれが差分で指定した行番号と違う場合には、 オフセットが報告されます。単独で非常に大きなオフセットが報告されたなら、 ひょっとしたらその部分が間違った位置に適用されてしまったかもしれません。 fuzz 因子が一致させることに使われたと報告された場合も、 ユーザはその結果について少し疑ってみるべきでしょう。
コマンド行で元のファイルを指定しないと、 patch は、編集するファイル名が何かを先頭のガベージから取り出そうとします。 コンテクスト差分のヘッダでは、 ファイル名は “∗∗∗” または “---” で始まる行にあり、 その中で一番短いファイル名が使われます。 コンテクスト差分にはそのような行がありますが、 もし先頭のガベージの中に “Index:” という行があると、 patch はその行のファイル名を使おうとします。 コンテクスト差分ヘッダは、Index 行よりも優先します。 先頭のガベージからファイル名が取り出せないと、 ユーザはパッチしたいファイルの名前を尋ねられます。
(もし元のファイルは見つからないが、 適当な SCCS や RCS ファイルがあるような場合には、 patch はそのファイルを得る、あるいはチェックします)。
また、先頭のガベージに “Prereq: ” という行が含まれていると、 patch は必要条件の行から最初の単語(通常、バージョン番号)を取り出し、 入力ファイルをチェックしてその単語があるかどうかを調べます。 もしなければ、 patch は処理を進める前に確認を求めてきます。
これらすべてを総合して考えると、ニュースインタフェースでは次のように 指定できることになります。 | patch −d /usr/src/local/blurfl こうすると、パッチを含んだ記事から直接 blurfl というディレクトリにある ファイルをパッチできます。
パッチファイルに複数のパッチが含まれている場合には、 patch はそれらが別々のパッチファイルから取られたかのように、 それぞれのパッチを適用しようとします。 つまり、パッチするファイル名はそれぞれの差分リストごとに判断され、 各差分リストの前のガベージは、前述のようにファイル名や 改訂レベルなどというような意味のある内容についてチェックされるのです。 対応する引数リストを‘+’で区切ってやると、2 番目以降のパッチに 対してスイッチ(および元になる新たなファイル名)を指定できるようになります (ただし、2 番目以降のパッチに対する引数リストは、新しいパッチファイルの 指定はできません)。
パッチ は以下のようなスイッチを識別します。
−b 次の引数をバックアップの際の拡張子として使うようにします (“.orig” や “~” の代わりに)。
−B 次の引数を、バックアップファイルの名前のプレフィックスとして解釈させます。 この引数が指定されると、−b の引数はすべて無視されます。 この引数は、Larry Wall の patch v2.0.1.4 のパッチレベル 8 に対して、 M.Greim(greim@sbsvax.uucp)が付け加えた拡張機能です。
−c patch にパッチファイルをコンテクスト差分として解釈させます。
−d patch に次の引数をディレクトリとして解釈させ、 何かする前にそこに cd するようにします。
−D "#ifdef...#endif" 構文を使って、 patch に変更をマークさせます。 続く引数は、区切りのシンボルとして使われることになります。 ただし、C コンパイラと違って、 −D と引数の間には空白が必要です。
−e patch にパッチファイルを ed スクリプトとして解釈させます。
−f patch に、ユーザは自分で何をしているかを正確にわかっていると仮定させ、 何も質問してこないようにします。ただし、コメントは抑制しません。 もしこれも抑制したければ、 −s を使ってください。
−F<number>
fuzz 因子の最大値を設定します。 このスイッチはコンテクスト差分にしか適用されません。 これを指定すると、 patch は、パッチのある部分を適用する場所を検索する際に、 最大この行数を無視するようになります。 ここで注意しなければならないのは、大きな fuzz 因子を指定すればするほど、 間違ったパッチを行う危険性も高くなるということです。 デフォルトの fuzz 因子は 2 で、 コンテクスト差分中のコンテクストの行数よりも 大きい数は指定できません(通常は 3)。
−l タブやスペースが入力ファイル中に入り乱れているような場合に、 パターンマッチングを緩やかにします。 パターン行中の空白の任意のシーケンスは、 入力ファイルの任意のシーケンスにマッチします。 ただし通常の文字は正確にマッチしなければなりません。 この場合も、コンテクストの各行は入力ファイル中の行に マッチしなければなりません。
−n patch に、パッチファイルを通常の差分として解釈させます。
−N patch に、反転されているパッチ、またはすでに適用済のパッチであると判断したも のについては、それらを無視するようにさせます。 −R も参照してください。
−o 次の引数を、出力ファイル名として解釈します。
−p<number>
パス名の削除カウントを設定します。これは、パッチを送った人とは違う ディレクトリにユーザが自分のファイルを置いているような場合に、 パッチファイル中でパス名を見つける方法を制御するものです。削除カウントは、 パス名の先頭からいくつのスラッシュを削除するかを指定します (その中間のディレクトリ名も削除される)。たとえば、パッチファイル中の ファイル名が次のようになっているとします。 /u/howard/src/blurfl/blurfl.c ここで −p または −p0 を指定するとパス名はまったく変更されませんが、 −p1 を指定すると次のようになります。 u/howard/src/blurfl/blurfl.c このように、先頭のスラッシュが削除されたわけです。 −p4 とすると次のようになります。 blurfl/blurfl.c また、 −p を全く指定しないと、単に "blurfl.c" が得られます。いずれにせよ、 ファイルはカレントディレクトリか、 −d スイッチで指定されたディレクトリのどちらかで検索されることになります。
−r 次の引数を、リジェクトファイルの名前として解釈させます。
−R patch に対して、このパッチが、古いファイルと新しいファイルを逆にして作って しまったものだということを知らせます(実際こういうことは時々起こるわけで、 人間である以上このような間違いは避けられません)。 patch は、それぞれの部分を適用する前にスワップしようとします。リジェクトは、 スワップされた形で出力されます。 −R スイッチは ed 差分スクリプトでは動作しませんが、これは、逆の操作を 作り出すには、情報が少なすぎるからです。 パッチの最初の部分が失敗すると、 patch はそれを反転して、それで適用できるかどうかを調べます。もしできれば、 −R スイッチをセットしたいかどうか尋ねてきます。 もしできなければ、パッチは通常どおり適用されていきます (注意:この方法では、パッチが通常の差分で、 最初のコマンドがアペンドである(つまりそれは本来なら削除であった)場合には、 反転パッチは検出できません。 なぜなら、空のコンテクストはすべてにマッチするという事実により、 アペンドは常に成功するからです。 幸運なことに、大部分のパッチは行を削除するよりも追加/変更を行うため、 反転された通常の差分のほとんどは削除から始まることになり、 しかもそれは失敗するので、反転パッチ検出のきっかけになります)。
−s エラーが発生しない限り、メッセージを出力しないで処理させます。
−S patch にこのパッチをパッチファイルから無視させ、 ファイル中の次のパッチを検索させることを続けます。 したがって、次のようにすると 3 つのパッチのうち最初と 2 番目のものを 無視させます。 patch −S + -S + <patchfile
−v patch に、その改訂ヘッダとパッチレベルを表示させます。
−x<number>
内部のデバッグフラグをセットします。これは patch にパッチを当てる者だけに必要なものです。
環境
patch は環境変数をまったく使っていません。
関連ファイル
/tmp/patch∗
関連事項
パッチ制作者への注意
もしパッチを外部に送ろうとするなら、 ここで述べるいくつかのことを心に留めておいてください。 まず、送りたいパッチファイルの最初の差分として、 パッチレベルを増加させるようにパッチした patchlevel.h というファイルを 用意しておくと、 ユーザがパッチに失敗しないようにできます。 そのパッチに Prereq: という行を入れておけば、 ユーザが間違った順序でパッチを実行しようとすると、 警告が出力されます。 2 番目に、コンテクスト差分ヘッダでも、Index: の行でも、 自分がファイル名を正確に指定したかどうかを確認するようにしてください。 サブディレクトリ中のものをパッチしているときには、 必要に応じて −p スイッチを指定するよう、パッチのユーザに教えてください。 3 番目に、 自分が作成したいファイルと空のファイルを比較する差分を送ると、 ファイルの作成ができます。 これは、作成したいファイルがターゲットディレクトリにない場合にだけ動作します。 4 番目に、反転パッチを送らないように注意してください。 これを送ってしまうと、ユーザはパッチをすでに実行したのかどうか戸惑う ことになります。5 番目に、たとえば 582 個の差分リストをひとつのファイルに 押し込めることもできなくはありませんが、ユーザが混乱しそうな場合には、 関連のあるパッチをグループにして別々のファイルに収めるようにしてください。
診断
ここにはいちいち挙げられないほど多いのですが、たいていの場合は、 patch がユーザのパッチファイルを解析できなかったということが原因です。
“Hmm...” というメッセージは、 パッチファイル中に処理できなかったテキストがあり、 patch がそのテキストにパッチがあるかどうか、 そしてもしあるなら、どのような種類のパッチかを調べようとしていることを 示しています。
patch は、リジェクトファイルが作成された場合、ゼロ以外のステータスで終了します。 ループ中で 1 組のパッチを適用している際には、 この終了ステータスをチェックするのはユーザの責任で、 これを怠ると、部分的にパッチ済みのファイルにその後のパッチが 適用されなくなってしまいます。
警告
patch は、ed スクリプトで行番号が範囲を超えているかどうか判断できず、 “change” または a “delete” コマンドを見つけたときに、 通常の差分中に間違った行番号があることしか検知できません。 fuzz 因子を 3 にしてコンテクスト差分を行った場合も、 同様の問題が起こり得ます。 適当な対話的なインタフェースが追加されない限り、 おそらくこのような場合には、自分でコンテクスト差分を実行してみて、 変更に意味があるかどうかをチェックする必要があるでしょう。 もちろん、エラーなしで終われば、 パッチが正常に動作した確率は非常に高いのですが、 それも絶対というわけではありません。
patch は、たとえ大量の推測を行わなければならなかったとしても、 通常は正しい結果を生み出します。 しかしそれは、パッチがパッチ作成の元になったファイルと 全く同じバージョンに対して適用された場合にだけ、 正しいことが保証されます。
バグ
部分的なマッチ、非常にかけ離れたオフセットやスワップされたコードについて、 もっと気のきいた方法も取れたでしょうが、 それには余分なパスが必要になったはずです。
複数の手順が存在する場合 (つまり"#ifdef OLDCODE ... #else ... #endif"のようなとき)には、 patch は両方の方法でパッチしてみることができず、 どちらかがとにかく動作したなら(間違ったパッチである場合もあるわけですが)、 ユーザには成功したと報告してしまいます。
適用済みのパッチをユーザが適用すると、 patch はそれが反転パッチであると考え、パッチを外してしまいます。 これはひとつの機能として考えてもよいでしょう。
NEWS-OSRelease 4.1C