hogehoge foobar Blog Style Beta

Web,Mac,Linux,JavaScript,Perl,PHP,RegExp,Git,Vim,Redmineなど技術的なことのメモや、ちょっと便利そうなものの紹介をしています。

diff & patch コマンドでのパッチを適用する方法

diffとpatchコマンドを使うとファイルの変更を別のファイルに簡単に適用することができます。
例えば、サイトでWEBサーバーを複数台で運用していて1台のWEBサーバーに行った設定ファイルの変更を他のサーバーに適用するときなどに使えます。

基本的な作業の流れ

ファイルへのパッチ適用を行う際はこんな感じです。

  1. diffコマンドでパッチファイルを作成
  2. 作成したパッチファイルを元に、patchコマンドでパッチを適用

パッチファイルの作成

パッチファイルの作成にはdiffコマンドを使用します。コマンドの書式は以下のような形になります。

$ diff -u(または -c) 変更前のファイル 変更後のファイル > パッチファイル名

オプションの「-u」はパッチファイルを、unified diff形式で出力するという指定です。
「-c」の場合はcontext diff形式での出力になります。
「-u(または -c)」無しでもパッチファイルの作成は可能ですが、パッチ適用の時にパッチを適用するファイル名などを別途指定する必要があるので、「-u(または -c)」は指定したほうがラクです。
ここでは、オプション「-u」を使っていきます。

パッチファイル作成のサンプル

例として以下のような2ファイル(test_before.txt、test_after.txt)を元にパッチファイルを作成してみます。










test_before.txt(変更前) test_after.txt(変更前)

1.Perl
2.Java
3.PHP
4.Ruby
5.SQL

1.Perl
2.C++
3.PHP
4.Ruby
5.SQL
6.JavaScript

diffコマンドでパッチファイルを作成したあと、パッチファイルの中身を確認してみます。

$ diff -u test_before.txt test_after.txt > test.patch
$ cat test.patch
--- test_before.txt      2010-10-19 23:23:07.735375000 +0900
+++ test_after.txt      2010-10-19 23:35:11.360375000 +0900
@@ -1,5 +1,6 @@
 1.Perl
-2.Java
+2.C++
 3.PHP
 4.Ruby
 5.SQL
+6.JavaScript

「-2.Java」「+2.C++」のような記述がありますが、

  • 先頭に「-」が付いている → 変更前のファイル(test_before.txt)にのみあった記述。
  • 先頭に「+」が付いている → 変更後のファイル(test_after.txt)で追加または変更になった記述。

という意味になります。
パッチファイルが正常に作成出来たので次はパッチを適用してみます。

パッチの適用

パッチの適用にはpatchコマンドを使用します。コマンドの書式は以下のような形になります。

$ patch [-u(または -c)] [適用するファイル名] < パッチファイル名

オプションの「-u」はパッチファイルを、unified diff形式のパッチを適用するという意味になります。(diffと同様に「-c」の場合はcontext diff形式での適用となります。)
「適用するファイル名」については省略することが出来ます。省略した場合はパッチファイルの先頭の行「--- ファイル名」のファイルにパッチが適用されます。

パッチ適用のサンプル

では、先程作成したパッチファイルを元にパッチを適用してみます。

$ patch -u < test.patch
patching file test_before.txt

今回はファイル名を省略したので、「patching file test_before.txt」という形で「test_before.txt」にパッチが適用されました。
本当にパッチが適用されたかを確認してみます。

diffコマンドで比較したファイルが同じ内容の場合、通常は何もメッセージが表示されませんが、オプション「-s」を指定することでファイルが同じ内容の場合にも明示的にメッセージを表示させる事がきます。

$ diff -s -u test_before.txt test_after.txt
Files test_before.txt and test_after.txt are identical

「test_before.txt」と「test_after.txt」の内容が同じになっており、パッチが適用された事が確認できました。

パッチ適用を戻す(リバースパッチ)

間違ってパッチを適用してしまった場合など、patchコマンドにオプション「-R」を指定することでパッチ適用を戻す(リバースパッチ)ことが出来ます。

$ patch -R [適用するファイル名] < パッチファイル名

パッチ適用戻し(リバースパッチ)のサンプル

では、先程適用したパッチを元の戻してみます。

$ patch -u -R < test.patch
patching file test_before.txt

パッチを元に戻したあと、先程と同じようにdiffコマンドでファイルを比較してみると、一番初めに作成したパッチファイルと同様の内容が表示され、パッチ適用が元に戻っていることが確認できました。

$ diff -s -u test_before.txt test_after.txt
--- test_before.txt      2010-10-19 23:31:26.672875000 +0900
+++ test_after.txt      2010-10-19 23:35:11.360375000 +0900
@@ -1,5 +1,6 @@
 1.Perl
-2.Java
+2.C++
 3.PHP
 4.Ruby
 5.SQL
+6.JavaScript

ちなみに、パッチ適用を元に戻す場合、オプション「-R」を指定しなくてもpatchコマンドが自動でパッチ適用の有無を判定したあとにパッチ適用を元に戻すか(リバースパッチするか?)を聞いてきます。ここで「y」を選択すると、上記と同じようにパッチ適用が元に戻されます。

$ patch < test.patch
patching file test_befor.txt
Reversed (or previously applied) patch detected!  Assume -R? [n] y

ディレクトリへのパッチ

パッチファイル作成/パッチ適用は単一のファイルだけで無く、ディレクトリ単位でも行う事が可能です。作業の流れや使用するコマンドは単一ファイルへのパッチと同じです。
但し、diff/patchコマンドを使用する際に一部オプションの指定が必要となります。

