Andrew Morton <akpm@digeo.com>
18 October 2002
訳: MUNEDA Takahiro <>
2005 04 23
訳者が追記した行をわかりやすくするために,行頭に縦棒を記してある.
この文章のオリジナル版は以下の場所にある: http://www.zip.com.au/~akpm/linux/patches/patch-scripts-0.18/
和訳版は以下の場所にある: http://mux03.panda64.net/docs/docco_ja.html
これは私がカーネルのパッチを管理するために使っている一連のシェルスクリプトの説明である.これらはかなり強力だ. Linux カーネル以外のプロジェクトでも使える.使うのは簡単で,とても早い.
いつもパッチを当てたり外したりするので,結局はこのスクリプトを使って何回も再コンパイルをすることになる. ccache はすべての苦痛をすべて取り去ってくれる. http://ccache.samba.org - 現在のワーキングディレクトリと同じファイルシステム上に必ず cache ディレクトリを置くように.そうすれば ccache はハードリンクを使える.
キーとなる賢明なコンセプトは,主要な生産物はパッチであること. ".c" ファイルでも ".h" ファイルでもない.むしろパッチ.そう,ここではパッチは最上級の目的物である.
すべてのスクリプトを PATH のどこかか, /usr/lib/patch-scirpts に置く.*1
パッチスクリプトは "pc", "patches", "txt" と呼ばれる 3 つの特別なディレクトリを必要とする.
もし環境変数 PATCHSCRIPTS がセットされていれば,その値がそれら 3 つのディレクトリが置かれるディレクトリとなる.通常,相対パス名である.なので
setenv PATCHSCRIPTS ./i-put-them-here
は,パッチスクリプトに ./i-put-them-here/pc などを見るように指示する.
もし環境変数 PATCHSCRIPTS が定義されていないが, ./patch-scripts が存在している場合,パッチスクリプトは ./patch-scripts/pc/, ./patch-scripts/patches/, ./patch-scripts/txt/ を使う.
それでもなければ,パッチスクリプトは ./pc, ./patches, ./txt を使う.
このドキュメントでは,記号 $P は上の検索方法で決定される, pc/, patches/, txt/ のディレクトリが存在するディレクトリを示すために使われる.
$P はいつも相対パスで展開されるものとする.
すべての作業は単一のディレクトリツリーで起こる.すべてのコマンドはそのツリーのルートで実行される.スクリプトはパッチの "スタック(積み重なり)" を管理する.
それぞれのパッチは基本のツリーと前提パッチとに対する変更である.
すべてのパッチは ./series ファイルの中に順番に並べられている.利用者は series ファイルを管理する. series ファイル中の `#' で始まる行は無視される.
現在適用されているパッチのどれもが ./applied-patches に記述されている.パッチスクリプトファイルはこのファイルを管理する.
それぞれのパッチはツリー中のいくつかのファイルに作用する.これらのファイルは "patch control (パッチ制御)" ファイルに並べられている.これらの .pc ファイルは $P/pc/ ディレクトリに存在する.
パッチは $P/patches/ ディレクトリに存在する.
パッチに関するドキュメントは $P/txt/ ディレクトリに存在する.
ここまでで,あるパッチ "my-first-patch" は以下のものが存在する:
"my-patch" パッチが apatch もしくは pushpatch(これは apatch を呼ぶ)を用いて当てられると,影響のあるすべてのファイル($P/pc/mypacth.pc から分かる)は,名前に ~my-patch を付けてコピーされる.つまり $P/pc/my-patch.pc が
kernel/sched.c fs/inode.c
を含んでいると, apatch はそれらのファイルを kernel/sched.c~my-patch と fs/inode.c~my-patch にコピーする.その後パッチが kernel/sched.c と fs/inode.c に当てられる.
refpatch(これは mpatch を呼ぶ)によって差分が作られると,その差分は kernel/sched.c と kernel/sched.c~ の間で作られる.どうやってスクリプトは "~my-patch" を使うことが分かるのか?それは my-patch が現在の最高位のパッチでだからである.それは ./applied-patches の最終行に書かれている.
このようにして,すべてのものは積み重ねることができる.もし 4 つのパッチ, "patch1","patch2","patch3","patch4" が当たっていて,かつ,もし patch-2 と patch-4 のどちらも kernel/sched.c に触れていれば,以下のファイルを持つ:
これが意味することは, diff ヘッダには "~patch-name" が含まれ,それはドキュメントを書くのにとても役に立つということである.
さぁ,はじめよう.
/usr/src/linux に移動する(もしくはどこでも)
mkdir pc patches txt
よし,パッチを作ろう.
fpatch my-patch kernel/sched.c
kernel/sched.c を kernel/sched.c~my-patch にコピーした. ./applied-patches に "my-patch" を追加し, "kernel/sched.c" をパッチ制御ファイル pc/my-patch.pc に追加した.
kernel/sched.c をちょっといじる
パッチのドキュメントを書く準備ができた.
txt/my-patch.txt を書く
パッチを作成する
refpatch
これで patches/my-patch.patch が作られる.見てみて.
次に,パッチを外す.
poppatch
applied-patches は今,空で,パッチは外されている.
それじゃ my-patch にファイルを追加して, my-second-patch を作ろう:
"my-patch.patch" を ./series に追加する(このファイルには空行を含めないように
pushpatch
よし,パッチは再度当てられた.さあ,他のファイルを足そう.
fpatch kernel/printk.c
ここで fpatch に引数を 1 つだけ指定したことに注意.新しいパッチを開始するんじゃなくて, kernel/printk.c を現存する最高位のパッチに追加する.それは my-patch だ.
kernel/printk.c をいじる
my-patch を更新する(最終的に refpatch は何度もする実行することになる)
refpatch
やっと 2 番目のパッチを開始する.
fpatch my-second-patch kernel/sched.c
ここで applied-patches を見て欲しい.また `ls kernel/sched*' もして欲しい.
my-second-patch に変化を与えるため, kernel/sched.c をいじる
my-second-patch を生成する
refpatch
patches/my-second-patch.patch を見てみて.
"my-second-patch.patch" を series ファイルに追加することを忘れないこと.
次に両方のパッチを削除する:
poppatch poppatch
マジで,こんな感じ.
一般的に,これらのコマンドは至るところで "patch-name" を必要とし, txt/patch-name.txt, patch-name.pc, ただの patch-name もしくは何かの形からなる.スクリプトは先頭の "txt/", "patch/" もしくは "pc/" や余計な拡張部を取り除く.それはこうすればよい
apatch patches/a<tab>
パッチ名を選択するために,シェルのタブ機能を有効に使う.
何らかの内部のもの
これはパッチを追加する低レベルの機能である.これは ~-files をコピーし,パッチを当てられたファイルを更新する.実際のパッチを当てる.
apatch はまずパッチを当てるお試し実行をし,もしお試しが失敗すればパッチを当てるのを取りやめる.
却下された時はこれをするとよい:
よし,今は patch-name は当てられているが,拒否もされた.拒否を修正したあと以下をする.
refpatch
これで次に行けるようになる.
拒否を扱う他の手は, -m オプションを使うことだ:
今はパッチが当たっている.多くの場合 wiggle は全ての変更を当てるための管理を行う.もしパッチ全体が適用できなかったら, wiggle はきれいに当たらなかったパッチに衝突した印を付ける.それらを修正し, "refpatch" を実行する.これで次に行けるようになる.
wiggle コマンドは,もし "merge base" ファイルが使えるのであれば 3 種の合体モードを扱える.たとえば,正しく適用できるファイルなど.それらのファイルはディレクトリツリー内に散乱するので,標準では作成されず,また多くの場合 wiggle はこれらのファイルがなくてもうまく機能する.しかし, merge base ファイルを持っていることで wiggle が機能するための追加情報を与え助けることができ, wiggle はよりうまく機能する.もし merge base ファイルが必要なら, rpatch ファイル中の kill_old_ones() 内の "mv" の行をコメントアウトしなさい.
'wiggle' ツールは http://cgi.cse.unsw.edu.ac/~neilb/source/wiggle/ から入手可能.
シリーズの 1 つの全体パッチを作るために,シリーズ中のすべてのパッチを徐々に combinediff する. patchutils に含まれている combinediff が必要である.
http://cyberlk.net/tim/patchutils/ を見なさい.( patchutils の "experimental(試験版)" をダウンロードしてはいけない - 半分しかコマンドしか含まれていないようなので."stable(安定版)" で行こう)
忘れた.
./series に並べられているパッチを,シリーズファイル中の順番と同じ順番に並ぶように名前を付けた一連のファイルとして出力する.
使い方:
export_patch directory [prefix]
例:
./series には以下が含まれているものとする.
mango.patch orange.patch banana.patch apple.patch pear.patch
export_patch ../mypatches fruit
パッチは以下のようにコピーされる.
../mypatches/p00001_fruit_mango.patch ../mypatches/p00002_fruit_orange.patch ../mypatches/p00003_fruit_banana.patch ../mypatches/p00004_fruit_apple.patch ../mypatches/p00005_fruit_pear.patch
このように名付けられ,誰でも簡単にパッチを当てられる:
cat mypatches/p*fruit* | patch -p1
もし prefix が指定されていなければ,パッチの名前は "original.patch" が "pXXXXX_original.patch" になるように変えられる.
もし patch-name が指定されると, fpatch はひとつのファイル foo.c を変更(または追加,削除)する新しいパッチを始める. ./applied-patches を更新し, pc/patch-name.pc を作成する. fpatch は foo.c を編集するための準備として foo.c を foo.c~patch-name にコピーする.
もし patch-name が指定されなければ, fpatch は foo.c を現在の最高位にあるパッチに追加する. "foo.c" を $P/pc$(toppatch).pc に追加する. foo.c を foo.c~$(toppatch) にコピーする.
一連のパッチファイルを取り込み, $P/pc, $P/txt, $P/patches と ./series を必要なものとして作成する.またパッチの先頭を取り出して $P/txt/*.txt を作成する(refpatch や export_patch の出力を破壊するかも知れないので,diffstat の出力があれば削除する).取り込まれたパッチの名前はシリーズファイルに追加される.
$P/txt/*.txt ファイルを作成する時に, formail を用いて "From:" と "Subject:" 行を残しメールヘッダは削除される. まだなければ "DESC" と "EDESC" の印が追加され, "From:" と "Subject" 行があれば DESC の一部に使われる.(これらの印について,詳しくは以下の "patchdesc" コマンドを参照.)
またパターンを削除することで,取り込みの時にパッチ名を変更することができる.もしパッチを正しい順番に並べるのを助けるために考えられたファイル名をもつ一連のパッチを持っている場合,これは役に立つ."p001_xxx_funky_stuff.patch" のようなファイルは import 時に自動的に funky_stuff.patch に名前を変更され,順番通りにシリーズファイルで管理させる.
import_patch は必要であれば,パッチ(*.Z, *.bz2, *.gz)を展開する.
使い方:
import_patch [-p pattern] patchfile ...
例:
% ls ../fruit/p*patch ../fruit/p00001_northern_apple.patch ../fruit/p00001_tropical_mango.patch ../fruit/p00002_northern_pear.patch ../fruit/p00002_tropical_orange.patch ../fruit/p00003_tropical_banana.patch
% import_patch -p 'p[0-9]*_tropical_' ../fruit/p*tropical*
pc/mango.pc が再作成される
pc/orange.pc が再作成される
pc/banana.pc が再作成される
% import_patch -p 'p[0-9]*_northern_' ../fruit/p*northern*
pc/apple.pc が再作成される
pc/pear.pc が再作成される
その後, "pushpatch; refpatch" を 5 回する.
現在最高位にあるパッチが影響を与えるファイルの一覧を表示する.
これは基本的に
cat pc/$(toppatch).pc
名前を指定されたパッチを現在最高位のパッチに "加える".
これは 2 つのパッチを 1 つにまとめたい時に使う.`patchname' が影響を与えるすべてのファイルが pc/$(toppatch).pc に追加され(もし影響を与えるファイルがまだなければ), `patchname' が当てられる.最高値のパッチには変更はないまま.あとで refpatch を実行する必要がある.
パッチを生成する低レベルのもの.
パッチの名前を変更する.
kernel.orgから新しいカーネルを取り込むために使うもの.
patch -p0 形式を patch -p1 形式に変換する内部コマンド.
パッチの一行説明を生成する.
txt/my-patch.txt は以下のフォーマットを採る.
<start of file> DESC 何か短い説明 EDESC 長い説明 <end of file>
パッチシリーズの省略形の要約を生成するために使う.
patchdesc $(cat series)
誰かにパッチを送る.
使い方:
patch-bomb person1@place1.com person2@place2.com ... \ patchname1 patchname2 ...
patch-bomb は Bryan Henderson が作った makemail と smtpsend を使う(ありがとう!).
patch-bomb は最初に記述されたメールアドレス宛("To:")にパッチを送る.その他記述されているメールアドレスは写し("Cc:")としてメールを受け取る.
もし txt ファイルの 4 行目の形式が:
From: Name Name <foo@bar.com>
ならば, patch-bomb は foo@bar.com を Cc: に追加する.これは patch-bomb が txt ファイルが以下の形式であることを期待しているということである:
1: DESC 2: パッチの題名 3: EDESC 4: From: person's name <person@place.com> 5: ... 6: ... ここに更新履歴 ... 7: ...
patch-bomb はすべてのパッチを昇順に番号付ける.受信者は以下を見る:
Subject: [patch 1/2] パッチの題名
patch-bomb は各パッチ間で 5 秒間待ち,そうすることで受信者が正しい順番でメールを受け取る機会を与える.
patch-bomb はメールを localhost で稼働している SMTP サーバに届ける.もし異なる SMTP サーバを使いたいのであれば(たとえば,他のサイトへの ssh ポートフォーワード), SMTP_SERVER 環境変数を設定する.たとえば:
export SMTP_SERVER=localhost:10025
patch-bomb は $EMAIL_ADDRESS 環境変数にメールアドレスが設定されていることが必要である:
export EMAIL_ADDRESS=yourname@yoursite.com
内部ユーティリティ
パッチから .pc ファイルを生成する独立したツール.
誰かが "his-patch.diff" を送ってきたとする.すべき事は:
cp ~/his-patch.diff patches/his-patch.patch pcpatch his-patch
これによって $P/pc/his-patch.pc が作られすべてセットされる. "his-patch.patch" を ./series の正しい場所に追加して pushing を始める.
忘れた
現在のパッチのスタック(積み重なり)から 1 つもしくはそれ以上のパッチを削除する.このコマンドはシリーズファイルを使わない.純粋に applied-patches に対して働く.
使い方:
パッチの状態を表示する
使い方:
pstatus [patchfile ...]
パッチごとに 1 行結果が出力される:
もしパッチ名が指定されなければ, $P/patches/*.patch が指定されたと推測する.
誤解を招かないための注意:
同じファイルを追加したり変更したりする別々のパッチが含まれているパッチセットでは,ファイルを追加するパッチもしくは最高位のパッチにおいて,偽の "Needs refpatch(refpatch を必要としています)" 状態を出すかも知れない.
2 つのモード:
ptk diff -
tkdiff を $(toppatch) によって影響を与えられるすべてのファイルに対して実行する.差分は最高位のパッチによる変更のみが表示される!たとえば, "filename" と "filename~toppatch-name" の間の差分.
ptkdiff filename
ただ,そのファイルに対して tkdiff を実行し, toppatch による変更が表示される.
シリーズファイルから次のパッチを適用する.
これは,最高位のパッチを見つけるために ./applied-patch を参考にし,その後次のファイルを見つけるために ./series ファイルを参考にする.そしてそれを push する.
最高位のパッチを再作成する. pc/$(toppatch).pc から影響を与えるすべてのファイル読み,チルダが付いたファイルに対して差分をとる.
またパッチのドキュメントと diffstat の要約をパッチに貼り付ける.
もし環境変数 $ADD_SIGNED_OFF_BY が設定されていれば, refpatch はその文字列を追加する*2
Signed-off-by: J Random Developer <jr.developer@wherever.com>
作成されたパッチの適切な場所に.これは $GECOS と $EMAIL_ADDRESS 環境変数を使う.
なんか.
パッチのためにCVSの名前を変更する
ちょっとしたハック.現在当たっているすべてのパッチのまとまった差分を作成する.しかし差分をとるために ../linux-2.x.y ツリーが必要である.もう 1 回する必要があり.
内部コマンド
なんか誰かが書いたパッチを分割するもの.使わない.
CVS のリビジョン管理配下で pc/*, patches/*, txt/* を持っていることを想定していて, tag-series はパッチセットそれぞれの構成要素にタグを付けられる.
tag-series s2_5_44-mm3 pc/2.5.44-mm3-series
のようにして使い, "pc/2.5.44-mm3-series" シリーズファイルの中に記述されているすべての .pc, .patch, .txt ファイルに "s2_5_44-mm3" という cvs タグを付ける.
それぞれのパッチセットリリースのために異なるシリーズファイルを使うので,ちょっと余計だが pc/2.5.44-mm3-series のタグも付ける.
最高位のパッチの名前を表示する. ./aplied-patchs から情報を得る.
diff によって影響を与えられるファイルの一覧を表示する.
Rasmus Andersen の diff を最小の文脈の形に変換するためのスクリプト.もし扱いにくい拒否を受けているなら,この形式はパッチを当てるのにいい可能性を持つかもしれない.しかし入力に小さな文脈が与えられた時には,パッチは間違いを犯せるし犯すだろう.
カーネルツリー, $P/pc, $P/patches, $P/txt の内容が CVS 配下にあるようにしている.これが重要…
私は複数の "シリーズ" ファイルを持っている.これらを $P/pc/foo-series に置き,
ln -s pc/foo-series series
を foo に対して作業を行う時に使う.
もし誰かがパッチを送ってきたら,以下を行う:
cp ~/whatever patches/his-patch.patch pcpatch his-patch apatch his-patch
もし apatch が失敗したら,その後 `apatch -f his-patch' を実行し拒否されたものを修正する.
refpatch
ちょっとした失敗をきれいにするため.
poppatch cvs add pc/his-patch.pc patches/his-patch.patch cvs commit pc patches
./series を編集し "his-patch.patch" を適切な場所に置く.
もし特定のパッチ("dud-patch")で作業していて,何かごちゃごちゃしているのなら,以下を実行するだけ:
すべて大丈夫.
私がするのは最新の -bk の差分を http://www.kernel.org/pub/linux/kernel/people/dwmw2/bk-2.5/ からとってきて,以下をする:
gzip -d < cs<tab> > patches/linus.patch pcpatch linus apatch linus | grep diff
bitkeeper との差分とは異なっているので,削除されたすべてのファイルを手に入れる.
cvs up -ko <missing files from the above diff>
apatch linus $EDITOR linus/linus.txt
変更セットの番号を txt/linus.txt に追加する.
refpatch poppatch
"linus.patch" が ./series ファイルの最初のエントリーとして追加され,その他のパッチをそのパッチの上に追加することを始められる.
めっちゃたくさん,ホンマたくさん.スクリプトは不十分なので,エラー処理はぎこちなく,もしちょっといらんことすると,最後には困ったことになる.
一般的に,スクリプトはファイルやパッチを壊さないようとても注意をしている.が,しかし ./applied-patches や ~-files をやっかいな状態にすることがある.
たいてい, ~-files を元のファイルの上にコピーし戻し, ./applied-patches の最終行を除くことで解決できる.もしくは "refpatch ; poppatch ; rm patches/troublesome-patch.patch ; cvs up patches".
もしホンマひどい時は,ツリー全体を吹っ飛ばして,新しく CVS checkout をする.
まぁ,同じ事.そう util-linux のコピーをダウンロードし,変更を加えようとする:
cd /usr/src tar xvfz ~/util-linux.tar.gz cd util-linux mkdir pc patches txt fpatch my-patch sys-utils/rdev.c fpatch sys-utils/ipcs.8 <編集,編集> refpatch <patches/my-patch.patch ができる>
これはやり方の一つ. 20 個のパッチが当たっていて,うち 3 つ("p1","p6" と "p11")はいずれも "foo.c" を編集する.
ここで foo.c に変更を加える.
どのパッチにその変更を入れ込む?決める必要がある. "p6" ということにしよう.
"p11" が最高位のパッチである時に `refpatch' をすると変更は失われる.その差分は p11 に含まれる.
するべきは:
1: poppatch p6 <編集> refpatch pushpatch p11 <テスト>
(なぜ ccache がよさそうなのか見よ)
もしくは
2: <編集> <テスト> poppatch p6 <他のパッチがきれいに削除される地獄のようなものを期待せよ> refpatch
それ以外のひどいミスの方法はごまかしである. "ちょうどこの 1 行修正をしたかった".そして,"そう,これも".
だんだん混乱してきている.ただシステムを使うだけよりははるかにいいい.
fpatch junk file1 fpatch file2 <編集> <試す> refpatch poppatch rm pc/junk.pc patches/junk.patch
これはまだ試してないけど,きっと動くに違いない:
さぁ,本格的に動き出した.これのよいことは,すでに持っている差分に対する増加分の差分送れるということである.
その他何でも.今や差分を適切に扱える.拒否されることは予想してくれ."差分ひとつでひとつのこと" が好ましい.
訳者注: patch-script のダウンロード http://www.zip.com.au/~akpm/linux/patches/. 20050423 現在の最新版は 0.18.(本文へ戻る)
訳者注: 20050423 現在最新版の 0.18 では ADD_SIGNED_OFF_BY を参照している箇所はないため,機能しないと思われる.(本文へ戻る)