Linux

【Linuxコマンド】パッチを使いこなしたい

Linuxのパッチの作り方、パッチの当て方がイマイチわかってなくて。

それぞれ整理しておきました。

この記事で解決できること

  • Linuxパッチの基礎がわかる。

関連記事

【PC】Linuxビルドのノウハウ(ビルドとインストール)

【PC】Linuxビルドのノウハウ(残課題)

【WSL】WindowsでLinuxを動かす

【Yocto】組み込みLinuxの主流はYocto

【Linuxコマンド】便利な小技たち(TIPS)

【ネットワーク】WindowsからUbuntu14.04に接続する方法2つ

【Linuxコマンド】パッチを使いこなしたい

【UNIX時間】2038年問題について整理する

パッチを作る方法

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 新規

差分ファイルを作成する。

 command
$ 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 @@
+あいうえお
+かきくけこ
+

差分ファイル(パッチ)を適用する

【 patch 】コマンド――テキストファイルに差分を適用する(基本編)を読んでみた。

コマンド実行例

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

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

①まず-p0で失敗

先ほどdiff -uprN before after > test.patchで作成した差分ファイルは、こんな配置になっていた。

 command
$ ls
after before test.patch

このディレクトリでパッチを適用してみた。-p0はディレクトリ名を無視せずそのまま使うことを意味する。私はbeforeにパッチが当たりafterと同じ内容になるという誤解をしていた。結果は失敗。

 command
$ 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行増えてしまった。

 command
$ 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を削除してパッチ適用してみた。【結果】パッチ適用が成功した。

 command
$ 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を指定する限りディレクトリ名は変えてはならぬということだ。

 command
$ 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のディレクトリがないため、結果ファイルが見つからない。

 command
$ 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つ上の階層のパッチファイルを指定すればよいのだ。ここまで来るのにちょっと苦労したが、パッチの仕組みについて良く分かったので良しとしよう。

 command
$ 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

まとめ

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

 

関連記事

【PC】Linuxビルドのノウハウ(ビルドとインストール)

【PC】Linuxビルドのノウハウ(残課題)

【VirtualBox】仮想OS環境にUbuntu18 LTS(Linux)をインストールする手順

【WSL】WindowsでLinuxを動かす

【Yocto】組み込みLinuxの主流はYocto

【Linuxコマンド】便利な小技たち(TIPS)

【ネットワーク】WindowsからUbuntu14.04に接続する方法2つ

【Linuxコマンド】パッチを使いこなしたい

【UNIX時間】2038年問題について整理する

  • この記事を書いた人
  • 最新記事

ペイヴメント

ペイヴメントのエンジニア塾(当ブログ)では20年以上の経験から得られたプログラミング系ノウハウについてベテランにも満足して頂けるような内容の濃いコンテンツを初心者にも分かりやすい形で日々発信しています。【経歴】ベンチャーのソフトハウスで4年勤務後、精密機器メーカーのソフト開発部門に勤務し今に至ります。

-Linux

Copyright© ペイヴメントのエンジニア塾 , 2020 All Rights Reserved.