パッチファイルの作成(ディレクトリ)

ディレクトリ単位でのパッチファイルを作成する場合、diffコマンドにオプション「-r」を指定します。
「-r」は「サブディレクトリを再帰的にたどる」という指定になります。

$ diff -u(または -c) -r 変更前のディレクトリ 変更後のディレクトリ > パッチファイル名

パッチファイル作成のサンプル(ディレクトリ)

ここではtest_beforeディレクトリとtest_afterディレクトリを比較して、dir_test.patchというパッチファイルを作成してみます。

$ diff -u -r test_before test_after > dir_test.patch

dir_test.patchの中身を見てみると、「dirA/test_A.txt」と「dirA/dirB/test_B.txt」というファイルが変更されていて、「dirA/dirB/test_C.txt」というファイルが新しく作成されていることがわかります。

$ cat dir_test.patch
diff -u -r test_before/dirA/dirB/test_B.txt test_after/dirA/dirB/test_B.txt
--- test_before/dirA/dirB/test_B.txt    2010-10-19 23:39:43.704125000 +0900
+++ test_after/dirA/dirB/test_B.txt     2010-10-19 23:40:17.297875000 +0900
@@ -1,3 +1,3 @@
 1.Perl
-2.C++
+2.Java
 3.PHP
Only in test_after/dirA/dirB: test_C.txt
diff -u -r test_before/dirA/test_A.txt test_after/dirA/test_A.txt
--- test_before/dirA/test_A.txt 2010-10-19 23:39:43.704125000 +0900
+++ test_after/dirA/test_A.txt  2010-10-19 23:40:12.735375000 +0900
@@ -1,3 +1,3 @@
 1.Perl
-2.C++
+2.Java
 3.PHP

パッチの適用(ディレクトリ)

パッチの適用にはpatchコマンドを使用します。
コマンドの書式は以下のような形になります。

$ patch [-u(または -c)] -p数 -d 適用するディレクトリ名 < パッチファイル名

「-p数」はパッチファイルで見つかったファイルパスから数で指定された分のパス(プレフィックス)を取り除きます。
例えば、「test_before/dirA/test_A.txt」で「-p1」と指定した場合は、「dirA/test_A.txt」というパスでパッチが適用されます。
「-d」はパッチを適用するディレクトリ名を指定するオプションです。

パッチ適用のサンプル(ディレクトリ)

では、先程作成したパッチファイルを元にディレクトリに対してパッチを適用してみます。

$ patch -p1 -d test_before < dir_test.patch
patching file dirA/dirB/test_B.txt
patching file dirA/test_A.txt

単一ファイルへのパッチと同じようにdiffコマンドでパッチが適用されたかを確認してみます。

$ diff -r -s test_before test_after
Files test_before/dirA/dirB/test_B.txt and test_after/dirA/dirB/test_B.txt are identical
Only in test_after/dirA/dirB: test_C.txt
Files test_before/dirA/test_A.txt and test_after/dirA/test_A.txt are identical

「dirA/test_A.txt」と「dirA/dirB/test_B.txt」は同じ内容になりましたが、「dirA/dirB/test_C.txt」は作成されていないままです。
diff+patchでのパッチ適用では、新規で作成したファイルの追加は行われません。
パッチが適用されるのは、あくまでも「存在するファイルのだけ」になります。

パッチ適用を戻す(リバースパッチ)(ディレクトリ)

単一ファイルと同じように、patchコマンドにオプション「-R」を指定することでパッチ適用を戻す(リバースパッチ)ことが出来ます。

$ patch -R -p数 -d 適用するディレクトリ名 < パッチファイル名

パッチ適用戻し(リバースパッチ)のサンプル(ディレクトリ)

では、先程適用したパッチを元の戻してみます。

$ patch -R -p1 -d test_before < dir_test.patch
patching file dirA/dirB/test_B.txt
patching file dirA/test_A.txt

パッチを元に戻したあと、先程と同じようにdiffコマンドでファイルを比較してみると、一番初めに作成したパッチファイルと同様の内容が表示され、パッチ適用が元に戻っていることが確認できました。

$ diff -u -r -s test_before test_after
diff -u -r -s test_before/dirA/dirB/test_B.txt test_after/dirA/dirB/test_B.txt
--- test_before/dirA/dirB/test_B.txt    2010-10-19 23:33:35.407250000 +0900
+++ test_after/dirA/dirB/test_B.txt     2010-10-19 23:40:17.297875000 +0900
@@ -1,3 +1,3 @@
 1.Perl
-2.C++
+2.Java
 3.PHP
Only in test_after/dirA/dirB: test_C.txt
diff -u -r -s test_before/dirA/test_A.txt test_after/dirA/test_A.txt
--- test_before/dirA/test_A.txt 2010-10-19 23:33:35.407250000 +0900
+++ test_after/dirA/test_A.txt  2010-10-19 23:40:12.735375000 +0900
@@ -1,3 +1,3 @@
 1.Perl
-2.C++
+2.Java
 3.PHP

今回参考にしたページ

Manpage of PATCH
http://www.linux.or.jp/JM/html/GNU_patch/man1/patch.1.html

UNIXの部屋 コマンド検索 patch ( BSD Linux)
http://x68000.q-e-d.net/~68user/unix/pickup?patch

patchコマンド-pオプションの覚え書き - ザリガニが見ていた...。
http://d.hatena.ne.jp/zariganitosh/20080325/1206435474

024.diffコマンド ファイルの相違点を抽出する:Linuxコマンド
http://xn--linux-op4dtfrgoh.com/01linux/024diff.html

command - Dear sirs ,Toru
http://toru.twpg.jp/?cat=28&paged=3