Linuxのパッチの作り方、パッチの当て方。それぞれ整理しておきます。
  • パッチを作る方法
コマンド実行例

1つのソースコードに対する差分ファイルを作成する
diff -up [オリジナルソース] [修正後ソース] > 差分ファイル

ディレクトリ以下のソースコードに対する差分ファイルを作成する
diff -uprN [オリジナルソースディレクトリ] [修正後ソースディレクトリ] > 差分ファイル

Linuxカーネルのドキュメントフォルダ(Documentation/)にSubmittingPatchesというドキュメントがあり、このドキュメントの1章にUse "diff -up" or "diff -uprN" to create patches.  git generates patches in this form by default; if you're using git, you can skip this section entirely.と書いてあるらしい。
オプション:意味

u:unified形式で出力する
p:変更箇所の関数名(C言語)を表示する
r:サブディレクトリを再帰的に比較する
N:比較するファイルが無い場合、同名の空ファイルがあるのと同じ動作をする
  • 差分ファイルを作成してみる
修正前のフォルダはこんな感じ。
before
+a
  a.txt
+b
  b.txt
+c
  c.txt
修正後のフォルダはこんな感じ。緑字が変更箇所。
after
+a
  a.txt 修正
+bbb リネーム
  +bbb.txt リネーム&修正
+c 配下のファイルを削除
+d 新規
  d.txt 新規
差分ファイルを作成する。
$ diff -uprN before after > test.patch
$ vi test.patch
diff -uprN before/a/a.txt after/a/a.txt
--- before/a/a.txt  2019-07-21 23:09:28.089420189 +0900
+++ after/a/a.txt   2019-07-21 23:10:30.037418387 +0900
@@ -1,2 +1,4 @@
+12345678901234567890
 1234567890

+
diff -uprN before/b/b.txt after/b/b.txt
--- before/b/b.txt  2019-07-21 23:09:40.381419832 +0900
+++ after/b/b.txt   2019-07-21 23:10:54.201417684 +0900
@@ -1 +1,2 @@
-abcdefg
+abcdefghijklmnop
+
diff -uprN before/c/c.txt after/c/c.txt
--- before/c/c.txt  2019-07-21 23:09:52.717419473 +0900
+++ after/c/c.txt   1970-01-01 09:00:00.000000000 +0900
@@ -1,2 +0,0 @@
-あいうえお
-
diff -uprN before/d/d.txt after/d/d.txt
--- before/d/d.txt  1970-01-01 09:00:00.000000000 +0900
+++ after/d/d.txt   2019-07-21 23:11:20.217416927 +0900
@@ -0,0 +1,3 @@
+あいうえお
+かきくけこ
+
  • 差分ファイル(パッチ)を適用する

コマンド実行例

1つのファイルに対する差分ファイル適用
patch 元のファイル 差分ファイル

-u、-c形式で作成した(ファイル名情報を持つ)差分ファイルからパッチを当てる。
-p数字については次に詳しく説明するが、数字の数だけディレクトリ名を無視できるらしい。
patch -p数字 < 差分ファイル
①まず-p0で失敗

先ほどdiff -uprN before after > test.patchで作成した差分ファイルは、こんな配置になっていた。
$ ls
after  before  test.patch
このディレクトリでパッチを適用してみた。-p0はディレクトリ名を無視せずそのまま使うことを意味する。私はbeforeにパッチが当たりafterと同じ内容になるという誤解をしていた。結果は失敗。
$ patch -p0 < test.patch
patching file after/a/a.txt
Hunk #1 succeeded at 2 (offset 1 line).
patching file after/b/b.txt
Reversed (or previously applied) patch detected!  Assume -R? [n]
Apply anyway? [n]
Skipping patch.
1 out of 1 hunk ignored -- saving rejects to file after/b/b.txt.rej
patching file before/c/c.txt
The next patch would create the file after/d/d.txt,
which already exists!  Assume -R? [n]
Apply anyway? [n]
Skipping patch.
1 out of 1 hunk ignored

まず、パッチ適用後のファイルafter/a/a.txtに対して更にパッチが当たってしまい、12345678901234567890がもう1行増えてしまった。
$ vi after/a/a.txt
12345678901234567890
12345678901234567890
1234567890
次にafter/b/b.txtへのパッチ適用はReversed (or previously applied) patch detected!  というエラーが検出されパッチ適用がスキップされた。(要はafter/b/b.txtがパッチ適用後の内容と一致したためpatchコマンドがオカシイと気づいてくれた)

次にafter/c/c.txtが無いためbefore/c/c.txtにパッチが適用されたがディレクトリcもろとも消えた。

最後にafter/d/d.txtはパッチ適用前は存在しないハズなのに存在してるからスキップされた。

悲惨な状況である。こんなのが開発プロジェクトで発生したら恐怖であるが、これはプライベートなので何度失敗しても良いのだ。

②失敗原因を考えてみた

①の失敗原因を分析した。【原因1】パッチ適用先のディレクトリが2つあったこと、【原因2】パッチ適用対象のディレクトリは昇順に検索されること。の2つが考えられる。

まず【原因1】の検証。afterを削除してパッチ適用してみた。【結果】パッチ適用が成功した。
$ ls
after  before  test.patch
$ rm -r after
$ ls
before test.patch
$ patch -p0 < test.patch
patching file before/a/a.txt
patching file before/b/b.txt
patching file before/c/c.txt
patching file before/d/d.txt
次に【原因2】の検証。afterをbに変更。beforeをaに変更してパッチ適用してみた。【結果】パッチ適用が失敗した。ディレクトリ名を変えてしまったのでファイルが見つからなくなったみたい。-p0を指定する限りディレクトリ名は変えてはならぬということだ。
$ ls
after  before  test.patch
$ mv after b
$ mv before a
$ ls
a  b  test.patch
$ patch -p0 < test.patch
can't find file to patch at input line 4
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|diff -uprN before/a/a.txt after/a/a.txt
|--- before/a/a.txt     2019-07-21 23:09:28.089420189 +0900
|+++ after/a/a.txt      2019-07-21 23:10:30.037418387 +0900
--------------------------
File to patch:
Skip this patch? [y]
Skipping patch.
1 out of 1 hunk ignored
can't find file to patch at input line 12
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|diff -uprN before/b/b.txt after/b/b.txt
|--- before/b/b.txt     2019-07-21 23:09:40.381419832 +0900
|+++ after/b/b.txt      2019-07-21 23:10:54.201417684 +0900
--------------------------
File to patch:
Skip this patch? [y]
Skipping patch.
1 out of 1 hunk ignored
The next patch would delete the file after/c/c.txt,
which does not exist!  Assume -R? [n]
Apply anyway? [n]
Skipping patch.
1 out of 1 hunk ignored
patching file after/d/d.txt
③というわけで-p1を試してみたが、1回目は失敗した。

まぁ私は要領が悪いので良く失敗する。以下のようにパッチを当てようとしたら失敗。今回の失敗要因は簡単だった。-p1を指定したので差分ファイル内に記載されたディレクトリ階層の1階層目であるafter、beforeが削除された。要は「after/a/a.txt」が「a/a.txt」となったわけだがpatchコマンドを実行したディレクトリには、a、b、cのディレクトリがないため、結果ファイルが見つからない。
$ ls
after  before  test.patch
$ rm -r after
$ mv before zzz
$ ls
zzz  test.patch
$ patch -p1 < test.patch
can't find file to patch at input line 4
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|diff -uprN before/a/a.txt after/a/a.txt
|--- before/a/a.txt     2019-07-21 23:09:28.089420189 +0900
|+++ after/a/a.txt      2019-07-21 23:10:30.037418387 +0900
--------------------------
File to patch:
Skip this patch? [y]
Skipping patch.
1 out of 1 hunk ignored
can't find file to patch at input line 12
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|diff -uprN before/b/b.txt after/b/b.txt
|--- before/b/b.txt     2019-07-21 23:09:40.381419832 +0900
|+++ after/b/b.txt      2019-07-21 23:10:54.201417684 +0900
--------------------------
File to patch:
Skip this patch? [y]
Skipping patch.
1 out of 1 hunk ignored
The next patch would delete the file c/c.txt,
which does not exist!  Assume -R? [n]
Apply anyway? [n]
Skipping patch.
1 out of 1 hunk ignored
patching file d/d.txt

④というわけで-p1でパッチ適用を成功させるためにはこう打てばよい。

フォルダ名を変えたりする小細工は不要。パッチを適用したいbeforeにディレクトリ移動(cd)し、1つ上の階層のパッチファイルを指定すればよいのだ。ここまで来るのにちょっと苦労したが、パッチの仕組みについて良く分かったので良しとしよう。
$ ls
after  before  test.patch
$ cd before
$ patch -p1 < ../test.patch
patching file a/a.txt
patching file b/b.txt
patching file c/c.txt
patching file d/c.txt

これまで、パッチ適用の仕組みがイマイチ分かってなくて、誰かが作った手順書通りにやってましたが、これからは迷いなくパッチ適用が出来そうです。良かった良かった。

続く
IMG_20190720_103751

スポンサードリンク